Unidbg和Frida练习

第一题

题目1:输入范围不限

输入 flag,屏幕上出现“恭喜你!”则为正确 flag

test1.apk

使用 jadx 打开 apk,逻辑非常简单,我们看到其中有一串看起来像是 base64 的字符串,解码后尝试一下,成功。

image-20220117153005438◎ Java 层代码逻辑

image-20220117153129245◎ base64解码

image-20220117154729479◎ 成功

第二题

题目2:五位数字,比如 13579(分别用 frida 和 unidbg 来解题)

输入 flag,屏幕上出现“恭喜你!”则为正确 flag

求正确 flag

test2.apk

思路

根据提示,flag 为五位数字,我们可以直接进行爆破。使用 jadx 分析 apk,代码逻辑与第一题类似,核心在于 native 层的 Sign() 函数,当返回值为 4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af 时即为正确的 flag。

Unidbg

解压 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());
    }
}

image-20220117161533057◎ 报错信息

很熟悉的报错内容,找不到这个变量,我们在代码中补全即可。

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

image-20220117165932143◎ 获得 flag

Frida

同样使用爆破,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);

image-20220117213534707◎ frida 爆破成功

第三题

题目3:五位数字,比如:24680(分别用 frida 和 unidbg 来解题) 输入 flag,屏幕上出现“恭喜你!”则为正确 flag 要求得到正确 flag

test3.apk

Unidbg

使用 IDA 打开 so 文件,发现 Sign 方法进行了动态注册,对应的函数名为 fuck,里面加入了对环境的检测。

image-20220118103216369◎ 环境检测代码

我们在 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);
    }
}

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
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

updatedupdated2022-01-202022-01-20