From e80e6193ed048fa9cecd2d12d5fad8313a6a5490 Mon Sep 17 00:00:00 2001 From: isPointer Date: Wed, 24 Jun 2026 09:07:29 +0600 Subject: [PATCH] #r_mod --- .../Translation/MethodT/MethodMaker.java | 2 +- .../Translation/TranslationPatcher.java | 686 +++++++++--------- antik/src/main/java/com/antik/Main.java | 92 ++- antik/src/main/java/com/antik/root/Base.java | 57 ++ .../com/antik/root/PackageM/Installer.java | 33 + .../com/antik/root/PackageM/uninstaller.java | 15 + antik/src/main/java/com/antik/root/Pull.java | 54 ++ .../main/java/com/antik/root/launchApp.java | 20 + antik/src/main/java/com/antik/ui/banner.java | 2 +- 9 files changed, 593 insertions(+), 368 deletions(-) create mode 100644 antik/src/main/java/com/antik/root/Base.java create mode 100644 antik/src/main/java/com/antik/root/PackageM/Installer.java create mode 100644 antik/src/main/java/com/antik/root/PackageM/uninstaller.java create mode 100644 antik/src/main/java/com/antik/root/Pull.java create mode 100644 antik/src/main/java/com/antik/root/launchApp.java diff --git a/antik/src/main/java/com/antik/DexPatcher/Translation/MethodT/MethodMaker.java b/antik/src/main/java/com/antik/DexPatcher/Translation/MethodT/MethodMaker.java index 874875c..180609e 100644 --- a/antik/src/main/java/com/antik/DexPatcher/Translation/MethodT/MethodMaker.java +++ b/antik/src/main/java/com/antik/DexPatcher/Translation/MethodT/MethodMaker.java @@ -129,7 +129,7 @@ public class MethodMaker { public static ImmutableMethod createClinit() { MethodImplementationBuilder builder = new MethodImplementationBuilder(1); - builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference("RePatcher v1.4.14"))); + builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference("RePatcher v1.5.20"))); builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_STATIC, 0, 0, 0, 0, 0, 0, new ImmutableMethodReference(PairipClass.STARTUP_LAUNCHER.type, "launch", null, "V"))); builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); return new ImmutableMethod(PairipClass.APPLICATION.type, "", null, "V", AccessFlags.STATIC.getValue() | AccessFlags.CONSTRUCTOR.getValue(), null, null, builder.getMethodImplementation()); diff --git a/antik/src/main/java/com/antik/DexPatcher/Translation/TranslationPatcher.java b/antik/src/main/java/com/antik/DexPatcher/Translation/TranslationPatcher.java index 61f4066..9052ed0 100644 --- a/antik/src/main/java/com/antik/DexPatcher/Translation/TranslationPatcher.java +++ b/antik/src/main/java/com/antik/DexPatcher/Translation/TranslationPatcher.java @@ -1,343 +1,343 @@ -package com.antik.DexPatcher.Translation; - -import com.antik.DexPatcher.Translation.antik.PairipClass; -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.writer.io.MemoryDataStore; -import org.jf.dexlib2.writer.pool.DexPool; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class TranslationPatcher { - - private static final byte[] DEX_MAGIC = new byte[]{0x64, 0x65, 0x78, 0x0A}; - private static final byte[] PAIRIP_ASSET_HEADER = new byte[]{0x00, 0x49, 0x41, 0x50, 0x02}; - - public static void patch(ApkModule m, File jsonFile) throws Exception { - System.out.println("[INFO] Loading translation file: " + jsonFile.getName()); - TranslationData data = new TranslationData(jsonFile); - TranslationRewriter rewriter = new TranslationRewriter(data); - - List dexNames = new ArrayList<>(); - for (InputSource s : m.getInputSources()) { - if (s.getName().endsWith(".dex")) { - dexNames.add(s.getName()); - } - } - - Set existingDexNames = new HashSet<>(dexNames); - Set existingClassTypes = new HashSet<>(); - for (String dn : dexNames) { - InputSource s = m.getInputSource(dn); - if (s == null) continue; - try (InputStream input = s.openStream()) { - DexFile dexFile = loadDexFile(readAllBytes(input), "trans_scan"); - for (ClassDef classDef : dexFile.getClasses()) { - existingClassTypes.add(classDef.getType()); - } - } - } - - List extraDexes = new ArrayList<>(); - if (data.hasMethods) { - extraDexes.addAll(collectEmbeddedAssetDexes(m, existingClassTypes)); - PendingDex restoreMethodDex = loadRestoreMethodDex(existingClassTypes); - if (restoreMethodDex != null) { - extraDexes.add(restoreMethodDex); - } - } - - for (String dn : dexNames) { - InputSource s = m.getInputSource(dn); - if (s == null) continue; - - byte[] d_bs; - try (InputStream i = s.openStream()) { - d_bs = readAllBytes(i); - } - - DexFile d_f = loadDexFile(d_bs, "trans"); - - List cds = new ArrayList<>(); - boolean mod = false; - - for (ClassDef cd : d_f.getClasses()) { - String type = cd.getType(); - if (type.startsWith("Lcom/pairip/")) { - if (PairipClass.contains(type)) { - System.out.println("[INFO] Patching " + type + " in " + dn); - cds.add(rewriter.rewrite(cd)); - mod = true; - } else if (!type.equals("Lcom/pairip/PairipLog;") && !type.equals("Lcom/pairip/RestoreMethod;")) { - System.out.println("[INFO] Removing class: " + type); - mod = true; - } else { - cds.add(cd); - } - } else { - cds.add(cd); - } - } - - 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 translation in " + dn); - } - } - - int nextDexNumber = nextDexNumber(existingDexNames); - for (PendingDex pendingDex : extraDexes) { - if (!pendingDex.canAddAgainst(existingClassTypes)) { - if (!pendingDex.isFullyPresentIn(existingClassTypes)) { - System.err.println("[WARN] Skipping " + pendingDex.sourceName + " due to partial class overlap"); - } - continue; - } - - String dexEntryName = nextDexEntryName(existingDexNames, nextDexNumber); - nextDexNumber = dexNumberOf(dexEntryName) + 1; - m.add(new ByteInputSource(pendingDex.dexBytes, dexEntryName)); - existingDexNames.add(dexEntryName); - existingClassTypes.addAll(pendingDex.classTypes); - System.out.println("[INFO] Added " + dexEntryName + " from " + pendingDex.sourceName); - } - - List libFiles = new ArrayList<>(); - for (InputSource source : m.getInputSources()) { - if (source.getName().contains("libpairipcore.so")) { - libFiles.add(source.getName()); - } - } - for (String libFile : libFiles) { - try { - m.getZipEntryMap().remove(libFile); - System.out.println("[INFO] Removing lib: " + libFile); - } catch (Exception e) { - System.err.println("[WARN] Could not remove lib: " + libFile); - } - } - } - - private static byte[] readAllBytes(InputStream input) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = input.read(buf)) != -1) { - baos.write(buf, 0, len); - } - return baos.toByteArray(); - } - - private static DexFile loadDexFile(byte[] dexBytes, String prefix) throws Exception { - File tempDex = File.createTempFile(prefix, ".dex"); - try { - Files.write(tempDex.toPath(), dexBytes); - return DexFileFactory.loadDexFile(tempDex, Opcodes.getDefault()); - } finally { - tempDex.delete(); - } - } - - private static List collectEmbeddedAssetDexes(ApkModule module, Set existingClassTypes) throws Exception { - List pendingDexes = new ArrayList<>(); - for (InputSource source : module.getInputSources()) { - if (!isTopLevelAsset(source.getName())) { - continue; - } - - byte[] assetBytes; - try (InputStream input = source.openStream()) { - assetBytes = readAllBytes(input); - } - - if (!startsWith(assetBytes, PAIRIP_ASSET_HEADER)) { - continue; - } - - int dexStart = findEmbeddedDexStart(assetBytes); - if (dexStart < 0) { - continue; - } - - try { - byte[] dexBytes = extractEmbeddedDex(assetBytes, dexStart); - PendingDex pendingDex = pendingDexFromBytes(source.getName(), dexBytes, "asset_dex"); - if (!pendingDex.isFullyPresentIn(existingClassTypes)) { - pendingDexes.add(pendingDex); - } - } catch (Exception ignored) { - } - } - - pendingDexes.sort((left, right) -> { - int bySize = Integer.compare(right.classTypes.size(), left.classTypes.size()); - if (bySize != 0) { - return bySize; - } - return left.sourceName.compareTo(right.sourceName); - }); - return pendingDexes; - } - - private static PendingDex loadRestoreMethodDex(Set existingClassTypes) throws Exception { - try (InputStream input = Main.class.getResourceAsStream("/restoreMethod.dex")) { - if (input == null) { - throw new FileNotFoundException("Missing resource: /restoreMethod.dex"); - } - PendingDex pendingDex = pendingDexFromBytes("restoreMethod.dex", readAllBytes(input), "restore_method"); - System.out.println("[INFO] Loaded classes from restoreMethod.dex"); - return pendingDex.isFullyPresentIn(existingClassTypes) ? null : pendingDex; - } - } - - private static PendingDex pendingDexFromBytes(String sourceName, byte[] dexBytes, String prefix) throws Exception { - DexFile dexFile = loadDexFile(dexBytes, prefix); - Set classTypes = new HashSet<>(); - for (ClassDef classDef : dexFile.getClasses()) { - classTypes.add(classDef.getType()); - } - return new PendingDex(sourceName, dexBytes, classTypes); - } - - private static boolean isTopLevelAsset(String entryName) { - if (!entryName.startsWith("assets/")) { - return false; - } - return entryName.indexOf('/', "assets/".length()) < 0; - } - - private static boolean startsWith(byte[] bytes, byte[] prefix) { - if (bytes.length < prefix.length) { - return false; - } - for (int i = 0; i < prefix.length; i++) { - if (bytes[i] != prefix[i]) { - return false; - } - } - return true; - } - - private static int findEmbeddedDexStart(byte[] bytes) { - for (int i = 0; i <= bytes.length - DEX_MAGIC.length; i++) { - boolean matches = true; - for (int j = 0; j < DEX_MAGIC.length; j++) { - if (bytes[i + j] != DEX_MAGIC[j]) { - matches = false; - break; - } - } - if (matches) { - return i; - } - } - return -1; - } - - private static byte[] extractEmbeddedDex(byte[] assetBytes, int dexStart) { - int headerOffset = dexStart + 0x20; - if (headerOffset + 4 > assetBytes.length) { - throw new IllegalArgumentException("Embedded dex header truncated"); - } - - int declaredFileSize = readLittleEndianInt(assetBytes, headerOffset); - if (declaredFileSize <= 0) { - throw new IllegalArgumentException("Embedded dex declared invalid file size: " + declaredFileSize); - } - - int dexEnd = dexStart + declaredFileSize; - if (dexEnd > assetBytes.length) { - throw new IllegalArgumentException("Embedded dex declared size exceeds asset bounds: " + declaredFileSize); - } - - return Arrays.copyOfRange(assetBytes, dexStart, dexEnd); - } - - private static int readLittleEndianInt(byte[] bytes, int offset) { - return (bytes[offset] & 0xFF) | ((bytes[offset + 1] & 0xFF) << 8) | ((bytes[offset + 2] & 0xFF) << 16) | ((bytes[offset + 3] & 0xFF) << 24); - } - - private static int nextDexNumber(Set dexEntryNames) { - int next = 1; - for (String name : dexEntryNames) { - next = Math.max(next, dexNumberOf(name) + 1); - } - return next; - } - - private static int dexNumberOf(String dexEntryName) { - if ("classes.dex".equals(dexEntryName)) { - return 1; - } - if (!dexEntryName.startsWith("classes") || !dexEntryName.endsWith(".dex")) { - return -1; - } - String middle = dexEntryName.substring("classes".length(), dexEntryName.length() - ".dex".length()); - if (middle.isEmpty()) { - return 1; - } - try { - return Integer.parseInt(middle); - } catch (NumberFormatException ignored) { - return -1; - } - } - - private static String nextDexEntryName(Set dexEntryNames, int startNumber) { - int number = Math.max(startNumber, 1); - while (true) { - String candidate = number == 1 ? "classes.dex" : "classes" + number + ".dex"; - if (!dexEntryNames.contains(candidate)) { - return candidate; - } - number++; - } - } - - private static final class PendingDex { - private final String sourceName; - private final byte[] dexBytes; - private final Set classTypes; - - private PendingDex(String sourceName, byte[] dexBytes, Set classTypes) { - this.sourceName = sourceName; - this.dexBytes = dexBytes; - this.classTypes = classTypes; - } - - private boolean isFullyPresentIn(Set existingClassTypes) { - return existingClassTypes.containsAll(classTypes); - } - - private boolean canAddAgainst(Set existingClassTypes) { - for (String classType : classTypes) { - if (existingClassTypes.contains(classType)) { - return false; - } - } - return true; - } - } -} +package com.antik.DexPatcher.Translation; + +import com.antik.DexPatcher.Translation.antik.PairipClass; +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.writer.io.MemoryDataStore; +import org.jf.dexlib2.writer.pool.DexPool; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class TranslationPatcher { + + private static final byte[] DEX_MAGIC = new byte[]{0x64, 0x65, 0x78, 0x0A}; + private static final byte[] PAIRIP_ASSET_HEADER = new byte[]{0x00, 0x49, 0x41, 0x50, 0x02}; + + public static void patch(ApkModule m, File jsonFile) throws Exception { + System.out.println("[INFO] Loading translation file: " + jsonFile.getName()); + TranslationData data = new TranslationData(jsonFile); + TranslationRewriter rewriter = new TranslationRewriter(data); + + List dexNames = new ArrayList<>(); + for (InputSource s : m.getInputSources()) { + if (s.getName().endsWith(".dex")) { + dexNames.add(s.getName()); + } + } + + Set existingDexNames = new HashSet<>(dexNames); + Set existingClassTypes = new HashSet<>(); + for (String dn : dexNames) { + InputSource s = m.getInputSource(dn); + if (s == null) continue; + try (InputStream input = s.openStream()) { + DexFile dexFile = loadDexFile(readAllBytes(input), "trans_scan"); + for (ClassDef classDef : dexFile.getClasses()) { + existingClassTypes.add(classDef.getType()); + } + } + } + + List extraDexes = new ArrayList<>(); + if (data.hasMethods) { + extraDexes.addAll(collectEmbeddedAssetDexes(m, existingClassTypes)); + PendingDex restoreMethodDex = loadRestoreMethodDex(existingClassTypes); + if (restoreMethodDex != null) { + extraDexes.add(restoreMethodDex); + } + } + + for (String dn : dexNames) { + InputSource s = m.getInputSource(dn); + if (s == null) continue; + + byte[] d_bs; + try (InputStream i = s.openStream()) { + d_bs = readAllBytes(i); + } + + DexFile d_f = loadDexFile(d_bs, "trans"); + + List cds = new ArrayList<>(); + boolean mod = false; + + for (ClassDef cd : d_f.getClasses()) { + String type = cd.getType(); + if (type.startsWith("Lcom/pairip/")) { + if (PairipClass.contains(type)) { + System.out.println("[INFO] Patching " + type + " in " + dn); + cds.add(rewriter.rewrite(cd)); + mod = true; + } else if (!type.equals("Lcom/pairip/PairipLog;") && !type.equals("Lcom/pairip/RestoreMethod;")) { + System.out.println("[INFO] Removing class: " + type); + mod = true; + } else { + cds.add(cd); + } + } else { + cds.add(cd); + } + } + + 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 translation in " + dn); + } + } + + int nextDexNumber = nextDexNumber(existingDexNames); + for (PendingDex pendingDex : extraDexes) { + if (!pendingDex.canAddAgainst(existingClassTypes)) { + if (!pendingDex.isFullyPresentIn(existingClassTypes)) { + System.err.println("[WARN] Skipping " + pendingDex.sourceName + " due to partial class overlap"); + } + continue; + } + + String dexEntryName = nextDexEntryName(existingDexNames, nextDexNumber); + nextDexNumber = dexNumberOf(dexEntryName) + 1; + m.add(new ByteInputSource(pendingDex.dexBytes, dexEntryName)); + existingDexNames.add(dexEntryName); + existingClassTypes.addAll(pendingDex.classTypes); + System.out.println("[INFO] Added " + dexEntryName + " from " + pendingDex.sourceName); + } + + List libFiles = new ArrayList<>(); + for (InputSource source : m.getInputSources()) { + if (source.getName().contains("libpairipcore.so")) { + libFiles.add(source.getName()); + } + } + for (String libFile : libFiles) { + try { + m.getZipEntryMap().remove(libFile); + System.out.println("[INFO] Removing lib: " + libFile); + } catch (Exception e) { + System.err.println("[WARN] Could not remove lib: " + libFile); + } + } + } + + private static byte[] readAllBytes(InputStream input) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = input.read(buf)) != -1) { + baos.write(buf, 0, len); + } + return baos.toByteArray(); + } + + private static DexFile loadDexFile(byte[] dexBytes, String prefix) throws Exception { + File tempDex = File.createTempFile(prefix, ".dex"); + try { + Files.write(tempDex.toPath(), dexBytes); + return DexFileFactory.loadDexFile(tempDex, Opcodes.getDefault()); + } finally { + tempDex.delete(); + } + } + + private static List collectEmbeddedAssetDexes(ApkModule module, Set existingClassTypes) throws Exception { + List pendingDexes = new ArrayList<>(); + for (InputSource source : module.getInputSources()) { + if (!isTopLevelAsset(source.getName())) { + continue; + } + + byte[] assetBytes; + try (InputStream input = source.openStream()) { + assetBytes = readAllBytes(input); + } + + if (!startsWith(assetBytes, PAIRIP_ASSET_HEADER)) { + continue; + } + + int dexStart = findEmbeddedDexStart(assetBytes); + if (dexStart < 0) { + continue; + } + + try { + byte[] dexBytes = extractEmbeddedDex(assetBytes, dexStart); + PendingDex pendingDex = pendingDexFromBytes(source.getName(), dexBytes, "asset_dex"); + if (!pendingDex.isFullyPresentIn(existingClassTypes)) { + pendingDexes.add(pendingDex); + } + } catch (Exception ignored) { + } + } + + pendingDexes.sort((left, right) -> { + int bySize = Integer.compare(right.classTypes.size(), left.classTypes.size()); + if (bySize != 0) { + return bySize; + } + return left.sourceName.compareTo(right.sourceName); + }); + return pendingDexes; + } + + private static PendingDex loadRestoreMethodDex(Set existingClassTypes) throws Exception { + try (InputStream input = Main.class.getResourceAsStream("/restoreMethod.dex")) { + if (input == null) { + throw new FileNotFoundException("Missing resource: /restoreMethod.dex"); + } + PendingDex pendingDex = pendingDexFromBytes("restoreMethod.dex", readAllBytes(input), "restore_method"); + System.out.println("[INFO] Loaded classes from restoreMethod.dex"); + return pendingDex.isFullyPresentIn(existingClassTypes) ? null : pendingDex; + } + } + + private static PendingDex pendingDexFromBytes(String sourceName, byte[] dexBytes, String prefix) throws Exception { + DexFile dexFile = loadDexFile(dexBytes, prefix); + Set classTypes = new HashSet<>(); + for (ClassDef classDef : dexFile.getClasses()) { + classTypes.add(classDef.getType()); + } + return new PendingDex(sourceName, dexBytes, classTypes); + } + + private static boolean isTopLevelAsset(String entryName) { + if (!entryName.startsWith("assets/")) { + return false; + } + return entryName.indexOf('/', "assets/".length()) < 0; + } + + private static boolean startsWith(byte[] bytes, byte[] prefix) { + if (bytes.length < prefix.length) { + return false; + } + for (int i = 0; i < prefix.length; i++) { + if (bytes[i] != prefix[i]) { + return false; + } + } + return true; + } + + private static int findEmbeddedDexStart(byte[] bytes) { + for (int i = 0; i <= bytes.length - DEX_MAGIC.length; i++) { + boolean matches = true; + for (int j = 0; j < DEX_MAGIC.length; j++) { + if (bytes[i + j] != DEX_MAGIC[j]) { + matches = false; + break; + } + } + if (matches) { + return i; + } + } + return -1; + } + + private static byte[] extractEmbeddedDex(byte[] assetBytes, int dexStart) { + int headerOffset = dexStart + 0x20; + if (headerOffset + 4 > assetBytes.length) { + throw new IllegalArgumentException("Embedded dex header truncated"); + } + + int declaredFileSize = readLittleEndianInt(assetBytes, headerOffset); + if (declaredFileSize <= 0) { + throw new IllegalArgumentException("Embedded dex declared invalid file size: " + declaredFileSize); + } + + int dexEnd = dexStart + declaredFileSize; + if (dexEnd > assetBytes.length) { + throw new IllegalArgumentException("Embedded dex declared size exceeds asset bounds: " + declaredFileSize); + } + + return Arrays.copyOfRange(assetBytes, dexStart, dexEnd); + } + + private static int readLittleEndianInt(byte[] bytes, int offset) { + return (bytes[offset] & 0xFF) | ((bytes[offset + 1] & 0xFF) << 8) | ((bytes[offset + 2] & 0xFF) << 16) | ((bytes[offset + 3] & 0xFF) << 24); + } + + private static int nextDexNumber(Set dexEntryNames) { + int next = 1; + for (String name : dexEntryNames) { + next = Math.max(next, dexNumberOf(name) + 1); + } + return next; + } + + private static int dexNumberOf(String dexEntryName) { + if ("classes.dex".equals(dexEntryName)) { + return 1; + } + if (!dexEntryName.startsWith("classes") || !dexEntryName.endsWith(".dex")) { + return -1; + } + String middle = dexEntryName.substring("classes".length(), dexEntryName.length() - ".dex".length()); + if (middle.isEmpty()) { + return 1; + } + try { + return Integer.parseInt(middle); + } catch (NumberFormatException ignored) { + return -1; + } + } + + private static String nextDexEntryName(Set dexEntryNames, int startNumber) { + int number = Math.max(startNumber, 1); + while (true) { + String candidate = number == 1 ? "classes.dex" : "classes" + number + ".dex"; + if (!dexEntryNames.contains(candidate)) { + return candidate; + } + number++; + } + } + + private static final class PendingDex { + private final String sourceName; + private final byte[] dexBytes; + private final Set classTypes; + + private PendingDex(String sourceName, byte[] dexBytes, Set classTypes) { + this.sourceName = sourceName; + this.dexBytes = dexBytes; + this.classTypes = classTypes; + } + + private boolean isFullyPresentIn(Set existingClassTypes) { + return existingClassTypes.containsAll(classTypes); + } + + private boolean canAddAgainst(Set existingClassTypes) { + for (String classType : classTypes) { + if (existingClassTypes.contains(classType)) { + return false; + } + } + return true; + } + } +} diff --git a/antik/src/main/java/com/antik/Main.java b/antik/src/main/java/com/antik/Main.java index 6299ef0..696e3fc 100644 --- a/antik/src/main/java/com/antik/Main.java +++ b/antik/src/main/java/com/antik/Main.java @@ -4,11 +4,14 @@ import com.antik.DexPatcher.DexPatcher; import com.antik.DexPatcher.Translation.TranslationPatcher; import com.antik.crc32.crc32; import com.antik.manifest.manifestP; +import com.antik.root.*; +import com.antik.root.PackageM.Installer; +import com.antik.root.PackageM.uninstaller; import com.antik.ui.*; import com.antik.utils.*; import com.reandroid.apk.ApkBundle; import com.reandroid.apk.ApkModule; -import com.reandroid.archive.WriteProgress; + import java.io.*; import java.nio.file.Files; @@ -22,12 +25,15 @@ public class Main { String inputPath = null; String translatePath = null; + boolean R_mod = false; for (int i = 0; i < args.length; i++) { if ("-i".equals(args[i]) && i + 1 < args.length) { inputPath = args[i + 1]; } else if ("-t".equals(args[i]) && i + 1 < args.length) { translatePath = args[i + 1]; + } else if ("-r".equals(args[i])) { + R_mod = true; } } @@ -39,37 +45,37 @@ public class Main { File inputApk = new File(inputPath); if (!inputApk.exists()) { - System.err.println("Input file not found: " + inputPath); + System.err.println("Input file not found : " + inputPath); return; } banner.banner(); - File tempDir = null; + File T_dIR = null; try { ApkModule module; File mergedApkFile; if (inputPath.endsWith(".apk")) { - System.out.println("[INFO] Loading APK..."); + System.out.println("[INFO] Loading APK "); module = ApkModule.loadApkFile(inputApk); mergedApkFile = inputApk; } else { - tempDir = Files.createTempDirectory("antik_merge").toFile(); - System.out.println("[MERGE] Extracting APKS..."); - AntikUtils.ex_apks(inputApk, tempDir); + T_dIR = Files.createTempDirectory("antik_merge").toFile(); + System.out.println("[MERGE] Extracting APKS "); + AntikUtils.ex_apks(inputApk, T_dIR); - System.out.println("[MERGE] Merging APK..."); + System.out.println("[MERGE] Merging APK "); ApkBundle bundle = new ApkBundle(); - bundle.loadApkDirectory(tempDir); + bundle.loadApkDirectory(T_dIR); module = bundle.mergeModules(); System.out.println("[INFO] Patching AndroidManifest.xml"); try { manifestP.patch(module); } catch (Exception e) { - System.err.println("[ERROR] Manifest patching failed: " + e.getMessage()); + System.err.println("[ERROR] Manifest patching failed : " + e.getMessage()); } String name = inputApk.getName(); @@ -77,7 +83,7 @@ public class Main { name = (dot > 0 ? name.substring(0, dot) : name) + "_merged.apk"; mergedApkFile = new File(output.get_out(inputApk, name)); - System.out.println("[BUILD] Writing merged APK..."); + System.out.println("[BUILD] Writing merged APK "); loading.progress(module, mergedApkFile); System.out.println("[MERGE] APK merged successfully: " + mergedApkFile.getAbsolutePath()); } @@ -85,7 +91,7 @@ public class Main { if (translatePath != null) { File jsonFile = new File(translatePath); if (jsonFile.exists()) { - System.out.println("[INFO] Starting Translation Patch..."); + System.out.println("[INFO] Starting Translation Patch "); TranslationPatcher.patch(module, jsonFile); String tn = mergedApkFile.getName(); @@ -93,19 +99,18 @@ public class Main { tn = (td > 0 ? tn.substring(0, td) : tn) + "_translated.apk"; File transFile = new File(output.get_out(inputApk, tn)); - System.out.println("[BUILD] Building Translated APK..."); + System.out.println("[BUILD] Building Translated APK "); output.write(module, transFile); - System.out.println("[BUILD] Translated APK built at: " + transFile.getAbsolutePath()); + System.out.println("[BUILD] Translated APK built at : " + transFile.getAbsolutePath()); } else { - System.err.println("[ERROR] Translation file not found: " + translatePath); + System.err.println("[ERROR] Translation file not found : " + translatePath); } } else if (!inputPath.endsWith(".apk")) { - // Default logging patch only if merging and no translation requested - System.out.println("[INFO] Patching classes.dex for logging..."); + System.out.println("[INFO] Patching classes.dex for logging "); try { DexPatcher.patch(module); } catch (Exception e) { - System.err.println("[ERROR] Patching failed: " + e.getMessage()); + System.err.println("[ERROR] Patching failed : " + e.getMessage()); } String pn = mergedApkFile.getName(); @@ -113,20 +118,61 @@ public class Main { pn = (pd > 0 ? pn.substring(0, pd) : pn) + "_pairip.apk"; File paiFile = new File(output.get_out(inputApk, pn)); - System.out.println("[BUILD] Building Logging APK..."); + System.out.println("[BUILD] Building Logging APK "); output.write(module, paiFile); crc32.patch(mergedApkFile, paiFile); - System.out.println("[BUILD] Logging APK built at: " + paiFile.getAbsolutePath()); + System.out.println("[BUILD] Logging APK built at : " + paiFile.getAbsolutePath()); + + if (R_mod) { + String pkg = module.getPackageName(); + System.out.println("[ROOT] Starting Root Mode for package : " + pkg); + uninstaller.uninstall(pkg); + Installer.install(paiFile); + + if (T_dIR != null) { + File originalBase = new File(T_dIR, "base.apk"); + if (originalBase.exists()) { + Base.replaceBase(pkg, originalBase); + launchApp.launchApp(pkg); + + File pulledJson = new File(output.get_out(inputApk, "pairip.json")); + if (Pull.waitForFileAndPull(pkg, "dictionary/pairip.json", pulledJson)) { + System.out.println("[ROOT] pairip.json captured!"); + Runtime.getRuntime().exec(new String[]{"su", "-c", "am force-stop " + pkg}).waitFor(); + uninstaller.uninstall(pkg); + + System.out.println("[INFO] Reloading clean merged APK for final patching "); + module = ApkModule.loadApkFile(mergedApkFile); + + System.out.println("[INFO] Starting Translation Patch with captured JSON "); + TranslationPatcher.patch(module, pulledJson); + + String tn = mergedApkFile.getName(); + int td = tn.lastIndexOf('.'); + tn = (td > 0 ? tn.substring(0, td) : tn) + "_translated.apk"; + File transFile = new File(output.get_out(inputApk, tn)); + + System.out.println("[BUILD] Building Final Translated APK "); + output.write(module, transFile); + System.out.println("[BUILD] Final APK built at : " + transFile.getAbsolutePath()); + } + } else { + System.err.println("[ERROR] Original base.apk not found in APKS for root replacement"); + } + } else { + System.out.println("[ROOT] Root mode replacement skipped (Input was not an APKS file)"); + } + } } System.out.println("[BUILD] Process completed"); } catch (Exception e) { - System.err.println("[ERROR] Process failed: " + e.getMessage()); + System.err.println("[ERROR] Process failed : " + e.getMessage()); e.printStackTrace(); } finally { - if (tempDir != null) { - deleteDir.del_dir(tempDir); + if (T_dIR != null) { + deleteDir.del_dir(T_dIR); } } } diff --git a/antik/src/main/java/com/antik/root/Base.java b/antik/src/main/java/com/antik/root/Base.java new file mode 100644 index 0000000..4ff4cbf --- /dev/null +++ b/antik/src/main/java/com/antik/root/Base.java @@ -0,0 +1,57 @@ +package com.antik.root; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; + +public class Base { + public static void replaceBase(String _Pname, File Org) { + try { + System.out.println("[ROOT] Finding base.apk path {/}"); + + Process P_Proc = Runtime.getRuntime().exec( new String[]{ + "su", + "-c", + "pm path " + _Pname + } + ); + + BufferedReader ___Read = new BufferedReader(new InputStreamReader(P_Proc.getInputStream())); + + String line = ___Read.readLine(); + P_Proc.waitFor(); + + if (line == null || !line.startsWith("package:")) { + System.err.println("[ERROR] Package path not found : " + _Pname); + return; + } + + String installedPath = line.substring(8).trim(); + + System.out.println("[ROOT] Replacing : " + installedPath); + + Runtime.getRuntime().exec(new String[]{ + "su", + "-c", + "am force-stop " + _Pname + } + ).waitFor(); + + String cmd = "cp " + Org.getAbsolutePath() + " " + installedPath + " && chmod 644 " + installedPath + " && chown system:system " + installedPath; + + Process replaceProcess = Runtime.getRuntime().exec( new String[]{ + "su", + "-c", + cmd + } + ); + + replaceProcess.waitFor(); + + System.out.println("[ROOT] Finished "); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/antik/src/main/java/com/antik/root/PackageM/Installer.java b/antik/src/main/java/com/antik/root/PackageM/Installer.java new file mode 100644 index 0000000..67785af --- /dev/null +++ b/antik/src/main/java/com/antik/root/PackageM/Installer.java @@ -0,0 +1,33 @@ +package com.antik.root.PackageM; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; + +public class Installer { + public static void install(File D_TOY) { + try { + System.out.println("[ROOT] Installing : " + D_TOY.getName()); + + Process p = Runtime.getRuntime().exec(new String[]{ + "su", "-c", "pm install -r -t -g " + D_TOY.getAbsolutePath() + }); + + BufferedReader _TRead = new BufferedReader(new InputStreamReader(p.getInputStream())); + + String LN; + + while ((LN = _TRead.readLine()) != null) { + System.out.println("[PM] " + LN); + } + + p.waitFor(); + + System.out.println("[ROOT] Finished"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/antik/src/main/java/com/antik/root/PackageM/uninstaller.java b/antik/src/main/java/com/antik/root/PackageM/uninstaller.java new file mode 100644 index 0000000..ebfbaab --- /dev/null +++ b/antik/src/main/java/com/antik/root/PackageM/uninstaller.java @@ -0,0 +1,15 @@ +package com.antik.root.PackageM; + +public class uninstaller { + + public static void uninstall(String _Pname) { + try { + System.out.println("[ROOT] Uninstalling: " + _Pname); + Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "pm uninstall " + _Pname}); + p.waitFor(); + System.out.println("[ROOT] Finished"); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/antik/src/main/java/com/antik/root/Pull.java b/antik/src/main/java/com/antik/root/Pull.java new file mode 100644 index 0000000..75b463f --- /dev/null +++ b/antik/src/main/java/com/antik/root/Pull.java @@ -0,0 +1,54 @@ +package com.antik.root; + +import java.io.File; + +public class Pull { + public static boolean waitForFileAndPull(String packageName, String internalPath, File destination) { + String fullPath = "/data/data/" + packageName + "/" + internalPath; + + try { + System.out.println("[ROOT] Waiting for: " + fullPath); + + long startTime = System.currentTimeMillis(); + long timeout = 60000; + + while (System.currentTimeMillis() - startTime < timeout) { + Process checkProcess = Runtime.getRuntime().exec( + new String[]{ + "su", + "-c", + "ls " + fullPath + } + ); + + if (checkProcess.waitFor() == 0) { + System.out.println("[ROOT] File found."); + + String cmd = "cp " + fullPath + " " + destination.getAbsolutePath() + " && chmod 666 " + destination.getAbsolutePath(); + + Process C_pad = Runtime.getRuntime().exec( + new String[]{ + "su", + "-c", + cmd + } + ); + + C_pad.waitFor(); + + System.out.println("[ROOT] Pulled successfully"); + return true; + } + + Thread.sleep(2000); + } + + System.err.println("[ERROR] Timeout "); + + } catch (Exception e) { + e.printStackTrace(); + } + + return false; + } +} diff --git a/antik/src/main/java/com/antik/root/launchApp.java b/antik/src/main/java/com/antik/root/launchApp.java new file mode 100644 index 0000000..d06076e --- /dev/null +++ b/antik/src/main/java/com/antik/root/launchApp.java @@ -0,0 +1,20 @@ +package com.antik.root; + +public class launchApp { + public static void launchApp(String _Pname) { + try { + System.out.println("[ROOT] Launching " + _Pname); + Runtime.getRuntime().exec(new String[]{ + "su", + "-c", + "monkey -p " + _Pname + " -c android.intent.category.LAUNCHER 1" + } + ).waitFor(); + + System.out.println("[ROOT] Finished "); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/antik/src/main/java/com/antik/ui/banner.java b/antik/src/main/java/com/antik/ui/banner.java index 41b7272..702d65b 100644 --- a/antik/src/main/java/com/antik/ui/banner.java +++ b/antik/src/main/java/com/antik/ui/banner.java @@ -8,7 +8,7 @@ public class banner { System.out.println("░█▀▄░█▀▀░█▀█░█▀█░▀█▀░█▀▄░▀█▀░█▀█░"); System.out.println("░█▀▄░█▀▀░█▀▀░█▀█░░█░░█▀▄░░█░░█▀▀░"); System.out.println("░▀░▀░▀▀▀░▀░░░▀░▀░▀▀▀░▀░▀░▀▀▀░▀░░░"); - System.out.println("Version : 1.3.10"); + System.out.println("Version : 1.5.20"); System.out.println("--------------------------------------"); System.out.println("Dev : Antik"); System.out.println("Channel : https://t.me/RevDex");