一、目標
最近又開始研究Unidbg了,費了好大勁,沒有跑起來。今天就先找個軟柿子捏捏看。
今天的目標是 之前研究的 某段子App簽名計算方法(一)
- 某段子App版本 5.5.10
二、步驟
先搭起框架來
在 /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層勾勾搭搭,故意為難我們。
布衣暖菜根香詩書滋味長
TIP: 本文的目的只有一個就是學習更多的逆向技巧和思路,如果有人利用本文技術去進行非法商業(yè)獲取利益帶來的法律責任都是操作者自己承擔,和本文以及作者沒關系。
關注微信公眾號: 奮飛安全,最新技術干貨實時推送
本文摘自 :https://blog.51cto.com/u