capacitor-dex-editor 0.0.16 → 0.0.18
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.
|
@@ -29,6 +29,8 @@ import com.android.tools.smali.dexlib2.writer.io.FileDataStore;
|
|
|
29
29
|
import com.android.tools.smali.dexlib2.writer.pool.DexPool;
|
|
30
30
|
import com.android.tools.smali.baksmali.Baksmali;
|
|
31
31
|
import com.android.tools.smali.baksmali.BaksmaliOptions;
|
|
32
|
+
import com.android.tools.smali.baksmali.Adaptors.ClassDefinition;
|
|
33
|
+
import com.android.tools.smali.baksmali.formatter.BaksmaliWriter;
|
|
32
34
|
import com.android.tools.smali.smali.Smali;
|
|
33
35
|
import com.android.tools.smali.smali.SmaliOptions;
|
|
34
36
|
|
|
@@ -1588,120 +1590,16 @@ public class DexManager {
|
|
|
1588
1590
|
return result;
|
|
1589
1591
|
}
|
|
1590
1592
|
|
|
1591
|
-
//
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
int accessFlags = targetClass.getAccessFlags();
|
|
1595
|
-
if ((accessFlags & 0x0001) != 0) smali.append("public ");
|
|
1596
|
-
if ((accessFlags & 0x0010) != 0) smali.append("final ");
|
|
1597
|
-
if ((accessFlags & 0x0020) != 0) smali.append("super ");
|
|
1598
|
-
if ((accessFlags & 0x0200) != 0) smali.append("interface ");
|
|
1599
|
-
if ((accessFlags & 0x0400) != 0) smali.append("abstract ");
|
|
1600
|
-
if ((accessFlags & 0x1000) != 0) smali.append("synthetic ");
|
|
1601
|
-
if ((accessFlags & 0x2000) != 0) smali.append("annotation ");
|
|
1602
|
-
if ((accessFlags & 0x4000) != 0) smali.append("enum ");
|
|
1603
|
-
smali.append(targetClass.getType()).append("\n");
|
|
1593
|
+
// 使用 baksmali 库生成正确的 Smali 代码
|
|
1594
|
+
BaksmaliOptions options = new BaksmaliOptions();
|
|
1595
|
+
ClassDefinition classDefinition = new ClassDefinition(options, targetClass);
|
|
1604
1596
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
// 源文件
|
|
1612
|
-
String sourceFile = targetClass.getSourceFile();
|
|
1613
|
-
if (sourceFile != null) {
|
|
1614
|
-
smali.append(".source \"").append(sourceFile).append("\"\n");
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
// 实现的接口
|
|
1618
|
-
for (String iface : targetClass.getInterfaces()) {
|
|
1619
|
-
smali.append(".implements ").append(iface).append("\n");
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
smali.append("\n");
|
|
1623
|
-
|
|
1624
|
-
// 分离静态字段和实例字段
|
|
1625
|
-
java.util.List<Field> staticFields = new java.util.ArrayList<>();
|
|
1626
|
-
java.util.List<Field> instanceFields = new java.util.ArrayList<>();
|
|
1627
|
-
for (Field field : targetClass.getFields()) {
|
|
1628
|
-
if ((field.getAccessFlags() & 0x0008) != 0) {
|
|
1629
|
-
staticFields.add(field);
|
|
1630
|
-
} else {
|
|
1631
|
-
instanceFields.add(field);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
// 静态字段
|
|
1636
|
-
if (!staticFields.isEmpty()) {
|
|
1637
|
-
smali.append("# static fields\n");
|
|
1638
|
-
for (Field field : staticFields) {
|
|
1639
|
-
smali.append(".field ");
|
|
1640
|
-
int fFlags = field.getAccessFlags();
|
|
1641
|
-
if ((fFlags & 0x0001) != 0) smali.append("public ");
|
|
1642
|
-
if ((fFlags & 0x0002) != 0) smali.append("private ");
|
|
1643
|
-
if ((fFlags & 0x0004) != 0) smali.append("protected ");
|
|
1644
|
-
smali.append("static ");
|
|
1645
|
-
if ((fFlags & 0x0010) != 0) smali.append("final ");
|
|
1646
|
-
if ((fFlags & 0x0040) != 0) smali.append("volatile ");
|
|
1647
|
-
if ((fFlags & 0x0080) != 0) smali.append("transient ");
|
|
1648
|
-
if ((fFlags & 0x1000) != 0) smali.append("synthetic ");
|
|
1649
|
-
if ((fFlags & 0x4000) != 0) smali.append("enum ");
|
|
1650
|
-
smali.append(field.getName()).append(":").append(field.getType()).append("\n");
|
|
1651
|
-
}
|
|
1652
|
-
smali.append("\n");
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
// 实例字段
|
|
1656
|
-
if (!instanceFields.isEmpty()) {
|
|
1657
|
-
smali.append("# instance fields\n");
|
|
1658
|
-
for (Field field : instanceFields) {
|
|
1659
|
-
smali.append(".field ");
|
|
1660
|
-
int fFlags = field.getAccessFlags();
|
|
1661
|
-
if ((fFlags & 0x0001) != 0) smali.append("public ");
|
|
1662
|
-
if ((fFlags & 0x0002) != 0) smali.append("private ");
|
|
1663
|
-
if ((fFlags & 0x0004) != 0) smali.append("protected ");
|
|
1664
|
-
if ((fFlags & 0x0010) != 0) smali.append("final ");
|
|
1665
|
-
if ((fFlags & 0x0040) != 0) smali.append("volatile ");
|
|
1666
|
-
if ((fFlags & 0x0080) != 0) smali.append("transient ");
|
|
1667
|
-
if ((fFlags & 0x1000) != 0) smali.append("synthetic ");
|
|
1668
|
-
if ((fFlags & 0x4000) != 0) smali.append("enum ");
|
|
1669
|
-
smali.append(field.getName()).append(":").append(field.getType()).append("\n");
|
|
1670
|
-
}
|
|
1671
|
-
smali.append("\n");
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
// 分离直接方法和虚拟方法
|
|
1675
|
-
java.util.List<Method> directMethods = new java.util.ArrayList<>();
|
|
1676
|
-
java.util.List<Method> virtualMethods = new java.util.ArrayList<>();
|
|
1677
|
-
for (Method method : targetClass.getMethods()) {
|
|
1678
|
-
int mFlags = method.getAccessFlags();
|
|
1679
|
-
// direct methods: private, static, constructor
|
|
1680
|
-
if ((mFlags & 0x0002) != 0 || (mFlags & 0x0008) != 0 ||
|
|
1681
|
-
method.getName().equals("<init>") || method.getName().equals("<clinit>")) {
|
|
1682
|
-
directMethods.add(method);
|
|
1683
|
-
} else {
|
|
1684
|
-
virtualMethods.add(method);
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
// 直接方法
|
|
1689
|
-
if (!directMethods.isEmpty()) {
|
|
1690
|
-
smali.append("\n# direct methods\n");
|
|
1691
|
-
for (Method method : directMethods) {
|
|
1692
|
-
outputMethod(smali, method);
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
// 虚拟方法
|
|
1697
|
-
if (!virtualMethods.isEmpty()) {
|
|
1698
|
-
smali.append("\n# virtual methods\n");
|
|
1699
|
-
for (Method method : virtualMethods) {
|
|
1700
|
-
outputMethod(smali, method);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1597
|
+
java.io.StringWriter stringWriter = new java.io.StringWriter();
|
|
1598
|
+
BaksmaliWriter writer = new BaksmaliWriter(stringWriter, null);
|
|
1599
|
+
classDefinition.writeTo(writer);
|
|
1600
|
+
writer.close();
|
|
1703
1601
|
|
|
1704
|
-
result.put("smali",
|
|
1602
|
+
result.put("smali", stringWriter.toString());
|
|
1705
1603
|
|
|
1706
1604
|
} finally {
|
|
1707
1605
|
if (dexInputStream != null) {
|
|
@@ -1715,179 +1613,6 @@ public class DexManager {
|
|
|
1715
1613
|
return result;
|
|
1716
1614
|
}
|
|
1717
1615
|
|
|
1718
|
-
/**
|
|
1719
|
-
* 输出单个方法的 Smali 代码 (MT 风格)
|
|
1720
|
-
*/
|
|
1721
|
-
private void outputMethod(StringBuilder smali, Method method) {
|
|
1722
|
-
smali.append(".method ");
|
|
1723
|
-
int mFlags = method.getAccessFlags();
|
|
1724
|
-
if ((mFlags & 0x0001) != 0) smali.append("public ");
|
|
1725
|
-
if ((mFlags & 0x0002) != 0) smali.append("private ");
|
|
1726
|
-
if ((mFlags & 0x0004) != 0) smali.append("protected ");
|
|
1727
|
-
if ((mFlags & 0x0008) != 0) smali.append("static ");
|
|
1728
|
-
if ((mFlags & 0x0010) != 0) smali.append("final ");
|
|
1729
|
-
if ((mFlags & 0x0020) != 0) smali.append("synchronized ");
|
|
1730
|
-
if ((mFlags & 0x0040) != 0) smali.append("bridge ");
|
|
1731
|
-
if ((mFlags & 0x0080) != 0) smali.append("varargs ");
|
|
1732
|
-
if ((mFlags & 0x0100) != 0) smali.append("native ");
|
|
1733
|
-
if ((mFlags & 0x0400) != 0) smali.append("abstract ");
|
|
1734
|
-
if ((mFlags & 0x0800) != 0) smali.append("strictfp ");
|
|
1735
|
-
if ((mFlags & 0x1000) != 0) smali.append("synthetic ");
|
|
1736
|
-
if ((mFlags & 0x10000) != 0) smali.append("constructor ");
|
|
1737
|
-
if ((mFlags & 0x20000) != 0) smali.append("declared-synchronized ");
|
|
1738
|
-
|
|
1739
|
-
smali.append(method.getName());
|
|
1740
|
-
smali.append("(");
|
|
1741
|
-
for (CharSequence param : method.getParameterTypes()) {
|
|
1742
|
-
smali.append(param);
|
|
1743
|
-
}
|
|
1744
|
-
smali.append(")");
|
|
1745
|
-
smali.append(method.getReturnType());
|
|
1746
|
-
smali.append("\n");
|
|
1747
|
-
|
|
1748
|
-
MethodImplementation impl = method.getImplementation();
|
|
1749
|
-
if (impl != null) {
|
|
1750
|
-
smali.append(" .registers ").append(impl.getRegisterCount()).append("\n\n");
|
|
1751
|
-
|
|
1752
|
-
// 输出指令,跟踪标签位置
|
|
1753
|
-
java.util.Map<Integer, String> labelMap = new java.util.HashMap<>();
|
|
1754
|
-
int codeOffset = 0;
|
|
1755
|
-
|
|
1756
|
-
// 第一遍:收集所有标签位置
|
|
1757
|
-
for (Instruction instruction : impl.getInstructions()) {
|
|
1758
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction) {
|
|
1759
|
-
com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction offInstr =
|
|
1760
|
-
(com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction) instruction;
|
|
1761
|
-
int targetOffset = codeOffset + offInstr.getCodeOffset();
|
|
1762
|
-
labelMap.put(targetOffset, ":cond_" + Integer.toHexString(targetOffset));
|
|
1763
|
-
}
|
|
1764
|
-
codeOffset += instruction.getCodeUnits();
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
// 第二遍:输出指令 (MT 风格:每条指令后有空行)
|
|
1768
|
-
codeOffset = 0;
|
|
1769
|
-
for (Instruction instruction : impl.getInstructions()) {
|
|
1770
|
-
// 输出标签(如果有)
|
|
1771
|
-
if (labelMap.containsKey(codeOffset)) {
|
|
1772
|
-
smali.append("\n ").append(labelMap.get(codeOffset)).append("\n");
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
smali.append(" ").append(instruction.getOpcode().name.toLowerCase().replace('_', '-'));
|
|
1776
|
-
String params = formatInstruction(instruction);
|
|
1777
|
-
if (!params.isEmpty()) {
|
|
1778
|
-
smali.append(" ").append(params);
|
|
1779
|
-
}
|
|
1780
|
-
smali.append("\n\n"); // MT 风格:每条指令后有空行
|
|
1781
|
-
|
|
1782
|
-
codeOffset += instruction.getCodeUnits();
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
smali.append(".end method\n");
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
/**
|
|
1790
|
-
* 格式化指令参数 - 使用正确的 Smali 语法格式
|
|
1791
|
-
* invoke 指令格式: invoke-xxx {v0, v1}, Lclass;->method()V
|
|
1792
|
-
*/
|
|
1793
|
-
private String formatInstruction(Instruction instruction) {
|
|
1794
|
-
StringBuilder sb = new StringBuilder();
|
|
1795
|
-
String opName = instruction.getOpcode().name.toLowerCase();
|
|
1796
|
-
boolean isInvokeOrFilled = opName.startsWith("invoke") || opName.startsWith("filled-new-array");
|
|
1797
|
-
|
|
1798
|
-
// 收集寄存器
|
|
1799
|
-
java.util.List<Integer> registers = new java.util.ArrayList<>();
|
|
1800
|
-
|
|
1801
|
-
// 处理多寄存器指令 (invoke 指令使用 FiveRegisterInstruction)
|
|
1802
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction) {
|
|
1803
|
-
com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction regInstr =
|
|
1804
|
-
(com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction) instruction;
|
|
1805
|
-
int regCount = regInstr.getRegisterCount();
|
|
1806
|
-
if (regCount >= 1) registers.add(regInstr.getRegisterC());
|
|
1807
|
-
if (regCount >= 2) registers.add(regInstr.getRegisterD());
|
|
1808
|
-
if (regCount >= 3) registers.add(regInstr.getRegisterE());
|
|
1809
|
-
if (regCount >= 4) registers.add(regInstr.getRegisterF());
|
|
1810
|
-
if (regCount >= 5) registers.add(regInstr.getRegisterG());
|
|
1811
|
-
} else if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction) {
|
|
1812
|
-
com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction regInstr =
|
|
1813
|
-
(com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction) instruction;
|
|
1814
|
-
int start = regInstr.getStartRegister();
|
|
1815
|
-
int count = regInstr.getRegisterCount();
|
|
1816
|
-
for (int i = 0; i < count; i++) {
|
|
1817
|
-
registers.add(start + i);
|
|
1818
|
-
}
|
|
1819
|
-
} else {
|
|
1820
|
-
// 处理普通寄存器指令
|
|
1821
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction) {
|
|
1822
|
-
com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction regInstr =
|
|
1823
|
-
(com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction) instruction;
|
|
1824
|
-
registers.add(regInstr.getRegisterA());
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction) {
|
|
1828
|
-
com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction regInstr =
|
|
1829
|
-
(com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction) instruction;
|
|
1830
|
-
registers.add(regInstr.getRegisterB());
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction) {
|
|
1834
|
-
com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction regInstr =
|
|
1835
|
-
(com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction) instruction;
|
|
1836
|
-
registers.add(regInstr.getRegisterC());
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
// 格式化寄存器部分
|
|
1841
|
-
if (!registers.isEmpty()) {
|
|
1842
|
-
if (isInvokeOrFilled) {
|
|
1843
|
-
// invoke 指令使用 {} 包裹寄存器
|
|
1844
|
-
sb.append("{");
|
|
1845
|
-
for (int i = 0; i < registers.size(); i++) {
|
|
1846
|
-
if (i > 0) sb.append(", ");
|
|
1847
|
-
sb.append("v").append(registers.get(i));
|
|
1848
|
-
}
|
|
1849
|
-
sb.append("}");
|
|
1850
|
-
} else {
|
|
1851
|
-
// 普通指令直接列出寄存器
|
|
1852
|
-
for (int i = 0; i < registers.size(); i++) {
|
|
1853
|
-
if (i > 0) sb.append(", ");
|
|
1854
|
-
sb.append("v").append(registers.get(i));
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
// 处理引用指令
|
|
1860
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction) {
|
|
1861
|
-
com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction refInstr =
|
|
1862
|
-
(com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction) instruction;
|
|
1863
|
-
if (sb.length() > 0) sb.append(", ");
|
|
1864
|
-
sb.append(refInstr.getReference().toString());
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
// 处理字面量指令
|
|
1868
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction) {
|
|
1869
|
-
com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction litInstr =
|
|
1870
|
-
(com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction) instruction;
|
|
1871
|
-
if (sb.length() > 0) sb.append(", ");
|
|
1872
|
-
sb.append("0x").append(Long.toHexString(litInstr.getWideLiteral()));
|
|
1873
|
-
} else if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction) {
|
|
1874
|
-
com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction litInstr =
|
|
1875
|
-
(com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction) instruction;
|
|
1876
|
-
if (sb.length() > 0) sb.append(", ");
|
|
1877
|
-
sb.append("0x").append(Integer.toHexString(litInstr.getNarrowLiteral()));
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
// 处理偏移指令
|
|
1881
|
-
if (instruction instanceof com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction) {
|
|
1882
|
-
com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction offInstr =
|
|
1883
|
-
(com.android.tools.smali.dexlib2.iface.instruction.OffsetInstruction) instruction;
|
|
1884
|
-
if (sb.length() > 0) sb.append(", ");
|
|
1885
|
-
sb.append(":label_").append(offInstr.getCodeOffset());
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
return sb.toString();
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
1616
|
/**
|
|
1892
1617
|
* 保存修改后的 Smali 代码到 APK 中的 DEX 文件
|
|
1893
1618
|
* 注意:这是一个复杂操作,需要重新编译 Smali 并修改 DEX 文件
|
|
@@ -1954,20 +1679,127 @@ public class DexManager {
|
|
|
1954
1679
|
|
|
1955
1680
|
Log.d(TAG, "Smali compiled successfully to: " + outputDex.getAbsolutePath());
|
|
1956
1681
|
|
|
1957
|
-
//
|
|
1958
|
-
//
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1682
|
+
// 将编译后的类合并到原 DEX 文件中
|
|
1683
|
+
// 1. 读取原 APK 中的 DEX 文件
|
|
1684
|
+
java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
|
|
1685
|
+
java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
|
|
1686
|
+
|
|
1687
|
+
if (dexEntry == null) {
|
|
1688
|
+
result.put("success", false);
|
|
1689
|
+
result.put("error", "DEX 文件未找到: " + dexPath);
|
|
1690
|
+
zipFile.close();
|
|
1691
|
+
cleanupTempDir(tempDir);
|
|
1692
|
+
return result;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// 读取原 DEX
|
|
1696
|
+
java.io.InputStream dexInputStream = zipFile.getInputStream(dexEntry);
|
|
1697
|
+
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
1698
|
+
byte[] buffer = new byte[8192];
|
|
1699
|
+
int len;
|
|
1700
|
+
while ((len = dexInputStream.read(buffer)) != -1) {
|
|
1701
|
+
baos.write(buffer, 0, len);
|
|
1702
|
+
}
|
|
1703
|
+
byte[] originalDexBytes = baos.toByteArray();
|
|
1704
|
+
dexInputStream.close();
|
|
1705
|
+
|
|
1706
|
+
// 解析原 DEX 和新编译的 DEX
|
|
1707
|
+
DexBackedDexFile originalDex = new DexBackedDexFile(Opcodes.getDefault(), originalDexBytes);
|
|
1708
|
+
DexBackedDexFile newDex = DexBackedDexFile.fromInputStream(Opcodes.getDefault(),
|
|
1709
|
+
new java.io.BufferedInputStream(new java.io.FileInputStream(outputDex)));
|
|
1710
|
+
|
|
1711
|
+
// 合并 DEX:用新类替换原类
|
|
1712
|
+
String targetType = "L" + className.replace(".", "/") + ";";
|
|
1713
|
+
java.util.List<ClassDef> mergedClasses = new java.util.ArrayList<>();
|
|
1714
|
+
|
|
1715
|
+
// 添加原 DEX 中除目标类外的所有类
|
|
1716
|
+
for (ClassDef classDef : originalDex.getClasses()) {
|
|
1717
|
+
if (!classDef.getType().equals(targetType)) {
|
|
1718
|
+
mergedClasses.add(classDef);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// 添加新编译的类
|
|
1723
|
+
for (ClassDef classDef : newDex.getClasses()) {
|
|
1724
|
+
mergedClasses.add(classDef);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
Log.d(TAG, "Merged " + mergedClasses.size() + " classes");
|
|
1728
|
+
|
|
1729
|
+
// 创建新的 DEX 文件
|
|
1730
|
+
java.io.File mergedDexFile = new java.io.File(tempDir, "classes_merged.dex");
|
|
1731
|
+
DexPool dexPool = new DexPool(Opcodes.getDefault());
|
|
1732
|
+
for (ClassDef classDef : mergedClasses) {
|
|
1733
|
+
dexPool.internClass(classDef);
|
|
1734
|
+
}
|
|
1735
|
+
dexPool.writeTo(new FileDataStore(mergedDexFile));
|
|
1736
|
+
|
|
1737
|
+
Log.d(TAG, "Merged DEX written to: " + mergedDexFile.getAbsolutePath());
|
|
1738
|
+
|
|
1739
|
+
// 2. 创建新的 APK(复制原 APK 并替换 DEX)
|
|
1740
|
+
java.io.File outputApk = new java.io.File(apkPath + ".modified.apk");
|
|
1741
|
+
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(
|
|
1742
|
+
new java.io.FileOutputStream(outputApk));
|
|
1743
|
+
|
|
1744
|
+
// 复制原 APK 中的所有条目(除了要替换的 DEX)
|
|
1745
|
+
java.util.Enumeration<? extends java.util.zip.ZipEntry> entries = zipFile.entries();
|
|
1746
|
+
while (entries.hasMoreElements()) {
|
|
1747
|
+
java.util.zip.ZipEntry entry = entries.nextElement();
|
|
1748
|
+
if (entry.getName().equals(dexPath)) {
|
|
1749
|
+
// 跳过原 DEX,稍后添加新的
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// 复制其他条目
|
|
1754
|
+
java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry(entry.getName());
|
|
1755
|
+
zos.putNextEntry(newEntry);
|
|
1756
|
+
|
|
1757
|
+
if (!entry.isDirectory()) {
|
|
1758
|
+
java.io.InputStream is = zipFile.getInputStream(entry);
|
|
1759
|
+
while ((len = is.read(buffer)) != -1) {
|
|
1760
|
+
zos.write(buffer, 0, len);
|
|
1761
|
+
}
|
|
1762
|
+
is.close();
|
|
1763
|
+
}
|
|
1764
|
+
zos.closeEntry();
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// 添加修改后的 DEX
|
|
1768
|
+
java.util.zip.ZipEntry newDexEntry = new java.util.zip.ZipEntry(dexPath);
|
|
1769
|
+
zos.putNextEntry(newDexEntry);
|
|
1770
|
+
java.io.FileInputStream fis = new java.io.FileInputStream(mergedDexFile);
|
|
1771
|
+
while ((len = fis.read(buffer)) != -1) {
|
|
1772
|
+
zos.write(buffer, 0, len);
|
|
1773
|
+
}
|
|
1774
|
+
fis.close();
|
|
1775
|
+
zos.closeEntry();
|
|
1776
|
+
|
|
1777
|
+
zos.close();
|
|
1778
|
+
zipFile.close();
|
|
1779
|
+
|
|
1780
|
+
Log.d(TAG, "Modified APK written to: " + outputApk.getAbsolutePath());
|
|
1781
|
+
|
|
1782
|
+
// 3. 替换原 APK(先备份)
|
|
1783
|
+
java.io.File backupApk = new java.io.File(apkPath + ".backup");
|
|
1784
|
+
java.io.File originalApkFile = new java.io.File(apkPath);
|
|
1785
|
+
|
|
1786
|
+
// 复制原 APK 作为备份
|
|
1787
|
+
copyFile(originalApkFile, backupApk);
|
|
1788
|
+
|
|
1789
|
+
// 用修改后的 APK 替换原 APK
|
|
1790
|
+
copyFile(outputApk, originalApkFile);
|
|
1791
|
+
|
|
1792
|
+
// 删除临时修改的 APK
|
|
1793
|
+
outputApk.delete();
|
|
1794
|
+
|
|
1795
|
+
Log.d(TAG, "APK updated successfully. Backup at: " + backupApk.getAbsolutePath());
|
|
1963
1796
|
|
|
1964
|
-
// 目前先返回成功,实际保存功能需要更多实现
|
|
1965
1797
|
result.put("success", true);
|
|
1966
|
-
result.put("message", "Smali
|
|
1967
|
-
result.put("
|
|
1798
|
+
result.put("message", "Smali 保存成功!已更新 APK");
|
|
1799
|
+
result.put("backupPath", backupApk.getAbsolutePath());
|
|
1968
1800
|
|
|
1969
|
-
//
|
|
1970
|
-
|
|
1801
|
+
// 清理临时文件
|
|
1802
|
+
cleanupTempDir(tempDir);
|
|
1971
1803
|
|
|
1972
1804
|
} catch (Exception e) {
|
|
1973
1805
|
Log.e(TAG, "Error saving smali: " + e.getMessage(), e);
|
|
@@ -1978,6 +1810,21 @@ public class DexManager {
|
|
|
1978
1810
|
return result;
|
|
1979
1811
|
}
|
|
1980
1812
|
|
|
1813
|
+
/**
|
|
1814
|
+
* 复制文件
|
|
1815
|
+
*/
|
|
1816
|
+
private void copyFile(java.io.File src, java.io.File dst) throws java.io.IOException {
|
|
1817
|
+
java.io.FileInputStream fis = new java.io.FileInputStream(src);
|
|
1818
|
+
java.io.FileOutputStream fos = new java.io.FileOutputStream(dst);
|
|
1819
|
+
byte[] buffer = new byte[8192];
|
|
1820
|
+
int len;
|
|
1821
|
+
while ((len = fis.read(buffer)) != -1) {
|
|
1822
|
+
fos.write(buffer, 0, len);
|
|
1823
|
+
}
|
|
1824
|
+
fis.close();
|
|
1825
|
+
fos.close();
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1981
1828
|
/**
|
|
1982
1829
|
* 清理临时目录
|
|
1983
1830
|
*/
|