capacitor-dex-editor 0.0.57 → 0.0.59

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.
@@ -571,6 +571,36 @@ public class DexEditorPluginPlugin extends Plugin {
571
571
  );
572
572
  break;
573
573
 
574
+ case "listMethodsFromSession":
575
+ result.put("data", dexManager.listMethodsFromSession(
576
+ params.getString("sessionId"),
577
+ params.getString("className")
578
+ ));
579
+ break;
580
+
581
+ case "listFieldsFromSession":
582
+ result.put("data", dexManager.listFieldsFromSession(
583
+ params.getString("sessionId"),
584
+ params.getString("className")
585
+ ));
586
+ break;
587
+
588
+ case "renameClassInSession":
589
+ dexManager.renameClassInSession(
590
+ params.getString("sessionId"),
591
+ params.getString("oldClassName"),
592
+ params.getString("newClassName")
593
+ );
594
+ break;
595
+
596
+ case "modifyResource":
597
+ result.put("data", dexManager.modifyResourceInApk(
598
+ params.getString("apkPath"),
599
+ params.getString("resourcePath"),
600
+ params.getString("newContent")
601
+ ));
602
+ break;
603
+
574
604
  case "listSessions":
575
605
  result.put("data", dexManager.listAllSessions());
576
606
  break;
@@ -1158,30 +1158,6 @@ public class DexManager {
1158
1158
  return "";
1159
1159
  }
1160
1160
 
1161
- private String replaceMethodInSmali(String classSmali, String methodName,
1162
- String signature, String newMethodCode) {
1163
- String methodStart = ".method";
1164
- String methodEnd = ".end method";
1165
-
1166
- int searchStart = 0;
1167
- while (true) {
1168
- int start = classSmali.indexOf(methodStart, searchStart);
1169
- if (start == -1) break;
1170
-
1171
- int end = classSmali.indexOf(methodEnd, start);
1172
- if (end == -1) break;
1173
-
1174
- String methodBlock = classSmali.substring(start, end + methodEnd.length());
1175
- if (methodBlock.contains(methodName)) {
1176
- return classSmali.substring(0, start) + newMethodCode +
1177
- classSmali.substring(end + methodEnd.length());
1178
- }
1179
-
1180
- searchStart = end + methodEnd.length();
1181
- }
1182
-
1183
- return classSmali;
1184
- }
1185
1161
 
