當前位置:首頁 > IT技術 > 數(shù)據庫 > 正文

Unidbg模擬執(zhí)行某段子so實操教程(一) 先把框架搭起來
2022-04-19 11:15:19

一、目標

最近又開始研究Unidbg了,費了好大勁,沒有跑起來。今天就先找個軟柿子捏捏看。

今天的目標是 之前研究的 某段子App簽名計算方法(一)

  • 某段子App版本 5.5.10

二、步驟

先搭起框架來

Unidbg模擬執(zhí)行某段子so實操教程(一) 先把框架搭起來_Android

/unidbg/unidbg-android/src/test/java/ 下面新建一個 com/fenfei/test 包, 我們的例子都放在這個包下。

然后再創(chuàng)建一個 RunZy

public class RunZy extends AbstractJni {
public static void main(String[] args) throws IOException {
// 1、需要調用的Apk文件所在路徑
String apkFilePath = "/Users/fenfei/Desktop/zy/cn.xxxxchuanxxxx.tieba_5.5.10_505100.apk";
// 2、需要調用函數(shù)所在的Java類完整路徑,比如a/b/c/d等等,注意需要用/代替.
String classPath = "com/izxxyxx/network/NetCrypto";
// 3、需要調用方法,再jadx中找到對應的方法,然后點擊下面的Smail,復制方法的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進程,包名可任意寫
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.fenfei.RunZy").build(); // 創(chuàng)建模擬器實例,要模擬32位或者64位,在這里區(qū)分
final Memory memory = emulator.getMemory(); // 模擬器的內存操作接口
// 作者支持19和23兩個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);

// (關鍵處1)加載so,填寫so的文件路徑
DalvikModule dm = vm.loadLibrary("net_crypto", false);

// 調用jni
dm.callJNI_OnLoad(emulator);
module = dm.getModule();

//emulator.traceCode(module.base, module.base + module.size);

// (關鍵處2)加載so文件中的哪個類,填寫完整的類路徑
TTEncryptUtils = vm.resolveClass(classPath);
}

/**
* 關閉模擬器
* @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)

這個報錯好解決,我們重寫 callStaticObjectMethodV 來實現(xiàn)這個函數(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);
}

這個 getAppContext 我們之前的文章實現(xiàn)過,這里就依葫蘆畫瓢。

再跑一下,Ok,native_init 算是跑過了。

執(zhí)行sign

通過之前的分析我們知道,sign的入參有兩個,第一個參數(shù)是個字符串,實際是個url,第二個參數(shù)也是這個so里面的加密結果,一個buf。我們從hook結果里面找一個入參來玩玩。

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);


/**
* 調用so文件中的指定函數(shù)
* @param methodSign 傳入你要執(zhí)行的函數(shù)信息,需要完整的smali語法格式的函數(shù)簽名
* @param args 是即將調用的函數(shù)需要的參數(shù)
* @return 函數(shù)調用結果
*/
private String getSign(String methodSign, Object ...args) {
// 使用jni調用傳入的函數(shù)簽名對應的方法()
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)

沒明白這個 getClass 是干啥用的,不管了,先實現(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)

這次是找我們要個 getSimpleName 這個值是啥呀?我也不知道,后面我再教大家找這個值的方法,這里先寫死一個值吧。

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 之類的要敏感,這個報錯后面分析的時候會用到。 這里我們就先實現(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);

}

因為這個函數(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)

要讀文件?先實現(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)

獲取路徑?我們也給他實現(xiàn)一個

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)

判斷是否被調試?這我哪能讓你得逞

@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);
}

必須是要告訴你,我根本木有在調試呀。

java.lang.UnsupportedOperationException: android/os/Process->myPid()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticIntMethodV(AbstractJni.java:174)

要pid?給你一個

@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...

歐耶,結果出來了。

結果很憂傷

我們之前Hook的結果是 v2-1ff7402d2b4fa9a4c39b3853262f18fd 現(xiàn)在跑出來的結果是 v2-ABC1ff7402d2b4fa9a4c39b3853262f18fd , 不大對勁呀。

以結果輪英雄,我們可以多跑幾組,如果確定模擬執(zhí)行出來的結果都是 加上了固定的 ABC ,那也好辦,直接過濾掉就行。

但是我們是寫教程了,得搞明白。 怎么搞明白?模擬執(zhí)行的結果有些不對勁該怎么辦? 我們下回分解。

三、總結

Unidbg執(zhí)行純算法,那效果是剛剛的。就是這些不純的so,都玩C了,還非要和jave層勾勾搭搭,故意為難我們。

Unidbg模擬執(zhí)行某段子so實操教程(一) 先把框架搭起來_java_02

布衣暖菜根香詩書滋味長

TIP: 本文的目的只有一個就是學習更多的逆向技巧和思路,如果有人利用本文技術去進行非法商業(yè)獲取利益帶來的法律責任都是操作者自己承擔,和本文以及作者沒關系。

關注微信公眾號: 奮飛安全,最新技術干貨實時推送

本文摘自 :https://blog.51cto.com/u

開通會員,享受整站包年服務立即開通 >