apk逆向__他趣APP脱壳&运行环境检测分析

ROOT检测

“ 当前APP运行环境存在风险 (Root设备),请您及时知晓并注意信息安全! “

Hook 检测

使用算法助手定位到root检测弹窗的一些常规设置,去除弹窗,或者其他的绕过。
当使用算法助手hook app 的时候,app 会检测hook 的环境,从而闪退。

使用frida 脚本直接闪退

frida -U -p 32218 -l .\java_hook通杀.js
可以看到直接会闪退。

load_so.js

这里可以判定apk 使用了一些方法来阻止frida 进行hook, 一般是在 so 文件内进行处理。所以这里我们直接可以找找app在运行的过程中,使用了那些so。最后调用的那个so 文件,就是检测hook环境的库文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// load_so.js
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
Interceptor.attach(dlopen, {
onEnter: function(args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
console.log("[dlopen:]", path);
},
onLeave: function(retval) {}
});
Interceptor.attach(android_dlopen_ext, {
onEnter: function(args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
console.log("[dlopen_ext:]", path);
},
onLeave: function(retval) {}
});

使用脚本一直不行,因为用的一直是attach模式

1
2
3
4
5
6
7
8
Frida中的`spawn`和`attach`是两种不同的操作模式,用于与目标进程进行交互。

1. `spawn`模式:在`spawn`模式下,Frida会启动一个新的目标进程,并且在该进程中加载指定的脚本。这意味着目标进程是由Frida直接创建的,并且可以在进程启动时就注入脚本。使用`spawn`模式可以在目标进程运行之前就对其进行动态修改、监控或调试。

2. `attach`模式:在`attach`模式下,Frida会连接到一个已经运行的目标进程,并且在该进程中加载指定的脚本。这意味着目标进程必须已经在运行中,而且Frida需要通过进程ID或进程名称将其连接起来。使用`attach`模式可以在目标进程运行时对其进行动态修改、监控或调试。


总结而言,`spawn`模式适用于在目标进程启动之前注入脚本,而`attach`模式适用于连接到已经运行的目标进程并注入脚本。选择使用哪种模式取决于您的具体需求和目标进程的状态。

使用 spawn 模式,可以看到是 libnesec.so 文件,这是网易易盾的加壳so。

这是pixel 6Pro 手机hook ,可以看到。

但是换成moto 手机却不行,“ Failed to spawn: unexpectedly timed out while waiting for signal from process with PID 12337 ”,猜测这与手机系统有关。

hook libnesc.so

前面我们知道 libnesec 会检测frida hook, 那么可以直接hook libnesec.so ,当调用libnesec 库的时候,让其返回空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function hook_pthread_create() {
var pt_create_func = Module.findExportByName("libc.so", 'pthread_create');
Interceptor.attach(pt_create_func, {
onEnter: function(args) {
// 是那个so文件调用我,创建线程
var so_name = Process.findModuleByAddress(args[2]).name;
if (so_name.indexOf("libnesec") != -1) {
try {
Interceptor.replace(args[2], new NativeCallback(function() {
console.log('replace success');
return null;
}, 'void', ["void"]));
} catch (e) {}
}
},
onLeave: function(retval) {}
})
}
hook_pthread_create();

可以看到替换成功了。

网易易盾脱壳

使用 jadx 打开 apk ,会发现存在 libnesec.so 文件,猜测加固了。因为反编译看不到要的伪java源码。因此需要去脱壳。

查壳

MT 管理器

可以查出来是网易易盾的加固

PKID 查壳

查不出来,看过别人用pkid 可以查出来,可能版本不一样。

dump_dex脱壳

脱壳前提: 要一直运行hook_libnesec.js 文件,保证frida 能够hook 而不被libnesec.so 检测到闪退,影响脱壳。

脱壳脚本

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
(function() {
function klog(data) {
var message = {};
message["jsname"] = "dump_dex";
message["data"] = data;
send(message);
}

function klogData(data, key, value) {
var message = {};
message["jsname"] = "dump_dex";
message["data"] = data;
message[key] = value;
send(message);
}

function get_self_process_name() {
var openPtr = Module.getExportByName('libc.so', 'open');
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);

var readPtr = Module.getExportByName("libc.so", "read");
var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]);

