capacitor-dex-editor 0.0.32 → 0.0.33
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
CHANGED
|
@@ -67,6 +67,9 @@ dependencies {
|
|
|
67
67
|
// APK Signer - V1/V2/V3/V4 签名支持 (Android 7.0+)
|
|
68
68
|
api 'com.android.tools.build:apksig:8.7.2'
|
|
69
69
|
|
|
70
|
+
// ARSCLib - 二进制 XML 解析和修改(无限制)
|
|
71
|
+
api 'io.github.nicholsontech:arsc:2.6.3'
|
|
72
|
+
|
|
70
73
|
testImplementation "junit:junit:$junitVersion"
|
|
71
74
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
72
75
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
@@ -2407,18 +2407,47 @@ public class DexManager {
|
|
|
2407
2407
|
// ==================== XML/资源操作方法 ====================
|
|
2408
2408
|
|
|
2409
2409
|
/**
|
|
2410
|
-
* 获取 APK 的 AndroidManifest.xml
|
|
2410
|
+
* 获取 APK 的 AndroidManifest.xml(使用 ARSCLib 解码为可读 XML)
|
|
2411
2411
|
*/
|
|
2412
2412
|
public JSObject getManifestFromApk(String apkPath) throws Exception {
|
|
2413
2413
|
JSObject result = new JSObject();
|
|
2414
2414
|
|
|
2415
|
+
try {
|
|
2416
|
+
// 使用 ARSCLib 读取 Manifest
|
|
2417
|
+
com.reandroid.apk.ApkModule apkModule = com.reandroid.apk.ApkModule.loadApkFile(new java.io.File(apkPath));
|
|
2418
|
+
com.reandroid.arsc.chunk.xml.AndroidManifestBlock manifest = apkModule.getAndroidManifest();
|
|
2419
|
+
|
|
2420
|
+
if (manifest == null) {
|
|
2421
|
+
apkModule.close();
|
|
2422
|
+
throw new Exception("AndroidManifest.xml not found in APK");
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// 序列化为可读 XML
|
|
2426
|
+
String xmlContent = manifest.serializeToXml();
|
|
2427
|
+
apkModule.close();
|
|
2428
|
+
|
|
2429
|
+
result.put("manifest", xmlContent);
|
|
2430
|
+
|
|
2431
|
+
} catch (Exception e) {
|
|
2432
|
+
Log.e(TAG, "Get manifest error: " + e.getMessage(), e);
|
|
2433
|
+
// 回退到旧实现
|
|
2434
|
+
result.put("manifest", getManifestFallback(apkPath));
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
return result;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
/**
|
|
2441
|
+
* 获取 Manifest 的回退实现(使用简单 AXML 解析器)
|
|
2442
|
+
*/
|
|
2443
|
+
private String getManifestFallback(String apkPath) {
|
|
2415
2444
|
java.util.zip.ZipFile zipFile = null;
|
|
2416
2445
|
try {
|
|
2417
2446
|
zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2418
2447
|
java.util.zip.ZipEntry manifestEntry = zipFile.getEntry("AndroidManifest.xml");
|
|
2419
2448
|
|
|
2420
2449
|
if (manifestEntry == null) {
|
|
2421
|
-
|
|
2450
|
+
return "# AndroidManifest.xml not found";
|
|
2422
2451
|
}
|
|
2423
2452
|
|
|
2424
2453
|
java.io.InputStream is = zipFile.getInputStream(manifestEntry);
|
|
@@ -2430,19 +2459,15 @@ public class DexManager {
|
|
|
2430
2459
|
}
|
|
2431
2460
|
is.close();
|
|
2432
2461
|
|
|
2433
|
-
|
|
2434
|
-
byte[] axmlData = baos.toByteArray();
|
|
2435
|
-
String xmlContent = decodeAxml(axmlData);
|
|
2436
|
-
|
|
2437
|
-
result.put("manifest", xmlContent);
|
|
2462
|
+
return decodeAxml(baos.toByteArray());
|
|
2438
2463
|
|
|
2464
|
+
} catch (Exception e) {
|
|
2465
|
+
return "# Error reading manifest: " + e.getMessage();
|
|
2439
2466
|
} finally {
|
|
2440
2467
|
if (zipFile != null) {
|
|
2441
2468
|
try { zipFile.close(); } catch (Exception ignored) {}
|
|
2442
2469
|
}
|
|
2443
2470
|
}
|
|
2444
|
-
|
|
2445
|
-
return result;
|
|
2446
2471
|
}
|
|
2447
2472
|
|
|
2448
2473
|
/**
|
|
@@ -2636,7 +2661,7 @@ public class DexManager {
|
|
|
2636
2661
|
}
|
|
2637
2662
|
|
|
2638
2663
|
/**
|
|
2639
|
-
* 精准替换 AndroidManifest.xml
|
|
2664
|
+
* 精准替换 AndroidManifest.xml 中的字符串(使用 ARSCLib,无长度限制)
|
|
2640
2665
|
*/
|
|
2641
2666
|
public JSObject replaceInManifest(String apkPath, org.json.JSONArray replacements) throws Exception {
|
|
2642
2667
|
JSObject result = new JSObject();
|
|
@@ -2644,26 +2669,17 @@ public class DexManager {
|
|
|
2644
2669
|
int replacedCount = 0;
|
|
2645
2670
|
|
|
2646
2671
|
try {
|
|
2647
|
-
//
|
|
2648
|
-
|
|
2649
|
-
|
|
2672
|
+
// 使用 ARSCLib 读取和修改 Manifest
|
|
2673
|
+
com.reandroid.apk.ApkModule apkModule = com.reandroid.apk.ApkModule.loadApkFile(new java.io.File(apkPath));
|
|
2674
|
+
com.reandroid.arsc.chunk.xml.AndroidManifestBlock manifest = apkModule.getAndroidManifest();
|
|
2650
2675
|
|
|
2651
|
-
if (
|
|
2676
|
+
if (manifest == null) {
|
|
2652
2677
|
throw new Exception("AndroidManifest.xml not found in APK");
|
|
2653
2678
|
}
|
|
2654
2679
|
|
|
2655
|
-
//
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
byte[] buffer = new byte[8192];
|
|
2659
|
-
int len;
|
|
2660
|
-
while ((len = is.read(buffer)) != -1) {
|
|
2661
|
-
baos.write(buffer, 0, len);
|
|
2662
|
-
}
|
|
2663
|
-
is.close();
|
|
2664
|
-
zipFile.close();
|
|
2665
|
-
|
|
2666
|
-
byte[] axmlData = baos.toByteArray();
|
|
2680
|
+
// 获取 XML 字符串进行替换
|
|
2681
|
+
String xmlContent = manifest.serializeToXml();
|
|
2682
|
+
String originalContent = xmlContent;
|
|
2667
2683
|
|
|
2668
2684
|
// 执行替换
|
|
2669
2685
|
for (int i = 0; i < replacements.length(); i++) {
|
|
@@ -2671,20 +2687,28 @@ public class DexManager {
|
|
|
2671
2687
|
String oldValue = replacement.getString("oldValue");
|
|
2672
2688
|
String newValue = replacement.getString("newValue");
|
|
2673
2689
|
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2690
|
+
int count = 0;
|
|
2691
|
+
String before = xmlContent;
|
|
2692
|
+
xmlContent = xmlContent.replace(oldValue, newValue);
|
|
2693
|
+
|
|
2694
|
+
// 计算替换次数
|
|
2695
|
+
int idx = 0;
|
|
2696
|
+
while ((idx = before.indexOf(oldValue, idx)) != -1) {
|
|
2697
|
+
count++;
|
|
2698
|
+
idx += oldValue.length();
|
|
2699
|
+
}
|
|
2677
2700
|
|
|
2678
2701
|
JSObject detail = new JSObject();
|
|
2679
2702
|
detail.put("oldValue", oldValue);
|
|
2680
2703
|
detail.put("newValue", newValue);
|
|
2681
|
-
detail.put("count",
|
|
2704
|
+
detail.put("count", count);
|
|
2682
2705
|
details.put(detail);
|
|
2683
2706
|
|
|
2684
|
-
replacedCount +=
|
|
2707
|
+
replacedCount += count;
|
|
2685
2708
|
}
|
|
2686
2709
|
|
|
2687
2710
|
if (replacedCount == 0) {
|
|
2711
|
+
apkModule.close();
|
|
2688
2712
|
result.put("success", true);
|
|
2689
2713
|
result.put("replacedCount", 0);
|
|
2690
2714
|
result.put("details", details);
|
|
@@ -2692,52 +2716,17 @@ public class DexManager {
|
|
|
2692
2716
|
return result;
|
|
2693
2717
|
}
|
|
2694
2718
|
|
|
2695
|
-
//
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(
|
|
2700
|
-
new java.io.BufferedInputStream(new java.io.FileInputStream(apkFile)));
|
|
2701
|
-
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(
|
|
2702
|
-
new java.io.BufferedOutputStream(new java.io.FileOutputStream(tempApk)));
|
|
2703
|
-
|
|
2704
|
-
java.util.zip.ZipEntry entry;
|
|
2705
|
-
while ((entry = zis.getNextEntry()) != null) {
|
|
2706
|
-
if (entry.getName().equals("AndroidManifest.xml")) {
|
|
2707
|
-
// 写入修改后的 Manifest
|
|
2708
|
-
java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry("AndroidManifest.xml");
|
|
2709
|
-
newEntry.setMethod(java.util.zip.ZipEntry.DEFLATED);
|
|
2710
|
-
zos.putNextEntry(newEntry);
|
|
2711
|
-
zos.write(axmlData);
|
|
2712
|
-
zos.closeEntry();
|
|
2713
|
-
} else {
|
|
2714
|
-
// 复制其他文件
|
|
2715
|
-
java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry(entry.getName());
|
|
2716
|
-
newEntry.setTime(entry.getTime());
|
|
2717
|
-
if (entry.getMethod() == java.util.zip.ZipEntry.STORED) {
|
|
2718
|
-
newEntry.setMethod(java.util.zip.ZipEntry.STORED);
|
|
2719
|
-
newEntry.setSize(entry.getSize());
|
|
2720
|
-
newEntry.setCrc(entry.getCrc());
|
|
2721
|
-
} else {
|
|
2722
|
-
newEntry.setMethod(java.util.zip.ZipEntry.DEFLATED);
|
|
2723
|
-
}
|
|
2724
|
-
zos.putNextEntry(newEntry);
|
|
2725
|
-
if (!entry.isDirectory()) {
|
|
2726
|
-
byte[] buf = new byte[8192];
|
|
2727
|
-
int n;
|
|
2728
|
-
while ((n = zis.read(buf)) != -1) {
|
|
2729
|
-
zos.write(buf, 0, n);
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
zos.closeEntry();
|
|
2733
|
-
}
|
|
2734
|
-
zis.closeEntry();
|
|
2735
|
-
}
|
|
2719
|
+
// 解析修改后的 XML 并更新 Manifest
|
|
2720
|
+
manifest.parseXmlString(xmlContent);
|
|
2721
|
+
manifest.refresh();
|
|
2736
2722
|
|
|
2737
|
-
|
|
2738
|
-
|
|
2723
|
+
// 保存 APK
|
|
2724
|
+
java.io.File tempApk = new java.io.File(apkPath + ".tmp");
|
|
2725
|
+
apkModule.writeApk(tempApk);
|
|
2726
|
+
apkModule.close();
|
|
2739
2727
|
|
|
2740
2728
|
// 替换原文件
|
|
2729
|
+
java.io.File apkFile = new java.io.File(apkPath);
|
|
2741
2730
|
if (!apkFile.delete()) {
|
|
2742
2731
|
Log.e(TAG, "Failed to delete original APK");
|
|
2743
2732
|
}
|