mirror of
https://github.com/ispointer/RePairip.git
synced 2026-09-29 20:05:21 +02:00
🪔
This commit is contained in:
@@ -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<String> dx_ns = new ArrayList<String>();
|
||||
for (InputSource s : m.getInputSources()) {
|
||||
if (s.getName().endsWith(".dex")) {
|
||||
dx_ns.add(s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
List<ClassDef> l_cds = new ArrayList<ClassDef>();
|
||||
Set<String> a_ts = new HashSet<String>();
|
||||
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<org.jf.dexlib2.iface.Field> s_fs = new ArrayList<org.jf.dexlib2.iface.Field>();
|
||||
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<String> j_ts = new ArrayList<String>();
|
||||
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<ClassDef> cds = new ArrayList<ClassDef>();
|
||||
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<Method> d_ms = new ArrayList<Method>();
|
||||
for (Method method : cd.getDirectMethods()) {
|
||||
d_ms.add(patchMethodIfTarget(method));
|
||||
}
|
||||
|
||||
List<Method> v_ms = new ArrayList<Method>();
|
||||
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<Instruction> 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<Instruction> 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<String> j_ts) {
|
||||
System.out.println("[INFO] Patching StartupLauncher");
|
||||
List<Method> d_ms = new ArrayList<Method>();
|
||||
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<Instruction> o_ins = new ArrayList<Instruction>();
|
||||
for (Instruction i : im.getInstructions()) {
|
||||
o_ins.add(i);
|
||||
}
|
||||
|
||||
List<Instruction> n_ins = new ArrayList<Instruction>();
|
||||
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<? extends org.jf.dexlib2.iface.TryBlock<? extends org.jf.dexlib2.iface.ExceptionHandler>> o_tbs = im.getTryBlocks();
|
||||
List<org.jf.dexlib2.immutable.ImmutableTryBlock> n_tbs = new ArrayList<org.jf.dexlib2.immutable.ImmutableTryBlock>();
|
||||
|
||||
for (org.jf.dexlib2.iface.TryBlock<? extends org.jf.dexlib2.iface.ExceptionHandler> t : o_tbs) {
|
||||
int s = t.getStartCodeAddress();
|
||||
int c = t.getCodeUnitCount();
|
||||
int e = s + c;
|
||||
|
||||
List<org.jf.dexlib2.immutable.ImmutableExceptionHandler> h = new ArrayList<org.jf.dexlib2.immutable.ImmutableExceptionHandler>();
|
||||
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<String> j_ts) {
|
||||
List<Instruction> ins = new ArrayList<Instruction>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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<String> dx_ns = new ArrayList<String>();
|
||||
for (InputSource s : m.getInputSources()) {
|
||||
if (s.getName().endsWith(".dex")) {
|
||||
dx_ns.add(s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
List<ClassDef> l_cds = new ArrayList<ClassDef>();
|
||||
Set<String> a_ts = new HashSet<String>();
|
||||
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<org.jf.dexlib2.iface.Field> s_fs = new ArrayList<org.jf.dexlib2.iface.Field>();
|
||||
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<String> j_ts = new ArrayList<String>();
|
||||
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<ClassDef> cds = new ArrayList<ClassDef>();
|
||||
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<Method> d_ms = new ArrayList<Method>();
|
||||
for (Method method : cd.getDirectMethods()) {
|
||||
d_ms.add(patchM.patchMethodIfTarget(method));
|
||||
}
|
||||
List<Method> v_ms = new ArrayList<Method>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Instruction> o_ins = new ArrayList<Instruction>();
|
||||
for (Instruction i : im.getInstructions()) {
|
||||
o_ins.add(i);
|
||||
}
|
||||
List<Instruction> n_ins = new ArrayList<Instruction>();
|
||||
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<? extends org.jf.dexlib2.iface.TryBlock<? extends org.jf.dexlib2.iface.ExceptionHandler>> o_tbs = im.getTryBlocks();
|
||||
List<org.jf.dexlib2.immutable.ImmutableTryBlock> n_tbs = new ArrayList<org.jf.dexlib2.immutable.ImmutableTryBlock>();
|
||||
|
||||
for (org.jf.dexlib2.iface.TryBlock<? extends org.jf.dexlib2.iface.ExceptionHandler> t : o_tbs) {
|
||||
int s = t.getStartCodeAddress();
|
||||
int c = t.getCodeUnitCount();
|
||||
int e = s + c;
|
||||
List<org.jf.dexlib2.immutable.ImmutableExceptionHandler> h = new ArrayList<org.jf.dexlib2.immutable.ImmutableExceptionHandler>();
|
||||
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()));
|
||||
}
|
||||
}
|
||||
@@ -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<String> j_ts) {
|
||||
System.out.println("[INFO] Patching StartupLauncher");
|
||||
List<Method> d_ms = new ArrayList<Method>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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<Instruction> 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<Instruction> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> j_ts) {
|
||||
List<Instruction> ins = new ArrayList<Instruction>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<String, long[]> s_es = new LinkedHashMap<String, long[]>();
|
||||
Map<String, long[]> t_es = new LinkedHashMap<String, long[]>();
|
||||
/**
|
||||
* 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<String, long[]> 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<String, long[]> readDexCrcInfo(File apkFile) throws IOException {
|
||||
Map<String, long[]> dexInfoMap = new LinkedHashMap<String, long[]>();
|
||||
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<String, long[]> sourceDexInfo = readDexCrcInfo(sourceApk);
|
||||
Map<String, long[]> 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<Map.Entry<String, long[]>> it = s_es.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, long[]> 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<String> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.antik;
|
||||
|
||||
public interface logCat {
|
||||
String TAG = "RevDex";
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user