攻防世界 黑客精神unidbg破解
一、Jadx分析
首先用jadx打开apk文件,查看MainActivity可以发现,页面判断了MyApp.m这个类变量的值,并调用类work()这个函数,且当类变量m的值为0时会跳转到RegActivity注册页面
RegActivity界面比较简单,就是把输入的sn传入MyApp.saveSN()函数,然后退出,可以看出关键都在MyApp这个类。
所以我们继续查看MyApp这个类,发现类有三个native函数,所以需要进一步分析so文件。
### 二、IDA PRO分析
(1)找关键函数
将对应的so文件拖到ida pro后通过Export栏可以发现有JNi_OnLoad函数,说明函数为动态注册,所以进入JNi_OnLoad函数查看注册的函数。
Tips: ida pro直接反编译的格式可能会很乱,这个时候可以把变量右键设置set item type设置成为JNIEnv*,然后许多函数都能解析出来类,就好看了很多(如果不知道设置哪一个变量,就把能试试的都试一便,总有一个能行_)
通过分析Onload函数可以发现注册函数在off_5044这个位置上,点击跳转后发现注册的函数名字符串找到了,但是函数名却是n1,n2,n3,可以对这些函数重命名,这样好看一点儿。
由此三个关键函数,work(), initSN(), saveSN()就找到了。
(2)分析关键函数
- work()函数分析
进入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()函数分析
进入initSN()函数分析其逻辑为:读取reg.dat的内容,如果内容为"EoPAoY62@ElRD",则MyApp.m的值设置为1,否则为0
- saveSN()函数分析
进入saveSN()函数分析,首先修改变量类型,使得反编译更加人性化,一般变量第一个为JEIEnv*, 第二个参数jobject或者jclass, 后面的参数就是传入的native 函数中传入的参数,依次修改尝试就行。
通过分析代码,v10为数组的索引,从0-sn的长度,依次增长,然后将v10的值会在字符串的指定位置取一个值来与sn对应索引位置的字符串作异或运算。所以可以看出逻辑应该为,输入的sn的每一位和字符串"W3_arE_whO_we_ARE"的固定位置的字符进行了异或运算,然后输出到V8上,最后使用f_puts函数保存到文件中。(至于在"W3_arE_whO_we_ARE"取了那几位,不重要,反正异或运算可逆)
(3)整体流程思路
通过分析三个函数,可以看出该程序的整体调用思路为,work->initSN->saveSN,逻辑思路为:
- (1) work函数:判断MyApp.m的值是否为1,然后赋值对应地址的值给v3
- (2) initSN函数:判断reg.dat的内容是否为 “EoPAoY62@ElRD”,若是则MyApp.m赋值为1
- (3) saveSN函数: 将输入的sn与"W3_arE_whO_we_ARE"做异或运算后保存到reg.dat中
通过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脚本编写
通过编写unidbg脚本,需要实现的函数有
- saveSN: 主动调用saveSN函数,传入sn参数
- f_puts: saveSN函数中的子函数,hook该函数可以得到运算后的字符串
- work: 打印work中的地址unk_2EFB查看是否有提示
整体代码如下
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}