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

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

一、目標

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

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

  • 某段子App版本 5.5.10

二、步驟

先搭起框架來

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

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

然后再創建一個 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、需要調用函數所在的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 需要執行的apk文件路徑
* @param classPath 需要執行的函數所在的Java類路徑
* @throws IOException
*/
public RunZy(String apkFilePath, String classPath) throws IOException {
// 創建app進程,包名可任意寫
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.fenfei.RunZy").build(); // 創建模擬器實例,要模擬32位或者64位,在這里區分
final Memory memory = emulator.getMemory(); // 模擬器的內存操作接口
// 作者支持19和23兩個sdk
memory.setLibraryResolver(new AndroidResolver(23));

// 創建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

從之前的分析我們知道,在執行 sign函數之前,需要執行 native_init

// runZyObj.initCall();
private void initCall(){
TTEncryptUtils.callStaticJniMethod(emulator,"native_init()V");
}

執行一下

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 來實現這個函數

@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 我們之前的文章實現過,這里就依葫蘆畫瓢。

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

執行sign

通過之前的分析我們知道,sign的入參有兩個,第一個參數是個字符串,實際是個url,第二個參數也是這個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文件中的指定函數
* @param methodSign 傳入你要執行的函數信息,需要完整的smali語法格式的函數簽名
* @param args 是即將調用的函數需要的參數
* @return 函數調用結果
*/
private String getSign(String methodSign, Object ...args) {
// 使用jni調用傳入的函數簽名對應的方法()
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 是干啥用的,不管了,先實現再說

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

繼續跑

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

繼續跑一下,

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

}

因為這個函數沒有返回值,所以我們直接return即可。 繼續跑......

java.lang.UnsupportedOperationException: android/content/Context->getFilesDir()Ljava/io/File;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)

要讀文件?先實現一把

case "android/content/Context->getFilesDir()Ljava/io/File;":
return vm.resolveClass("java/io/File");

繼續跑

java.lang.UnsupportedOperationException: java/lang/Class->getAbsolutePath()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:349)

獲取路徑?我們也給他實現一個

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 現在跑出來的結果是 v2-ABC1ff7402d2b4fa9a4c39b3853262f18fd , 不大對勁呀。

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

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

三、總結

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

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

布衣暖菜根香詩書滋味長

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

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

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

開通會員,享受整站包年服務
国产呦精品一区二区三区网站|久久www免费人咸|精品无码人妻一区二区|久99久热只有精品国产15|中文字幕亚洲无线码