capacitor-dex-editor 0.0.70 → 0.0.72
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.
|
@@ -307,6 +307,31 @@ public class DexEditorPluginPlugin extends Plugin {
|
|
|
307
307
|
));
|
|
308
308
|
break;
|
|
309
309
|
|
|
310
|
+
// ============ 交叉引用分析(C++ 实现)============
|
|
311
|
+
case "findMethodXrefs":
|
|
312
|
+
result.put("data", dexManager.findMethodXrefs(
|
|
313
|
+
params.getString("sessionId"),
|
|
314
|
+
params.getString("className"),
|
|
315
|
+
params.getString("methodName")
|
|
316
|
+
));
|
|
317
|
+
break;
|
|
318
|
+
|
|
319
|
+
case "findFieldXrefs":
|
|
320
|
+
result.put("data", dexManager.findFieldXrefs(
|
|
321
|
+
params.getString("sessionId"),
|
|
322
|
+
params.getString("className"),
|
|
323
|
+
params.getString("fieldName")
|
|
324
|
+
));
|
|
325
|
+
break;
|
|
326
|
+
|
|
327
|
+
// ============ Smali 转 Java(C++ 实现)============
|
|
328
|
+
case "smaliToJava":
|
|
329
|
+
result.put("data", dexManager.smaliToJava(
|
|
330
|
+
params.getString("sessionId"),
|
|
331
|
+
params.getString("className")
|
|
332
|
+
));
|
|
333
|
+
break;
|
|
334
|
+
|
|
310
335
|
// ============ 工具操作 ============
|
|
311
336
|
case "fixDex":
|
|
312
337
|
dexManager.fixDex(
|
|
@@ -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
|
|
|
@@ -485,21 +562,42 @@ public class DexManager {
|
|
|
485
562
|
}
|
|
486
563
|
|
|
487
564
|
/**
|
|
488
|
-
* 获取方法的 Smali
|
|
565
|
+
* 获取方法的 Smali 代码(优先使用 C++ 实现)
|
|
489
566
|
*/
|
|
490
567
|
public JSObject getMethodSmali(String sessionId, String className,
|
|
491
568
|
String methodName, String methodSignature) throws Exception {
|
|
492
569
|
DexSession session = getSession(sessionId);
|
|
493
570
|
|
|
494
|
-
//
|
|
495
|
-
|
|
571
|
+
// 优先使用 C++ 实现
|
|
572
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
573
|
+
try {
|
|
574
|
+
String jsonResult = CppDex.getMethodSmali(session.dexBytes, className, methodName, methodSignature);
|
|
575
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
576
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
577
|
+
String smali = cppResult.optString("smali", "");
|
|
578
|
+
if (!smali.isEmpty()) {
|
|
579
|
+
JSObject result = new JSObject();
|
|
580
|
+
result.put("className", className);
|
|
581
|
+
result.put("methodName", methodName);
|
|
582
|
+
result.put("methodSignature", methodSignature);
|
|
583
|
+
result.put("smali", smali);
|
|
584
|
+
result.put("engine", "cpp");
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
} catch (Exception e) {
|
|
589
|
+
Log.w(TAG, "C++ getMethodSmali failed, fallback to Java", e);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
496
592
|
|
|
497
|
-
//
|
|
593
|
+
// Java 回退实现
|
|
594
|
+
String classSmali = classToSmali(sessionId, className).getString("smali");
|
|
498
595
|
JSObject result = new JSObject();
|
|
499
596
|
result.put("className", className);
|
|
500
597
|
result.put("methodName", methodName);
|
|
501
598
|
result.put("methodSignature", methodSignature);
|
|
502
599
|
result.put("smali", extractMethodSmali(classSmali, methodName, methodSignature));
|
|
600
|
+
result.put("engine", "java");
|
|
503
601
|
return result;
|
|
504
602
|
}
|
|
505
603
|
|
|
@@ -583,12 +681,38 @@ public class DexManager {
|
|
|
583
681
|
// ==================== 字段操作 ====================
|
|
584
682
|
|
|
585
683
|
/**
|
|
586
|
-
*
|
|
684
|
+
* 获取类的所有字段(优先使用 C++ 实现)
|
|
587
685
|
*/
|
|
588
686
|
public JSArray getFields(String sessionId, String className) throws Exception {
|
|
589
687
|
DexSession session = getSession(sessionId);
|
|
688
|
+
|
|
689
|
+
// 优先使用 C++ 实现
|
|
690
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
691
|
+
try {
|
|
692
|
+
String jsonResult = CppDex.listFields(session.dexBytes, className);
|
|
693
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
694
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
695
|
+
org.json.JSONArray cppFields = cppResult.optJSONArray("fields");
|
|
696
|
+
if (cppFields != null) {
|
|
697
|
+
JSArray fields = new JSArray();
|
|
698
|
+
for (int i = 0; i < cppFields.length(); i++) {
|
|
699
|
+
org.json.JSONObject f = cppFields.getJSONObject(i);
|
|
700
|
+
JSObject fieldInfo = new JSObject();
|
|
701
|
+
fieldInfo.put("name", f.optString("name"));
|
|
702
|
+
fieldInfo.put("type", f.optString("type"));
|
|
703
|
+
fieldInfo.put("accessFlags", f.optInt("accessFlags"));
|
|
704
|
+
fields.put(fieldInfo);
|
|
705
|
+
}
|
|
706
|
+
return fields;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} catch (Exception e) {
|
|
710
|
+
Log.w(TAG, "C++ getFields failed, fallback to Java", e);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Java 回退实现
|
|
590
715
|
ClassDef classDef = findClass(session, className);
|
|
591
|
-
|
|
592
716
|
if (classDef == null) {
|
|
593
717
|
throw new IllegalArgumentException("Class not found: " + className);
|
|
594
718
|
}
|
|
@@ -679,21 +803,40 @@ public class DexManager {
|
|
|
679
803
|
// ==================== Smali 操作 ====================
|
|
680
804
|
|
|
681
805
|
/**
|
|
682
|
-
* 将类转换为 Smali
|
|
806
|
+
* 将类转换为 Smali 代码(优先使用 C++ 实现)
|
|
683
807
|
*/
|
|
684
808
|
public JSObject classToSmali(String sessionId, String className) throws Exception {
|
|
685
809
|
DexSession session = getSession(sessionId);
|
|
810
|
+
|
|
811
|
+
// 优先使用 C++ 实现
|
|
812
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
813
|
+
try {
|
|
814
|
+
String jsonResult = CppDex.getClassSmali(session.dexBytes, className);
|
|
815
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
816
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
817
|
+
String smali = cppResult.optString("smali", "");
|
|
818
|
+
if (!smali.isEmpty()) {
|
|
819
|
+
JSObject result = new JSObject();
|
|
820
|
+
result.put("className", className);
|
|
821
|
+
result.put("smali", smali);
|
|
822
|
+
result.put("engine", "cpp");
|
|
823
|
+
return result;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
} catch (Exception e) {
|
|
827
|
+
Log.w(TAG, "C++ classToSmali failed, fallback to Java", e);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Java 回退实现
|
|
686
832
|
ClassDef classDef = findClass(session, className);
|
|
687
|
-
|
|
688
833
|
if (classDef == null) {
|
|
689
834
|
throw new IllegalArgumentException("Class not found: " + className);
|
|
690
835
|
}
|
|
691
836
|
|
|
692
|
-
// 使用 baksmali 转换
|
|
693
837
|
StringWriter writer = new StringWriter();
|
|
694
838
|
BaksmaliOptions options = new BaksmaliOptions();
|
|
695
839
|
|
|
696
|
-
// 创建临时 DEX 只包含该类
|
|
697
840
|
List<ClassDef> singleClass = new ArrayList<>();
|
|
698
841
|
singleClass.add(classDef);
|
|
699
842
|
ImmutableDexFile singleDex = new ImmutableDexFile(
|
|
@@ -701,7 +844,6 @@ public class DexManager {
|
|
|
701
844
|
singleClass
|
|
702
845
|
);
|
|
703
846
|
|
|
704
|
-
// 使用临时目录输出
|
|
705
847
|
File tempDir = File.createTempFile("smali_", "_temp");
|
|
706
848
|
tempDir.delete();
|
|
707
849
|
tempDir.mkdirs();
|
|
@@ -709,7 +851,6 @@ public class DexManager {
|
|
|
709
851
|
try {
|
|
710
852
|
Baksmali.disassembleDexFile(singleDex, tempDir, 1, options);
|
|
711
853
|
|
|
712
|
-
// 读取生成的 smali 文件
|
|
713
854
|
String smaliPath = className.substring(1, className.length() - 1) + ".smali";
|
|
714
855
|
File smaliFile = new File(tempDir, smaliPath);
|
|
715
856
|
|
|
@@ -718,6 +859,7 @@ public class DexManager {
|
|
|
718
859
|
JSObject result = new JSObject();
|
|
719
860
|
result.put("className", className);
|
|
720
861
|
result.put("smali", smali);
|
|
862
|
+
result.put("engine", "java");
|
|
721
863
|
return result;
|
|
722
864
|
} else {
|
|
723
865
|
throw new IOException("Failed to generate smali for: " + className);
|
|
@@ -784,30 +926,51 @@ public class DexManager {
|
|
|
784
926
|
// ==================== 搜索操作 ====================
|
|
785
927
|
|
|
786
928
|
/**
|
|
787
|
-
*
|
|
929
|
+
* 搜索字符串(优先使用 C++ 实现)
|
|
788
930
|
*/
|
|
789
931
|
public JSArray searchString(String sessionId, String query,
|
|
790
932
|
boolean regex, boolean caseSensitive) throws Exception {
|
|
791
933
|
DexSession session = getSession(sessionId);
|
|
934
|
+
|
|
935
|
+
// 优先使用 C++ 实现
|
|
936
|
+
if (CppDex.isAvailable() && session.dexBytes != null && !regex) {
|
|
937
|
+
try {
|
|
938
|
+
String jsonResult = CppDex.searchInDex(session.dexBytes, query, "string", caseSensitive, 1000);
|
|
939
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
940
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
941
|
+
org.json.JSONArray cppResults = cppResult.optJSONArray("results");
|
|
942
|
+
if (cppResults != null) {
|
|
943
|
+
JSArray results = new JSArray();
|
|
944
|
+
for (int i = 0; i < cppResults.length(); i++) {
|
|
945
|
+
org.json.JSONObject r = cppResults.getJSONObject(i);
|
|
946
|
+
JSObject item = new JSObject();
|
|
947
|
+
item.put("value", r.optString("value"));
|
|
948
|
+
item.put("index", r.optInt("index"));
|
|
949
|
+
results.put(item);
|
|
950
|
+
}
|
|
951
|
+
return results;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
} catch (Exception e) {
|
|
955
|
+
Log.w(TAG, "C++ searchString failed, fallback to Java", e);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Java 回退实现
|
|
792
960
|
JSArray results = new JSArray();
|
|
793
|
-
|
|
794
961
|
Pattern pattern = null;
|
|
795
962
|
if (regex) {
|
|
796
963
|
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
|
|
797
964
|
pattern = Pattern.compile(query, flags);
|
|
798
965
|
}
|
|
799
966
|
|
|
800
|
-
// 遍历所有类中的字符串引用进行搜索
|
|
801
967
|
Set<String> searchedStrings = new HashSet<>();
|
|
802
968
|
for (ClassDef classDef : session.originalDexFile.getClasses()) {
|
|
803
|
-
// 类名
|
|
804
969
|
checkAndAddString(classDef.getType(), query, regex, caseSensitive, pattern, searchedStrings, results);
|
|
805
|
-
// 父类
|
|
806
970
|
if (classDef.getSuperclass() != null) {
|
|
807
971
|
checkAndAddString(classDef.getSuperclass(), query, regex, caseSensitive, pattern, searchedStrings, results);
|
|
808
972
|
}
|
|
809
973
|
}
|
|
810
|
-
|
|
811
974
|
return results;
|
|
812
975
|
}
|
|
813
976
|
|
|
@@ -842,16 +1005,41 @@ public class DexManager {
|
|
|
842
1005
|
}
|
|
843
1006
|
|
|
844
1007
|
/**
|
|
845
|
-
*
|
|
1008
|
+
* 搜索方法(优先使用 C++ 实现)
|
|
846
1009
|
*/
|
|
847
1010
|
public JSArray searchMethod(String sessionId, String query) throws Exception {
|
|
848
1011
|
DexSession session = getSession(sessionId);
|
|
1012
|
+
|
|
1013
|
+
// 优先使用 C++ 实现
|
|
1014
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
1015
|
+
try {
|
|
1016
|
+
String jsonResult = CppDex.searchInDex(session.dexBytes, query, "method", false, 1000);
|
|
1017
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1018
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
1019
|
+
org.json.JSONArray cppResults = cppResult.optJSONArray("results");
|
|
1020
|
+
if (cppResults != null) {
|
|
1021
|
+
JSArray results = new JSArray();
|
|
1022
|
+
for (int i = 0; i < cppResults.length(); i++) {
|
|
1023
|
+
org.json.JSONObject r = cppResults.getJSONObject(i);
|
|
1024
|
+
JSObject item = new JSObject();
|
|
1025
|
+
item.put("className", r.optString("className"));
|
|
1026
|
+
item.put("methodName", r.optString("name"));
|
|
1027
|
+
item.put("returnType", r.optString("returnType", ""));
|
|
1028
|
+
results.put(item);
|
|
1029
|
+
}
|
|
1030
|
+
return results;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
} catch (Exception e) {
|
|
1034
|
+
Log.w(TAG, "C++ searchMethod failed, fallback to Java", e);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// Java 回退实现
|
|
849
1039
|
JSArray results = new JSArray();
|
|
850
1040
|
String queryLower = query.toLowerCase();
|
|
851
|
-
|
|
852
1041
|
for (ClassDef classDef : session.originalDexFile.getClasses()) {
|
|
853
1042
|
if (session.removedClasses.contains(classDef.getType())) continue;
|
|
854
|
-
|
|
855
1043
|
for (Method method : classDef.getMethods()) {
|
|
856
1044
|
if (method.getName().toLowerCase().contains(queryLower)) {
|
|
857
1045
|
JSObject item = new JSObject();
|
|
@@ -862,21 +1050,45 @@ public class DexManager {
|
|
|
862
1050
|
}
|
|
863
1051
|
}
|
|
864
1052
|
}
|
|
865
|
-
|
|
866
1053
|
return results;
|
|
867
1054
|
}
|
|
868
1055
|
|
|
869
1056
|
/**
|
|
870
|
-
*
|
|
1057
|
+
* 搜索字段(优先使用 C++ 实现)
|
|
871
1058
|
*/
|
|
872
1059
|
public JSArray searchField(String sessionId, String query) throws Exception {
|
|
873
1060
|
DexSession session = getSession(sessionId);
|
|
1061
|
+
|
|
1062
|
+
// 优先使用 C++ 实现
|
|
1063
|
+
if (CppDex.isAvailable() && session.dexBytes != null) {
|
|
1064
|
+
try {
|
|
1065
|
+
String jsonResult = CppDex.searchInDex(session.dexBytes, query, "field", false, 1000);
|
|
1066
|
+
if (jsonResult != null && !jsonResult.contains("\"error\"")) {
|
|
1067
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
1068
|
+
org.json.JSONArray cppResults = cppResult.optJSONArray("results");
|
|
1069
|
+
if (cppResults != null) {
|
|
1070
|
+
JSArray results = new JSArray();
|
|
1071
|
+
for (int i = 0; i < cppResults.length(); i++) {
|
|
1072
|
+
org.json.JSONObject r = cppResults.getJSONObject(i);
|
|
1073
|
+
JSObject item = new JSObject();
|
|
1074
|
+
item.put("className", r.optString("className"));
|
|
1075
|
+
item.put("fieldName", r.optString("name"));
|
|
1076
|
+
item.put("fieldType", r.optString("type", ""));
|
|
1077
|
+
results.put(item);
|
|
1078
|
+
}
|
|
1079
|
+
return results;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
} catch (Exception e) {
|
|
1083
|
+
Log.w(TAG, "C++ searchField failed, fallback to Java", e);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// Java 回退实现
|
|
874
1088
|
JSArray results = new JSArray();
|
|
875
1089
|
String queryLower = query.toLowerCase();
|
|
876
|
-
|
|
877
1090
|
for (ClassDef classDef : session.originalDexFile.getClasses()) {
|
|
878
1091
|
if (session.removedClasses.contains(classDef.getType())) continue;
|
|
879
|
-
|
|
880
1092
|
for (Field field : classDef.getFields()) {
|
|
881
1093
|
if (field.getName().toLowerCase().contains(queryLower)) {
|
|
882
1094
|
JSObject item = new JSObject();
|
|
@@ -887,10 +1099,109 @@ public class DexManager {
|
|
|
887
1099
|
}
|
|
888
1100
|
}
|
|
889
1101
|
}
|
|
890
|
-
|
|
891
1102
|
return results;
|
|
892
1103
|
}
|
|
893
1104
|
|
|
1105
|
+
// ==================== 交叉引用分析(C++ 实现)====================
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* 查找方法的交叉引用
|
|
1109
|
+
*/
|
|
1110
|
+
public JSObject findMethodXrefs(String sessionId, String className, String methodName) throws Exception {
|
|
1111
|
+
DexSession session = getSession(sessionId);
|
|
1112
|
+
|
|
1113
|
+
if (!CppDex.isAvailable() || session.dexBytes == null) {
|
|
1114
|
+
throw new UnsupportedOperationException("C++ library not available for xref analysis");
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
String jsonResult = CppDex.findMethodXrefs(session.dexBytes, className, methodName);
|
|
1118
|
+
if (jsonResult == null || jsonResult.contains("\"error\"")) {
|
|
1119
|
+
throw new Exception("Failed to find method xrefs");
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
1123
|
+
JSObject result = new JSObject();
|
|
1124
|
+
result.put("className", className);
|
|
1125
|
+
result.put("methodName", methodName);
|
|
1126
|
+
|
|
1127
|
+
org.json.JSONArray xrefs = cppResult.optJSONArray("xrefs");
|
|
1128
|
+
JSArray xrefArray = new JSArray();
|
|
1129
|
+
if (xrefs != null) {
|
|
1130
|
+
for (int i = 0; i < xrefs.length(); i++) {
|
|
1131
|
+
org.json.JSONObject x = xrefs.getJSONObject(i);
|
|
1132
|
+
JSObject xref = new JSObject();
|
|
1133
|
+
xref.put("callerClass", x.optString("callerClass"));
|
|
1134
|
+
xref.put("callerMethod", x.optString("callerMethod"));
|
|
1135
|
+
xref.put("offset", x.optInt("offset"));
|
|
1136
|
+
xrefArray.put(xref);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
result.put("xrefs", xrefArray);
|
|
1140
|
+
result.put("count", xrefArray.length());
|
|
1141
|
+
return result;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* 查找字段的交叉引用
|
|
1146
|
+
*/
|
|
1147
|
+
public JSObject findFieldXrefs(String sessionId, String className, String fieldName) throws Exception {
|
|
1148
|
+
DexSession session = getSession(sessionId);
|
|
1149
|
+
|
|
1150
|
+
if (!CppDex.isAvailable() || session.dexBytes == null) {
|
|
1151
|
+
throw new UnsupportedOperationException("C++ library not available for xref analysis");
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
String jsonResult = CppDex.findFieldXrefs(session.dexBytes, className, fieldName);
|
|
1155
|
+
if (jsonResult == null || jsonResult.contains("\"error\"")) {
|
|
1156
|
+
throw new Exception("Failed to find field xrefs");
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
1160
|
+
JSObject result = new JSObject();
|
|
1161
|
+
result.put("className", className);
|
|
1162
|
+
result.put("fieldName", fieldName);
|
|
1163
|
+
|
|
1164
|
+
org.json.JSONArray xrefs = cppResult.optJSONArray("xrefs");
|
|
1165
|
+
JSArray xrefArray = new JSArray();
|
|
1166
|
+
if (xrefs != null) {
|
|
1167
|
+
for (int i = 0; i < xrefs.length(); i++) {
|
|
1168
|
+
org.json.JSONObject x = xrefs.getJSONObject(i);
|
|
1169
|
+
JSObject xref = new JSObject();
|
|
1170
|
+
xref.put("accessorClass", x.optString("accessorClass"));
|
|
1171
|
+
xref.put("accessorMethod", x.optString("accessorMethod"));
|
|
1172
|
+
xref.put("accessType", x.optString("accessType"));
|
|
1173
|
+
xrefArray.put(xref);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
result.put("xrefs", xrefArray);
|
|
1177
|
+
result.put("count", xrefArray.length());
|
|
1178
|
+
return result;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// ==================== Smali 转 Java(C++ 实现)====================
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* 将 Smali 代码转换为 Java 伪代码
|
|
1185
|
+
*/
|
|
1186
|
+
public JSObject smaliToJava(String sessionId, String className) throws Exception {
|
|
1187
|
+
DexSession session = getSession(sessionId);
|
|
1188
|
+
|
|
1189
|
+
if (!CppDex.isAvailable() || session.dexBytes == null) {
|
|
1190
|
+
throw new UnsupportedOperationException("C++ library not available for smali to java conversion");
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
String jsonResult = CppDex.smaliToJava(session.dexBytes, className);
|
|
1194
|
+
if (jsonResult == null || jsonResult.contains("\"error\"")) {
|
|
1195
|
+
throw new Exception("Failed to convert smali to java");
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
org.json.JSONObject cppResult = new org.json.JSONObject(jsonResult);
|
|
1199
|
+
JSObject result = new JSObject();
|
|
1200
|
+
result.put("className", className);
|
|
1201
|
+
result.put("java", cppResult.optString("java", ""));
|
|
1202
|
+
return result;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
894
1205
|
// ==================== 工具操作 ====================
|
|
895
1206
|
|
|
896
1207
|
/**
|
|
@@ -1254,6 +1565,14 @@ public class DexManager {
|
|
|
1254
1565
|
return content.toString();
|
|
1255
1566
|
}
|
|
1256
1567
|
|
|
1568
|
+
private byte[] readFileBytes(File file) throws IOException {
|
|
1569
|
+
byte[] bytes = new byte[(int) file.length()];
|
|
1570
|
+
try (FileInputStream fis = new FileInputStream(file)) {
|
|
1571
|
+
fis.read(bytes);
|
|
1572
|
+
}
|
|
1573
|
+
return bytes;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1257
1576
|
private void deleteRecursive(File file) {
|
|
1258
1577
|
if (file.isDirectory()) {
|
|
1259
1578
|
File[] children = file.listFiles();
|
|
@@ -2903,21 +3222,6 @@ public class DexManager {
|
|
|
2903
3222
|
return result;
|
|
2904
3223
|
}
|
|
2905
3224
|
|
|
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
3225
|
/**
|
|
2922
3226
|
* 计算 CRC32
|
|
2923
3227
|
*/
|