r2pay-v0.9分析

r2pay-v0.9.apk

安装完后,一打开就闪退。

通过adb logcat查看日志,筛选中下面的日志记录。

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
02-28 10:34:38.788 11795 11795 I Magisk  : zygisk64: [re.pwnme] is on the denylist
02-28 10:34:38.823 11795 11795 I re.pwnme: Late-enabling -Xcheck:jni
02-28 10:34:38.845 1187 1204 I adbd : jdwp connection from 11795
02-28 10:34:38.867 11795 11795 D ProcessState: Binder ioctl to enable oneway spam detection failed: Invalid argument
02-28 10:34:38.785 0 0 I binder : 11795:11795 ioctl 40046210 7fe5e224b4 returned -22
02-28 10:34:38.881 11795 11795 D CompatibilityChangeReporter: Compat change id reported: 171979766; UID 10137; state: DISABLED
02-28 10:34:38.887 11795 11795 D ApplicationLoaders: Returning zygote-cached class loader: /system/framework/android.test.base.jar
02-28 10:34:38.909 11795 11795 V GraphicsEnvironment: ANGLE Developer option for 're.pwnme' set to: 'default'
02-28 10:34:38.909 11795 11795 V GraphicsEnvironment: ANGLE GameManagerService for re.pwnme: false
02-28 10:34:38.909 11795 11795 V GraphicsEnvironment: Neither updatable production driver nor prerelease driver is supported.
02-28 10:34:38.911 11795 11795 D NetworkSecurityConfig: No Network Security Config specified, using platform default
02-28 10:34:38.912 11795 11795 D NetworkSecurityConfig: No Network Security Config specified, using platform default
02-28 10:34:39.391 2400 3117 W FrameTracker: Missing HWUI jank callback for vsyncId: 84855
02-28 10:34:39.612 11795 11795 W re.pwnme: type=1400 audit(0.0:371): avc: denied { read } for name="cache" dev="sda14" ino=16 scontext=u:r:untrusted_app_29:s0:c137,c256,c512,c768 tcontext=u:object_r:cache_file:s0 tclass=lnk_file permissive=0 app=re.pwnme
02-28 10:34:39.612 11795 11795 W re.pwnme: type=1400 audit(0.0:372): avc: denied { read } for name="cache" dev="sda14" ino=16 scontext=u:r:untrusted_app_29:s0:c137,c256,c512,c768 tcontext=u:object_r:cache_file:s0 tclass=lnk_file permissive=0 app=re.pwnme
02-28 10:34:39.612 11795 11795 W re.pwnme: type=1400 audit(0.0:373): avc: denied { read } for name="cache" dev="sda14" ino=16 scontext=u:r:untrusted_app_29:s0:c137,c256,c512,c768 tcontext=u:object_r:cache_file:s0 tclass=lnk_file permissive=0 app=re.pwnme
02-28 10:34:39.678 11795 11795 W re.pwnme: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (unsupported, reflection, allowed)
02-28 10:34:39.678 11795 11795 W re.pwnme: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (unsupported, reflection, allowed)
02-28 10:34:39.725 11795 11795 E RootBeer: b: a() [249] - com.topjohnwu.magisk ROOT management app detected!
02-28 10:34:39.725 11795 11795 E QLog : b: a() [249] - com.topjohnwu.magisk ROOT management app detected!
02-28 10:34:39.726 11795 11795 D AndroidRuntime: Shutting down VM
02-28 10:34:39.727 11795 11795 E AndroidRuntime: FATAL EXCEPTION: main
02-28 10:34:39.727 11795 11795 E AndroidRuntime: Process: re.pwnme, PID: 11795
02-28 10:34:39.727 11795 11795 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{re.pwnme/re.pwnme.MainActivity}: java.lang.ArithmeticException: divide by zero
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3707)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.os.Looper.loop(Looper.java:288)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7870)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: Caused by: java.lang.ArithmeticException: divide by zero
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at re.pwnme.MainActivity.onCreate(SourceFile:38)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8057)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8037)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1341)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3688)
02-28 10:34:39.727 11795 11795 E AndroidRuntime: ... 12 more
02-28 10:34:39.730 1045 11817 I DropBoxManagerService: add tag=data_app_crash isTagEnabled=true flags=0x2
02-28 10:34:39.730 1045 5059 W ActivityTaskManager: Force finishing activity re.pwnme/.MainActivity
02-28 10:34:39.737 11795 11795 I Process : Sending signal. PID: 11795 SIG: 9