var closePtr = Module.getExportByName('libc.so', 'close');
var close = new NativeFunction(closePtr, 'int', ['int']);

var path = Memory.allocUtf8String("/proc/self/cmdline");
var fd = open(path, 0);
if (fd != -1) {
var buffer = Memory.alloc(0x1000);

var result = read(fd, buffer, 0x1000);
close(fd);
result = ptr(buffer).readCString();
return result;
}

return "-1";
}


function mkdir(path) {
var mkdirPtr = Module.getExportByName('libc.so', 'mkdir');
var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']);



var opendirPtr = Module.getExportByName('libc.so', 'opendir');
var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']);

var closedirPtr = Module.getExportByName('libc.so', 'closedir');
var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']);

var cPath = Memory.allocUtf8String(path);
var dir = opendir(cPath);
if (dir != 0) {
closedir(dir);
return 0;
}
mkdir(cPath, 755);
chmod(path);
}

function chmod(path) {
var chmodPtr = Module.getExportByName('libc.so', 'chmod');
var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']);
var cPath = Memory.allocUtf8String(path);
chmod(cPath, 755);
}

function dump_dex() {
var libart = Process.findModuleByName("libart.so");
var addr_DefineClass = null;
var symbols = libart.enumerateSymbols();
for (var index = 0; index < symbols.length; index++) {
var symbol = symbols[index];
var symbol_name = symbol.name;
//这个DefineClass的函数签名是Android9的
//_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE
if (symbol_name.indexOf("ClassLinker") >= 0 &&
symbol_name.indexOf("DefineClass") >= 0 &&
symbol_name.indexOf("Thread") >= 0 &&
symbol_name.indexOf("DexFile") >= 0) {
console.log(symbol_name, symbol.address);
addr_DefineClass = symbol.address;
}
}
var dex_maps = {};
var dex_count = 1;

console.log("[DefineClass:]", addr_DefineClass);
if (addr_DefineClass) {
Interceptor.attach(addr_DefineClass, {
onEnter: function(args) {
var dex_file = args[5];
//ptr(dex_file).add(Process.pointerSize) is "const uint8_t* const begin_;"
//ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is "const size_t size_;"
var base = ptr(dex_file).add(Process.pointerSize).readPointer();
var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();

if (dex_maps[base] == undefined) {
dex_maps[base] = size;
var magic = ptr(base).readCString();
if (magic.indexOf("dex") == 0) {

var process_name = get_self_process_name();
if (process_name != "-1") {
var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name;
mkdir(dex_dir_path);
var dex_path = dex_dir_path + "/class" + (dex_count == 1 ? "" : dex_count) + ".dex";
console.log("[find dex]:", dex_path);
var fd = new File(dex_path, "wb");
if (fd && fd != null) {
dex_count++;
var dex_buffer = ptr(base).readByteArray(size);
fd.write(dex_buffer);
fd.flush();
fd.close();
console.log("[dump dex]:", dex_path);

}
}
}
}
},
onLeave: function(retval) {}
});
}
}

function main() {
klogData("", "init", "dump_dex.js init hook success")
dump_dex();
}
setImmediate(main);
})();

