capacitor-dex-editor 0.0.70 → 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.
|
@@ -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();
|
|
@@ -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
|
*/
|