我把日志丢给grok进行分析,似乎是调用了RootBeer库进行root检测,发现了magisk,因此退出了。

image-20250228132546057

尝试搜索字符串 ROOT management app detected,这里的try catch就是用来检测root的,如果读包错误,就说明这个包不存在,不做任何处理;如果读到“危险”包,就会继续执行b.a.a.c.a.a,并将result置为true。

image-20250228134713528

追踪b.a.a.c.a.a进去看一下,发现就写了一个Log.e。

image-20250228140326772

查看方法b.a.a.b.a(List )的调用。

image-20250228140623670

Index为0的方法b.a.a.b.a(String[] )如下,属于重载,可以看到,这里只是将字符串加入到packages里,并调用b.a.a.b.a(List )。

image-20250228140601697

而Index为1的方法b.a.a.b.b(String[] )如下,它调用了b.a.a.b.a(String[] )。

image-20250228141911196

之后又一直查看引用,找到b.a.a.j(),这里做了一堆检查root的内容。

image-20250228142312579

不如直接将函数j给hook了,也省得hook其它这么多函数,我观察了一下众多this.X,发现其中的this.e不仅仅在j()被调用,还在其它地方被调用。

总结了一下,需要hook的函数如下:

b.a.a.a()/b.a.a.j()/b.a.a.e()。

首先hook方法a,一般来说,可以根据参数列表不同hook特定的、存在同名的方法,但这里要hook的方法a属于无参方法,而且存在两个无参方法a。

下面是我找ds-r1生成的,不知道行不行得通,看得挺靠谱的,根据返回值类型来判断。(事后发现下面的脚本用不了,别学,这里作为错误示范)

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
Java.perform(function() {
var targetClass = Java.use("b.a.a.b");
var methods = targetClass.class.getDeclaredMethods();

methods.forEach(function(method) {
if (method.getName() === "a" && method.getParameterTypes().length === 0) {
var returnType = method.getReturnType().getName();

// Hook返回boolean的a方法
if (returnType === "boolean") {
method.implementation = function() {
console.log("Hooked boolean a() method");
return this.a.apply(this, arguments);
};
}
// Hook返回String数组的a方法
else if (returnType === "[Ljava.lang.String;") {
method.implementation = function() {
console.log("Hooked String[] a() method");
return this.a.apply(this, arguments);
};
}
}
});
});

