题目1:输入范围不限
输入 flag,屏幕上出现“恭喜你!”则为正确 flag
test1.apk
使用 jadx 打开 apk,逻辑非常简单,我们看到其中有一串看起来像是 base64 的字符串,解码后尝试一下,成功。
◎ Java 层代码逻辑
◎ base64解码
◎ 成功
题目2:五位数字,比如 13579(分别用 frida 和 unidbg 来解题)
输入 flag,屏幕上出现“恭喜你!”则为正确 flag
求正确 flag
test2.apk
根据提示,flag 为五位数字,我们可以直接进行爆破。使用 jadx 分析 apk,代码逻辑与第一题类似,核心在于 native 层的 Sign() 函数,当返回值为 4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af 时即为正确的 flag。
解压 apk,得到 so 文件,使用 unidbg 进行加载,简单传入一个参数进行测试,发现报错。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  | 
package dev.svip.lessiontask;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.arm.backend.DynarmicFactory;
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 java.io.File;
public class Test2 extends AbstractJni {
    public static void main(String[] args) {
        Test2 test2 = new Test2();
        test2.crack();
    }
    private final AndroidEmulator androidEmulator;
    private final DvmClass dvmClass;
    private Test2(){
        androidEmulator = AndroidEmulatorBuilder.for32Bit().addBackendFactory(new DynarmicFactory(true)).build();
        Memory memory = androidEmulator.getMemory();
        LibraryResolver resolver = new AndroidResolver(23);
        memory.setLibraryResolver(resolver);
        VM vm = androidEmulator.createDalvikVM();
        vm.setJni(this);
        vm.setVerbose(false);
        File libfile = new File("unidbg-android/src/test/resources/my_binaries/armeabi-v7a/libroysue.so");
        DalvikModule dalvikModule = vm.loadLibrary(libfile, false);
        dalvikModule.callJNI_OnLoad(androidEmulator);
        dvmClass = vm.resolveClass("com/roysue/easyso1/MainActivity");
    }
    private void crack(){
        DvmObject<?> dvmObject = dvmClass.callStaticJniMethodObject(androidEmulator, "Sign(Ljava/lang/String;)Ljava/lang/String;", "12345");
        System.out.println(dvmObject.getValue());
    }
}
  | 
 
 
◎ 报错信息
很熟悉的报错内容,找不到这个变量,我们在代码中补全即可。
1
2
3
4
5
6
7
  | 
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
	if (signature.equals("android/os/Build->FINGERPRINT:Ljava/lang/String;")) {
		return new StringObject(vm, "abcd");
	}
	return super.getStaticObjectField(vm, dvmClass, signature);
}
  | 
 
 
爆破代码如下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  | 
private void crack() {
    for (int i = 0; i < 99999; i++) {
        String value = String.format("%05d", i);
        DvmObject<?> dvmObject = dvmClass.callStaticJniMethodObject(androidEmulator, "Sign(Ljava/lang/String;)Ljava/lang/String;", value);
        if (dvmObject.getValue().equals("4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af")) {
            System.out.println("flag found!!! => " + value);
            break;
        }
    }
}
  | 
 
 
◎ 获得 flag
同样使用爆破,frida 脚本如下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  | 
function crack() {
    Java.perform(function () {
        var MainActivity = Java.use("com.roysue.easyso1.MainActivity");
        for (var i = 0; i < 99999; i++){
            
            var str = `${i}`
            str = str.padStart(5, "0");
            if (i % 5000 == 0) {
                console.log("now is", str);
            }
            var result = MainActivity.Sign(str);
            if (result == "4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af") {
                console.warn("found flag:", str)
                break;
            }
        }
    })   
}
function main() {
    console.log("start crack...");
    crack();
}
setImmediate(main);
  | 
 
 
