apk逆向__婚礼纪逆向

婚礼纪在root环境中,会检测root,闪退,并且还有签名校验。

提示信息 :当前运行环境有风险,请在安全的环境运行“婚礼纪“应用

婚礼纪app 版本: 9.5.3

查壳:

定位闪退点

搜索

文本搜索,并没搜索到,不知道咋回事。

算法助手拦截

拦截方法:

  • LSPPosed 应用启用算法助手模块 应用于 婚礼纪app
  • 算法助手 应用 点击 log捕获,监听,防止闪退等记录。

确定app 应用结束进程的堆栈在 “me.suncloud.marrymemo.view.SplashActivity.onCreate “ 内。

jadx 分析

定位root检测点

直接找到 me.suncloud.marrymemo.view.SplashActivity.onCreate 函数。

根据代码,可以看到在 line 93 有一个判断 ZGRootChecker.isDeviceRooted() 来检测当前root 环境。另外在 line 88 还会检测app 签名,如果后期修改smail 代码,可以绕过这些判断校验。如果不通这些校验,就会调用 finish() 函数终止进程。

root检测代码

根据上面的分析, ZGRootChecker.isDeviceRooted() 这个函数会检测当前设备root 权限,检测代码如下所示:

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
/* loaded from: classes16.dex */
public class ZGRootChecker {
public static boolean isDeviceRooted() {
return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
}
private static boolean checkRootMethod1() {
String str = Build.TAGS;
return str != null && str.contains("test-keys");
}
private static boolean checkRootMethod2() {
String[] strArr = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"};
for (int i = 0; i < 10; i++) {
if (new File(strArr[i]).exists()) {
return true;
}
}
return false;
}
private static boolean checkRootMethod3() {
boolean z = false;
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{"/system/xbin/which", "su"});
if (new BufferedReader(new InputStreamReader(process.getInputStream())).readLine() != null) {
z = true;
}
process.destroy();
return z;
} catch (Throwable unused) {
if (process != null) {
process.destroy();
}
return false;
}
}
}

签名校验

ZGSafetyMonitor.checkSHA1(this, getString(R.string.certificate_sha1)) 判断,代码很简单,计算SHA 的值,然后进行匹配。

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

/* loaded from: classes16.dex */
public class ZGSafetyMonitor {
public static boolean checkSHA1(Context context, String sha1) {
return new ZGSignatureChecker(context, sha1).checkSHA1();
}
public static boolean checkMD5(Context context, String md5) {
return new ZGSignatureChecker(context, md5).checkMD5();
}
}

class ZGSignatureChecker

public boolean checkSHA1() {
if (this.realCer != null) {
this.cer = CommonUtil.getMD5(getCertificateSHA1Fingerprint().trim());
String trim = this.realCer.trim();
this.realCer = trim;
return this.cer.equals(trim);
}
return false;
}
public boolean checkMD5() {
if (this.realCer != null) {
this.cer = CommonUtil.getMD5(getCertificateMD5Fingerprint().trim());
String trim = this.realCer.trim();
this.realCer = trim;
return this.cer.equals(trim);
}
return false;
}

绕过root检测

frida hook

从jadx内转出frida脚本

1
2
3
4
5
6
7
let ZGRootChecker = Java.use("me.suncloud.marrymemo.fragment.login.zg.ZGRootChecker");
ZGRootChecker["isDeviceRooted"].implementation = function () {
console.log('isDeviceRooted is called');
let ret = this.isDeviceRooted();
console.log('isDeviceRooted ret value is ' + ret);
return ret;
};

获取app 进程号,我发现dumpsys 获取不到,可以通过ps 查看进程pid

Frida 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
45
46
47
48
49
50
51
52
'''
Author : TigerHu
Version : V1.0
Date : 2024-01-03 22:30:03
Description :
'''

#-*- coding:utf-8 -*-
import frida
import sys
import os

def adb_forward():
os.system('adb forward tcp:27042 tcp:27042')
os.system('adb forward tcp:27043 tcp:27043')

