capacitor-dex-editor 0.0.54 → 0.0.55

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.
@@ -2232,51 +2232,31 @@ public class DexManager {
2232
2232
  }
2233
2233
 
2234
2234
  try {
2235
- // 创建临时目录存储 smali 文件(使用系统临时目录)
2236
- java.io.File tempDir = new java.io.File(System.getProperty("java.io.tmpdir"), "smali_temp_" + System.currentTimeMillis());
2237
- if (!tempDir.mkdirs()) {
2238
- result.put("success", false);
2239
- result.put("error", "无法创建临时目录");
2240
- return result;
2241
- }
2242
-
2243
- // 将类名转换为文件路径
2244
- String classPath = className.replace(".", "/") + ".smali";
2245
- java.io.File smaliFile = new java.io.File(tempDir, classPath);
2246
- smaliFile.getParentFile().mkdirs();
2247
-
2248
- // 写入 smali 文件
2249
- try (java.io.FileWriter writer = new java.io.FileWriter(smaliFile)) {
2250
- writer.write(smaliContent);
2251
- }
2252
-
2253
- Log.d(TAG, "Smali file written to: " + smaliFile.getAbsolutePath());
2254
-
2255
- // 使用 smali 库编译 smali 文件为 dex
2256
- // 注意:这需要 smali 库的支持
2257
- java.io.File outputDex = new java.io.File(tempDir, "classes_modified.dex");
2235
+ // 使用优化的 smali2.Smali 直接编译成 ClassDef(无需临时文件)
2236
+ reportTitle("编译 Smali");
2237
+ reportMessage("正在编译 " + className + "...");
2238
+ reportProgress(10, 100);
2258
2239
 
2259
- // 使用 SmaliOptions 编译
2260
2240
  com.android.tools.smali.smali.SmaliOptions options = new com.android.tools.smali.smali.SmaliOptions();
2261
- options.outputDexFile = outputDex.getAbsolutePath();
2262
2241
  options.apiLevel = 30;
2263
2242
 
2264
- java.util.List<String> inputFiles = new java.util.ArrayList<>();
2265
- inputFiles.add(smaliFile.getAbsolutePath());
2266
-
2267
- boolean success = com.android.tools.smali.smali.Smali.assemble(options, inputFiles);
2268
-
2269
- if (!success) {
2243
+ // 直接编译 Smali 代码为 ClassDef
2244
+ ClassDef newClassDef;
2245
+ try {
2246
+ newClassDef = com.android.tools.smali.smali2.Smali.assemble(smaliContent, options, 35);
2247
+ } catch (Exception e) {
2270
2248
  result.put("success", false);
2271
- result.put("error", "Smali 编译失败");
2272
- cleanupTempDir(tempDir);
2249
+ result.put("error", "Smali 编译失败: " + e.getMessage());
2273
2250
  return result;
2274
2251
  }
2275
2252
 
2276
- Log.d(TAG, "Smali compiled successfully to: " + outputDex.getAbsolutePath());
2253
+ reportProgress(30, 100);
2254
+ Log.d(TAG, "Smali compiled successfully to ClassDef");
2255
+
2256
+ // 读取原 APK 中的 DEX 文件
2257
+ reportTitle("合并 DEX");
2258
+ reportMessage("读取原 DEX...");
2277
2259
 
2278
- // 将编译后的类合并到原 DEX 文件中
2279
- // 1. 读取原 APK 中的 DEX 文件
2280
2260
  java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
2281
2261
  java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
2282
2262
 
@@ -2284,14 +2264,13 @@ public class DexManager {
2284
2264
  result.put("success", false);
2285
2265
  result.put("error", "DEX 文件未找到: " + dexPath);
2286
2266
  zipFile.close();
2287
- cleanupTempDir(tempDir);
2288
2267
  return result;
2289
2268
  }
2290
2269
 
2291
2270
  // 读取原 DEX
2292
2271
  java.io.InputStream dexInputStream = zipFile.getInputStream(dexEntry);
2293
2272
  java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
2294
- byte[] buffer = new byte[8192];
2273
+ byte[] buffer = new byte[16384];
2295
2274
  int len;
2296
2275
  while ((len = dexInputStream.read(buffer)) != -1) {
2297
2276
  baos.write(buffer, 0, len);
@@ -2299,12 +2278,12 @@ public class DexManager {
2299
2278
  byte[] originalDexBytes = baos.toByteArray();
2300
2279
  dexInputStream.close();
2301
2280
 
2302
- // 解析原 DEX 和新编译的 DEX
2281
+ // 解析原 DEX
2303
2282
  DexBackedDexFile originalDex = new DexBackedDexFile(Opcodes.getDefault(), originalDexBytes);
2304
- DexBackedDexFile newDex = DexBackedDexFile.fromInputStream(Opcodes.getDefault(),
2305
- new java.io.BufferedInputStream(new java.io.FileInputStream(outputDex)));
2306
2283
 
2307
- // 合并 DEX:用新类替换原类
2284
+ reportMessage("收集类定义...");
2285
+ reportProgress(40, 100);
2286
+
2308
2287
  String targetType = "L" + className.replace(".", "/") + ";";
2309
2288
  java.util.List<ClassDef> mergedClasses = new java.util.ArrayList<>();
2310
2289
 
@@ -2316,28 +2295,43 @@ public class DexManager {
2316
2295
  }
2317
2296
 
2318
2297
  // 添加新编译的类
2319
- for (ClassDef classDef : newDex.getClasses()) {
2320
- mergedClasses.add(classDef);
2321
- }
2298
+ mergedClasses.add(newClassDef);
2322
2299
 
2323
2300
  Log.d(TAG, "Merged " + mergedClasses.size() + " classes");
2324
2301
 
2325
- // 创建新的 DEX 文件
2326
- java.io.File mergedDexFile = new java.io.File(tempDir, "classes_merged.dex");
2302
+ // 创建新的 DEX 文件(使用内存)
2303
+ reportMessage("写入 DEX (" + mergedClasses.size() + " 个类)...");
2304
+ reportProgress(50, 100);
2305
+
2327
2306
  DexPool dexPool = new DexPool(Opcodes.getDefault());
2307
+ int totalClasses = mergedClasses.size();
2308
+ int currentClass = 0;
2328
2309
  for (ClassDef classDef : mergedClasses) {
2329
2310
  dexPool.internClass(classDef);
2311
+ currentClass++;
2312
+ if (currentClass % 100 == 0 || currentClass == totalClasses) {
2313
+ reportProgress(50 + (currentClass * 30 / totalClasses), 100);
2314
+ }
2330
2315
  }
2331
- dexPool.writeTo(new FileDataStore(mergedDexFile));
2332
2316
 
2333
- Log.d(TAG, "Merged DEX written to: " + mergedDexFile.getAbsolutePath());
2317
+ // 使用内存写入 DEX(避免临时文件)
2318
+ reportProgress(80, 100);
2319
+ com.android.tools.smali.dexlib2.writer.io.MemoryDataStore memoryStore =
2320
+ new com.android.tools.smali.dexlib2.writer.io.MemoryDataStore();
2321
+ dexPool.writeTo(memoryStore);
2322
+ byte[] newDexBytes = java.util.Arrays.copyOf(memoryStore.getBuffer(), memoryStore.getSize());
2323
+
2324
+ Log.d(TAG, "Merged DEX size: " + newDexBytes.length + " bytes");
2334
2325
 
2335
2326
  zipFile.close();
2336
2327
 
2337
2328
  // MT 风格:直接替换 APK 内的 DEX
2329
+ reportTitle("更新 APK");
2330
+ reportMessage("正在替换 DEX...");
2331
+ reportProgress(85, 100);
2332
+
2338
2333
  java.io.File apkFile = new java.io.File(apkPath);
2339
2334
  java.io.File tempApkFile = new java.io.File(apkPath + ".tmp");
2340
- byte[] newDexBytes = readFileBytes(mergedDexFile);
2341
2335
 
2342
2336
  Log.d(TAG, "Replacing DEX in APK (MT style)...");
2343
2337
 
@@ -2393,15 +2387,13 @@ public class DexManager {
2393
2387
  }
2394
2388
 
2395
2389
  Log.d(TAG, "APK updated successfully: " + apkPath);
2390
+ reportProgress(100, 100);
2396
2391
 
2397
2392
  result.put("success", true);
2398
2393
  result.put("message", "Smali 编译成功!APK 已更新");
2399
2394
  result.put("apkPath", apkPath);
2400
2395
  result.put("needSign", true);
2401
2396
 
2402
- // 清理临时文件
2403
- cleanupTempDir(tempDir);
2404
-
2405
2397
  } catch (Exception e) {
2406
2398
  Log.e(TAG, "Error saving smali: " + e.getMessage(), e);
2407
2399
  result.put("success", false);
@@ -0,0 +1,102 @@
1
+ /*
2
+ * Based on Dex-Editor-Android by developer-krushna
3
+ * Original Author @MikeAndrson
4
+ *
5
+ * 直接将 Smali 代码编译成 ClassDef,无需临时文件
6
+ */
7
+
8
+ package com.android.tools.smali.smali2;
9
+
10
+ import com.android.tools.smali.dexlib2.Opcodes;
11
+ import com.android.tools.smali.dexlib2.iface.ClassDef;
12
+ import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
13
+ import com.android.tools.smali.smali.SmaliOptions;
14
+
15
+ import java.io.StringReader;
16
+ import org.antlr.runtime.CommonTokenStream;
17
+ import org.antlr.runtime.tree.CommonTree;
18
+ import org.antlr.runtime.tree.CommonTreeNodeStream;
19
+
20
+ public class Smali {
21
+
22
+ public static ClassDef assemble(String smaliCode, SmaliOptions options, int dexVer)
23
+ throws Exception {
24
+ DexBuilder dexBuilder = new DexBuilder(Opcodes.forDexVersion(dexVer));
25
+
26
+ SmaliCatchErrFlexLexer lexer = new SmaliCatchErrFlexLexer(new StringReader(smaliCode), options.apiLevel);
27
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
28
+
29
+ SmaliCatchErrParser parser = new SmaliCatchErrParser(tokens);
30
+ parser.setVerboseErrors(options.verboseErrors);
31
+ parser.setAllowOdex(options.allowOdexOpcodes);
32
+ parser.setApiLevel(dexVer);
33
+
34
+ SmaliCatchErrParser.smali_file_return result = parser.smali_file();
35
+
36
+ if (lexer.getNumberOfSyntaxErrors() > 0) {
37
+ throw new Exception(lexer.getErrorsString());
38
+ }
39
+
40
+ if (parser.getNumberOfSyntaxErrors() > 0) {
41
+ throw new Exception(parser.getErrorsString());
42
+ }
43
+
44
+ CommonTree t = result.getTree();
45
+
46
+ CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
47
+ treeStream.setTokenStream(tokens);
48
+
49
+ SmaliCatchErrTreeWalker treeWalker = new SmaliCatchErrTreeWalker(treeStream);
50
+ treeWalker.setApiLevel(dexVer);
51
+ treeWalker.setVerboseErrors(options.verboseErrors);
52
+ treeWalker.setDexBuilder(dexBuilder);
53
+
54
+ ClassDef classDef = treeWalker.smali_file();
55
+
56
+ if (treeWalker.getNumberOfSyntaxErrors() > 0) {
57
+ throw new Exception(treeWalker.getErrorsString());
58
+ }
59
+
60
+ return classDef;
61
+ }
62
+
63
+ public static ClassDef assemble(String smaliCode, SmaliOptions options) throws Exception {
64
+ DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel));
65
+
66
+ SmaliCatchErrFlexLexer lexer = new SmaliCatchErrFlexLexer(new StringReader(smaliCode), options.apiLevel);
67
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
68
+
69
+ SmaliCatchErrParser parser = new SmaliCatchErrParser(tokens);
70
+ parser.setVerboseErrors(options.verboseErrors);
71
+ parser.setAllowOdex(options.allowOdexOpcodes);
72
+ parser.setApiLevel(options.apiLevel);
73
+
74
+ SmaliCatchErrParser.smali_file_return result = parser.smali_file();
75
+
76
+ if (lexer.getNumberOfSyntaxErrors() > 0) {
77
+ throw new Exception(lexer.getErrorsString());
78
+ }
79
+
80
+ if (parser.getNumberOfSyntaxErrors() > 0) {
81
+ throw new Exception(parser.getErrorsString());
82
+ }
83
+
84
+ CommonTree t = result.getTree();
85
+
86
+ CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
87
+ treeStream.setTokenStream(tokens);
88
+
89
+ SmaliCatchErrTreeWalker treeWalker = new SmaliCatchErrTreeWalker(treeStream);
90
+ treeWalker.setApiLevel(options.apiLevel);
91
+ treeWalker.setVerboseErrors(options.verboseErrors);
92
+ treeWalker.setDexBuilder(dexBuilder);
93
+
94
+ ClassDef classDef = treeWalker.smali_file();
95
+
96
+ if (treeWalker.getNumberOfSyntaxErrors() > 0) {
97
+ throw new Exception(treeWalker.getErrorsString());
98
+ }
99
+
100
+ return classDef;
101
+ }
102
+ }
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Based on Dex-Editor-Android by developer-krushna
3
+ * Original Author @MikeAndrson
4
+ */
5
+
6
+ package com.android.tools.smali.smali2;
7
+
8
+ import com.android.tools.smali.smali.InvalidToken;
9
+ import com.android.tools.smali.smali.smaliFlexLexer;
10
+
11
+ import java.io.Reader;
12
+ import java.util.ArrayList;
13
+ import java.util.List;
14
+
15
+ public class SmaliCatchErrFlexLexer extends smaliFlexLexer {
16
+
17
+ private StringBuilder errors = new StringBuilder();
18
+ private List<SyntaxError> syntaxErrors = new ArrayList<>();
19
+
20
+ public SmaliCatchErrFlexLexer(Reader reader, int apiLevel) {
21
+ super(reader, apiLevel);
22
+ }
23
+
24
+ @Override
25
+ public String getErrorHeader(InvalidToken invalidToken) {
26
+ errors.append("[")
27
+ .append(invalidToken.getLine())
28
+ .append(",")
29
+ .append(invalidToken.getCharPositionInLine())
30
+ .append("] Error for input '")
31
+ .append(invalidToken.getText())
32
+ .append("': ")
33
+ .append(invalidToken.getMessage())
34
+ .append("\n");
35
+
36
+ syntaxErrors.add(new SyntaxError(
37
+ invalidToken.getLine(),
38
+ invalidToken.getCharPositionInLine(),
39
+ invalidToken.getLine(),
40
+ invalidToken.getCharPositionInLine() + invalidToken.getText().length(),
41
+ invalidToken.getMessage()
42
+ ));
43
+
44
+ return super.getErrorHeader(invalidToken);
45
+ }
46
+
47
+ public String getErrorsString() {
48
+ return errors.toString();
49
+ }
50
+
51
+ public List<SyntaxError> getErrors() {
52
+ return syntaxErrors;
53
+ }
54
+ }
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Based on Dex-Editor-Android by developer-krushna
3
+ * Original Author @MikeAndrson
4
+ */
5
+
6
+ package com.android.tools.smali.smali2;
7
+
8
+ import org.antlr.runtime.CommonTokenStream;
9
+ import org.antlr.runtime.RecognitionException;
10
+ import com.android.tools.smali.smali.smaliParser;
11
+
12
+ import java.util.ArrayList;
13
+ import java.util.List;
14
+
15
+ public class SmaliCatchErrParser extends smaliParser {
16
+
17
+ private StringBuilder errors = new StringBuilder();
18
+ private List<SyntaxError> syntaxErrors = new ArrayList<>();
19
+
20
+ public SmaliCatchErrParser(CommonTokenStream tokens) {
21
+ super(tokens);
22
+ }
23
+
24
+ @Override
25
+ public void emitErrorMessage(String msg) {
26
+ errors.append(msg).append("\n");
27
+ }
28
+
29
+ public String getErrorsString() {
30
+ return errors.toString();
31
+ }
32
+
33
+ @Override
34
+ public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
35
+ syntaxErrors.add(new SyntaxError(
36
+ e.line,
37
+ e.charPositionInLine,
38
+ e.line,
39
+ e.charPositionInLine + (e.token != null ? e.token.getText().length() : 1),
40
+ getErrorMessage(e, tokenNames)
41
+ ));
42
+
43
+ super.displayRecognitionError(tokenNames, e);
44
+ }
45
+
46
+ public List<SyntaxError> getErrors() {
47
+ return syntaxErrors;
48
+ }
49
+ }
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Based on Dex-Editor-Android by developer-krushna
3
+ * Original Author @MikeAndrson
4
+ */
5
+
6
+ package com.android.tools.smali.smali2;
7
+
8
+ import org.antlr.runtime.RecognitionException;
9
+ import org.antlr.runtime.tree.CommonTreeNodeStream;
10
+ import com.android.tools.smali.smali.smaliTreeWalker;
11
+
12
+ import java.util.ArrayList;
13
+ import java.util.List;
14
+
15
+ public class SmaliCatchErrTreeWalker extends smaliTreeWalker {
16
+
17
+ private StringBuilder errors = new StringBuilder();
18
+ private List<SyntaxError> syntaxErrors = new ArrayList<>();
19
+
20
+ public SmaliCatchErrTreeWalker(CommonTreeNodeStream treeStream) {
21
+ super(treeStream);
22
+ }
23
+
24
+ @Override
25
+ public void emitErrorMessage(String msg) {
26
+ errors.append(msg);
27
+ errors.append("\n");
28
+ }
29
+
30
+ public String getErrorsString() {
31
+ return errors.toString();
32
+ }
33
+
34
+ @Override
35
+ public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
36
+ syntaxErrors.add(new SyntaxError(
37
+ e.line,
38
+ e.charPositionInLine,
39
+ e.line,
40
+ e.charPositionInLine + (e.token != null ? e.token.getText().length() : 1),
41
+ getErrorMessage(e, tokenNames)
42
+ ));
43
+ super.displayRecognitionError(tokenNames, e);
44
+ }
45
+
46
+ public List<SyntaxError> getErrors() {
47
+ return syntaxErrors;
48
+ }
49
+ }
@@ -0,0 +1,42 @@
1
+ /*
2
+ * Based on Dex-Editor-Android by developer-krushna
3
+ * Original Author @MikeAndrson
4
+ */
5
+
6
+ package com.android.tools.smali.smali2;
7
+
8
+ public class SyntaxError {
9
+ private final int startLine;
10
+ private final int startColumn;
11
+ private final int endLine;
12
+ private final int endColumn;
13
+ private final String message;
14
+
15
+ public SyntaxError(int startLine, int startColumn, int endLine, int endColumn, String message) {
16
+ this.startLine = startLine;
17
+ this.startColumn = startColumn;
18
+ this.endLine = endLine;
19
+ this.endColumn = endColumn;
20
+ this.message = message;
21
+ }
22
+
23
+ public int getStartLine() {
24
+ return startLine;
25
+ }
26
+
27
+ public int getStartColumn() {
28
+ return startColumn;
29
+ }
30
+
31
+ public int getEndLine() {
32
+ return endLine;
33
+ }
34
+
35
+ public int getEndColumn() {
36
+ return endColumn;
37
+ }
38
+
39
+ public String getMessage() {
40
+ return message;
41
+ }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.54",
3
+ "version": "0.0.55",
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",