运行 frida -UF -l .\dump_dex.js , 为了保证能脱壳,app 的每个板块都点一点。

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
PS C:\Users\TigerHu\NewFlold\7__study\2__Frida\他趣脱壳> frida -UF -l .\dump_dex.js
____
/ _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Pixel 6 Pro (id=1B231FDEE005T0)
_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS_3dex8ClassDefE 0x6d87590ab0
[DefineClass:] 0x6d87590ab0
message: {'type': 'send', 'payload': {'jsname': 'dump_dex', 'data': '', 'init': 'dump_dex.js init hook success'}} data: None
[Pixel 6 Pro::他趣 ]-> [find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class2.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class2.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class3.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class3.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class4.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class4.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class5.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class5.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class6.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class6.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class7.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class7.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class8.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class8.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class9.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class9.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class10.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class10.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class11.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class11.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class12.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class12.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class13.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class13.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class14.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class14.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class15.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class15.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class16.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class16.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class17.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class17.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class18.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class18.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class19.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class19.dex
[find dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class20.dex
[dump dex]: /data/data/com.xingjiabi.shengsheng/files/dump_dex_com.xingjiabi.shengsheng/class20.dex

在手机内可以看到脱壳出来的dex 文件,随后直接拖到jadx里,就可以分析了。

分析app内各种检测

分析app 反调试、运行环境检测技术。

root 检测

前面我们知道app 会对运行环境进行检测,但是直接搜索 字符串 弹窗的信息,是搜不出来的,这是由于 apk 对字符串做了base64 处理。

然后以前分析过一些对root 环境监测的代码,所以这里直接搜索,定位到代码 cn.taqu.lib.base.utils.AppAntiUtils.isDeviceRooted()

然后找到这一块的代码

base64 解码

另外还看到不仅仅对 su 进行检测,还检测了其他的项,如下函数。这个函数在 cn.taqu.lib.base.utils.AppAntiUtils.initAnti() 初始化的时候调用,

1
2
3
4
5
6
7
public static boolean isDeviceRooted(RootBeer rootBeer) {

HbTrace.d(38850);
boolean z2 = rootBeer.checkForBinary(x.f13157a) || rootBeer.checkForDangerousProps() || rootBeer.checkForRWPaths() || rootBeer.detectTestKeys() || rootBeer.checkSuExists() || rootBeer.checkForRootNative() || rootBeer.checkForMagiskBinary();
HbTrace.h(38850);
return z2;
}

可以看到调用了 isDeviceRooted,两个重构函数,来判断是否root,最后弹窗

1
2
3
4
5
6
7
8
9
10
11
12
cn.taqu.lib.base.utils.AppAntiUtils.initAnti()

if (!AppAntiUtils.access$900() && !AppAntiUtils.access$1000() && "1".equals(AppAntiUtils.access$200())) {
boolean isDeviceRooted = AppAntiUtils.isDeviceRooted(); // 判断su
boolean isDeviceRooted2 = AppAntiUtils.isDeviceRooted(RootBeer.this); // 判断其他
if (isDeviceRooted2 || isDeviceRooted) {
Loger.g(AppAntiUtils.TAG, PushConstants.PUSH_VALUE_ROOT);
XLogUpload.k("AntiRoot").b("device rooted.").m("RootBeer", String.valueOf(isDeviceRooted2)).m("DeviceRooted", String.valueOf(isDeviceRooted)).o();
AppAntiUtils.access$1100(); // 弹窗
}
}

这里来分析一下 isDeviceRooted(RootBeer rootBeer) 内对运行环境是如何检测的代码。
首先分析一下 rootBeer.checkForBinary(x.f13157a),这个函数是检测在一些文件路径内是否存在 x.f13157a ( su 文件 ), 有关Const.getPaths() 函数的代码详见附录 1 。

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean checkForBinary(String str) {
String[] paths;
boolean z2 = false;
for (String str2 : Const.getPaths()) {
String str3 = str2 + str;
if (new File(str2, str).exists()) {
QLog.v(str3 + " binary detected!");
z2 = true;
}
}
return z2;
}

rootBeer.checkForDangerousProps() 就是判断系统的调试模式是否被打开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean checkForDangerousProps() {
HashMap hashMap = new HashMap();
hashMap.put("ro.debuggable", "1");
hashMap.put("ro.secure", "0");
String[] propsReader = propsReader();
if (propsReader == null) {
return false;
}
boolean z2 = false;
for (String str : propsReader) {
for (String str2 : hashMap.keySet()) {
if (str.contains(str2)) {
String str3 = "[" + ((String) hashMap.get(str2)) + "]";
if (str.contains(str3)) {
QLog.v(str2 + " = " + str3 + " detected!");
z2 = true;
}
}
}
}
return z2;

rootBeer.checkForRWPaths() 检查系统中是否存在被挂载为可读写(rw)权限的应用程序数据目录以及其他指定的路径,并返回一个布尔值表示是否存在这样的路径。该函数会读取系统中关于挂载点的信息,检查每一个挂载点的路径和权限信息,如果发现某个路径被挂载为可读写权限,则会输出相应的日志信息并将返回值设为 true。该函数可以用于检测系统安全性,防止应用程序滥用高权限目录,代码太长,详见 附录 2 。

rootBeer.detectTestKeys() 该函数会读取设备的 Build.TAGS 字段是否有 “ test-keys ”,检测当前设备是否使用测试用的密钥签名。

root.Beer. checkSuExists() 函数会调用 which 命令,查找是否存在 su 命令。

rootBeer.checkForRootNative() 函数会加载本地 toolChecker 的 lib 库文件来检测是否被获取root 权限,调用 native 内的 checkForRoot() 函数,来判断设备是否获得root权限,其实还是检测是否存在 su 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class RootBeerNative {
private static boolean libraryLoaded = false;

static {
try {
System.loadLibrary("toolChecker");
libraryLoaded = true;
} catch (UnsatisfiedLinkError e) {
QLog.e(e);
}
}

public native int checkForRoot(Object[] objArr);
public native int setLogDebugMessages(boolean z2);
public boolean wasNativeLibraryLoaded() {
return libraryLoaded;
}
}

rootBeer.checkForMagiskBinary() 检测是否存在Magisk 面具,如果进程名被更改就检测不到了。

1
2
3
public boolean checkForMagiskBinary() {
return checkForBinary("magisk");
}

其实还有一些检测的代码,这个app 对root 检测已经是相当的全面了。

完整性签名校验

计算签名摘要,进行对比。

1
2
3
4
5
6
7
8
9
} else if (!sHbSignature.equals(getSHA1Signature(BaseApplication.getInstance()))) {
XLogUpload.k("AntiSign").b("签名文件不一致").o();
ToastUtils.b(BaseApplication.getInstance(), new String(Base64.decode("5qOA5rWL5Yiw5oKo5a6J6KOF5YiwQVBQ5a2Y5Zyo6Zeu6aKY77yM5Li65LqG5L+d6K+B5q2j5bi45L2/55So6K+35LiL6L295a6Y5pa554mI5pys", 0)));
System.exit(0);
HbTrace.h(38784);




代理检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cn.taqu.lib.base.utils.AppAntiUtils line21~line22

if (!AppAntiUtils.access$600() && !AppAntiUtils.access$700() && !ApplicationHelper.u() && OkHttpDns.a(BaseApplication.getInstance())) {
AppAntiUtils.access$800();
}

hb.common.helper.OKHttpDns line1~line2
public static boolean a(Context ctx) {
HbTrace.d(44874);
String property = System.getProperty("http.proxyHost");
String property2 = System.getProperty("http.proxyPort");
if (property2 == null) {
property2 = "-1";
}
boolean z2 = (property == null || Integer.parseInt(property2) == -1) ? false : true;
HbTrace.h(44874);
return z2;
}

参考

APP 使用frida 反调试检测绕过
https://www.cnblogs.com/dxmao/articles/17678351.html

视频讲解
https://www.bilibili.com/video/BV1e94y1V7wZ/?spm_id_from=333.999.0.0&vd_source=cd4aba572b443cbbb4c522c3c649652d

附录

附录 1

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
package com.scottyab.rootbeer;

import java.util.ArrayList;
import java.util.Arrays;
import me.panpf.sketch.uri.FileUriModel;

/* loaded from: E:\file\android逆向\蓝狐视频\他趣app\class13.dex */
final class Const {
static final String BINARY_BUSYBOX = "busybox";
static final String BINARY_SU = "su";
static final String[] knownRootAppsPackages = {"com.noshufou.android.su", "com.noshufou.android.su.elite", "eu.chainfire.supersu", "com.koushikdutta.superuser", "com.thirdparty.superuser", "com.yellowes.su", "com.topjohnwu.magisk", "com.kingroot.kinguser", "com.kingo.root", "com.smedialink.oneclickroot", "com.zhiqupk.root.global", "com.alephzain.framaroot"};
public static final String[] knownDangerousAppsPackages = {"com.koushikdutta.rommanager", "com.koushikdutta.rommanager.license", "com.dimonvideo.luckypatcher", "com.chelpus.lackypatch", "com.ramdroid.appquarantine", "com.ramdroid.appquarantinepro", "com.android.vending.billing.InAppBillingService.COIN", "com.android.vending.billing.InAppBillingService.LUCK", "com.chelpus.luckypatcher", "com.blackmartalpha", "org.blackmart.market", "com.allinone.free", "com.repodroid.app", "org.creeplays.hack", "com.baseappfull.fwd", "com.zmapp", "com.dv.marketmod.installer", "org.mobilism.android", "com.android.wp.net.log", "com.android.camera.update", "cc.madkite.freedom", "com.solohsu.android.edxp.manager", "org.meowcat.edxposed.manager", "com.xmodgame", "com.cih.game_cih", "com.charles.lpoqasert", "catch_.me_.if_.you_.can_"};
public static final String[] knownRootCloakingPackages = {"com.devadvance.rootcloak", "com.devadvance.rootcloakplus", "de.robv.android.xposed.installer", "com.saurik.substrate", "com.zachspong.temprootremovejb", "com.amphoras.hidemyroot", "com.amphoras.hidemyrootadfree", "com.formyhm.hiderootPremium", "com.formyhm.hideroot"};
private static final String[] suPaths = {"/data/local/", "/data/local/bin/", "/data/local/xbin/", "/sbin/", "/su/bin/", "/system/bin/", "/system/bin/.ext/", "/system/bin/failsafe/", "/system/sd/xbin/", "/system/usr/we-need-root/", "/system/xbin/", "/cache/", "/data/", "/dev/"};
static final String[] pathsThatShouldNotBeWritable = {"/system", "/system/bin", "/system/sbin", "/system/xbin", "/vendor/bin", "/sbin", "/etc"};

private Const() throws InstantiationException {
throw new InstantiationException("This class is not for instantiation");
}

/* JADX INFO: Access modifiers changed from: package-private */
public static String[] getPaths() {
ArrayList arrayList = new ArrayList(Arrays.asList(suPaths));
String str = System.getenv("PATH");
if (str != null && !"".equals(str)) {
String[] split = str.split(":");
int length = split.length;
for (int i2 = 0; i2 < length; i2++) {
String str2 = split[i2];
if (!str2.endsWith(FileUriModel.SCHEME)) {
str2 = str2 + '/';
}
if (!arrayList.contains(str2)) {
arrayList.add(str2);
}
}
return (String[]) arrayList.toArray(new String[0]);
}
return (String[]) arrayList.toArray(new String[0]);
}
}

附录2

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

public boolean checkForRWPaths() {
String str;
String str2;
String[] strArr;
String[] mountReader = mountReader();
if (mountReader == null) {
return false;
}
int i2 = Build.VERSION.SDK_INT;
int length = mountReader.length;
int i3 = 0;
boolean z2 = false;
while (i3 < length) {
String str3 = mountReader[i3];
String[] split = str3.split(" ");
int i4 = 23;
if ((i2 <= 23 && split.length < 4) || (i2 > 23 && split.length < 6)) {
QLog.e("Error formatting mount line: " + str3);
} else {
if (i2 > 23) {
str = split[2];
str2 = split[5];
} else {
str = split[1];
str2 = split[3];
}
String[] strArr2 = Const.pathsThatShouldNotBeWritable;
int length2 = strArr2.length;
int i5 = 0;
while (i5 < length2) {
String str4 = strArr2[i5];
if (str.equalsIgnoreCase(str4)) {
if (Build.VERSION.SDK_INT > i4) {
str2 = str2.replace("(", "").replace(")", "");
}
String[] split2 = str2.split(",");
int length3 = split2.length;
int i6 = 0;
while (i6 < length3) {
strArr = mountReader;
if (split2[i6].equalsIgnoreCase("rw")) {
QLog.v(str4 + " path is mounted with rw permissions! " + str3);
z2 = true;
break;
}
i6++;
mountReader = strArr;
}
}
strArr = mountReader;
i5++;
mountReader = strArr;
i4 = 23;
}
}
i3++;
mountReader = mountReader;
}
return z2;
}





apk逆向__他趣APP脱壳&运行环境检测分析
https://tig3rhu.github.io/2024/01/14/70__逆向__他趣APP/
Author
Tig3rHu
Posted on
January 14, 2024
Licensed under