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