capacitor-dex-editor 0.0.69 → 0.0.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +22 -0
- package/android/src/main/cpp/CMakeLists.txt +57 -0
- package/android/src/main/cpp/apk/apk_handler.cpp +121 -0
- package/android/src/main/cpp/apk/zip_utils.cpp +425 -0
- package/android/src/main/cpp/arsc/arsc_parser.cpp +390 -0
- package/android/src/main/cpp/dex/dex_builder.cpp +752 -0
- package/android/src/main/cpp/dex/dex_parser.cpp +620 -0
- package/android/src/main/cpp/dex/smali_disasm.cpp +1223 -0
- package/android/src/main/cpp/dex/smali_to_java.cpp +576 -0
- package/android/src/main/cpp/include/apk/apk_handler.h +41 -0
- package/android/src/main/cpp/include/apk/zip_utils.h +57 -0
- package/android/src/main/cpp/include/arsc/arsc_parser.h +98 -0
- package/android/src/main/cpp/include/dex/dex_builder.h +189 -0
- package/android/src/main/cpp/include/dex/dex_parser.h +137 -0
- package/android/src/main/cpp/include/dex/smali_disasm.h +127 -0
- package/android/src/main/cpp/include/dex/smali_to_java.h +50 -0
- package/android/src/main/cpp/include/xml/android_resources.h +495 -0
- package/android/src/main/cpp/include/xml/axml_parser.h +147 -0
- package/android/src/main/cpp/jni_bridge.cpp +872 -0
- package/android/src/main/cpp/third_party/miniz.c +646 -0
- package/android/src/main/cpp/third_party/miniz.h +605 -0
- package/android/src/main/cpp/third_party/miniz_common.h +97 -0
- package/android/src/main/cpp/third_party/miniz_export.h +6 -0
- package/android/src/main/cpp/third_party/miniz_tdef.c +1597 -0
- package/android/src/main/cpp/third_party/miniz_tdef.h +199 -0
- package/android/src/main/cpp/third_party/miniz_tinfl.c +770 -0
- package/android/src/main/cpp/third_party/miniz_tinfl.h +150 -0
- package/android/src/main/cpp/third_party/miniz_zip.c +4895 -0
- package/android/src/main/cpp/third_party/miniz_zip.h +454 -0
- package/android/src/main/cpp/third_party/nlohmann_json/CMakeLists.txt +0 -0
- package/android/src/main/cpp/third_party/nlohmann_json/single_include/nlohmann/json.hpp +24765 -0
- package/android/src/main/cpp/xml/axml_parser.cpp +1701 -0
- package/android/src/main/java/com/aetherlink/dexeditor/CppDex.java +295 -0
- package/android/src/main/java/com/aetherlink/dexeditor/DexManager.java +146 -50
- package/package.json +1 -1
- package/android/src/main/java/com/aetherlink/dexeditor/RustDex.java +0 -203
- package/android/src/main/jniLibs/arm64-v8a/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/x86/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/x86_64/libdex_rust.so +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
package com.aetherlink.dexeditor;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CppDex - C++ 实现的 DEX 解析器 JNI 接口
|
|
7
|
+
* 高性能 DEX 解析、搜索、反汇编
|
|
8
|
+
*/
|
|
9
|
+
public class CppDex {
|
|
10
|
+
private static final String TAG = "CppDex";
|
|
11
|
+
private static boolean libraryLoaded = false;
|
|
12
|
+
|
|
13
|
+
static {
|
|
14
|
+
try {
|
|
15
|
+
System.loadLibrary("dex_cpp");
|
|
16
|
+
libraryLoaded = true;
|
|
17
|
+
Log.d(TAG, "C++ library loaded successfully");
|
|
18
|
+
} catch (UnsatisfiedLinkError e) {
|
|
19
|
+
Log.w(TAG, "Failed to load C++ library: " + e.getMessage());
|
|
20
|
+
libraryLoaded = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 检查 C++ 库是否可用
|
|
26
|
+
*/
|
|
27
|
+
public static boolean isAvailable() {
|
|
28
|
+
return libraryLoaded;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ==================== DEX 解析操作 ====================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 获取 DEX 文件信息
|
|
35
|
+
* @param dexBytes DEX 文件字节数组
|
|
36
|
+
* @return JSON 格式的 DEX 信息
|
|
37
|
+
*/
|
|
38
|
+
public static native String getDexInfo(byte[] dexBytes);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 列出 DEX 中的类
|
|
42
|
+
* @param dexBytes DEX 文件字节数组
|
|
43
|
+
* @param packageFilter 包名过滤器
|
|
44
|
+
* @param offset 偏移量
|
|
45
|
+
* @param limit 限制数量
|
|
46
|
+
* @return JSON 格式的类列表
|
|
47
|
+
*/
|
|
48
|
+
public static native String listClasses(
|
|
49
|
+
byte[] dexBytes,
|
|
50
|
+
String packageFilter,
|
|
51
|
+
int offset,
|
|
52
|
+
int limit
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 在 DEX 中搜索
|
|
57
|
+
* @param dexBytes DEX 文件字节数组
|
|
58
|
+
* @param query 搜索查询
|
|
59
|
+
* @param searchType 搜索类型: class, method, field, string
|
|
60
|
+
* @param caseSensitive 是否区分大小写
|
|
61
|
+
* @param maxResults 最大结果数
|
|
62
|
+
* @return JSON 格式的搜索结果
|
|
63
|
+
*/
|
|
64
|
+
public static native String searchInDex(
|
|
65
|
+
byte[] dexBytes,
|
|
66
|
+
String query,
|
|
67
|
+
String searchType,
|
|
68
|
+
boolean caseSensitive,
|
|
69
|
+
int maxResults
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 获取类的 Smali 代码
|
|
74
|
+
* @param dexBytes DEX 文件字节数组
|
|
75
|
+
* @param className 类名
|
|
76
|
+
* @return JSON 格式的 Smali 代码
|
|
77
|
+
*/
|
|
78
|
+
public static native String getClassSmali(
|
|
79
|
+
byte[] dexBytes,
|
|
80
|
+
String className
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 获取单个方法的 Smali 代码
|
|
85
|
+
* @param dexBytes DEX 文件字节数组
|
|
86
|
+
* @param className 类名
|
|
87
|
+
* @param methodName 方法名
|
|
88
|
+
* @param methodSignature 方法签名
|
|
89
|
+
* @return JSON 格式的方法 Smali 代码
|
|
90
|
+
*/
|
|
91
|
+
public static native String getMethodSmali(
|
|
92
|
+
byte[] dexBytes,
|
|
93
|
+
String className,
|
|
94
|
+
String methodName,
|
|
95
|
+
String methodSignature
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// ==================== Smali 转 Java ====================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 将 Smali 代码转换为 Java 伪代码
|
|
102
|
+
* @param dexBytes DEX 文件字节数组
|
|
103
|
+
* @param className 类名
|
|
104
|
+
* @return JSON 格式的 Java 伪代码
|
|
105
|
+
*/
|
|
106
|
+
public static native String smaliToJava(
|
|
107
|
+
byte[] dexBytes,
|
|
108
|
+
String className
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// ==================== DEX 修改操作 ====================
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 修改 DEX 中的类
|
|
115
|
+
* @param dexBytes DEX 文件字节数组
|
|
116
|
+
* @param className 类名
|
|
117
|
+
* @param newSmali 新的 Smali 代码
|
|
118
|
+
* @return 修改后的 DEX 字节数组,失败返回 null
|
|
119
|
+
*/
|
|
120
|
+
public static native byte[] modifyClass(
|
|
121
|
+
byte[] dexBytes,
|
|
122
|
+
String className,
|
|
123
|
+
String newSmali
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 添加新类到 DEX
|
|
128
|
+
* @param dexBytes DEX 文件字节数组
|
|
129
|
+
* @param newSmali 新类的 Smali 代码
|
|
130
|
+
* @return 修改后的 DEX 字节数组,失败返回 null
|
|
131
|
+
*/
|
|
132
|
+
public static native byte[] addClass(
|
|
133
|
+
byte[] dexBytes,
|
|
134
|
+
String newSmali
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 从 DEX 中删除类
|
|
139
|
+
* @param dexBytes DEX 文件字节数组
|
|
140
|
+
* @param className 要删除的类名
|
|
141
|
+
* @return 修改后的 DEX 字节数组,失败返回 null
|
|
142
|
+
*/
|
|
143
|
+
public static native byte[] deleteClass(
|
|
144
|
+
byte[] dexBytes,
|
|
145
|
+
String className
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// ==================== 方法级操作 ====================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 列出类中的所有方法
|
|
152
|
+
* @param dexBytes DEX 文件字节数组
|
|
153
|
+
* @param className 类名
|
|
154
|
+
* @return JSON 格式的方法列表
|
|
155
|
+
*/
|
|
156
|
+
public static native String listMethods(
|
|
157
|
+
byte[] dexBytes,
|
|
158
|
+
String className
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 列出类中的所有字段
|
|
163
|
+
* @param dexBytes DEX 文件字节数组
|
|
164
|
+
* @param className 类名
|
|
165
|
+
* @return JSON 格式的字段列表
|
|
166
|
+
*/
|
|
167
|
+
public static native String listFields(
|
|
168
|
+
byte[] dexBytes,
|
|
169
|
+
String className
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// ==================== 字符串操作 ====================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 列出 DEX 中的字符串
|
|
176
|
+
* @param dexBytes DEX 文件字节数组
|
|
177
|
+
* @param filter 过滤器
|
|
178
|
+
* @param limit 限制数量
|
|
179
|
+
* @return JSON 格式的字符串列表
|
|
180
|
+
*/
|
|
181
|
+
public static native String listStrings(
|
|
182
|
+
byte[] dexBytes,
|
|
183
|
+
String filter,
|
|
184
|
+
int limit
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// ==================== 交叉引用分析 ====================
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 查找方法的交叉引用
|
|
191
|
+
* @param dexBytes DEX 文件字节数组
|
|
192
|
+
* @param className 类名
|
|
193
|
+
* @param methodName 方法名
|
|
194
|
+
* @return JSON 格式的交叉引用列表
|
|
195
|
+
*/
|
|
196
|
+
public static native String findMethodXrefs(
|
|
197
|
+
byte[] dexBytes,
|
|
198
|
+
String className,
|
|
199
|
+
String methodName
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 查找字段的交叉引用
|
|
204
|
+
* @param dexBytes DEX 文件字节数组
|
|
205
|
+
* @param className 类名
|
|
206
|
+
* @param fieldName 字段名
|
|
207
|
+
* @return JSON 格式的交叉引用列表
|
|
208
|
+
*/
|
|
209
|
+
public static native String findFieldXrefs(
|
|
210
|
+
byte[] dexBytes,
|
|
211
|
+
String className,
|
|
212
|
+
String fieldName
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// ==================== Smali 编译 ====================
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 将 Smali 代码编译为 DEX
|
|
219
|
+
* @param smaliCode Smali 代码
|
|
220
|
+
* @return 编译后的 DEX 字节数组,失败返回 null
|
|
221
|
+
*/
|
|
222
|
+
public static native byte[] smaliToDex(String smaliCode);
|
|
223
|
+
|
|
224
|
+
// ==================== XML/资源解析 ====================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 解析二进制 XML (AndroidManifest.xml)
|
|
228
|
+
* @param axmlBytes AXML 字节数组
|
|
229
|
+
* @return JSON 格式的解析结果
|
|
230
|
+
*/
|
|
231
|
+
public static native String parseAxml(byte[] axmlBytes);
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 编辑 AndroidManifest.xml 属性
|
|
235
|
+
* @param axmlBytes AXML 字节数组
|
|
236
|
+
* @param action 操作: set_package, set_version_name, set_version_code, set_min_sdk, set_target_sdk
|
|
237
|
+
* @param value 新值
|
|
238
|
+
* @return 修改后的 AXML 字节数组,失败返回 null
|
|
239
|
+
*/
|
|
240
|
+
public static native byte[] editManifest(
|
|
241
|
+
byte[] axmlBytes,
|
|
242
|
+
String action,
|
|
243
|
+
String value
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 在 AndroidManifest.xml 中搜索
|
|
248
|
+
* @param axmlBytes AXML 字节数组
|
|
249
|
+
* @param attrName 属性名
|
|
250
|
+
* @param value 值
|
|
251
|
+
* @param limit 限制数量
|
|
252
|
+
* @return JSON 格式的搜索结果
|
|
253
|
+
*/
|
|
254
|
+
public static native String searchXml(
|
|
255
|
+
byte[] axmlBytes,
|
|
256
|
+
String attrName,
|
|
257
|
+
String value,
|
|
258
|
+
int limit
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 解析 resources.arsc
|
|
263
|
+
* @param arscBytes ARSC 字节数组
|
|
264
|
+
* @return JSON 格式的解析结果
|
|
265
|
+
*/
|
|
266
|
+
public static native String parseArsc(byte[] arscBytes);
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 搜索 ARSC 字符串
|
|
270
|
+
* @param arscBytes ARSC 字节数组
|
|
271
|
+
* @param pattern 搜索模式
|
|
272
|
+
* @param limit 限制数量
|
|
273
|
+
* @return JSON 格式的搜索结果
|
|
274
|
+
*/
|
|
275
|
+
public static native String searchArscStrings(
|
|
276
|
+
byte[] arscBytes,
|
|
277
|
+
String pattern,
|
|
278
|
+
int limit
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 搜索 ARSC 资源
|
|
283
|
+
* @param arscBytes ARSC 字节数组
|
|
284
|
+
* @param pattern 搜索模式
|
|
285
|
+
* @param type 资源类型 (string, drawable, layout 等)
|
|
286
|
+
* @param limit 限制数量
|
|
287
|
+
* @return JSON 格式的搜索结果
|
|
288
|
+
*/
|
|
289
|
+
public static native String searchArscResources(
|
|
290
|
+
byte[] arscBytes,
|
|
291
|
+
String pattern,
|
|
292
|
+
String type,
|
|
293
|
+
int limit
|
|
294
|
+
);
|
|
295
|
+
}
|
|
@@ -130,14 +130,16 @@ public class DexManager {
|
|
|
130
130
|
String sessionId;
|
|
131
131
|
String filePath;
|
|
132
132
|
DexBackedDexFile originalDexFile;
|
|
133
|
+
byte[] dexBytes; // DEX 字节数据,用于 C++ 解析
|
|
133
134
|
List<ClassDef> modifiedClasses;
|
|
134
135
|
Set<String> removedClasses;
|
|
135
136
|
boolean modified = false;
|
|
136
137
|
|
|
137
|
-
DexSession(String sessionId, String filePath, DexBackedDexFile dexFile) {
|
|
138
|
+
DexSession(String sessionId, String filePath, DexBackedDexFile dexFile, byte[] bytes) {
|
|
138
139
|
this.sessionId = sessionId;
|
|
139
140
|
this.filePath = filePath;
|
|
140
141
|
this.originalDexFile = dexFile;
|
|
142
|
+
this.dexBytes = bytes;
|
|
141
143
|
this.modifiedClasses = new ArrayList<>();
|
|
142
144
|
this.removedClasses = new HashSet<>();
|
|
143
145
|
}
|
|
@@ -188,6 +190,9 @@ public class DexManager {
|
|
|
188
190
|
// 生成或使用提供的 sessionId
|
|
189
191
|
String sid = (sessionId != null && !sessionId.isEmpty()) ? sessionId : UUID.randomUUID().toString();
|
|
190
192
|
|
|
193
|
+
// 读取 DEX 字节数据(用于 C++ 解析)
|
|
194
|
+
byte[] dexBytes = readFileBytes(file);
|
|
195
|
+
|
|
191
196
|
// 加载 DEX 文件 (使用官方推荐的 DexFileFactory)
|
|
192
197
|
DexBackedDexFile dexFile = (DexBackedDexFile) DexFileFactory.loadDexFile(
|
|
193
198
|
file,
|
|
@@ -195,7 +200,7 @@ public class DexManager {
|
|
|
195
200
|
);
|
|
196
201
|
|
|
197
202
|
// 创建会话
|
|
198
|
-
DexSession session = new DexSession(sid, path, dexFile);
|
|
203
|
+
DexSession session = new DexSession(sid, path, dexFile, dexBytes);
|
|
199
204
|
sessions.put(sid, session);
|
|
200
205
|
|
|
201
206
|
Log.d(TAG, "Loaded DEX: " + path + " with session: " + sid);
|
|
@@ -262,18 +267,41 @@ public class DexManager {
|
|
|
262
267
|
}
|
|
263
268
|
|
|
264
269
|
/**
|
|
265
|
-
* 获取 DEX
|
|
270
|
+
* 获取 DEX 文件信息(优先使用 C++ 实现)
|
|
266
271
|
*/
|
|
267
272
|
public JSObject getDexInfo(String sessionId) throws Exception {
|
|
268
273
|
DexSession session = getSession(sessionId);
|
|
274
|
+
|
|
275
|
+
// 优先使用 C++ 实现
|
|
276
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
277
|
+
try {
|
|
278
|
+
String jsonResult = CppDex.getDexInfo(session.dexBytes);
|
|
279
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
280
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
281
|
+
JSObject info = new JSObject();
|
|
282
|
+
info.put("sessionId", sessionId);
|
|
283
|
+
info.put("filePath", session.filePath);
|
|
284
|
+
info.put("classCount", cppResult.optInt("classCount", 0));
|
|
285
|
+
info.put("methodCount", cppResult.optInt("methodCount", 0));
|
|
286
|
+
info.put("fieldCount", cppResult.optInt("fieldCount", 0));
|
|
287
|
+
info.put("stringCount", cppResult.optInt("stringCount", 0));
|
|
288
|
+
info.put("dexVersion", cppResult.optInt("version", 35));
|
|
289
|
+
info.put("modified", session.modified);
|
|
290
|
+
info.put("engine", "cpp");
|
|
291
|
+
return info;
|
|
292
|
+
}
|
|
293
|
+
} catch (Exception e) {
|
|
294
|
+
Log.w(TAG, "C++ getDexInfo failed, fallback to Java", e);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Java 回退实现
|
|
269
299
|
DexBackedDexFile dexFile = session.originalDexFile;
|
|
270
|
-
|
|
271
300
|
JSObject info = new JSObject();
|
|
272
301
|
info.put("sessionId", sessionId);
|
|
273
302
|
info.put("filePath", session.filePath);
|
|
274
303
|
info.put("classCount", dexFile.getClasses().size());
|
|
275
304
|
|
|
276
|
-
// 统计方法和字段数量
|
|
277
305
|
int methodCount = 0;
|
|
278
306
|
int fieldCount = 0;
|
|
279
307
|
for (ClassDef classDef : dexFile.getClasses()) {
|
|
@@ -284,18 +312,45 @@ public class DexManager {
|
|
|
284
312
|
info.put("fieldCount", fieldCount);
|
|
285
313
|
info.put("dexVersion", dexFile.getOpcodes().api);
|
|
286
314
|
info.put("modified", session.modified);
|
|
315
|
+
info.put("engine", "java");
|
|
287
316
|
return info;
|
|
288
317
|
}
|
|
289
318
|
|
|
290
319
|
// ==================== 类操作 ====================
|
|
291
320
|
|
|
292
321
|
/**
|
|
293
|
-
*
|
|
322
|
+
* 获取所有类列表(优先使用 C++ 实现)
|
|
294
323
|
*/
|
|
295
324
|
public JSArray getClasses(String sessionId) throws Exception {
|
|
296
325
|
DexSession session = getSession(sessionId);
|
|
326
|
+
|
|
327
|
+
// 优先使用 C++ 实现
|
|
328
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
329
|
+
try {
|
|
330
|
+
String jsonResult = CppDex.listClasses(session.dexBytes, "", 0, 100000);
|
|
331
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
332
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
333
|
+
org.json.JSONArray cppClasses = cppResult.optJSONArray("classes");
|
|
334
|
+
if (cppClasses != null) {
|
|
335
|
+
JSArray classes = new JSArray();
|
|
336
|
+
for (int i = 0; i < cppClasses.length(); i++) {
|
|
337
|
+
String className = cppClasses.getString(i);
|
|
338
|
+
if (!session.removedClasses.contains(className)) {
|
|
339
|
+
JSObject classInfo = new JSObject();
|
|
340
|
+
classInfo.put("type", className);
|
|
341
|
+
classes.put(classInfo);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return classes;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch (Exception e) {
|
|
348
|
+
Log.w(TAG, "C++ getClasses failed, fallback to Java", e);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Java 回退实现
|
|
297
353
|
JSArray classes = new JSArray();
|
|
298
|
-
|
|
299
354
|
for (ClassDef classDef : session.originalDexFile.getClasses()) {
|
|
300
355
|
if (!session.removedClasses.contains(classDef.getType())) {
|
|
301
356
|
JSObject classInfo = new JSObject();
|
|
@@ -305,7 +360,6 @@ public class DexManager {
|
|
|
305
360
|
classes.put(classInfo);
|
|
306
361
|
}
|
|
307
362
|
}
|
|
308
|
-
|
|
309
363
|
return classes;
|
|
310
364
|
}
|
|
311
365
|
|
|
@@ -407,12 +461,38 @@ public class DexManager {
|
|
|
407
461
|
// ==================== 方法操作 ====================
|
|
408
462
|
|
|
409
463
|
/**
|
|
410
|
-
*
|
|
464
|
+
* 获取类的所有方法(优先使用 C++ 实现)
|
|
411
465
|
*/
|
|
412
466
|
public JSArray getMethods(String sessionId, String className) throws Exception {
|
|
413
467
|
DexSession session = getSession(sessionId);
|
|
468
|
+
|
|
469
|
+
// 优先使用 C++ 实现
|
|
470
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
471
|
+
try {
|
|
472
|
+
String jsonResult = CppDex.listMethods(session.dexBytes, className);
|
|
473
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
474
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
475
|
+
org.json.JSONArray cppMethods = cppResult.optJSONArray("methods");
|
|
476
|
+
if (cppMethods != null) {
|
|
477
|
+
JSArray methods = new JSArray();
|
|
478
|
+
for (int i = 0; i < cppMethods.length(); i++) {
|
|
479
|
+
org.json.JSONObject m = cppMethods.getJSONObject(i);
|
|
480
|
+
JSObject methodInfo = new JSObject();
|
|
481
|
+
methodInfo.put("name", m.optString("name"));
|
|
482
|
+
methodInfo.put("signature", m.optString("prototype"));
|
|
483
|
+
methodInfo.put("accessFlags", m.optInt("accessFlags"));
|
|
484
|
+
methods.put(methodInfo);
|
|
485
|
+
}
|
|
486
|
+
return methods;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
} catch (Exception e) {
|
|
490
|
+
Log.w(TAG, "C++ getMethods failed, fallback to Java", e);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Java 回退实现
|
|
414
495
|
ClassDef classDef = findClass(session, className);
|
|
415
|
-
|
|
416
496
|
if (classDef == null) {
|
|
417
497
|
throw new IllegalArgumentException("Class not found: " + className);
|
|
418
498
|
}
|
|
@@ -424,14 +504,12 @@ public class DexManager {
|
|
|
424
504
|
methodInfo.put("returnType", method.getReturnType());
|
|
425
505
|
methodInfo.put("accessFlags", method.getAccessFlags());
|
|
426
506
|
|
|
427
|
-
// 参数类型
|
|
428
507
|
JSArray params = new JSArray();
|
|
429
508
|
for (CharSequence param : method.getParameterTypes()) {
|
|
430
509
|
params.put(param.toString());
|
|
431
510
|
}
|
|
432
511
|
methodInfo.put("parameters", params);
|
|
433
512
|
|
|
434
|
-
// 方法签名
|
|
435
513
|
StringBuilder sig = new StringBuilder("(");
|
|
436
514
|
for (CharSequence param : method.getParameterTypes()) {
|
|
437
515
|
sig.append(param);
|
|
@@ -441,7 +519,6 @@ public class DexManager {
|
|
|
441
519
|
|
|
442
520
|
methods.put(methodInfo);
|
|
443
521
|
}
|
|
444
|
-
|
|
445
522
|
return methods;
|
|
446
523
|
}
|
|
447
524
|
|
|
@@ -583,12 +660,38 @@ public class DexManager {
|
|
|
583
660
|
// ==================== 字段操作 ====================
|
|
584
661
|
|
|
585
662
|
/**
|
|
586
|
-
*
|
|
663
|
+
* 获取类的所有字段(优先使用 C++ 实现)
|
|
587
664
|
*/
|
|
588
665
|
public JSArray getFields(String sessionId, String className) throws Exception {
|
|
589
666
|
DexSession session = getSession(sessionId);
|
|
667
|
+
|
|
668
|
+
// 优先使用 C++ 实现
|
|
669
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
670
|
+
try {
|
|
671
|
+
String jsonResult = CppDex.listFields(session.dexBytes, className);
|
|
672
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
673
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
674
|
+
org.json.JSONArray cppFields = cppResult.optJSONArray("fields");
|
|
675
|
+
if (cppFields != null) {
|
|
676
|
+
JSArray fields = new JSArray();
|
|
677
|
+
for (int i = 0; i < cppFields.length(); i++) {
|
|
678
|
+
org.json.JSONObject f = cppFields.getJSONObject(i);
|
|
679
|
+
JSObject fieldInfo = new JSObject();
|
|
680
|
+
fieldInfo.put("name", f.optString("name"));
|
|
681
|
+
fieldInfo.put("type", f.optString("type"));
|
|
682
|
+
fieldInfo.put("accessFlags", f.optInt("accessFlags"));
|
|
683
|
+
fields.put(fieldInfo);
|
|
684
|
+
}
|
|
685
|
+
return fields;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
} catch (Exception e) {
|
|
689
|
+
Log.w(TAG, "C++ getFields failed, fallback to Java", e);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Java 回退实现
|
|
590
694
|
ClassDef classDef = findClass(session, className);
|
|
591
|
-
|
|
592
695
|
if (classDef == null) {
|
|
593
696
|
throw new IllegalArgumentException("Class not found: " + className);
|
|
594
697
|
}
|
|
@@ -1254,6 +1357,14 @@ public class DexManager {
|
|
|
1254
1357
|
return content.toString();
|
|
1255
1358
|
}
|
|
1256
1359
|
|
|
1360
|
+
private byte[] readFileBytes(File file) throws IOException {
|
|
1361
|
+
byte[] bytes = new byte[(int) file.length()];
|
|
1362
|
+
try (FileInputStream fis = new FileInputStream(file)) {
|
|
1363
|
+
fis.read(bytes);
|
|
1364
|
+
}
|
|
1365
|
+
return bytes;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1257
1368
|
private void deleteRecursive(File file) {
|
|
1258
1369
|
if (file.isDirectory()) {
|
|
1259
1370
|
File[] children = file.listFiles();
|
|
@@ -1709,8 +1820,8 @@ public class DexManager {
|
|
|
1709
1820
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1710
1821
|
}
|
|
1711
1822
|
|
|
1712
|
-
if (!
|
|
1713
|
-
throw new RuntimeException("
|
|
1823
|
+
if (!CppDex.isAvailable() || session.dexBytes.isEmpty()) {
|
|
1824
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1714
1825
|
}
|
|
1715
1826
|
|
|
1716
1827
|
JSObject result = new JSObject();
|
|
@@ -1723,7 +1834,7 @@ public class DexManager {
|
|
|
1723
1834
|
String dexName = entry.getKey();
|
|
1724
1835
|
byte[] dexData = entry.getValue();
|
|
1725
1836
|
|
|
1726
|
-
String jsonResult =
|
|
1837
|
+
String jsonResult = CppDex.listClasses(dexData, filter, 0, 100000);
|
|
1727
1838
|
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1728
1839
|
org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
|
|
1729
1840
|
org.json.JSONArray rustClasses = rustResult.optJSONArray("classes");
|
|
@@ -1770,8 +1881,8 @@ public class DexManager {
|
|
|
1770
1881
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1771
1882
|
}
|
|
1772
1883
|
|
|
1773
|
-
if (!
|
|
1774
|
-
throw new RuntimeException("
|
|
1884
|
+
if (!CppDex.isAvailable()) {
|
|
1885
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1775
1886
|
}
|
|
1776
1887
|
|
|
1777
1888
|
if (session.dexBytes.isEmpty()) {
|
|
@@ -1785,7 +1896,7 @@ public class DexManager {
|
|
|
1785
1896
|
String dexName = entry.getKey();
|
|
1786
1897
|
byte[] dexData = entry.getValue();
|
|
1787
1898
|
|
|
1788
|
-
String jsonResult =
|
|
1899
|
+
String jsonResult = CppDex.searchInDex(dexData, query, searchType, caseSensitive, maxResults);
|
|
1789
1900
|
|
|
1790
1901
|
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1791
1902
|
org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
|
|
@@ -1848,8 +1959,8 @@ public class DexManager {
|
|
|
1848
1959
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1849
1960
|
}
|
|
1850
1961
|
|
|
1851
|
-
if (!
|
|
1852
|
-
throw new RuntimeException("
|
|
1962
|
+
if (!CppDex.isAvailable()) {
|
|
1963
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1853
1964
|
}
|
|
1854
1965
|
|
|
1855
1966
|
// 使用 Rust 获取 Smali
|
|
@@ -1857,7 +1968,7 @@ public class DexManager {
|
|
|
1857
1968
|
String dexName = entry.getKey();
|
|
1858
1969
|
byte[] dexData = entry.getValue();
|
|
1859
1970
|
|
|
1860
|
-
String jsonResult =
|
|
1971
|
+
String jsonResult = CppDex.getClassSmali(dexData, className);
|
|
1861
1972
|
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1862
1973
|
org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
|
|
1863
1974
|
JSObject result = new JSObject();
|
|
@@ -1881,14 +1992,14 @@ public class DexManager {
|
|
|
1881
1992
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1882
1993
|
}
|
|
1883
1994
|
|
|
1884
|
-
if (!
|
|
1885
|
-
throw new RuntimeException("
|
|
1995
|
+
if (!CppDex.isAvailable()) {
|
|
1996
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1886
1997
|
}
|
|
1887
1998
|
|
|
1888
1999
|
// 找到类所在的 DEX
|
|
1889
2000
|
String targetDex = null;
|
|
1890
2001
|
for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
|
|
1891
|
-
String jsonResult =
|
|
2002
|
+
String jsonResult = CppDex.getClassSmali(entry.getValue(), className);
|
|
1892
2003
|
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1893
2004
|
targetDex = entry.getKey();
|
|
1894
2005
|
break;
|
|
@@ -1901,7 +2012,7 @@ public class DexManager {
|
|
|
1901
2012
|
|
|
1902
2013
|
// 使用 Rust 修改类
|
|
1903
2014
|
byte[] originalDex = session.dexBytes.get(targetDex);
|
|
1904
|
-
byte[] modifiedDex =
|
|
2015
|
+
byte[] modifiedDex = CppDex.modifyClass(originalDex, className, smaliContent);
|
|
1905
2016
|
|
|
1906
2017
|
if (modifiedDex == null) {
|
|
1907
2018
|
throw new RuntimeException("Failed to modify class: " + className);
|
|
@@ -1923,8 +2034,8 @@ public class DexManager {
|
|
|
1923
2034
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1924
2035
|
}
|
|
1925
2036
|
|
|
1926
|
-
if (!
|
|
1927
|
-
throw new RuntimeException("
|
|
2037
|
+
if (!CppDex.isAvailable()) {
|
|
2038
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1928
2039
|
}
|
|
1929
2040
|
|
|
1930
2041
|
// 添加到第一个 DEX(默认 classes.dex)
|
|
@@ -1935,7 +2046,7 @@ public class DexManager {
|
|
|
1935
2046
|
|
|
1936
2047
|
// 使用 Rust 添加类
|
|
1937
2048
|
byte[] originalDex = session.dexBytes.get(targetDex);
|
|
1938
|
-
byte[] modifiedDex =
|
|
2049
|
+
byte[] modifiedDex = CppDex.addClass(originalDex, smaliContent);
|
|
1939
2050
|
|
|
1940
2051
|
if (modifiedDex == null) {
|
|
1941
2052
|
throw new RuntimeException("Failed to add class: " + className);
|
|
@@ -1957,14 +2068,14 @@ public class DexManager {
|
|
|
1957
2068
|
throw new IllegalArgumentException("Session not found: " + sessionId);
|
|
1958
2069
|
}
|
|
1959
2070
|
|
|
1960
|
-
if (!
|
|
1961
|
-
throw new RuntimeException("
|
|
2071
|
+
if (!CppDex.isAvailable()) {
|
|
2072
|
+
throw new RuntimeException("C++ DEX library not available");
|
|
1962
2073
|
}
|
|
1963
2074
|
|
|
1964
2075
|
// 找到类所在的 DEX
|
|
1965
2076
|
String targetDex = null;
|
|
1966
2077
|
for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
|
|
1967
|
-
String jsonResult =
|
|
2078
|
+
String jsonResult = CppDex.getClassSmali(entry.getValue(), className);
|
|
1968
2079
|
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1969
2080
|
targetDex = entry.getKey();
|
|
1970
2081
|
break;
|
|
@@ -1977,7 +2088,7 @@ public class DexManager {
|
|
|
1977
2088
|
|
|
1978
2089
|
// 使用 Rust 删除类
|
|
1979
2090
|
byte[] originalDex = session.dexBytes.get(targetDex);
|
|
1980
|
-
byte[] modifiedDex =
|
|
2091
|
+
byte[] modifiedDex = CppDex.deleteClass(originalDex, className);
|
|
1981
2092
|
|
|
1982
2093
|
if (modifiedDex == null) {
|
|
1983
2094
|
throw new RuntimeException("Failed to delete class: " + className);
|
|
@@ -2903,21 +3014,6 @@ public class DexManager {
|
|
|
2903
3014
|
return result;
|
|
2904
3015
|
}
|
|
2905
3016
|
|
|
2906
|
-
/**
|
|
2907
|
-
* 读取文件为字节数组
|
|
2908
|
-
*/
|
|
2909
|
-
private byte[] readFileBytes(java.io.File file) throws java.io.IOException {
|
|
2910
|
-
java.io.FileInputStream fis = new java.io.FileInputStream(file);
|
|
2911
|
-
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
2912
|
-
byte[] buffer = new byte[8192];
|
|
2913
|
-
int len;
|
|
2914
|
-
while ((len = fis.read(buffer)) != -1) {
|
|
2915
|
-
baos.write(buffer, 0, len);
|
|
2916
|
-
}
|
|
2917
|
-
fis.close();
|
|
2918
|
-
return baos.toByteArray();
|
|
2919
|
-
}
|
|
2920
|
-
|
|
2921
3017
|
/**
|
|
2922
3018
|
* 计算 CRC32
|
|
2923
3019
|
*/
|