capacitor-dex-editor 0.0.65 → 0.0.67

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.
@@ -152,13 +152,6 @@ public class DexManager {
152
152
  Map<String, DexBackedDexFile> dexFiles;
153
153
  Map<String, byte[]> dexBytes; // DEX 字节数据,用于 Rust 搜索
154
154
  Map<String, ClassDef> modifiedClasses;
155
- // 使用 LRU 缓存限制内存,最多缓存 200 个类
156
- Map<String, String> smaliCache = new java.util.LinkedHashMap<String, String>(200, 0.75f, true) {
157
- @Override
158
- protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
159
- return size() > 200;
160
- }
161
- };
162
155
  boolean modified = false;
163
156
 
164
157
  MultiDexSession(String sessionId, String apkPath) {
@@ -1708,7 +1701,7 @@ public class DexManager {
1708
1701
  }
1709
1702
 
1710
1703
  /**
1711
- * 获取多 DEX 会话中的类列表(支持分页和过滤)
1704
+ * 获取多 DEX 会话中的类列表(Rust 实现)
1712
1705
  */
1713
1706
  public JSObject getClassesFromMultiSession(String sessionId, String packageFilter, int offset, int limit) throws Exception {
1714
1707
  MultiDexSession session = multiDexSessions.get(sessionId);
@@ -1716,26 +1709,29 @@ public class DexManager {
1716
1709
  throw new IllegalArgumentException("Session not found: " + sessionId);
1717
1710
  }
1718
1711
 
1712
+ if (!RustDex.isAvailable() || session.dexBytes.isEmpty()) {
1713
+ throw new RuntimeException("Rust DEX library not available");
1714
+ }
1715
+
1719
1716
  JSObject result = new JSObject();
1720
1717
  JSArray classes = new JSArray();
1721
1718
  List<String> allClasses = new ArrayList<>();
1719
+ String filter = packageFilter != null ? packageFilter : "";
1722
1720
 
1723
- // 收集所有类
1724
- for (Map.Entry<String, DexBackedDexFile> entry : session.dexFiles.entrySet()) {
1721
+ // 使用 Rust 获取每个 DEX 的类列表
1722
+ for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
1725
1723
  String dexName = entry.getKey();
1726
- DexBackedDexFile dexFile = entry.getValue();
1724
+ byte[] dexData = entry.getValue();
1727
1725
 
1728
- for (ClassDef classDef : dexFile.getClasses()) {
1729
- String className = convertTypeToClassName(classDef.getType());
1730
-
1731
- // 包名过滤
1732
- if (packageFilter != null && !packageFilter.isEmpty()) {
1733
- if (!className.startsWith(packageFilter)) {
1734
- continue;
1726
+ String jsonResult = RustDex.listClasses(dexData, filter, 0, 100000);
1727
+ if (jsonResult != null && !jsonResult.contains("\"error\"")) {
1728
+ org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
1729
+ org.json.JSONArray rustClasses = rustResult.optJSONArray("classes");
1730
+ if (rustClasses != null) {
1731
+ for (int i = 0; i < rustClasses.length(); i++) {
1732
+ allClasses.add(rustClasses.getString(i) + "|" + dexName);
1735
1733
  }
1736
1734
  }
1737
-
1738
- allClasses.add(className + "|" + dexName);
1739
1735
  }
1740
1736
  }
1741
1737
 
@@ -1759,13 +1755,13 @@ public class DexManager {
1759
1755
  result.put("limit", limit);
1760
1756
  result.put("classes", classes);
1761
1757
  result.put("hasMore", end < total);
1758
+ result.put("engine", "rust");
1762
1759
 
1763
1760
  return result;
1764
1761
  }
1765
1762
 
1766
1763
  /**
1767
- * 在多 DEX 会话中搜索
1768
- * 优先使用 Rust 实现,如果不可用则回退到 Java 实现
1764
+ * 在多 DEX 会话中搜索(Rust 实现)
1769
1765
  */
1770
1766
  public JSObject searchInMultiSession(String sessionId, String query, String searchType,
1771
1767
  boolean caseSensitive, int maxResults) throws Exception {
@@ -1774,137 +1770,14 @@ public class DexManager {
1774
1770
  throw new IllegalArgumentException("Session not found: " + sessionId);
1775
1771
  }
1776
1772
 
1777
- // 尝试使用 Rust 实现进行搜索
1778
- if (RustDex.isAvailable() && !session.dexBytes.isEmpty()) {
1779
- try {
1780
- return searchWithRust(session, query, searchType, caseSensitive, maxResults);
1781
- } catch (Exception e) {
1782
- Log.w(TAG, "Rust search failed, falling back to Java: " + e.getMessage());
1783
- }
1773
+ if (!RustDex.isAvailable()) {
1774
+ throw new RuntimeException("Rust DEX library not available");
1784
1775
  }
1785
1776
 
1786
- // Java 回退实现
1787
- JSObject result = new JSObject();
1788
- JSArray results = new JSArray();
1789
- String queryMatch = caseSensitive ? query : query.toLowerCase();
1790
-
1791
- outerLoop:
1792
- for (Map.Entry<String, DexBackedDexFile> entry : session.dexFiles.entrySet()) {
1793
- String dexName = entry.getKey();
1794
- DexBackedDexFile dexFile = entry.getValue();
1795
-
1796
- for (ClassDef classDef : dexFile.getClasses()) {
1797
- if (results.length() >= maxResults) break outerLoop;
1798
-
1799
- String className = convertTypeToClassName(classDef.getType());
1800
- String classNameMatch = caseSensitive ? className : className.toLowerCase();
1801
-
1802
- switch (searchType) {
1803
- case "class":
1804
- if (classNameMatch.contains(queryMatch)) {
1805
- JSObject item = new JSObject();
1806
- item.put("type", "class");
1807
- item.put("className", className);
1808
- item.put("dexFile", dexName);
1809
- results.put(item);
1810
- }
1811
- break;
1812
-
1813
- case "package":
1814
- if (classNameMatch.startsWith(queryMatch)) {
1815
- JSObject item = new JSObject();
1816
- item.put("type", "package");
1817
- item.put("className", className);
1818
- item.put("dexFile", dexName);
1819
- results.put(item);
1820
- }
1821
- break;
1822
-
1823
- case "method":
1824
- for (Method method : classDef.getMethods()) {
1825
- if (results.length() >= maxResults) break outerLoop;
1826
- String methodName = method.getName();
1827
- String methodMatch = caseSensitive ? methodName : methodName.toLowerCase();
1828
- if (methodMatch.contains(queryMatch)) {
1829
- JSObject item = new JSObject();
1830
- item.put("type", "method");
1831
- item.put("className", className);
1832
- item.put("methodName", methodName);
1833
- item.put("dexFile", dexName);
1834
- results.put(item);
1835
- }
1836
- }
1837
- break;
1838
-
1839
- case "field":
1840
- for (Field field : classDef.getFields()) {
1841
- if (results.length() >= maxResults) break outerLoop;
1842
- String fieldName = field.getName();
1843
- String fieldMatch = caseSensitive ? fieldName : fieldName.toLowerCase();
1844
- if (fieldMatch.contains(queryMatch)) {
1845
- JSObject item = new JSObject();
1846
- item.put("type", "field");
1847
- item.put("className", className);
1848
- item.put("fieldName", fieldName);
1849
- item.put("dexFile", dexName);
1850
- results.put(item);
1851
- }
1852
- }
1853
- break;
1854
-
1855
- case "string":
1856
- case "code":
1857
- // 使用缓存的 smali 搜索
1858
- String smali = getCachedSmali(session, className, dexFile, classDef);
1859
- String smaliMatch = caseSensitive ? smali : smali.toLowerCase();
1860
- if (smaliMatch.contains(queryMatch)) {
1861
- JSObject item = new JSObject();
1862
- item.put("type", searchType);
1863
- item.put("className", className);
1864
- item.put("dexFile", dexName);
1865
- // 找到匹配的行
1866
- String[] lines = smali.split("\n");
1867
- for (int i = 0; i < lines.length; i++) {
1868
- String lineMatch = caseSensitive ? lines[i] : lines[i].toLowerCase();
1869
- if (lineMatch.contains(queryMatch)) {
1870
- item.put("line", i + 1);
1871
- item.put("content", lines[i].trim());
1872
- break;
1873
- }
1874
- }
1875
- results.put(item);
1876
- }
1877
- break;
1878
-
1879
- case "int":
1880
- // 搜索整数常量(使用缓存)
1881
- String smaliForInt = getCachedSmali(session, className, dexFile, classDef);
1882
- if (smaliForInt.contains("0x" + query) || smaliForInt.contains(" " + query + "\n") ||
1883
- smaliForInt.contains(" " + query + " ")) {
1884
- JSObject item = new JSObject();
1885
- item.put("type", "int");
1886
- item.put("className", className);
1887
- item.put("dexFile", dexName);
1888
- results.put(item);
1889
- }
1890
- break;
1891
- }
1892
- }
1777
+ if (session.dexBytes.isEmpty()) {
1778
+ throw new RuntimeException("No DEX data loaded");
1893
1779
  }
1894
1780
 
1895
- result.put("query", query);
1896
- result.put("searchType", searchType);
1897
- result.put("total", results.length());
1898
- result.put("results", results);
1899
-
1900
- return result;
1901
- }
1902
-
1903
- /**
1904
- * 使用 Rust 实现搜索(高性能)
1905
- */
1906
- private JSObject searchWithRust(MultiDexSession session, String query, String searchType,
1907
- boolean caseSensitive, int maxResults) throws Exception {
1908
1781
  JSObject result = new JSObject();
1909
1782
  JSArray allResults = new JSArray();
1910
1783
 
@@ -1912,11 +1785,9 @@ public class DexManager {
1912
1785
  String dexName = entry.getKey();
1913
1786
  byte[] dexData = entry.getValue();
1914
1787
 
1915
- // 调用 Rust 搜索
1916
1788
  String jsonResult = RustDex.searchInDex(dexData, query, searchType, caseSensitive, maxResults);
1917
1789
 
1918
1790
  if (jsonResult != null && !jsonResult.contains("\"error\"")) {
1919
- // 解析 Rust 返回的 JSON
1920
1791
  org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
1921
1792
  org.json.JSONArray rustResults = rustResult.optJSONArray("results");
1922
1793
 
@@ -1967,23 +1838,9 @@ public class DexManager {
1967
1838
  }
1968
1839
  }
1969
1840
 
1970
- /**
1971
- * 获取缓存的 Smali 代码(用于搜索优化)
1972
- */
1973
- private String getCachedSmali(MultiDexSession session, String className, DexBackedDexFile dexFile, ClassDef classDef) {
1974
- // 先查缓存
1975
- String cached = session.smaliCache.get(className);
1976
- if (cached != null) {
1977
- return cached;
1978
- }
1979
- // 缓存未命中,反编译并缓存
1980
- String smali = getSmaliForClass(dexFile, classDef);
1981
- session.smaliCache.put(className, smali);
1982
- return smali;
1983
- }
1984
1841
 
1985
1842
  /**
1986
- * 从多 DEX 会话获取类的 Smali 代码
1843
+ * 从多 DEX 会话获取类的 Smali 代码(Rust 实现)
1987
1844
  */
1988
1845
  public JSObject getClassSmaliFromSession(String sessionId, String className) throws Exception {
1989
1846
  MultiDexSession session = multiDexSessions.get(sessionId);
@@ -1991,20 +1848,24 @@ public class DexManager {
1991
1848
  throw new IllegalArgumentException("Session not found: " + sessionId);
1992
1849
  }
1993
1850
 
1994
- String targetType = convertClassNameToType(className);
1851
+ if (!RustDex.isAvailable()) {
1852
+ throw new RuntimeException("Rust DEX library not available");
1853
+ }
1995
1854
 
1996
- for (Map.Entry<String, DexBackedDexFile> entry : session.dexFiles.entrySet()) {
1855
+ // 使用 Rust 获取 Smali
1856
+ for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
1997
1857
  String dexName = entry.getKey();
1998
- DexBackedDexFile dexFile = entry.getValue();
1858
+ byte[] dexData = entry.getValue();
1999
1859
 
2000
- for (ClassDef classDef : dexFile.getClasses()) {
2001
- if (classDef.getType().equals(targetType)) {
2002
- JSObject result = new JSObject();
2003
- result.put("className", className);
2004
- result.put("dexFile", dexName);
2005
- result.put("smaliContent", getSmaliForClass(dexFile, classDef));
2006
- return result;
2007
- }
1860
+ String jsonResult = RustDex.getClassSmali(dexData, className);
1861
+ if (jsonResult != null && !jsonResult.contains("\"error\"")) {
1862
+ org.json.JSONObject rustResult = new org.json.JSONObject(jsonResult);
1863
+ JSObject result = new JSObject();
1864
+ result.put("className", className);
1865
+ result.put("dexFile", dexName);
1866
+ result.put("smaliContent", rustResult.optString("smaliContent", ""));
1867
+ result.put("engine", "rust");
1868
+ return result;
2008
1869
  }
2009
1870
  }
2010
1871
 
@@ -2012,7 +1873,7 @@ public class DexManager {
2012
1873
  }
2013
1874
 
2014
1875
  /**
2015
- * 修改类并保存到多 DEX 会话
1876
+ * 修改类并保存到多 DEX 会话(Rust 实现)
2016
1877
  */
2017
1878
  public void modifyClassInSession(String sessionId, String className, String smaliContent) throws Exception {
2018
1879
  MultiDexSession session = multiDexSessions.get(sessionId);
@@ -2020,36 +1881,41 @@ public class DexManager {
2020
1881
  throw new IllegalArgumentException("Session not found: " + sessionId);
2021
1882
  }
2022
1883
 
2023
- String targetType = convertClassNameToType(className);
1884
+ if (!RustDex.isAvailable()) {
1885
+ throw new RuntimeException("Rust DEX library not available");
1886
+ }
2024
1887
 
2025
1888
  // 找到类所在的 DEX
2026
1889
  String targetDex = null;
2027
- for (Map.Entry<String, DexBackedDexFile> entry : session.dexFiles.entrySet()) {
2028
- for (ClassDef classDef : entry.getValue().getClasses()) {
2029
- if (classDef.getType().equals(targetType)) {
2030
- targetDex = entry.getKey();
2031
- break;
2032
- }
1890
+ for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
1891
+ String jsonResult = RustDex.getClassSmali(entry.getValue(), className);
1892
+ if (jsonResult != null && !jsonResult.contains("\"error\"")) {
1893
+ targetDex = entry.getKey();
1894
+ break;
2033
1895
  }
2034
- if (targetDex != null) break;
2035
1896
  }
2036
1897
 
2037
1898
  if (targetDex == null) {
2038
1899
  throw new IllegalArgumentException("Class not found: " + className);
2039
1900
  }
2040
1901
 
2041
- // 编译新的 Smali
2042
- ClassDef newClassDef = compileSmaliToClass(smaliContent, Opcodes.getDefault());
1902
+ // 使用 Rust 修改类
1903
+ byte[] originalDex = session.dexBytes.get(targetDex);
1904
+ byte[] modifiedDex = RustDex.modifyClass(originalDex, className, smaliContent);
1905
+
1906
+ if (modifiedDex == null) {
1907
+ throw new RuntimeException("Failed to modify class: " + className);
1908
+ }
2043
1909
 
2044
- // 记录修改
2045
- session.modifiedClasses.put(targetDex + "|" + className, newClassDef);
1910
+ // 更新 DEX 字节数据
1911
+ session.dexBytes.put(targetDex, modifiedDex);
2046
1912
  session.modified = true;
2047
1913
 
2048
- Log.d(TAG, "Modified class in session: " + className);
1914
+ Log.d(TAG, "Modified class in session (Rust): " + className);
2049
1915
  }
2050
1916
 
2051
1917
  /**
2052
- * 添加新类到会话
1918
+ * 添加新类到会话(Rust 实现)
2053
1919
  */
2054
1920
  public void addClassToSession(String sessionId, String className, String smaliContent) throws Exception {
2055
1921
  MultiDexSession session = multiDexSessions.get(sessionId);
@@ -2057,23 +1923,33 @@ public class DexManager {
2057
1923
  throw new IllegalArgumentException("Session not found: " + sessionId);
2058
1924
  }
2059
1925
 
2060
- // 编译 Smali
2061
- ClassDef newClassDef = compileSmaliToClass(smaliContent, Opcodes.getDefault());
1926
+ if (!RustDex.isAvailable()) {
1927
+ throw new RuntimeException("Rust DEX library not available");
1928
+ }
2062
1929
 
2063
1930
  // 添加到第一个 DEX(默认 classes.dex)
2064
1931
  String targetDex = "classes.dex";
2065
- if (!session.dexFiles.containsKey(targetDex) && !session.dexFiles.isEmpty()) {
2066
- targetDex = session.dexFiles.keySet().iterator().next();
1932
+ if (!session.dexBytes.containsKey(targetDex) && !session.dexBytes.isEmpty()) {
1933
+ targetDex = session.dexBytes.keySet().iterator().next();
2067
1934
  }
2068
1935
 
2069
- session.modifiedClasses.put(targetDex + "|" + className, newClassDef);
1936
+ // 使用 Rust 添加类
1937
+ byte[] originalDex = session.dexBytes.get(targetDex);
1938
+ byte[] modifiedDex = RustDex.addClass(originalDex, smaliContent);
1939
+
1940
+ if (modifiedDex == null) {
1941
+ throw new RuntimeException("Failed to add class: " + className);
1942
+ }
1943
+
1944
+ // 更新 DEX 字节数据
1945
+ session.dexBytes.put(targetDex, modifiedDex);
2070
1946
  session.modified = true;
2071
1947
 
2072
- Log.d(TAG, "Added class to session: " + className);
1948
+ Log.d(TAG, "Added class to session (Rust): " + className);
2073
1949
  }
2074
1950
 
2075
1951
  /**
2076
- * 从会话中删除类
1952
+ * 从会话中删除类(Rust 实现)
2077
1953
  */
2078
1954
  public void deleteClassFromSession(String sessionId, String className) throws Exception {
2079
1955
  MultiDexSession session = multiDexSessions.get(sessionId);
@@ -2081,29 +1957,37 @@ public class DexManager {
2081
1957
  throw new IllegalArgumentException("Session not found: " + sessionId);
2082
1958
  }
2083
1959
 
2084
- String targetType = convertClassNameToType(className);
1960
+ if (!RustDex.isAvailable()) {
1961
+ throw new RuntimeException("Rust DEX library not available");
1962
+ }
2085
1963
 
2086
- // 找到类所在的 DEX 并标记为删除
1964
+ // 找到类所在的 DEX
2087
1965
  String targetDex = null;
2088
- for (Map.Entry<String, DexBackedDexFile> entry : session.dexFiles.entrySet()) {
2089
- for (ClassDef classDef : entry.getValue().getClasses()) {
2090
- if (classDef.getType().equals(targetType)) {
2091
- targetDex = entry.getKey();
2092
- break;
2093
- }
1966
+ for (Map.Entry<String, byte[]> entry : session.dexBytes.entrySet()) {
1967
+ String jsonResult = RustDex.getClassSmali(entry.getValue(), className);
1968
+ if (jsonResult != null && !jsonResult.contains("\"error\"")) {
1969
+ targetDex = entry.getKey();
1970
+ break;
2094
1971
  }
2095
- if (targetDex != null) break;
2096
1972
  }
2097
1973
 
2098
1974
  if (targetDex == null) {
2099
1975
  throw new IllegalArgumentException("Class not found: " + className);
2100
1976
  }
2101
1977
 
2102
- // null 标记删除
2103
- session.modifiedClasses.put(targetDex + "|" + className, null);
1978
+ // 使用 Rust 删除类
1979
+ byte[] originalDex = session.dexBytes.get(targetDex);
1980
+ byte[] modifiedDex = RustDex.deleteClass(originalDex, className);
1981
+
1982
+ if (modifiedDex == null) {
1983
+ throw new RuntimeException("Failed to delete class: " + className);
1984
+ }
1985
+
1986
+ // 更新 DEX 字节数据
1987
+ session.dexBytes.put(targetDex, modifiedDex);
2104
1988
  session.modified = true;
2105
1989
 
2106
- Log.d(TAG, "Deleted class from session: " + className);
1990
+ Log.d(TAG, "Deleted class from session (Rust): " + className);
2107
1991
  }
2108
1992
 
2109
1993
  /**
@@ -72,15 +72,37 @@ public class RustDex {
72
72
  );
73
73
 
74
74
  /**
75
- * Java 回退实现 - 当 Rust 库不可用时使用
75
+ * 修改 DEX 中的类
76
+ * @param dexBytes DEX 文件字节数组
77
+ * @param className 类名
78
+ * @param newSmali 新的 Smali 代码
79
+ * @return 修改后的 DEX 字节数组,失败返回 null
76
80
  */
77
- public static String searchInDexFallback(
81
+ public static native byte[] modifyClass(
78
82
  byte[] dexBytes,
79
- String query,
80
- String searchType,
81
- boolean caseSensitive,
82
- int maxResults
83
- ) {
84
- return "{\"error\": \"Rust library not available, using Java implementation\", \"fallback\": true}";
85
- }
83
+ String className,
84
+ String newSmali
85
+ );
86
+
87
+ /**
88
+ * 添加新类到 DEX
89
+ * @param dexBytes DEX 文件字节数组
90
+ * @param newSmali 新类的 Smali 代码
91
+ * @return 修改后的 DEX 字节数组,失败返回 null
92
+ */
93
+ public static native byte[] addClass(
94
+ byte[] dexBytes,
95
+ String newSmali
96
+ );
97
+
98
+ /**
99
+ * 从 DEX 中删除类
100
+ * @param dexBytes DEX 文件字节数组
101
+ * @param className 要删除的类名
102
+ * @return 修改后的 DEX 字节数组,失败返回 null
103
+ */
104
+ public static native byte[] deleteClass(
105
+ byte[] dexBytes,
106
+ String className
107
+ );
86
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
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",