◎ frida 爆破成功
题目3:五位数字,比如:24680(分别用 frida 和 unidbg 来解题)
输入 flag,屏幕上出现“恭喜你!”则为正确 flag
要求得到正确 flag
test3.apk
使用 IDA 打开 so 文件,发现 Sign 方法进行了动态注册,对应的函数名为 fuck,里面加入了对环境的检测。
◎ 环境检测代码
我们在 Unidbg 中对前两个函数进行 hook,直接返回 0,将获取到的指纹设置为不包含 "aosp" 的任意字段,即可绕过检测,正常执行后面的计算逻辑,在题目二代码的基础上,最终代码修改如下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  | 
package dev.svip.lessiontask;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;
import com.github.unidbg.hook.hookzz.Dobby;
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 java.io.File;
public class Test3 extends AbstractJni {
    public static void main(String[] args) {
        Test3 test3 = new Test3();
        test3.crack();
    }
    private final AndroidEmulator androidEmulator;
    private final DvmClass dvmClass;
    private final Module module;
    private Test3() {
        androidEmulator = AndroidEmulatorBuilder.for32Bit().addBackendFactory(new DynarmicFactory(true)).build();
        Memory memory = androidEmulator.getMemory();
        LibraryResolver resolver = new AndroidResolver(23);
        memory.setLibraryResolver(resolver);
        VM vm = androidEmulator.createDalvikVM();
        vm.setJni(this);
        vm.setVerbose(false);
        File libfile = new File("unidbg-android/src/test/resources/my_binaries/armeabi-v7a/libroysue.so");
        DalvikModule dalvikModule = vm.loadLibrary(libfile, false);
        dalvikModule.callJNI_OnLoad(androidEmulator);
        dvmClass = vm.resolveClass("com/roysue/easyso1/MainActivity");
        module = dalvikModule.getModule();
    }
    private void crack() {
        Dobby dobby = Dobby.getInstance(androidEmulator);
        // hook function_check_tracerPID(),返回 0
        dobby.replace(module.findSymbolByName("_Z24function_check_tracerPIDv"), new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
                return HookStatus.LR(emulator, 0);
            }
        }, false);
        // hook system_getproperty_check(),返回 0
        dobby.replace(module.findSymbolByName("_Z24system_getproperty_checkv"), new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
                return HookStatus.LR(emulator, 0);
            }
        });
        for (int i = 0; i < 99999; i++) {
            String value = String.format("%05d", i);
            DvmObject<?> dvmObject = dvmClass.callStaticJniMethodObject(androidEmulator, "Sign(Ljava/lang/String;)Ljava/lang/String;", value);
            if (dvmObject.getValue().equals("57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b")) {
                System.out.println("flag found!!! => " + value);
                break;
            }
        }
    }
    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        if (signature.equals("android/os/Build->FINGERPRINT:Ljava/lang/String;")) {
            // 返回值不包含aosp
            return new StringObject(vm, "abcd");
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }
}
  | 
 
 
 
加入对检测方法的绕过,代码如下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  | 
function crack() {
    Java.perform(function () {
        var MainActivity = Java.use("com.roysue.easyso1.MainActivity");
        for (var i = 0; i < 99999; i++) {
            var str = `${i}`
            str = str.padStart(5, "0");
            if (i % 5000 == 0) {
                console.log("now is", str);
            }
            var result = MainActivity.Sign(str);
            if (result == "57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b") {
                console.warn("found flag:", str)
                break;
            }
        }
    })
}
function hookNative() {
    var roysue = Process.findModuleByName("libroysue.so");
    if (roysue) {
        var symbols = roysue.enumerateExports();
        var check = null;
        for (var i = 0; i < symbols.length; i++) {
            var symbol = symbols[i];
            //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
            if (symbol.name.indexOf("function_check_tracer") >= 0) {
                check = symbol.address;
                console.log("function_check_tracerPID is at ", symbol.address, symbol.name);
                Interceptor.attach(check, {
                    onEnter: function (args) {
                    }, onLeave: function (retval) {
                        console.log("function_check_tracer retval is => ", retval)
                        retval.replace(new NativePointer(0));
                    }
                })
            }
            if (symbol.name.indexOf("system_getproperty_check") >= 0) {
                check = symbol.address;
                console.log("function_check_tracerPID is at ", symbol.address, symbol.name);
                Interceptor.attach(check, {
                    onEnter: function (args) {
                    }, onLeave: function (retval) {
                        console.log("system_getproperty_check retval is => ", retval)
                        retval.replace(new NativePointer(0));
                    }
                })
            }
        }
    }
}
function main() {
    hookNative();
    console.log("start crack...");
    crack();
}
setImmediate(main);
  | 
 
 
 
题目4:在没有提示五位数字的情况下,解开题目 3 ,还原出算法。
输入 flag,屏幕上出现“恭喜你!”则为正确 flag
求得到正确 flag