不过既然要hook的方法都在b.a.a类中,不妨直接将hook的方法补充进上面这个脚本里,执行后,又闪退了,不过这回查看日志,并没看到之前的那几条记录了。

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
02-28 15:10:41.848  4531  4531 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto
02-28 15:10:41.849 655 655 I tombstoned: received crash request for pid 4528
02-28 15:10:41.850 4531 4531 I crash_dump64: performing dump of process 4498 (target tid = 4528)
02-28 15:10:41.871 4531 4531 E DEBUG : failed to read /proc/uptime: Permission denied
02-28 15:10:42.485 0 0 I logd : logdr: UID=10137 GID=10137 PID=4531 n tail=0 logMask=8 pid=4498 start=0ns deadline=0ns
02-28 15:10:42.486 0 0 I logd : logdr: UID=10137 GID=10137 PID=4531 n tail=0 logMask=1 pid=4498 start=0ns deadline=0ns
02-28 15:10:42.498 4531 4531 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
02-28 15:10:42.498 4531 4531 F DEBUG : Build fingerprint: 'OnePlus/OnePlus6/OnePlus6:8.1.0/OPM1.171019.011/06140300:user/release-keys'
02-28 15:10:42.498 4531 4531 F DEBUG : Revision: '0'
02-28 15:10:42.498 4531 4531 F DEBUG : ABI: 'arm64'
02-28 15:10:42.498 4531 4531 F DEBUG : Timestamp: 2025-02-28 15:10:41.870217324+0800
02-28 15:10:42.498 4531 4531 F DEBUG : Process uptime: 0s
02-28 15:10:42.498 4531 4531 F DEBUG : Cmdline: com.google.android.videos
02-28 15:10:42.498 4531 4531 F DEBUG : pid: 4498, tid: 4528, name: re.pwnme >>> com.google.android.videos <<<
02-28 15:10:42.498 4531 4531 F DEBUG : uid: 10137
02-28 15:10:42.498 4531 4531 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xfaba4975
02-28 15:10:42.498 4531 4531 F DEBUG : x0 0000006fe4986670 x1 000000730a2a77cc x2 00000071ceef6220 x3 0000006fe4986618
02-28 15:10:42.498 4531 4531 F DEBUG : x4 00000000000010b0 x5 0000000000000001 x6 0000000000000000 x7 0000000000000000
02-28 15:10:42.498 4531 4531 F DEBUG : x8 00000000000035b2 x9 00000000faba4975 x10 000000008ef93fe9 x11 000000006df6246c
02-28 15:10:42.498 4531 4531 F DEBUG : x12 0000000000000001 x13 00000000fffffff6 x14 00000000cf86a786 x15 0000000000000001
02-28 15:10:42.499 4531 4531 F DEBUG : x16 000000730a2911f8 x17 000000730a20db40 x18 0000000000000000 x19 0000006fe4986670
02-28 15:10:42.499 4531 4531 F DEBUG : x20 0000000000000000 x21 0000006fe498acb0 x22 0000000000001192 x23 0000000000001192
02-28 15:10:42.499 4531 4531 F DEBUG : x24 0000006fe498acb0 x25 0000006fe498acb0 x26 0000006fe498aff8 x27 00000000000fc000
02-28 15:10:42.499 4531 4531 F DEBUG : x28 0000006fe4892000 x29 0000006fe498ac40
02-28 15:10:42.499 4531 4531 F DEBUG : lr 0000006fdffa2668 sp 0000006fe4986670 pc 0000006fdfe77f7c pst 0000000060000000
02-28 15:10:42.499 4531 4531 F DEBUG : backtrace:
02-28 15:10:42.499 4531 4531 F DEBUG : #00 pc 0000000000038f7c /data/app/~~IG0Dzbwr_V__IsLZxDqnLA==/re.pwnme-5cCb3aQP1IrcfKWzdCWgYQ==/lib/arm64/libnative-lib.so (BuildId: f87b3bd9fcae36e63939958f412d03a42e0ce406)
02-28 15:10:42.499 4531 4531 F DEBUG : #01 pc 00000000000b1810 /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (__pthread_start(void*)+264) (BuildId: 6bfaf10f10e5ff343703efae2f1bdbdb)
02-28 15:10:42.499 4531 4531 F DEBUG : #02 pc 00000000000512f0 /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (__start_thread+64) (BuildId: 6bfaf10f10e5ff343703efae2f1bdbdb)

查看调用栈,锁定在libnative-lib.so,偏移量是38f7c,用ida打开后,访问那块地址,结果说函数太大,无法进行f5,没辙了,没啥思绪了,看看博客。

https://blog.csdn.net/qq_61253776/article/details/140026070

看来我的第一步分析是正确的,将函数j a e进行了hook,避免了java层的检测,但so层好难。

接下来找博客一步一步做了。

So层的检测针对了root权限和frida注入,下面主要写分析过程。

在re.pwnme.MainActivity类中,静态初始化块加载了native-lib,

image-20250304081920396

然后声明了其中的一个native函数并用于MainActivity的页面代码中。

image-20250304082019387

通过ida观察libnative-lib.so的export表,导出函数和全局变量都被加密了。

image-20250304082137127