jscode = """
Java.perform(function(){
let ZGRootChecker = Java.use("me.suncloud.marrymemo.fragment.login.zg.ZGRootChecker");
ZGRootChecker["isDeviceRooted"].implementation = function () {
console.log('isDeviceRooted is called');
let ret = this.isDeviceRooted();
console.log('isDeviceRooted ret value is ' + ret);
return False;
};
});
"""


def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
adb_forward()
# 查找远程设备并附加到目标进程
# frida.get_remote_device() 远程设备,模拟器
# frida.get_usb_device 获取usb 设备
process = frida.get_usb_device().attach(3470)
#pid = device.spawn(["com.android.chrome"])
#session = device.attach(pid)
#device.resume(pid)
# 在目标进程里创建脚本
script = process.create_script(jscode)
# 注册消息回调
script.on("message", on_message)
print('[*] Hook Start Running')
# 加载创建好的javascript 脚本
script.load()
# 读取系统输入
sys.stdin.read()

测试通过

绕过签名校验

测试一下app的签名校验是否有效,使用MT管理器重新签名安装,可以看到会弹出提示词,提示词内容和前面分析的是一致的。

直接上frida hook 一下。

hook 这一块无效

1
2
3
4
5
6
7
let ZGSafetyMonitor = Java.use("me.suncloud.marrymemo.fragment.login.zg.ZGSafetyMonitor");
ZGSafetyMonitor["checkSHA1"].implementation = function (context, sha1) {
console.log('checkSHA1 is called' + ', ' + 'context: ' + context + ', ' + 'sha1: ' + sha1);
let ret = this.checkSHA1(context, sha1);
console.log('checkSHA1 ret value is ' + ret);
return ret;
};

最终hook 了这个函数

frida 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
'''
Author : TigerHu
Version : V1.0
Date : 2024-01-03 22:30:03
Description :
'''

#-*- coding:utf-8 -*-
import frida
import sys
import os

def adb_forward():
os.system('adb forward tcp:27042 tcp:27042')
os.system('adb forward tcp:27043 tcp:27043')

jscode = """
// 这一部分是绕过签名校验
Java.perform(function(){
let ZGSignatureChecker = Java.use("me.suncloud.marrymemo.fragment.login.zg.ZGSignatureChecker");
ZGSignatureChecker["checkSHA1"].implementation = function () {
console.log('checkSHA1 is called');
let ret = this.checkSHA1();
console.log('checkSHA1 ret value is ' + ret);
return true;
};
});
// 这一部分是绕过root检测
Java.perform(function(){
let ZGRootChecker = Java.use("me.suncloud.marrymemo.fragment.login.zg.ZGRootChecker");
ZGRootChecker["isDeviceRooted"].implementation = function () {
console.log('isDeviceRooted is called');
let ret = this.isDeviceRooted();
console.log('isDeviceRooted ret value is ' + ret);
return False;
};
});
"""


def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
adb_forward()
# 查找远程设备并附加到目标进程
# frida.get_remote_device() 远程设备,模拟器
# frida.get_usb_device 获取usb 设备
process = frida.get_usb_device().attach(16572)
#pid = device.spawn(["com.android.chrome"])
#session = device.attach(pid)
#device.resume(pid)
# 在目标进程里创建脚本
script = process.create_script(jscode)
# 注册消息回调
script.on("message", on_message)
print('[*] Hook Start Running')
# 加载创建好的javascript 脚本
script.load()
# 读取系统输入
sys.stdin.read()

修改Smali绕过检测

绕过签名校验

这里要让 checkSHA1() 返回true 。

绕过root 检测

这里记住是改为 0 ,因为要让isDeviceRooted函数返回false。


apk逆向__婚礼纪逆向
https://tig3rhu.github.io/2024/01/03/67__逆向__婚礼纪APP/
Author
Tig3rHu
Posted on
January 3, 2024
Licensed under