一、目標(biāo)
最近又開始研究Unidbg了,費(fèi)了好大勁,沒有跑起來。今天就先找個(gè)軟柿子捏捏看。
今天的目標(biāo)是 之前研究的 某段子App簽名計(jì)算方法(一)
- 某段子App版本 5.5.10
二、步驟
先搭起框架來
在 /unidbg/unidbg-android/src/test/java/ 下面新建一個(gè) com/fenfei/test 包, 我們的例子都放在這個(gè)包下。
然后再創(chuàng)建一個(gè) RunZy 類
public class RunZy extends AbstractJni {
public static void main(String[] args) throws IOException {
// 1、需要調(diào)用的Apk文件所在路徑
String apkFilePath = "/Users/fenfei/Desktop/zy/cn.xxxxchuanxxxx.tieba_5.5.10_505100.apk";
// 2、需要調(diào)用函數(shù)所在的Java類完整路徑,比如a/b/c/d等等,注意需要用/代替.
String classPath = "com/izxxyxx/network/NetCrypto";
// 3、需要調(diào)用方法,再jadx中找到對(duì)應(yīng)的方法,然后點(diǎn)擊下面的Smail,復(fù)制方法的Smail代碼。
String methodSign = "sign(Ljava/lang/String;[B)Ljava/lang/String;";
RunZy runZyObj = new RunZy(apkFilePath, classPath);
runZyObj.destroy();
}
// ARM模擬器
private final ARMEmulator emulator;
// vm
private final VM vm;
// 載入的模塊
private final Module module;
private final DvmClass TTEncryptUtils;
/**
*
* @param apkFilePath 需要執(zhí)行的apk文件路徑
* @param classPath 需要執(zhí)行的函數(shù)所在的Java類路徑
* @throws IOException
*/
public RunZy(String apkFilePath, String classPath) throws IOException {
// 創(chuàng)建app進(jìn)程,包名可任意寫
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.fenfei.RunZy").build(); // 創(chuàng)建模擬器實(shí)例,要模擬32位或者64位,在這里區(qū)分
final Memory memory = emulator.getMemory(); // 模擬器的內(nèi)存操作接口
// 作者支持19和23兩個(gè)sdk
memory.setLibraryResolver(new AndroidResolver(23));
// 創(chuàng)建DalvikVM,利用apk本身,可以為null
vm = ((AndroidARMEmulator) emulator).createDalvikVM(new File(apkFilePath));
vm.setVerbose(true);
vm.setJni(this);
new AndroidModule(emulator, vm).register(memory);
// (關(guān)鍵處1)加載so,填寫so的文件路徑
DalvikModule dm = vm.loadLibrary("net_crypto", false);
// 調(diào)用jni
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
//emulator.traceCode(module.base, module.base + module.size);
// (關(guān)鍵處2)加載so文件中的哪個(gè)類,填寫完整的類路徑
TTEncryptUtils = vm.resolveClass(classPath);
}
/**
* 關(guān)閉模擬器
* @throws IOException
*/
private void destroy() throws IOException {
emulator.close();
System.out.println("emulator destroy...");
}
}
跑 native_init
從之前的分析我們知道,在執(zhí)行 sign函數(shù)之前,需要執(zhí)行 native_init
// runZyObj.initCall();
private void initCall(){
TTEncryptUtils.callStaticJniMethod(emulator,"native_init()V");
}
執(zhí)行一下
java.lang.UnsupportedOperationException: com/izxxyxx/common/base/BaseApplication->getAppContext()Landroid/content/Context;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:402)
這個(gè)報(bào)錯(cuò)好解決,我們重寫 callStaticObjectMethodV 來實(shí)現(xiàn)這個(gè)函數(shù)
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "com/izxxyxx/common/base/BaseApplication->getAppContext()Landroid/content/Context;":
return vm.resolveClass("android/content/Context", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(signature);
return super.callStaticObjectMethodV(vm,dvmClass,signature,vaList);
}
這個(gè) getAppContext 我們之前的文章實(shí)現(xiàn)過,這里就依葫蘆畫瓢。
再跑一下,Ok,native_init 算是跑過了。
執(zhí)行sign
通過之前的分析我們知道,sign的入?yún)⒂袃蓚€(gè),第一個(gè)參數(shù)是個(gè)字符串,實(shí)際是個(gè)url,第二個(gè)參數(shù)也是這個(gè)so里面的加密結(jié)果,一個(gè)buf。我們從hook結(jié)果里面找一個(gè)入?yún)硗嫱妗?/p>
String InBuf = "50027f7f7f7f8e8e8e8e8e1......";
String ret = runZyObj.getSign(methodSign
,new StringObject(runZyObj.vm, "https://zyadapi.izxxyxx.com/ad/popup_ad")
,hexStringToBytes(InBuf));
// Out Rc=v2-1ff7402d2b4fa9a4c39b3853262f18fd
System.out.printf("ret:%s ", ret);
/**
* 調(diào)用so文件中的指定函數(shù)
* @param methodSign 傳入你要執(zhí)行的函數(shù)信息,需要完整的smali語(yǔ)法格式的函數(shù)簽名
* @param args 是即將調(diào)用的函數(shù)需要的參數(shù)
* @return 函數(shù)調(diào)用結(jié)果
*/
private String getSign(String methodSign, Object ...args) {
// 使用jni調(diào)用傳入的函數(shù)簽名對(duì)應(yīng)的方法()
Object value = TTEncryptUtils.callStaticJniMethodObject(emulator, methodSign, args).getValue();
return value.toString();
}
再跑一下
java.lang.UnsupportedOperationException: android/content/Context->getClass()Ljava/lang/Class;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)
沒明白這個(gè) getClass 是干啥用的,不管了,先實(shí)現(xiàn)再說
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/content/Context->getClass()Ljava/lang/Class;":
return vm.resolveClass("java/lang/Class");
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}
繼續(xù)跑
java.lang.UnsupportedOperationException: java/lang/Class->getSimpleName()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)
這次是找我們要個(gè) getSimpleName 這個(gè)值是啥呀?我也不知道,后面我再教大家找這個(gè)值的方法,這里先寫死一個(gè)值吧。
case "java/lang/Class->getSimpleName()Ljava/lang/String;":
return new StringObject(vm, "izxxyxx");
繼續(xù)跑一下,
java.lang.UnsupportedOperationException: cn/xiaochuankeji/tieba/common/debug/AppLogReporter->reportAppRuntime(Ljava/lang/String;Ljava/lang/String;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticVoidMethodV(AbstractJni.java:576)
遇上 debug 之類的要敏感,這個(gè)報(bào)錯(cuò)后面分析的時(shí)候會(huì)用到。 這里我們就先實(shí)現(xiàn) reportAppRuntime
@Override
public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "cn/xiaochuankeji/tieba/common/debug/AppLogReporter->reportAppRuntime(Ljava/lang/String;Ljava/lang/String;)V":
return;
}
throw new UnsupportedOperationException(signature);
}
因?yàn)檫@個(gè)函數(shù)沒有返回值,所以我們直接return即可。 繼續(xù)跑......
java.lang.UnsupportedOperationException: android/content/Context->getFilesDir()Ljava/io/File;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)
要讀文件?先實(shí)現(xiàn)一把
case "android/content/Context->getFilesDir()Ljava/io/File;":
return vm.resolveClass("java/io/File");
繼續(xù)跑
java.lang.UnsupportedOperationException: java/lang/Class->getAbsolutePath()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)
獲取路徑?我們也給他實(shí)現(xiàn)一個(gè)
case "java/lang/Class->getAbsolutePath()Ljava/lang/String;":
return new StringObject(vm, "/sdcard");
再來
java.lang.UnsupportedOperationException: android/os/Debug->isDebuggerConnected()Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticBooleanMethodV(AbstractJni.java:154)
判斷是否被調(diào)試?這我哪能讓你得逞
@Override
public boolean callStaticBooleanMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "android/os/Debug->isDebuggerConnected()Z":
return Boolean.FALSE;
}
return super.callStaticBooleanMethodV(vm,dvmClass,signature,vaList);
}
必須是要告訴你,我根本木有在調(diào)試呀。
java.lang.UnsupportedOperationException: android/os/Process->myPid()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticIntMethodV(AbstractJni.java:174)
要pid?給你一個(gè)
@Override
public int callStaticIntMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "android/os/Process->myPid()I":
return 123;
}
return super.callStaticIntMethodV(vm,dvmClass,signature,vaList);
}
終極一跑
ret:v2-ABC1ff7402d2b4fa9a4c39b3853262f18fd
emulator destroy...
歐耶,結(jié)果出來了。
結(jié)果很憂傷
我們之前Hook的結(jié)果是 v2-1ff7402d2b4fa9a4c39b3853262f18fd 現(xiàn)在跑出來的結(jié)果是 v2-ABC1ff7402d2b4fa9a4c39b3853262f18fd , 不大對(duì)勁呀。
以結(jié)果輪英雄,我們可以多跑幾組,如果確定模擬執(zhí)行出來的結(jié)果都是 加上了固定的 ABC ,那也好辦,直接過濾掉就行。
但是我們是寫教程了,得搞明白。 怎么搞明白?模擬執(zhí)行的結(jié)果有些不對(duì)勁該怎么辦? 我們下回分解。
三、總結(jié)
Unidbg執(zhí)行純算法,那效果是剛剛的。就是這些不純的so,都玩C了,還非要和jave層勾勾搭搭,故意為難我們。
布衣暖菜根香詩(shī)書滋味長(zhǎng)
TIP: 本文的目的只有一個(gè)就是學(xué)習(xí)更多的逆向技巧和思路,如果有人利用本文技術(shù)去進(jìn)行非法商業(yè)獲取利益帶來的法律責(zé)任都是操作者自己承擔(dān),和本文以及作者沒關(guān)系。
關(guān)注微信公眾號(hào): 奮飛安全,最新技術(shù)干貨實(shí)時(shí)推送
本文摘自 :https://blog.51cto.com/u