点开.datadiv_decodexxxxxx,大部分这类函数只写了一个RET,对应的字节码是C0 03 5F D6。

image-20250304082448246

根据观察,其中的函数datadiv_decode4432700155380705947,它存在函数体,并且大部分操作似乎在做解密。大部分资料显示,这里是在做全局字符串的解密操作。

image-20250304082642593

除此之外,在.init_array段中,存在两个拥有函数体的函数,它们无法f5,反编译成c。

image-20250304082900458

image-20250304082937873

过了java层后,会在so层的某处断开,地址是38f7c。

image-20250304083458534

在ida中来到38f7c,一路跟踪交叉引用,最后判定,这段代码属于sub_83DC。

由于静态无法分析,只能动态分析了,博客中,这里使用了工具QBDI,QBDI是一个动态二进制插桩的DBI框架,可以追踪函数细节,如果只用frida的话,只能看到函数调用前的参数和调用后的结果。

但QBDI咋用啊,wtf,教程也太少了。


时隔多天,在分析完豆瓣后,再次看看这个apk如何分析。

在分析豆瓣的过程中,学会了找退出点的一些方法。

比如:hook函数pthread_create,线程在跑起来后,如果是检测frida的线程,就会杀死frida,所以可以观察哪个线程跑起来后,导致frida退出了。因为libc.so是系统库,加载先于frida的spawn模式,无需担心过早hook。

对应的检测线程脚本如下,不是直接复制就能用,这里放在这仅供参考:

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
function hook_linker_call_constructors() {
let linker64_base_addr = Module.getBaseAddress('linker64');
let offset = 0x51BA8; // __dl__ZN6soinfo17call_constructorsEv
let call_constructors = linker64_base_addr.add(offset);
let listener = Interceptor.attach(call_constructors, {
onEnter: function(args) {
console.log('hook_linker_call_constructors onEnter');
let libnative_module = Process.findModuleByName("libnative-lib.so");

// 打印模块信息
if (libnative_module) {
console.log("Module name: " + libnative_module.name);
console.log("Base address: 0x" + libnative_module.base.toString(16));
console.log("Size: " + libnative_module.size);
console.log("Path: " + libnative_module.path);

check_pthread_create(libnative_module.base);
//hook_20954();
//hook_7a660();
listener.detach();
}

}
});
}

function check_pthread_create(baseaddr){
/* 找到函数pthread_create的地址 */
var pthread_create = Module.findExportByName("libc.so", "pthread_create");

if(pthread_create){
console.log("pthread_create is exist");
}else{
console.log("pthread_create is not exist");
}

/* hook */
Interceptor.attach(pthread_create, {
onEnter: function(args){
console.log("pthread_create is called");
console.log("arg2: 0x" + (args[2] - baseaddr).toString(16));
}
});
}

之后应该就会看到几个函数的地址,然后尝试hook它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
function hook_20954(){
let libnative_module = Process.findModuleByName("libnative-lib.so");
Interceptor.replace(libnative_module.base.add(0x20954), new NativeCallback(function () {
console.log(`hook_20954 >>>>>>>>>>>>>>>>> replace`);
}, 'void', []));
}

function hook_7a660(){
let libnative_module = Process.findModuleByName("libnative-lib.so");
Interceptor.replace(libnative_module.base.add(0x7a660), new NativeCallback(function () {
console.log(`hook_7a660 >>>>>>>>>>>>>>>>> replace`);
}, 'void', []));
}

然后再次跑frida脚本,发现提示除以0的报错,这个报错不是在之前的java层解决了吗?