1186
1162
  private String insertMethodToSmali(String classSmali, String methodCode) {
1187
1163
  // 在类结束前插入方法
@@ -2171,6 +2147,191 @@ public class DexManager {
2171
2147
  return result.toString();
2172
2148
  }
2173
2149
 
2150
+ /**
2151
+ * 列出会话中类的所有方法
2152
+ */
2153
+ public JSObject listMethodsFromSession(String sessionId, String className) throws Exception {
2154
+ MultiDexSession session = multiDexSessions.get(sessionId);
2155
+ if (session == null) {
2156
+ throw new IllegalArgumentException("Session not found: " + sessionId);
2157
+ }
2158
+
2159
+ String targetType = convertClassNameToType(className);
2160
+ JSObject result = new JSObject();
2161
+ JSArray methods = new JSArray();
2162
+
2163
+ for (DexBackedDexFile dexFile : session.dexFiles.values()) {
2164
+ for (ClassDef classDef : dexFile.getClasses()) {
2165
+ if (classDef.getType().equals(targetType)) {
2166
+ for (com.android.tools.smali.dexlib2.iface.Method method : classDef.getMethods()) {
2167
+ JSObject methodInfo = new JSObject();
2168
+ methodInfo.put("name", method.getName());
2169
+ methodInfo.put("returnType", method.getReturnType());
2170
+ methodInfo.put("accessFlags", method.getAccessFlags());
2171
+
2172
+ // 参数类型
2173
+ StringBuilder params = new StringBuilder("(");
2174
+ for (CharSequence param : method.getParameterTypes()) {
2175
+ params.append(param);
2176
+ }
2177
+ params.append(")").append(method.getReturnType());
2178
+ methodInfo.put("signature", params.toString());
2179
+
2180
+ methods.put(methodInfo);
2181
+ }
2182
+ break;
2183
+ }
2184
+ }
2185
+ }
2186
+
2187
+ result.put("className", className);
2188
+ result.put("methods", methods);
2189
+ result.put("count", methods.length());
2190
+ return result;
2191
+ }
2192
+
2193
+ /**
2194
+ * 列出会话中类的所有字段
2195
+ */
2196
+ public JSObject listFieldsFromSession(String sessionId, String className) throws Exception {
2197
+ MultiDexSession session = multiDexSessions.get(sessionId);
2198
+ if (session == null) {
2199
+ throw new IllegalArgumentException("Session not found: " + sessionId);
2200
+ }
2201
+
2202
+ String targetType = convertClassNameToType(className);
2203
+ JSObject result = new JSObject();
2204
+ JSArray fields = new JSArray();
2205
+
2206
+ for (DexBackedDexFile dexFile : session.dexFiles.values()) {
2207
+ for (ClassDef classDef : dexFile.getClasses()) {
2208
+ if (classDef.getType().equals(targetType)) {
2209
+ for (com.android.tools.smali.dexlib2.iface.Field field : classDef.getFields()) {
2210
+ JSObject fieldInfo = new JSObject();
2211
+ fieldInfo.put("name", field.getName());
2212
+ fieldInfo.put("type", field.getType());
2213
+ fieldInfo.put("accessFlags", field.getAccessFlags());
2214
+ fields.put(fieldInfo);
2215
+ }
2216
+ break;
2217
+ }
2218
+ }
2219
+ }
2220
+
2221
+ result.put("className", className);
2222
+ result.put("fields", fields);
2223
+ result.put("count", fields.length());
2224
+ return result;
2225
+ }
2226
+
2227
+ /**
2228
+ * 重命名会话中的类
2229
+ */
2230
+ public void renameClassInSession(String sessionId, String oldClassName, String newClassName) throws Exception {
2231
+ MultiDexSession session = multiDexSessions.get(sessionId);
2232
+ if (session == null) {
2233
+ throw new IllegalArgumentException("Session not found: " + sessionId);
2234
+ }
2235
+
2236
+ // 获取原类的 Smali
2237
+ JSObject classResult = getClassSmaliFromSession(sessionId, oldClassName);
2238
+ String smaliContent = classResult.optString("smaliContent", "");
2239
+
2240
+ if (smaliContent.isEmpty()) {
2241
+ throw new IllegalArgumentException("Class not found: " + oldClassName);
2242
+ }
2243
+
2244
+ String oldType = convertClassNameToType(oldClassName);
2245
+ String newType = convertClassNameToType(newClassName);
2246
+
2247
+ // 替换类名
2248
+ String newSmaliContent = smaliContent.replace(oldType, newType);
2249
+
2250
+ // 删除旧类,添加新类
2251
+ deleteClassFromSession(sessionId, oldClassName);
2252
+ addClassToSession(sessionId, newClassName, newSmaliContent);
2253
+
2254
+ Log.d(TAG, "Renamed class: " + oldClassName + " -> " + newClassName);
2255
+ }
2256
+
2257
+ /**
2258
+ * 修改 APK 中的资源文件
2259
+ */
2260
+ public JSObject modifyResourceInApk(String apkPath, String resourcePath, String newContent) throws Exception {
2261
+ JSObject result = new JSObject();
2262
+
2263
+ // 注意:这是一个简化实现,实际上修改二进制 XML 需要使用 AXML 编码
2264
+ // 这里仅支持非编译的文本文件(如 assets 中的文件)
2265
+
2266
+ java.io.File apkFile = new java.io.File(apkPath);
2267
+ java.io.File tempApkFile = new java.io.File(apkPath + ".tmp");
2268
+
2269
+ java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(new java.io.FileInputStream(apkFile));
2270
+ java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(new java.io.FileOutputStream(tempApkFile));
2271
+
2272
+ java.util.zip.ZipEntry entry;
2273
+ boolean found = false;
2274
+
2275
+ while ((entry = zis.getNextEntry()) != null) {
2276
+ String entryName = entry.getName();
2277
+
2278
+ if (entryName.equals(resourcePath) || entryName.equals(resourcePath.replaceFirst("^/+", ""))) {
2279
+ // 替换资源内容
2280
+ java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry(entryName);
2281
+ byte[] contentBytes = newContent.getBytes("UTF-8");
2282
+ newEntry.setSize(contentBytes.length);
2283
+ zos.putNextEntry(newEntry);
2284
+ zos.write(contentBytes);
2285
+ zos.closeEntry();
2286
+ found = true;
2287
+ } else {
2288
+ // 复制原内容
2289
+ java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry(entryName);
2290
+ if (entry.getMethod() == java.util.zip.ZipEntry.STORED) {
2291
+ newEntry.setMethod(java.util.zip.ZipEntry.STORED);
2292
+ newEntry.setSize(entry.getSize());
2293
+ newEntry.setCrc(entry.getCrc());
2294
+ }
2295
+ zos.putNextEntry(newEntry);
2296
+
2297
+ byte[] buffer = new byte[8192];
2298
+ int len;
2299
+ while ((len = zis.read(buffer)) > 0) {
2300
+ zos.write(buffer, 0, len);
2301
+ }
2302
+ zos.closeEntry();
2303
+ }
2304
+ }
2305
+
2306
+ zis.close();
2307
+ zos.close();
2308
+
2309
+ if (!found) {
2310
+ tempApkFile.delete();
2311
+ result.put("success", false);
2312
+ result.put("error", "资源文件未找到: " + resourcePath);
2313
+ return result;
2314
+ }
2315
+
2316
+ // 替换原文件
2317
+ if (!apkFile.delete()) {
2318
+ tempApkFile.delete();
2319
+ result.put("success", false);
2320
+ result.put("error", "无法删除原 APK");
2321
+ return result;
2322
+ }
2323
+
2324
+ if (!tempApkFile.renameTo(apkFile)) {
2325
+ copyFile(tempApkFile, apkFile);
2326
+ tempApkFile.delete();
2327
+ }
2328
+
2329
+ result.put("success", true);
2330
+ result.put("message", "资源文件已修改");
2331
+ result.put("needSign", true);
2332
+ return result;
2333
+ }
2334
+
2174
2335
  /**
2175
2336
  * 保存多 DEX 会话的修改到 APK
2176
2337
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.57",
3
+ "version": "0.0.59",
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",