赞
踩
首先用jadx打开apk文件,查看MainActivity可以发现,页面判断了MyApp.m这个类变量的值,并调用类work()这个函数,且当类变量m的值为0时会跳转到RegActivity注册页面
RegActivity界面比较简单,就是把输入的sn传入MyApp.saveSN()函数,然后退出,可以看出关键都在MyApp这个类。
所以我们继续查看MyApp这个类,发现类有三个native函数,所以需要进一步分析so文件。
### 二、IDA PRO分析
将对应的so文件拖到ida pro后通过Export栏可以发现有JNi_OnLoad函数,说明函数为动态注册,所以进入JNi_OnLoad函数查看注册的函数。
Tips: ida pro直接反编译的格式可能会很乱,这个时候可以把变量右键设置set item type设置成为JNIEnv*,然后许多函数都能解析出来类,就好看了很多(如果不知道设置哪一个变量,就把能试试的都试一便,总有一个能行_)
通过分析Onload函数可以发现注册函数在off_5044这个位置上,点击跳转后发现注册的函数名字符串找到了,但是函数名却是n1,n2,n3,可以对这些函数重命名,这样好看一点儿。
由此三个关键函数,work(), initSN(), saveSN()就找到了。
进入work函数,将传入的变量类型右键设置set lvar type设置为JNIEnv*后,F5反编译如下,可以看到该函数用getValue的方法获取了MyApp.m的值(getValue 函数用同样的方法进行反编译),然后将unk_2EFB或unk_2F25处的值赋给了V3,其中unk_2F25处的值啥也看不到,unk_2EFB处的值能看到有flag字样,应该和flag有关。最后该函数调用类callWork函数(反编译了一下,暂时没看懂,不过不重要)
因此work函数的主要逻辑就是判断MyApp.m的值是否为1,如果为1则赋值对应地址的值给V3,然后调用callWork。
进入initSN()函数分析其逻辑为:读取reg.dat的内容,如果内容为"EoPAoY62@ElRD",则MyApp.m的值设置为1,否则为0
进入saveSN()函数分析,首先修改变量类型,使得反编译更加人性化,一般变量第一个为JEIEnv*, 第二个参数jobject或者jclass, 后面的参数就是传入的native 函数中传入的参数,依次修改尝试就行。
通过分析代码,v10为数组的索引,从0-sn的长度,依次增长,然后将v10的值会在字符串的指定位置取一个值来与sn对应索引位置的字符串作异或运算。所以可以看出逻辑应该为,输入的sn的每一位和字符串"W3_arE_whO_we_ARE"的固定位置的字符进行了异或运算,然后输出到V8上,最后使用f_puts函数保存到文件中。(至于在"W3_arE_whO_we_ARE"取了那几位,不重要,反正异或运算可逆)
通过分析三个函数,可以看出该程序的整体调用思路为,work->initSN->saveSN,逻辑思路为:
通过jadx可以看出只有当MyApp.m的值为1时才算已注册,所以reg.dat的内容应该为"EoPAoY62@ElRD",而reg.dat的内容是根据输入sn与字符串"W3_arE_whO_we_ARE"通过异或的算法得出的,因此只要将"EoPAoY62@ElRD"与字符串"W3_arE_whO_we_ARE"做异或运算的算法,也能得出我们应该输入的sn,及输入"EoPAoY62@ElRD"进行注册就能得到应该输入的sn。
通过编写unidbg脚本,需要实现的函数有
整体代码如下
package com.hack; import com.github.unidbg.AndroidEmulator; import com.github.unidbg.Emulator; import com.github.unidbg.Module; import com.github.unidbg.arm.HookStatus; import com.github.unidbg.hook.HookContext; import com.github.unidbg.hook.ReplaceCallback; import com.github.unidbg.hook.hookzz.HookZz; import com.github.unidbg.linux.android.AndroidEmulatorBuilder; import com.github.unidbg.linux.android.AndroidResolver; import com.github.unidbg.linux.android.dvm.*; import com.github.unidbg.memory.Memory; import com.sun.jna.JNIEnv; import com.sun.jna.Pointer; import unicorn.ArmConst; import java.io.File; import java.util.ArrayList; import java.util.List; public class hack extends AbstractJni { private final AndroidEmulator emulator; private final VM vm; private final Module module; private DvmClass cNative; private hack () { emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.test").build(); final Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/hack/hack.apk")); DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/hack/libmyjni.so"), true); vm.setJni(this); vm.setVerbose(true); dm.callJNI_OnLoad(emulator); module = dm.getModule(); } @Override public void setStaticIntField(BaseVM vm, DvmClass dvmClass, String signature, int value) { switch (signature) { case "com/gdufs/xman/MyApp->m:I": System.out.println("> Patched: com/gdufs/xman/MyApp->m:I"); return; } super.setStaticIntField(vm, dvmClass, signature, value); } @Override public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) { switch (signature) { case "com/gdufs/xman/MyApp->m:I": System.out.println("> Patched: com/gdufs/xman/MyApp->m:I"); return 0; } return super.getStaticIntField(vm, dvmClass, signature); } @Override public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) { switch (signature) { case "com/gdufs/xman/MainActivity-><init>()V": System.out.println("> Patched: com/gdufs/xman/MainActivity-><init>()V"); return vm.resolveClass("com/gdufs/xman/MainActivity").newObject(null); } return super.newObject(vm, dvmClass, signature, varArg); } @Override public void callVoidMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) { switch (signature) { case "com/gdufs/xman/MainActivity->work(Ljava/lang/String;)V": System.out.println("> Patched: com/gdufs/xman/MainActivity->work(Ljava/lang/String;)V"); return; } super.callVoidMethod(vm, dvmObject, signature, varArg); } public static void main(String[] args) { hack test = new hack(); test.hookPuts(); test.hookWork(); test.saveSN(); test.work(); } private void saveSN() { List<Object> list = new ArrayList<>(10); list.add(vm.getJNIEnv()); list.add(0); list.add(vm.addLocalObject(new StringObject(vm, "201608Am!2333"))); // arg 3 Number number = module.callFunction(emulator, 0x000011F8+1, list.toArray()); } private void work() { DvmClass dvmClass = vm.resolveClass("com/gdufs/xman/MyApp"); String methodSign = "work()V"; DvmObject<?> dvmObject = dvmClass.newObject(null); DvmObject ret = dvmObject.callJniMethodObject(emulator, methodSign); Pointer pointer = emulator.getMemory().pointer(module.base + 0x00002EEB); System.out.println("> Pointer:"+pointer.getString(0x10)); } private void hookPuts() { // hook saveSN中的f_puts函数 HookZz hook = HookZz.getInstance(emulator); hook.replace(module.base + 0x00002C3C+1, new ReplaceCallback() { @Override public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) { System.out.println("> onCall:f_puts()"); System.out.println("> arg0:"+context.getPointerArg(0).getString(0)); // 入参1 R0寄存器 return super.onCall(emulator, context,originFunction); } }, true); } private void hookWork() { HookZz hook = HookZz.getInstance(emulator); hook.replace(module.base + 0x000014AC, new ReplaceCallback() { @Override public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) { System.out.println("onCall work"); System.out.println(context.getPointerArg(0).getString(0)); // 入参1 R0寄存器 return super.onCall(emulator, context,originFunction); } @Override public void postCall(Emulator<?> emulator, HookContext context) { System.out.println("postCall work"); System.out.println(context.getPointerArg(0).getString(0)); // 入参1 R0寄存器 super.postCall(emulator, context); } }, true); } }
运行结果如下
根据结果可以发现work中的函数是提示flag格式的,格式为xman{……},而且输入的sn即是flag,然后我们本应该输入的sn由异或运算可以得出为。
所以最终flag为:xman{201608Am!2333}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。