下面这个脚本是错的,是我早期通过deepseek生成的,我还以为能用。

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
function hook_java() {
var targetClass = Java.use("b.a.a.b");
var methods = targetClass.class.getDeclaredMethods();

console.log("Methods found in b.a.a.b:");
methods.forEach(function(method) {
if (method.getName() === "a" && method.getParameterTypes().length === 0) {
var returnType = method.getReturnType().getName();
console.log("Return type: " + typeof returnType + ", value: " + returnType);
if (returnType === "boolean") {
method.implementation = function() {
console.log("b.a.a.b.a() is called, res = ", this.a());
console.log("Hooked boolean a() method");
return false;
};
}
}

if (method.getName() === "j" && method.getParameterTypes().length === 0) {
var returnType = method.getReturnType().getName();
if (returnType === "boolean") {
method.implementation = function() {
console.log("b.a.a.b.j() is called, res = ", this.j());
console.log("Hooked boolean j() method");
return false;
};
}
}

if (method.getName() === "e" && method.getParameterTypes().length === 0) {
var returnType = method.getReturnType().getName();
if (returnType === "boolean") {
method.implementation = function() {
console.log("b.a.a.b.e() is called, res = ", this.e());
console.log("Hooked boolean e() method");
return false;
};
}
}
});
}

将它修改成正常的、简单的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function hook_java() {
Java.perform(function() {

var targetClass = Java.use("b.a.a.b");

targetClass.a.overload().implementation = function() {
console.log("Hooked b.a.a.b.a()");
return false; // 强制返回 false
};

targetClass.j.overload().implementation = function() {
console.log("Hooked b.a.a.b.j()");
return false;
};

targetClass.e.overload().implementation = function() {
console.log("Hooked b.a.a.b.e()");
return false;
};
});
}

最后整理出来的脚本如下,执行完后能正常跑apk了。

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
85
86
87
88
89
90
91
function hook_java() {
Java.perform(function() {

var targetClass = Java.use("b.a.a.b");

targetClass.a.overload().implementation = function() {
console.log("Hooked b.a.a.b.a()");
return false; // 强制返回 false
};

targetClass.j.overload().implementation = function() {
console.log("Hooked b.a.a.b.j()");
return false;
};

targetClass.e.overload().implementation = function() {
console.log("Hooked b.a.a.b.e()");
return false;
};
});
}

function hook_linker_call_constructors() {
let linker64_base_addr = Module.getBaseAddress('linker64');
let offset = 0x51BA8; // __dl__ZN6soinfo17call_constructorsEv
let call_constructors = linker64_base_addr.add(offset);
let listener = Interceptor.attach(call_constructors, {
onEnter: function(args) {
console.log('hook_linker_call_constructors onEnter');
let libnative_module = Process.findModuleByName("libnative-lib.so");

// 打印模块信息
if (libnative_module) {
console.log("Module name: " + libnative_module.name);
console.log("Base address: 0x" + libnative_module.base.toString(16));
console.log("Size: " + libnative_module.size);
console.log("Path: " + libnative_module.path);

check_pthread_create(libnative_module.base);
hook_20954();
hook_7a660();
listener.detach();
}

}
});
}

function check_pthread_create(baseaddr){
/* 找到函数pthread_create的地址 */
var pthread_create = Module.findExportByName("libc.so", "pthread_create");

if(pthread_create){
console.log("pthread_create is exist");
}else{
console.log("pthread_create is not exist");
}

/* hook */
Interceptor.attach(pthread_create, {
onEnter: function(args){
console.log("pthread_create is called");
console.log("arg2: 0x" + (args[2] - baseaddr).toString(16));
}
});
}

function hook_20954(){
let libnative_module = Process.findModuleByName("libnative-lib.so");
Interceptor.replace(libnative_module.base.add(0x20954), new NativeCallback(function () {
console.log(`hook_20954 >>>>>>>>>>>>>>>>> replace`);
}, 'void', []));
}

function hook_7a660(){
let libnative_module = Process.findModuleByName("libnative-lib.so");
Interceptor.replace(libnative_module.base.add(0x7a660), new NativeCallback(function () {
console.log(`hook_7a660 >>>>>>>>>>>>>>>>> replace`);
}, 'void', []));
}

function Avoid_divide_by_zero(){
let b = Java.use("b.a.a.b");
b["j"].implementation = function () {
return false
};
}

hook_linker_call_constructors();

setImmediate(hook_java);

成功在豆瓣进修,hhhhhh。