diff --git a/antik/src/main/java/com/antik/DexPatcher.java b/antik/src/main/java/com/antik/DexPatcher.java deleted file mode 100644 index 6f62bf1..0000000 --- a/antik/src/main/java/com/antik/DexPatcher.java +++ /dev/null @@ -1,377 +0,0 @@ -package com.antik; - -import com.reandroid.apk.ApkModule; -import com.reandroid.archive.ByteInputSource; -import com.reandroid.archive.InputSource; -import org.jf.dexlib2.DexFileFactory; -import org.jf.dexlib2.Opcode; -import org.jf.dexlib2.Opcodes; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.iface.DexFile; -import org.jf.dexlib2.iface.Method; -import org.jf.dexlib2.iface.MethodImplementation; -import org.jf.dexlib2.iface.instruction.Instruction; -import org.jf.dexlib2.iface.instruction.ReferenceInstruction; -import org.jf.dexlib2.iface.reference.MethodReference; -import org.jf.dexlib2.immutable.ImmutableClassDef; -import org.jf.dexlib2.immutable.ImmutableMethod; -import org.jf.dexlib2.immutable.ImmutableMethodImplementation; -import org.jf.dexlib2.immutable.instruction.ImmutableInstruction10x; -import org.jf.dexlib2.immutable.instruction.ImmutableInstruction11n; -import org.jf.dexlib2.immutable.instruction.ImmutableInstruction11x; -import org.jf.dexlib2.immutable.instruction.ImmutableInstruction21c; -import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35c; -import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; -import org.jf.dexlib2.immutable.reference.ImmutableTypeReference; -import org.jf.dexlib2.writer.io.MemoryDataStore; -import org.jf.dexlib2.writer.pool.DexPool; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.*; - -public class DexPatcher { - public static void patch(ApkModule m) throws Exception { - List dx_ns = new ArrayList(); - for (InputSource s : m.getInputSources()) { - if (s.getName().endsWith(".dex")) { - dx_ns.add(s.getName()); - } - } - - List l_cds = new ArrayList(); - Set a_ts = new HashSet(); - String pkg = "unknown"; - try { - pkg = m.getPackageName(); - } catch (Exception ignored) {} - - try (InputStream i = Main.class.getResourceAsStream("/log.dex")) { - if (i != null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = i.read(buf)) != -1) { - baos.write(buf, 0, len); - } - byte[] l_bs = baos.toByteArray(); - File t_l_dx = File.createTempFile("log", ".dex"); - Files.write(t_l_dx.toPath(), l_bs); - DexFile l_df = DexFileFactory.loadDexFile(t_l_dx, Opcodes.getDefault()); - t_l_dx.delete(); - - for (ClassDef c : l_df.getClasses()) { - if ("Lcom/pairip/PairipLog;".equals(c.getType())) { - List s_fs = new ArrayList(); - for (org.jf.dexlib2.iface.Field f : c.getStaticFields()) { - if ("DIR_PATH".equals(f.getName())) { - String n_v = "/data/data/" + pkg + "/dictionary"; - s_fs.add(new org.jf.dexlib2.immutable.ImmutableField( - f.getDefiningClass(), - f.getName(), - f.getType(), - f.getAccessFlags(), - new org.jf.dexlib2.immutable.value.ImmutableStringEncodedValue(n_v), - f.getAnnotations(), - f.getHiddenApiRestrictions())); - } else { - s_fs.add(f); - } - } - l_cds.add(new ImmutableClassDef( - c.getType(), c.getAccessFlags(), c.getSuperclass(), - c.getInterfaces(), c.getSourceFile(), c.getAnnotations(), - s_fs, c.getInstanceFields(), - c.getDirectMethods(), c.getVirtualMethods())); - } else { - l_cds.add(c); - } - a_ts.add(c.getType()); - } - System.out.println("[INFO] Added classes from log.dex (Package: " + pkg + ")"); - } - } - - List j_ts = new ArrayList(); - for (String dn : dx_ns) { - InputSource s = m.getInputSource(dn); - if (s == null) continue; - byte[] d_bs; - try (InputStream i = s.openStream()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = i.read(buf)) != -1) { - baos.write(buf, 0, len); - } - d_bs = baos.toByteArray(); - } - File t_dx = File.createTempFile("temp", ".dex"); - Files.write(t_dx.toPath(), d_bs); - DexFile d_f = DexFileFactory.loadDexFile(t_dx, Opcodes.getDefault()); - t_dx.delete(); - - for (ClassDef cd : d_f.getClasses()) { - if (cd.getMethods().iterator().hasNext()) continue; - if (!cd.getFields().iterator().hasNext()) continue; - if (!"Ljava/lang/Object;".equals(cd.getSuperclass())) continue; - if (cd.getAccessFlags() != 1) continue; - - boolean o_t = true; - for (org.jf.dexlib2.iface.Field f : cd.getFields()) { - if (f.getAccessFlags() != 9) { - o_t = false; - break; - } - String t = f.getType(); - if (!t.equals("Ljava/lang/String;") && !t.equals("Ljava/lang/reflect/Method;")) { - o_t = false; - break; - } - if (f.getInitialValue() != null) { - o_t = false; - break; - } - } - if (o_t) { - j_ts.add(cd.getType()); - } - } - } - System.out.println("[INFO] Found " + j_ts.size() + " junk classes"); - - for (String dn : dx_ns) { - InputSource s = m.getInputSource(dn); - if (s == null) continue; - byte[] d_bs; - try (InputStream i = s.openStream()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = i.read(buf)) != -1) { - baos.write(buf, 0, len); - } - d_bs = baos.toByteArray(); - } - - File t_dx = File.createTempFile("temp", ".dex"); - Files.write(t_dx.toPath(), d_bs); - DexFile d_f = DexFileFactory.loadDexFile(t_dx, Opcodes.getDefault()); - t_dx.delete(); - - List cds = new ArrayList(); - boolean mod = false; - - for (ClassDef cd : d_f.getClasses()) { - if (a_ts.contains(cd.getType())) { - continue; - } - if ("Lcom/pairip/StartupLauncher;".equals(cd.getType())) { - mod = true; - cds.add(patchStartupLauncher(cd, j_ts)); - } else if ("Lcom/pairip/SignatureCheck;".equals(cd.getType()) || - "Lcom/pairip/licensecheck/LicenseClient;".equals(cd.getType()) || - "Lcom/pairip/licensecheck3/LicenseClientV3;".equals(cd.getType())) { - mod = true; - - List d_ms = new ArrayList(); - for (Method method : cd.getDirectMethods()) { - d_ms.add(patchMethodIfTarget(method)); - } - - List v_ms = new ArrayList(); - for (Method method : cd.getVirtualMethods()) { - v_ms.add(patchMethodIfTarget(method)); - } - - cds.add(new ImmutableClassDef( - cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), - cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), - cd.getStaticFields(), cd.getInstanceFields(), - d_ms, v_ms)); - } else { - cds.add(cd); - } - } - - if (dn.equals("classes.dex")) { - if (!l_cds.isEmpty()) { - cds.addAll(l_cds); - mod = true; - } - } - - if (mod) { - MemoryDataStore ds = new MemoryDataStore(); - DexPool dp = new DexPool(Opcodes.getDefault()); - for (ClassDef c : cds) { - dp.internClass(c); - } - dp.writeTo(ds); - - byte[] r_bs = Arrays.copyOf(ds.getData(), ds.getSize()); - m.add(new ByteInputSource(r_bs, dn)); - System.out.println("[BUILD] Patched " + dn); - } - } - } - - private static Method patchMethodIfTarget(Method m) { - String n = m.getName(); - if ("verifySignatureMatches".equals(n)) { - System.out.println("[INFO] Bypassing verifySignatureMatches"); - List ins = Arrays.asList( - new ImmutableInstruction11n(Opcode.CONST_4, 0, 1), - new ImmutableInstruction11x(Opcode.RETURN, 0) - ); - return new ImmutableMethod( - m.getDefiningClass(), n, m.getParameters(), - m.getReturnType(), m.getAccessFlags(), - m.getAnnotations(), m.getHiddenApiRestrictions(), - new ImmutableMethodImplementation(1, ins, null, null)); - } else if ("verifyIntegrity".equals(n)) { - System.out.println("[INFO] Bypassing " + n); - String rt = m.getReturnType(); - List ins; - if ("V".equals(rt)) { - ins = Collections.singletonList(new ImmutableInstruction10x(Opcode.RETURN_VOID)); - } else if (rt.startsWith("L") || rt.startsWith("[")) { - ins = Arrays.asList( - new ImmutableInstruction11n(Opcode.CONST_4, 0, 0), - new ImmutableInstruction11x(Opcode.RETURN_OBJECT, 0) - ); - } else { - ins = Arrays.asList( - new ImmutableInstruction11n(Opcode.CONST_4, 0, 1), - new ImmutableInstruction11x(Opcode.RETURN, 0) - ); - } - return new ImmutableMethod( - m.getDefiningClass(), n, m.getParameters(), - rt, m.getAccessFlags(), - m.getAnnotations(), m.getHiddenApiRestrictions(), - new ImmutableMethodImplementation(1, ins, null, null)); - } - return m; - } - - private static ClassDef patchStartupLauncher(ClassDef cd, List j_ts) { - System.out.println("[INFO] Patching StartupLauncher"); - List d_ms = new ArrayList(); - for (Method m : cd.getDirectMethods()) { - if ("launch".equals(m.getName())) { - d_ms.add(patchLaunchMethod(m)); - } else if (!"pairip".equals(m.getName())) { - d_ms.add(m); - } - } - d_ms.add(createPairipMethod(cd.getType(), j_ts)); - - return new ImmutableClassDef( - cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), - cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), - cd.getStaticFields(), cd.getInstanceFields(), - d_ms, cd.getVirtualMethods()); - } - - private static Method patchLaunchMethod(Method m) { - MethodImplementation im = m.getImplementation(); - if (im == null) return m; - - List o_ins = new ArrayList(); - for (Instruction i : im.getInstructions()) { - o_ins.add(i); - } - - List n_ins = new ArrayList(); - int p_off = -1; - int c_off = 0; - - for (int i = 0; i < o_ins.size(); i++) { - Instruction inst = o_ins.get(i); - n_ins.add(inst); - int u = inst.getCodeUnits(); - if (inst instanceof ReferenceInstruction) { - ReferenceInstruction r_i = (ReferenceInstruction) inst; - if (r_i.getReference() instanceof MethodReference) { - MethodReference m_r = (MethodReference) r_i.getReference(); - if ("Lcom/pairip/VMRunner;".equals(m_r.getDefiningClass()) && "invoke".equals(m_r.getName())) { - - boolean a_p = false; - if (i + 1 < o_ins.size()) { - Instruction nx = o_ins.get(i + 1); - if (nx instanceof ReferenceInstruction) { - ReferenceInstruction n_r = (ReferenceInstruction) nx; - if (n_r.getReference() instanceof MethodReference) { - MethodReference n_m = (MethodReference) n_r.getReference(); - if ("pairip".equals(n_m.getName())) { - a_p = true; - } - } - } - } - - if (!a_p) { - p_off = c_off + u; - n_ins.add(new ImmutableInstruction35c(Opcode.INVOKE_STATIC, 0, 0, 0, 0, 0, 0, - new ImmutableMethodReference("Lcom/pairip/StartupLauncher;", "pairip", null, "V"))); - } - } - } - } - c_off += u; - } - - if (p_off == -1) return m; - - List> o_tbs = im.getTryBlocks(); - List n_tbs = new ArrayList(); - - for (org.jf.dexlib2.iface.TryBlock t : o_tbs) { - int s = t.getStartCodeAddress(); - int c = t.getCodeUnitCount(); - int e = s + c; - - List h = new ArrayList(); - for (org.jf.dexlib2.iface.ExceptionHandler ha : t.getExceptionHandlers()) { - int h_a = ha.getHandlerCodeAddress(); - if (h_a >= p_off) { - h.add(new org.jf.dexlib2.immutable.ImmutableExceptionHandler(ha.getExceptionType(), h_a + 3)); - } else { - h.add(org.jf.dexlib2.immutable.ImmutableExceptionHandler.of(ha)); - } - } - - if (s < p_off && e >= p_off) { - n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s, c + 3, h)); - } else if (s >= p_off) { - n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s + 3, c, h)); - } else { - n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s, c, h)); - } - } - - return new ImmutableMethod( - m.getDefiningClass(), m.getName(), m.getParameters(), - m.getReturnType(), m.getAccessFlags(), - m.getAnnotations(), m.getHiddenApiRestrictions(), - new ImmutableMethodImplementation(im.getRegisterCount(), n_ins, n_tbs, im.getDebugItems())); - } - - private static Method createPairipMethod(String dc, List j_ts) { - List ins = new ArrayList(); - for (String t : j_ts) { - ins.add(new ImmutableInstruction21c(Opcode.CONST_CLASS, 0, new ImmutableTypeReference(t))); - ins.add(new ImmutableInstruction35c(Opcode.INVOKE_STATIC, 1, 0, 0, 0, 0, 0, - new ImmutableMethodReference("Lcom/pairip/PairipLog;", "put", Collections.singletonList("Ljava/lang/Class;"), "V"))); - } - ins.add(new ImmutableInstruction10x(Opcode.RETURN_VOID)); - - return new ImmutableMethod( - dc, "pairip", null, "V", 0x08, - null, null, - new ImmutableMethodImplementation(1, ins, null, null)); - } -} diff --git a/antik/src/main/java/com/antik/DexPatcher/DexPatcher.java b/antik/src/main/java/com/antik/DexPatcher/DexPatcher.java new file mode 100644 index 0000000..8e01d7d --- /dev/null +++ b/antik/src/main/java/com/antik/DexPatcher/DexPatcher.java @@ -0,0 +1,188 @@ +package com.antik.DexPatcher; + +import static com.antik.DexPatcher.MethodT.patchLauncher.patchStartupLauncher; + +import com.antik.DexPatcher.MethodT.patchM; +import com.antik.Main; +import com.reandroid.apk.ApkModule; +import com.reandroid.archive.ByteInputSource; +import com.reandroid.archive.InputSource; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.immutable.ImmutableClassDef; +import org.jf.dexlib2.writer.io.MemoryDataStore; +import org.jf.dexlib2.writer.pool.DexPool; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.*; + +public class DexPatcher { + public static void patch(ApkModule m) throws Exception { + List dx_ns = new ArrayList(); + for (InputSource s : m.getInputSources()) { + if (s.getName().endsWith(".dex")) { + dx_ns.add(s.getName()); + } + } + + List l_cds = new ArrayList(); + Set a_ts = new HashSet(); + String pkg = "nill"; + try { + pkg = m.getPackageName(); + } catch (Exception ignored) { + //////////// + } + + try (InputStream i = Main.class.getResourceAsStream("/log.dex")) { + if (i != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = i.read(buf)) != -1) { + baos.write(buf, 0, len); + } + byte[] l_bs = baos.toByteArray(); + File t_l_dx = File.createTempFile("log", ".dex"); + Files.write(t_l_dx.toPath(), l_bs); + DexFile l_df = DexFileFactory.loadDexFile(t_l_dx, Opcodes.getDefault()); + t_l_dx.delete(); + + for (ClassDef c : l_df.getClasses()) { + if ("Lcom/pairip/PairipLog;".equals(c.getType())) { + List s_fs = new ArrayList(); + for (org.jf.dexlib2.iface.Field f : c.getStaticFields()) { + if ("DIR_PATH".equals(f.getName())) { + String n_v = "/data/data/" + pkg + "/dictionary"; + s_fs.add(new org.jf.dexlib2.immutable.ImmutableField(f.getDefiningClass(), f.getName(), f.getType(), f.getAccessFlags(), new org.jf.dexlib2.immutable.value.ImmutableStringEncodedValue(n_v), f.getAnnotations(), f.getHiddenApiRestrictions())); + } else { + s_fs.add(f); + } + } + l_cds.add(new ImmutableClassDef(c.getType(), c.getAccessFlags(), c.getSuperclass(), c.getInterfaces(), c.getSourceFile(), c.getAnnotations(), s_fs, c.getInstanceFields(), c.getDirectMethods(), c.getVirtualMethods())); + } else { + l_cds.add(c); + } + a_ts.add(c.getType()); + } + System.out.println("[INFO] Added classes from log.dex " + pkg); + } + } + + List j_ts = new ArrayList(); + for (String dn : dx_ns) { + InputSource s = m.getInputSource(dn); + if (s == null) continue; + byte[] d_bs; + try (InputStream i = s.openStream()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = i.read(buf)) != -1) { + baos.write(buf, 0, len); + } + d_bs = baos.toByteArray(); + } + File t_dx = File.createTempFile("temp", ".dex"); + Files.write(t_dx.toPath(), d_bs); + DexFile d_f = DexFileFactory.loadDexFile(t_dx, Opcodes.getDefault()); + t_dx.delete(); + for (ClassDef cd : d_f.getClasses()) { + if (cd.getMethods().iterator().hasNext()) continue; + if (!cd.getFields().iterator().hasNext()) continue; + if (!"Ljava/lang/Object;".equals(cd.getSuperclass())) continue; + if (cd.getAccessFlags() != 1) continue; + + boolean o_t = true; + for (org.jf.dexlib2.iface.Field f : cd.getFields()) { + if (f.getAccessFlags() != 9) { + o_t = false; + break; + } + String t = f.getType(); + if (!t.equals("Ljava/lang/String;") && !t.equals("Ljava/lang/reflect/Method;")) { + o_t = false; + break; + } + if (f.getInitialValue() != null) { + o_t = false; + break; + } + } + if (o_t) { + j_ts.add(cd.getType()); + } + } + } + System.out.println("[INFO] Found " + j_ts.size() + " junk classes"); + for (String dn : dx_ns) { + InputSource s = m.getInputSource(dn); + if (s == null) continue; + byte[] d_bs; + try (InputStream i = s.openStream()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = i.read(buf)) != -1) { + baos.write(buf, 0, len); + } + d_bs = baos.toByteArray(); + } + File t_dx = File.createTempFile("temp", ".dex"); + Files.write(t_dx.toPath(), d_bs); + DexFile d_f = DexFileFactory.loadDexFile(t_dx, Opcodes.getDefault()); + t_dx.delete(); + + List cds = new ArrayList(); + boolean mod = false; + + for (ClassDef cd : d_f.getClasses()) { + if (a_ts.contains(cd.getType())) { + continue; + } + if ("Lcom/pairip/StartupLauncher;".equals(cd.getType())) { + mod = true; + cds.add(patchStartupLauncher(cd, j_ts)); + } else if ("Lcom/pairip/SignatureCheck;".equals(cd.getType()) || "Lcom/pairip/licensecheck/LicenseClient;".equals(cd.getType()) || "Lcom/pairip/licensecheck3/LicenseClientV3;".equals(cd.getType())) { + mod = true; + List d_ms = new ArrayList(); + for (Method method : cd.getDirectMethods()) { + d_ms.add(patchM.patchMethodIfTarget(method)); + } + List v_ms = new ArrayList(); + for (Method method : cd.getVirtualMethods()) { + v_ms.add(patchM.patchMethodIfTarget(method)); + } + cds.add(new ImmutableClassDef(cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), cd.getStaticFields(), cd.getInstanceFields(), d_ms, v_ms)); + } else { + cds.add(cd); + } + } + + if (dn.equals("classes.dex")) { + if (!l_cds.isEmpty()) { + cds.addAll(l_cds); + mod = true; + } + } + + if (mod) { + MemoryDataStore ds = new MemoryDataStore(); + DexPool dp = new DexPool(Opcodes.getDefault()); + for (ClassDef c : cds) { + dp.internClass(c); + } + dp.writeTo(ds); + byte[] r_bs = Arrays.copyOf(ds.getData(), ds.getSize()); + m.add(new ByteInputSource(r_bs, dn)); + System.out.println("[BUILD] Patched " + dn); + } + } + } +} diff --git a/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLaunchMethod.java b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLaunchMethod.java new file mode 100644 index 0000000..5864da5 --- /dev/null +++ b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLaunchMethod.java @@ -0,0 +1,91 @@ +package com.antik.DexPatcher.MethodT; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.ReferenceInstruction; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodImplementation; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35c; +import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; + +import java.util.ArrayList; +import java.util.List; + +public class patchLaunchMethod { + public static Method patchLaunchMethod(Method m) { + MethodImplementation im = m.getImplementation(); + if (im == null) return m; + List o_ins = new ArrayList(); + for (Instruction i : im.getInstructions()) { + o_ins.add(i); + } + List n_ins = new ArrayList(); + int p_off = -1; + int c_off = 0; + + for (int i = 0; i < o_ins.size(); i++) { + Instruction inst = o_ins.get(i); + n_ins.add(inst); + int u = inst.getCodeUnits(); + if (inst instanceof ReferenceInstruction) { + ReferenceInstruction r_i = (ReferenceInstruction) inst; + if (r_i.getReference() instanceof MethodReference) { + MethodReference m_r = (MethodReference) r_i.getReference(); + if ("Lcom/pairip/VMRunner;".equals(m_r.getDefiningClass()) && "invoke".equals(m_r.getName())) { + + boolean a_p = false; + if (i + 1 < o_ins.size()) { + Instruction nx = o_ins.get(i + 1); + if (nx instanceof ReferenceInstruction) { + ReferenceInstruction n_r = (ReferenceInstruction) nx; + if (n_r.getReference() instanceof MethodReference) { + MethodReference n_m = (MethodReference) n_r.getReference(); + if ("pairip".equals(n_m.getName())) { + a_p = true; + } + } + } + } + if (!a_p) { + p_off = c_off + u; + n_ins.add(new ImmutableInstruction35c(Opcode.INVOKE_STATIC, 0, 0, 0, 0, 0, 0, new ImmutableMethodReference("Lcom/pairip/StartupLauncher;", "pairip", null, "V"))); + } + } + } + } + c_off += u; + } + + if (p_off == -1) return m; + + List> o_tbs = im.getTryBlocks(); + List n_tbs = new ArrayList(); + + for (org.jf.dexlib2.iface.TryBlock t : o_tbs) { + int s = t.getStartCodeAddress(); + int c = t.getCodeUnitCount(); + int e = s + c; + List h = new ArrayList(); + for (org.jf.dexlib2.iface.ExceptionHandler ha : t.getExceptionHandlers()) { + int h_a = ha.getHandlerCodeAddress(); + if (h_a >= p_off) { + h.add(new org.jf.dexlib2.immutable.ImmutableExceptionHandler(ha.getExceptionType(), h_a + 3)); + } else { + h.add(org.jf.dexlib2.immutable.ImmutableExceptionHandler.of(ha)); + } + } + if (s < p_off && e >= p_off) { + n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s, c + 3, h)); + } else if (s >= p_off) { + n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s + 3, c, h)); + } else { + n_tbs.add(new org.jf.dexlib2.immutable.ImmutableTryBlock(s, c, h)); + } + } + + return new ImmutableMethod(m.getDefiningClass(), m.getName(), m.getParameters(), m.getReturnType(), m.getAccessFlags(), m.getAnnotations(), m.getHiddenApiRestrictions(), new ImmutableMethodImplementation(im.getRegisterCount(), n_ins, n_tbs, im.getDebugItems())); + } +} diff --git a/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLauncher.java b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLauncher.java new file mode 100644 index 0000000..9b2b269 --- /dev/null +++ b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchLauncher.java @@ -0,0 +1,28 @@ +package com.antik.DexPatcher.MethodT; +import static com.antik.DexPatcher.MethodT.patchLaunchMethod.patchLaunchMethod; +import static com.antik.DexPatcher.PairipMethodMake.CMethod.createPairipMethod; + +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.immutable.ImmutableClassDef; + +import java.util.ArrayList; +import java.util.List; + +public class patchLauncher { + + public static ClassDef patchStartupLauncher(ClassDef cd, List j_ts) { + System.out.println("[INFO] Patching StartupLauncher"); + List d_ms = new ArrayList(); + for (Method m : cd.getDirectMethods()) { + if ("launch".equals(m.getName())) { + d_ms.add(patchLaunchMethod(m)); + } else if (!"pairip".equals(m.getName())) { + d_ms.add(m); + } + } + d_ms.add(createPairipMethod(cd.getType(), j_ts)); + + return new ImmutableClassDef(cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), cd.getStaticFields(), cd.getInstanceFields(), d_ms, cd.getVirtualMethods()); + } +} diff --git a/antik/src/main/java/com/antik/DexPatcher/MethodT/patchM.java b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchM.java new file mode 100644 index 0000000..b760811 --- /dev/null +++ b/antik/src/main/java/com/antik/DexPatcher/MethodT/patchM.java @@ -0,0 +1,38 @@ +package com.antik.DexPatcher.MethodT; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodImplementation; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction10x; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction11n; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction11x; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class patchM { + public static Method patchMethodIfTarget(Method m) { + String n = m.getName(); + if ("verifySignatureMatches".equals(n)) { + System.out.println("[INFO] Bypassing verifySignatureMatches"); + List ins = Arrays.asList(new ImmutableInstruction11n(Opcode.CONST_4, 0, 1), new ImmutableInstruction11x(Opcode.RETURN, 0)); + return new ImmutableMethod(m.getDefiningClass(), n, m.getParameters(), m.getReturnType(), m.getAccessFlags(), m.getAnnotations(), m.getHiddenApiRestrictions(), new ImmutableMethodImplementation(1, ins, null, null)); + } else if ("verifyIntegrity".equals(n)) { + System.out.println("[INFO] Bypassing " + n); + String rt = m.getReturnType(); + List ins; + if ("V".equals(rt)) { + ins = Collections.singletonList(new ImmutableInstruction10x(Opcode.RETURN_VOID)); + } else if (rt.startsWith("L") || rt.startsWith("[")) { + ins = Arrays.asList(new ImmutableInstruction11n(Opcode.CONST_4, 0, 0), new ImmutableInstruction11x(Opcode.RETURN_OBJECT, 0)); + } else { + ins = Arrays.asList(new ImmutableInstruction11n(Opcode.CONST_4, 0, 1), new ImmutableInstruction11x(Opcode.RETURN, 0)); + } + return new ImmutableMethod(m.getDefiningClass(), n, m.getParameters(), rt, m.getAccessFlags(), m.getAnnotations(), m.getHiddenApiRestrictions(), new ImmutableMethodImplementation(1, ins, null, null)); + } + return m; + } +} diff --git a/antik/src/main/java/com/antik/DexPatcher/PairipMethodMake/CMethod.java b/antik/src/main/java/com/antik/DexPatcher/PairipMethodMake/CMethod.java new file mode 100644 index 0000000..894dbee --- /dev/null +++ b/antik/src/main/java/com/antik/DexPatcher/PairipMethodMake/CMethod.java @@ -0,0 +1,28 @@ +package com.antik.DexPatcher.PairipMethodMake; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodImplementation; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction10x; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction21c; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35c; +import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; +import org.jf.dexlib2.immutable.reference.ImmutableTypeReference; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CMethod { + public static Method createPairipMethod(String dc, List j_ts) { + List ins = new ArrayList(); + for (String t : j_ts) { + ins.add(new ImmutableInstruction21c(Opcode.CONST_CLASS, 0, new ImmutableTypeReference(t))); + ins.add(new ImmutableInstruction35c(Opcode.INVOKE_STATIC, 1, 0, 0, 0, 0, 0, new ImmutableMethodReference("Lcom/pairip/PairipLog;", "put", Collections.singletonList("Ljava/lang/Class;"), "V"))); + } + ins.add(new ImmutableInstruction10x(Opcode.RETURN_VOID)); + return new ImmutableMethod(dc, "pairip", null, "V", 0x08, null, null, new ImmutableMethodImplementation(1, ins, null, null)); + } +} diff --git a/antik/src/main/java/com/antik/Main.java b/antik/src/main/java/com/antik/Main.java index e5c15af..f1e312e 100644 --- a/antik/src/main/java/com/antik/Main.java +++ b/antik/src/main/java/com/antik/Main.java @@ -1,5 +1,6 @@ package com.antik; +import com.antik.DexPatcher.DexPatcher; import com.antik.crc32.crc32; import com.antik.manifest.manifestP; import com.antik.ui.*; @@ -131,7 +132,6 @@ public class Main { } catch (Exception e) { System.err.println("[ERROR] Patching failed: " + e.getMessage()); } - System.out.println("[BUILD] Building APK..."); Sp_T.writeApk(Pai_Dir); diff --git a/antik/src/main/java/com/antik/crc32/Casting.java b/antik/src/main/java/com/antik/crc32/Casting.java new file mode 100644 index 0000000..8c91a15 --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/Casting.java @@ -0,0 +1,33 @@ +package com.antik.crc32; + +import com.antik.logCat; + +public final class Casting implements logCat { + + /* static { + System.out.println(TAG + " Casting loaded {} >>> "); + } +*/ + private Casting() { + ///////////////////// + } + + public static final long UNSIGNED_INT_MASK = 0xffffffffL; + + public static final int BYTE_MASK = 0xFF; + + public static final int BYTE_SHIFT_1 = 8; + public static final int BYTE_SHIFT_2 = 16; + public static final int BYTE_SHIFT_3 = 24; + + public static final int INT_BYTE_COUNT = 4; + + public static final long ZIP_MAX_COMMENT_SIZE = 65535L; + public static final long ZIP_EOCD_MIN_SIZE = 22L; + + public static final long ZIP_EOCD_SEARCH_RANGE = ZIP_MAX_COMMENT_SIZE + ZIP_EOCD_MIN_SIZE; + + public static long unsignedInt(int value) { + return ( (long) value) & UNSIGNED_INT_MASK; + } +} \ No newline at end of file diff --git a/antik/src/main/java/com/antik/crc32/JVMCasting.java b/antik/src/main/java/com/antik/crc32/JVMCasting.java new file mode 100644 index 0000000..095ae43 --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/JVMCasting.java @@ -0,0 +1,54 @@ +package com.antik.crc32; + +import com.antik.crc32.hyper.ZipSignature; +import java.io.IOException; +import java.io.RandomAccessFile; + +public class JVMCasting { + + public static int readIntLE(RandomAccessFile file) throws IOException { + int b1 = readByte(file); + int b2 = readByte(file); + int b3 = readByte(file); + int b4 = readByte(file); + return b1 | (b2 << Casting.BYTE_SHIFT_1) | (b3 << Casting.BYTE_SHIFT_2) | (b4 << Casting.BYTE_SHIFT_3); + } + public static int readShortLE(RandomAccessFile file) throws IOException { + int b1 = readByte(file); + int b2 = readByte(file); + return b1 | (b2 << Casting.BYTE_SHIFT_1); + } + + public static void writeIntLE(RandomAccessFile file, int value) throws IOException { + file.write(value & Casting.BYTE_MASK); + file.write((value >> Casting.BYTE_SHIFT_1) & Casting.BYTE_MASK); + file.write((value >> Casting.BYTE_SHIFT_2) & Casting.BYTE_MASK); + file.write((value >> Casting.BYTE_SHIFT_3) & Casting.BYTE_MASK); + } + + public static long findEndOfCentralDirectory(RandomAccessFile file) throws IOException { + long fileSize = file.length(); + long searchStart = fileSize - Casting.ZIP_EOCD_SEARCH_RANGE; + if (searchStart < 0) { + searchStart = 0; + } + long position = fileSize - Casting.INT_BYTE_COUNT; + while (position >= searchStart) { + file.seek(position); + if (readIntLE(file) == ZipSignature.END_OF_CENTRAL_DIRECTORY.value) { + return position; + } + position--; + } + + return -1; + } + + private static int readByte(RandomAccessFile file) throws IOException { + int value = file.read(); + if (value < 0) { + throw new IOException("Unexpected end of file"); + } + return value & Casting.BYTE_MASK; + } +} \ No newline at end of file diff --git a/antik/src/main/java/com/antik/crc32/crc32.java b/antik/src/main/java/com/antik/crc32/crc32.java index ba7f4c6..508ebe4 100644 --- a/antik/src/main/java/com/antik/crc32/crc32.java +++ b/antik/src/main/java/com/antik/crc32/crc32.java @@ -1,130 +1,130 @@ package com.antik.crc32; +import static com.antik.crc32.Casting.unsignedInt; + +import com.antik.crc32.hyper.*; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Iterator; -public class crc32 { - public static void patch(File s_a, File t_a) throws IOException { - - Map s_es = new LinkedHashMap(); - Map t_es = new LinkedHashMap(); +/** + * Created by aantik + * 3/16/2026 9:00 PM + * + * ⋆ ႔ ႔ + * ᠸ^ ^ ⸝⸝ + * |、˜〵 + * じしˍ,)⁐̤ᐷ + * + * Fox Mode 🍺 + */ - File[] f_ls = {s_a, t_a}; - - for (int i = 0; i < 2; i++) { - File f = f_ls[i]; - Map cur = (i == 0) ? s_es : t_es; +public class crc32 extends JVMCasting { - try (RandomAccessFile rf = new RandomAccessFile(f, "r")) { - FileChannel ch = rf.getChannel(); - long sz = ch.size(); - int t_sz = (int) Math.min(65558L, sz); - ByteBuffer t_bf = ByteBuffer.allocate(t_sz).order(ByteOrder.LITTLE_ENDIAN); - ch.read(t_bf, sz - t_sz); - t_bf.flip(); + public static Map readDexCrcInfo(File apkFile) throws IOException { + Map dexInfoMap = new LinkedHashMap(); + RandomAccessFile file = new RandomAccessFile(apkFile, "r"); - Long eo = null; - for (int j = t_bf.limit() - 4; j >= 0; j--) { - if (t_bf.getInt(j) == 0x06054b50) { - eo = (sz - t_sz) + (long)j; - break; - } - } - - if (eo != null) { - ByteBuffer eb = ByteBuffer.allocate(22).order(ByteOrder.LITTLE_ENDIAN); - ch.read(eb, eo); - eb.flip(); - long cds = eb.getInt(12) & 0xFFFFFFFFL; - long cdo = eb.getInt(16) & 0xFFFFFFFFL; - - ByteBuffer hb = ByteBuffer.allocate(46).order(ByteOrder.LITTLE_ENDIAN); - long co = cdo; - long e_o = cdo + cds; - while (co < e_o) { - hb.clear(); - ch.read(hb, co); - hb.flip(); - if (hb.remaining() < 46 || hb.getInt() != 0x02014b50) { - break; - } - - long co_c = co + 16; - hb.position(16); - int c = hb.getInt(); - - hb.position(28); - int nl = hb.getShort() & 0xFFFF; - int el = hb.getShort() & 0xFFFF; - int cl = hb.getShort() & 0xFFFF; - - hb.position(42); - long lho = hb.getInt() & 0xFFFFFFFFL; - - byte[] nb = new byte[nl]; - ByteBuffer nbf = ByteBuffer.wrap(nb); - ch.read(nbf, co + 46); - String n = new String(nb, StandardCharsets.UTF_8); - - if (n.startsWith("classes") && n.endsWith(".dex")) { - ByteBuffer lhb = ByteBuffer.allocate(30).order(ByteOrder.LITTLE_ENDIAN); - ch.read(lhb, lho); - lhb.flip(); - if (lhb.remaining() >= 30 && lhb.getInt() == 0x04034b50) { - cur.put(n, new long[]{co_c, lho + 14, (long) c}); - } - } - - co += 46L + nl + el + cl; - } - } + try { + long endOfCentralDirectory = findEndOfCentralDirectory(file); + if (endOfCentralDirectory < 0) { + return dexInfoMap; } - } - if (s_es.isEmpty() || t_es.isEmpty()) { - System.out.println("[CRC] No dex files found in apk"); + file.seek(endOfCentralDirectory + Directory.CENTRAL_DIRECTORY_SIZE.offset); + long centralDirectorySize = unsignedInt(readIntLE(file)); + + file.seek(endOfCentralDirectory + Directory.CENTRAL_DIRECTORY_OFFSET.offset); + long centralDirectoryOffset = unsignedInt(readIntLE(file)); + + long headerPosition = centralDirectoryOffset; + long centralDirectoryEnd = centralDirectoryOffset + centralDirectorySize; + + while (headerPosition < centralDirectoryEnd) { + file.seek(headerPosition); + + if (readIntLE(file) != ZipSignature.CENTRAL_DIRECTORY_HEADER.value) { + break; + } + file.seek(headerPosition + DHeader.CRC32_OFFSET.offset); + long crc32Value = unsignedInt(readIntLE(file)); + + file.seek(headerPosition + DHeader.FILE_NAME_LENGTH.offset); + int fileNameLength = readShortLE(file); + + file.seek(headerPosition + DHeader.EXTRA_FIELD_LENGTH.offset); + int extraFieldLength = readShortLE(file); + + file.seek(headerPosition + DHeader.COMMENT_LENGTH.offset); + int commentLength = readShortLE(file); + + file.seek(headerPosition + DHeader.LOCAL_HEADER_OFFSET.offset); + long localHeaderOffset = unsignedInt(readIntLE(file)); + + byte[] fileNameBytes = new byte[fileNameLength]; + file.seek(headerPosition + DHeader.HEADER_SIZE.offset); + file.readFully(fileNameBytes); + + String fileName = new String(fileNameBytes, "UTF-8"); + + if (fileName.startsWith("classes") && fileName.endsWith(".dex")) { + + long[] info = new long[3]; + info[0] = headerPosition + DHeader.CRC32_OFFSET.offset; + info[1] = localHeaderOffset + TFileHeader.CRC32_OFFSET.offset; + info[2] = crc32Value; + dexInfoMap.put(fileName, info); + + } + headerPosition = headerPosition + DHeader.HEADER_SIZE.offset + fileNameLength + extraFieldLength + commentLength; + } + } finally { + file.close(); + } + return dexInfoMap; + } + + public static void patch(File sourceApk, File targetApk) throws IOException { + Map sourceDexInfo = readDexCrcInfo(sourceApk); + Map targetDexInfo = readDexCrcInfo(targetApk); + + if (sourceDexInfo.isEmpty() || targetDexInfo.isEmpty()) { + System.out.println("[CRC] No dex files found"); return; } - boolean mod = false; - try (RandomAccessFile rf = new RandomAccessFile(t_a, "rw"); - FileChannel ch = rf.getChannel()) { - - ByteBuffer b = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); - - Iterator> it = s_es.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry e = it.next(); + RandomAccessFile targetFile = new RandomAccessFile(targetApk, "rw"); + boolean patched = false; - long[] se = e.getValue(); - long[] te = t_es.get(e.getKey()); - if (te != null && te[2] != se[2]) { - int n_c = (int) se[2]; - b.clear(); - b.putInt(n_c); - b.flip(); - ch.write(b, te[0]); - b.clear(); - b.putInt(n_c); - b.flip(); - ch.write(b, te[1]); - - mod = true; + try { + Iterator it = sourceDexInfo.keySet().iterator(); + while (it.hasNext()) { + String dexName = it.next(); + long[] sourceInfo = sourceDexInfo.get(dexName); + long[] targetInfo = targetDexInfo.get(dexName); + + if (targetInfo != null && targetInfo[2] != sourceInfo[2]) { + int newCrc32 = (int) sourceInfo[2]; + + targetFile.seek(targetInfo[0]); + writeIntLE(targetFile, newCrc32); + + targetFile.seek(targetInfo[1]); + writeIntLE(targetFile, newCrc32); + + patched = true; + System.out.println("[CRC] Patched: " + dexName); } } - - if (mod) { - ch.force(true); - System.out.println("[CRC] Patched dex CRC values from merged APK"); - } + } finally { + targetFile.close(); + } + + if (patched) { + System.out.println("[CRC] Patched dex CRC values from source APK"); } } } diff --git a/antik/src/main/java/com/antik/crc32/hyper/DHeader.java b/antik/src/main/java/com/antik/crc32/hyper/DHeader.java new file mode 100644 index 0000000..20b0103 --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/hyper/DHeader.java @@ -0,0 +1,16 @@ +package com.antik.crc32.hyper; + +public enum DHeader { + CRC32_OFFSET(16), + FILE_NAME_LENGTH(28), + EXTRA_FIELD_LENGTH(30), + COMMENT_LENGTH(32), + LOCAL_HEADER_OFFSET(42), + HEADER_SIZE(46); + + public final int offset; + + DHeader(int offset) { + this.offset = offset; + } +} diff --git a/antik/src/main/java/com/antik/crc32/hyper/Directory.java b/antik/src/main/java/com/antik/crc32/hyper/Directory.java new file mode 100644 index 0000000..ac09fe0 --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/hyper/Directory.java @@ -0,0 +1,12 @@ +package com.antik.crc32.hyper; + +public enum Directory { + CENTRAL_DIRECTORY_SIZE(12), + CENTRAL_DIRECTORY_OFFSET(16); + + public final int offset; + + Directory(int offset) { + this.offset = offset; + } +} diff --git a/antik/src/main/java/com/antik/crc32/hyper/TFileHeader.java b/antik/src/main/java/com/antik/crc32/hyper/TFileHeader.java new file mode 100644 index 0000000..d8d8cd1 --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/hyper/TFileHeader.java @@ -0,0 +1,11 @@ +package com.antik.crc32.hyper; + +public enum TFileHeader { + CRC32_OFFSET(14); + + public final int offset; + + TFileHeader(int offset) { + this.offset = offset; + } +} diff --git a/antik/src/main/java/com/antik/crc32/hyper/ZipSignature.java b/antik/src/main/java/com/antik/crc32/hyper/ZipSignature.java new file mode 100644 index 0000000..c7d532d --- /dev/null +++ b/antik/src/main/java/com/antik/crc32/hyper/ZipSignature.java @@ -0,0 +1,12 @@ +package com.antik.crc32.hyper; + +public enum ZipSignature { + CENTRAL_DIRECTORY_HEADER(0x02014b50), + END_OF_CENTRAL_DIRECTORY(0x06054b50); + + public final int value; + + ZipSignature(int value) { + this.value = value; + } +} diff --git a/antik/src/main/java/com/antik/logCat.java b/antik/src/main/java/com/antik/logCat.java new file mode 100644 index 0000000..0a088d3 --- /dev/null +++ b/antik/src/main/java/com/antik/logCat.java @@ -0,0 +1,5 @@ +package com.antik; + +public interface logCat { + String TAG = "RevDex"; +} \ No newline at end of file diff --git a/antik/src/main/resources/log.dex b/antik/src/main/resources/log.dex index cd4be8f..38a44dc 100644 Binary files a/antik/src/main/resources/log.dex and b/antik/src/main/resources/log.dex differ