capacitor-dex-editor 0.0.6 → 0.0.8

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.
@@ -401,6 +401,29 @@ public class DexEditorPluginPlugin extends Plugin {
401
401
  ));
402
402
  break;
403
403
 
404
+ // ==================== DEX 编辑器操作 ====================
405
+ case "listDexClasses":
406
+ result.put("data", dexManager.listDexClassesFromApk(
407
+ params.getString("apkPath"),
408
+ params.getString("dexPath")
409
+ ));
410
+ break;
411
+
412
+ case "getDexStrings":
413
+ result.put("data", dexManager.getDexStringsFromApk(
414
+ params.getString("apkPath"),
415
+ params.getString("dexPath")
416
+ ));
417
+ break;
418
+
419
+ case "searchInDex":
420
+ result.put("data", dexManager.searchInDexFromApk(
421
+ params.getString("apkPath"),
422
+ params.getString("dexPath"),
423
+ params.getString("query")
424
+ ));
425
+ break;
426
+
404
427
  default:
405
428
  result.put("success", false);
406
429
  result.put("error", "Unknown action: " + action);
@@ -1214,4 +1214,237 @@ public class DexManager {
1214
1214
  }
1215
1215
  return count;
1216
1216
  }
1217
+
1218
+ // ==================== APK 内 DEX 操作(无需会话) ====================
1219
+
1220
+ /**
1221
+ * 从 APK 中的 DEX 文件列出所有类
1222
+ * @param apkPath APK 文件路径
1223
+ * @param dexPath DEX 文件在 APK 中的路径(如 "classes.dex")
1224
+ */
1225
+ public JSObject listDexClassesFromApk(String apkPath, String dexPath) throws Exception {
1226
+ JSObject result = new JSObject();
1227
+ JSArray classes = new JSArray();
1228
+
1229
+ java.util.zip.ZipFile zipFile = null;
1230
+ java.io.InputStream dexInputStream = null;
1231
+
1232
+ try {
1233
+ zipFile = new java.util.zip.ZipFile(apkPath);
1234
+ java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
1235
+
1236
+ if (dexEntry == null) {
1237
+ throw new IOException("DEX file not found in APK: " + dexPath);
1238
+ }
1239
+
1240
+ dexInputStream = zipFile.getInputStream(dexEntry);
1241
+
1242
+ // 读取 DEX 文件到内存
1243
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
1244
+ byte[] buffer = new byte[8192];
1245
+ int len;
1246
+ while ((len = dexInputStream.read(buffer)) != -1) {
1247
+ baos.write(buffer, 0, len);
1248
+ }
1249
+ byte[] dexBytes = baos.toByteArray();
1250
+
1251
+ // 解析 DEX 文件
1252
+ DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), dexBytes);
1253
+
1254
+ // 收集所有类名
1255
+ for (ClassDef classDef : dexFile.getClasses()) {
1256
+ String type = classDef.getType();
1257
+ // 转换 Lcom/example/Class; 格式为 com.example.Class
1258
+ String className = convertTypeToClassName(type);
1259
+ classes.put(className);
1260
+ }
1261
+
1262
+ result.put("classes", classes);
1263
+ result.put("count", classes.length());
1264
+
1265
+ } finally {
1266
+ if (dexInputStream != null) {
1267
+ try { dexInputStream.close(); } catch (Exception ignored) {}
1268
+ }
1269
+ if (zipFile != null) {
1270
+ try { zipFile.close(); } catch (Exception ignored) {}
1271
+ }
1272
+ }
1273
+
1274
+ return result;
1275
+ }
1276
+
1277
+ /**
1278
+ * 从 APK 中的 DEX 文件获取字符串常量池
1279
+ */
1280
+ public JSObject getDexStringsFromApk(String apkPath, String dexPath) throws Exception {
1281
+ JSObject result = new JSObject();
1282
+ JSArray strings = new JSArray();
1283
+
1284
+ java.util.zip.ZipFile zipFile = null;
1285
+ java.io.InputStream dexInputStream = null;
1286
+
1287
+ try {
1288
+ zipFile = new java.util.zip.ZipFile(apkPath);
1289
+ java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
1290
+
1291
+ if (dexEntry == null) {
1292
+ throw new IOException("DEX file not found in APK: " + dexPath);
1293
+ }
1294
+
1295
+ dexInputStream = zipFile.getInputStream(dexEntry);
1296
+
1297
+ // 读取 DEX 文件到内存
1298
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
1299
+ byte[] buffer = new byte[8192];
1300
+ int len;
1301
+ while ((len = dexInputStream.read(buffer)) != -1) {
1302
+ baos.write(buffer, 0, len);
1303
+ }
1304
+ byte[] dexBytes = baos.toByteArray();
1305
+
1306
+ // 解析 DEX 文件
1307
+ DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), dexBytes);
1308
+
1309
+ // 直接从 DEX 字符串表中读取所有字符串
1310
+ int stringCount = dexFile.getStringCount();
1311
+ for (int i = 0; i < stringCount; i++) {
1312
+ try {
1313
+ String str = dexFile.getStringSection().get(i);
1314
+ JSObject item = new JSObject();
1315
+ item.put("index", i);
1316
+ item.put("value", str != null ? str : "");
1317
+ strings.put(item);
1318
+ } catch (Exception e) {
1319
+ // 跳过无法读取的字符串
1320
+ Log.w(TAG, "Failed to read string at index " + i, e);
1321
+ }
1322
+ }
1323
+
1324
+ result.put("strings", strings);
1325
+ result.put("count", strings.length());
1326
+
1327
+ } finally {
1328
+ if (dexInputStream != null) {
1329
+ try { dexInputStream.close(); } catch (Exception ignored) {}
1330
+ }
1331
+ if (zipFile != null) {
1332
+ try { zipFile.close(); } catch (Exception ignored) {}
1333
+ }
1334
+ }
1335
+
1336
+ return result;
1337
+ }
1338
+
1339
+ /**
1340
+ * 在 APK 中的 DEX 文件中搜索
1341
+ */
1342
+ public JSObject searchInDexFromApk(String apkPath, String dexPath, String query) throws Exception {
1343
+ JSObject result = new JSObject();
1344
+ JSArray results = new JSArray();
1345
+
1346
+ if (query == null || query.isEmpty()) {
1347
+ result.put("results", results);
1348
+ result.put("count", 0);
1349
+ return result;
1350
+ }
1351
+
1352
+ String queryLower = query.toLowerCase();
1353
+
1354
+ java.util.zip.ZipFile zipFile = null;
1355
+ java.io.InputStream dexInputStream = null;
1356
+
1357
+ try {
1358
+ zipFile = new java.util.zip.ZipFile(apkPath);
1359
+ java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
1360
+
1361
+ if (dexEntry == null) {
1362
+ throw new IOException("DEX file not found in APK: " + dexPath);
1363
+ }
1364
+
1365
+ dexInputStream = zipFile.getInputStream(dexEntry);
1366
+
1367
+ // 读取 DEX 文件到内存
1368
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
1369
+ byte[] buffer = new byte[8192];
1370
+ int len;
1371
+ while ((len = dexInputStream.read(buffer)) != -1) {
1372
+ baos.write(buffer, 0, len);
1373
+ }
1374
+ byte[] dexBytes = baos.toByteArray();
1375
+
1376
+ // 解析 DEX 文件
1377
+ DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), dexBytes);
1378
+
1379
+ // 搜索类名和方法名
1380
+ for (ClassDef classDef : dexFile.getClasses()) {
1381
+ String className = convertTypeToClassName(classDef.getType());
1382
+
1383
+ // 搜索类名
1384
+ if (className.toLowerCase().contains(queryLower)) {
1385
+ JSObject item = new JSObject();
1386
+ item.put("className", className);
1387
+ item.put("type", "class");
1388
+ item.put("content", className);
1389
+ results.put(item);
1390
+ }
1391
+
1392
+ // 搜索方法名
1393
+ for (Method method : classDef.getMethods()) {
1394
+ String methodName = method.getName();
1395
+ if (methodName.toLowerCase().contains(queryLower)) {
1396
+ JSObject item = new JSObject();
1397
+ item.put("className", className);
1398
+ item.put("methodName", methodName);
1399
+ item.put("type", "method");
1400
+ item.put("content", methodName + " in " + className);
1401
+ results.put(item);
1402
+ }
1403
+
1404
+ // 搜索方法内的字符串
1405
+ MethodImplementation impl = method.getImplementation();
1406
+ if (impl != null) {
1407
+ for (Instruction instruction : impl.getInstructions()) {
1408
+ String instrStr = instruction.toString();
1409
+ if (instrStr.toLowerCase().contains(queryLower)) {
1410
+ JSObject item = new JSObject();
1411
+ item.put("className", className);
1412
+ item.put("methodName", methodName);
1413
+ item.put("type", "instruction");
1414
+ item.put("content", instrStr);
1415
+ results.put(item);
1416
+ break; // 每个方法只记录一次
1417
+ }
1418
+ }
1419
+ }
1420
+ }
1421
+ }
1422
+
1423
+ result.put("results", results);
1424
+ result.put("count", results.length());
1425
+
1426
+ } finally {
1427
+ if (dexInputStream != null) {
1428
+ try { dexInputStream.close(); } catch (Exception ignored) {}
1429
+ }
1430
+ if (zipFile != null) {
1431
+ try { zipFile.close(); } catch (Exception ignored) {}
1432
+ }
1433
+ }
1434
+
1435
+ return result;
1436
+ }
1437
+
1438
+ /**
1439
+ * 将 DEX 类型格式转换为 Java 类名格式
1440
+ * 例如: Lcom/example/Class; -> com.example.Class
1441
+ */
1442
+ private String convertTypeToClassName(String type) {
1443
+ if (type == null) return "";
1444
+ String className = type;
1445
+ if (className.startsWith("L") && className.endsWith(";")) {
1446
+ className = className.substring(1, className.length() - 1);
1447
+ }
1448
+ return className.replace("/", ".");
1449
+ }
1217
1450
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Capacitor-plugin-for-editing-DEX-files-in-APK",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",