mirror of
https://github.com/ispointer/RePairip.git
synced 2026-09-14 12:03:39 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19a8b07717 | ||
|
|
978a4b12ce | ||
|
|
5f9c833473 | ||
|
|
e80e6193ed | ||
|
|
2983f474ed | ||
|
|
200a33e250 | ||
|
|
2c0edca6be | ||
|
|
75a2f7d0c1 | ||
|
|
920c19047a | ||
|
|
e3eab2393a | ||
|
|
bb4f41abd4 | ||
|
|
bb20f079a0 | ||
|
|
2cc06244a7 | ||
|
|
0de666f273 | ||
|
|
6844956c06 | ||
|
|
c855539ef7 | ||
|
|
3004c4543b | ||
|
|
dc38d1dabf | ||
|
|
433ad5e6fb | ||
|
|
05dc8da620 | ||
|
|
343394df95 | ||
|
|
aa16c29c6f | ||
|
|
0b6ca5b3db | ||
|
|
f28c927a63 | ||
|
|
7c7387d032 | ||
|
|
2856eca66b | ||
|
|
e627596cff | ||
|
|
cd81deb4ae |
+15
-15
@@ -1,15 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
|
||||
Generated
+3
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/antik" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,40 @@
|
||||
<p align="center">
|
||||
<img src="preview/re.png" width="100" height="100" alt="RePairip logo"/>
|
||||
</p>
|
||||
|
||||
<h1 align="center">RePairip</h1>
|
||||
|
||||
<p align="center">
|
||||
RePairip is a Google加固 reverse engineering tool that can dump all VMRunner snapshots and fully repack Android applications, removing protection layers completely
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>Current version:</b> 1.4.14 |
|
||||
<b>Language:</b> Java 17 |
|
||||
<b>Build:</b> Gradle + Shadow Jar
|
||||
</p>
|
||||
|
||||
|
||||
## Tutorial
|
||||
|
||||
[Watch Video on RevDex.Re](https://revdex.re/input.mp4)
|
||||
|
||||
|
||||
[Watch Video on Telegram](https://t.me/RevDex/557)
|
||||
|
||||
| App UI | Dump Style |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is distributed under the Apache License 2.0.
|
||||
|
||||
```text
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
```
|
||||
|
||||
Copyright (C) 2026 HighCapable
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
This is a resource file for the antik project.
|
||||
Project: RePairip
|
||||
Subproject: antik
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: com.antik.Main
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.antik.DexPatcher.Translation.MethodT;
|
||||
|
||||
import com.antik.DexPatcher.Translation.TranslationData;
|
||||
import com.antik.DexPatcher.Translation.antik.PairipClass;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.builder.Label;
|
||||
import org.jf.dexlib2.builder.MethodImplementationBuilder;
|
||||
import org.jf.dexlib2.builder.instruction.*;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod;
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableStringReference;
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableTypeReference;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class MethodMaker {
|
||||
|
||||
public static ImmutableMethod createCleanConstructor(Method method, String superclass) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(method.getParameters().size() + 1);
|
||||
builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_DIRECT, 1, 0, 0, 0, 0, 0, new ImmutableMethodReference(superclass, "<init>", null, "V")));
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
return new ImmutableMethod(method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), method.getHiddenApiRestrictions(), builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createPrivateConstructor(String className, String superclass) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
|
||||
builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_DIRECT, 1, 0, 0, 0, 0, 0, new ImmutableMethodReference(superclass, "<init>", null, "V")));
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
return new ImmutableMethod(className, "<init>", null, "V", AccessFlags.PRIVATE.getValue() | AccessFlags.CONSTRUCTOR.getValue(), null, null, builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createReturnNull(Method method) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(2);
|
||||
builder.addInstruction(new BuilderInstruction11n(Opcode.CONST_4, 0, 0));
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.RETURN_OBJECT, 0));
|
||||
return new ImmutableMethod(method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), method.getHiddenApiRestrictions(), builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createReturnVoid(Method method) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(2);
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
return new ImmutableMethod(method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), method.getHiddenApiRestrictions(), builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createLaunch(boolean hasMethods) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(3);
|
||||
String startupLauncher = PairipClass.STARTUP_LAUNCHER.type;
|
||||
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_CLASS, 0, new ImmutableTypeReference(startupLauncher)));
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MONITOR_ENTER, 0));
|
||||
|
||||
Label tryStart = builder.addLabel("try_start");
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.SGET_BOOLEAN, 1, new ImmutableFieldReference(startupLauncher, "launchCalled", "Z")));
|
||||
Label tryEnd = builder.addLabel("try_end");
|
||||
|
||||
builder.addInstruction(new BuilderInstruction21t(Opcode.IF_EQZ, 1, builder.getLabel("cond_false")));
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MONITOR_EXIT, 0));
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
|
||||
builder.addLabel("cond_false");
|
||||
builder.addInstruction(new BuilderInstruction11n(Opcode.CONST_4, 1, 1));
|
||||
|
||||
Label tryStart2 = builder.addLabel("try_start_2");
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.SPUT_BOOLEAN, 1, new ImmutableFieldReference(startupLauncher, "launchCalled", "Z")));
|
||||
builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_STATIC, 0, 0, 0, 0, 0, 0, new ImmutableMethodReference(startupLauncher, "restoreString", null, "V")));
|
||||
|
||||
if (hasMethods) {
|
||||
builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_STATIC, 0, 0, 0, 0, 0, 0, new ImmutableMethodReference(startupLauncher, "restoreMethod", null, "V")));
|
||||
}
|
||||
Label tryEnd2 = builder.addLabel("try_end_2");
|
||||
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MONITOR_EXIT, 0));
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
|
||||
Label catchAll = builder.addLabel("catchall");
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MOVE_EXCEPTION, 1));
|
||||
Label tryStart3 = builder.addLabel("try_start_3");
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MONITOR_EXIT, 0));
|
||||
Label tryEnd3 = builder.addLabel("try_end_3");
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.THROW, 1));
|
||||
|
||||
builder.addCatch(tryStart, tryEnd, catchAll);
|
||||
builder.addCatch(tryStart2, tryEnd2, catchAll);
|
||||
builder.addCatch(tryStart3, tryEnd3, catchAll);
|
||||
|
||||
return new ImmutableMethod(startupLauncher, "launch", null, "V", AccessFlags.STATIC.getValue() | AccessFlags.PUBLIC.getValue() | AccessFlags.DECLARED_SYNCHRONIZED.getValue(), null, null, builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createRestoreString(TranslationData data) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
|
||||
String startupLauncher = PairipClass.STARTUP_LAUNCHER.type;
|
||||
|
||||
for (Map.Entry<String, TranslationData.Entry> entry : data.entries.entrySet()) {
|
||||
if ("java.lang.String".equals(entry.getValue().type)) {
|
||||
String targetClass = TranslationData.normalizeClassName(entry.getKey());
|
||||
for (Map.Entry<String, String> field : entry.getValue().fields.entrySet()) {
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(field.getValue())));
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.SPUT_OBJECT, 0, new ImmutableFieldReference(targetClass, field.getKey(), "Ljava/lang/String;")));
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
return new ImmutableMethod(startupLauncher, "restoreString", null, "V", AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue(), null, null, builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createRestoreMethod(TranslationData data) {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
|
||||
String startupLauncher = PairipClass.STARTUP_LAUNCHER.type;
|
||||
|
||||
for (Map.Entry<String, TranslationData.Entry> entry : data.entries.entrySet()) {
|
||||
if ("java.lang.reflect.Method".equals(entry.getValue().type)) {
|
||||
String targetClass = TranslationData.normalizeClassName(entry.getKey());
|
||||
for (Map.Entry<String, String> field : entry.getValue().fields.entrySet()) {
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(field.getValue())));
|
||||
builder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_STATIC, 1, 0, 0, 0, 0, 0, new ImmutableMethodReference("Lcom/pairip/RestoreMethod;", "get", Collections.singletonList("Ljava/lang/String;"), "Ljava/lang/reflect/Method;")));
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.MOVE_RESULT_OBJECT, 0));
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.SPUT_OBJECT, 0, new ImmutableFieldReference(targetClass, field.getKey(), "Ljava/lang/reflect/Method;")));
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
|
||||
return new ImmutableMethod(startupLauncher, "restoreMethod", null, "V", AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue(), null, null, builder.getMethodImplementation());
|
||||
}
|
||||
|
||||
public static ImmutableMethod createClinit() {
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
|
||||
builder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference("RePatcher v1.6.30")));
|
||||
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, "<clinit>", null, "V", AccessFlags.STATIC.getValue() | AccessFlags.CONSTRUCTOR.getValue(), null, null, builder.getMethodImplementation());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.antik.DexPatcher.Translation.MethodT;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.builder.MethodImplementationBuilder;
|
||||
import org.jf.dexlib2.builder.instruction.*;
|
||||
import org.jf.dexlib2.immutable.ImmutableClassDef;
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod;
|
||||
import org.jf.dexlib2.immutable.ImmutableMethodParameter;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class RestoreMethodMaker {
|
||||
|
||||
public static ImmutableClassDef createRestoreMethodClass() {
|
||||
String type = "Lcom/pairip/RestoreMethod;";
|
||||
MethodImplementationBuilder builder = new MethodImplementationBuilder(3);
|
||||
builder.addInstruction(new BuilderInstruction11n(Opcode.CONST_4, 0, 0));
|
||||
builder.addInstruction(new BuilderInstruction11x(Opcode.RETURN_OBJECT, 0));
|
||||
|
||||
ImmutableMethod getMethod = new ImmutableMethod(
|
||||
type,
|
||||
"get",
|
||||
Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/String;", null, null)),
|
||||
"Ljava/lang/reflect/Method;",
|
||||
AccessFlags.PUBLIC.getValue() | AccessFlags.STATIC.getValue(),
|
||||
null,
|
||||
null,
|
||||
builder.getMethodImplementation());
|
||||
|
||||
return new ImmutableClassDef(
|
||||
type,
|
||||
AccessFlags.PUBLIC.getValue(),
|
||||
"Ljava/lang/Object;",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Collections.singletonList(getMethod),
|
||||
null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.antik.DexPatcher.Translation;
|
||||
|
||||
import com.reandroid.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TranslationData {
|
||||
public static class Entry {
|
||||
public final String type;
|
||||
public final Map<String, String> fields;
|
||||
|
||||
public Entry(String type, Map<String, String> fields) {
|
||||
this.type = type;
|
||||
this.fields = fields;
|
||||
}
|
||||
}
|
||||
|
||||
public final Map<String, Entry> entries = new HashMap<>();
|
||||
public boolean hasMethods = false;
|
||||
|
||||
public TranslationData(File jsonFile) throws Exception {
|
||||
String content = new String(Files.readAllBytes(jsonFile.toPath()));
|
||||
JSONObject json = new JSONObject(content);
|
||||
for (String className : json.keySet()) {
|
||||
String rawClassName = className.trim();
|
||||
JSONObject classObj = json.getJSONObject(className);
|
||||
String type = normalizeFieldType(classObj.getString("type"));
|
||||
if ("java.lang.reflect.Method".equals(type)) {
|
||||
hasMethods = true;
|
||||
}
|
||||
JSONObject fieldsObj = classObj.getJSONObject("fields");
|
||||
Map<String, String> fields = new HashMap<>();
|
||||
for (String fieldName : fieldsObj.keySet()) {
|
||||
fields.put(fieldName, fieldsObj.getString(fieldName));
|
||||
}
|
||||
entries.put(rawClassName, new Entry(type, fields));
|
||||
}
|
||||
}
|
||||
|
||||
public static String normalizeClassName(String className) {
|
||||
String normalized = className.trim();
|
||||
if (normalized.isEmpty()) {
|
||||
return normalized;
|
||||
}
|
||||
if (normalized.startsWith("L") && normalized.endsWith(";")) {
|
||||
return "L" + normalized.substring(1, normalized.length() - 1).replace('.', '/').replace('\\', '/') + ";";
|
||||
}
|
||||
if (normalized.startsWith("[")) {
|
||||
return normalized.replace('.', '/').replace('\\', '/');
|
||||
}
|
||||
return "L" + normalized.replace('.', '/').replace('\\', '/') + ";";
|
||||
}
|
||||
|
||||
private static String normalizeFieldType(String type) {
|
||||
if ("Ljava/lang/String;".equals(type)) {
|
||||
return "java.lang.String";
|
||||
}
|
||||
if ("Ljava/lang/reflect/Method;".equals(type)) {
|
||||
return "java.lang.reflect.Method";
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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<String> dexNames = new ArrayList<>();
|
||||
for (InputSource s : m.getInputSources()) {
|
||||
if (s.getName().endsWith(".dex")) {
|
||||
dexNames.add(s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> existingDexNames = new HashSet<>(dexNames);
|
||||
Set<String> 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<PendingDex> 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<ClassDef> 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<String> 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<PendingDex> collectEmbeddedAssetDexes(ApkModule module, Set<String> existingClassTypes) throws Exception {
|
||||
List<PendingDex> 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<String> 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<String> 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<String> 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<String> 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<String> classTypes;
|
||||
|
||||
private PendingDex(String sourceName, byte[] dexBytes, Set<String> classTypes) {
|
||||
this.sourceName = sourceName;
|
||||
this.dexBytes = dexBytes;
|
||||
this.classTypes = classTypes;
|
||||
}
|
||||
|
||||
private boolean isFullyPresentIn(Set<String> existingClassTypes) {
|
||||
return existingClassTypes.containsAll(classTypes);
|
||||
}
|
||||
|
||||
private boolean canAddAgainst(Set<String> existingClassTypes) {
|
||||
for (String classType : classTypes) {
|
||||
if (existingClassTypes.contains(classType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.antik.DexPatcher.Translation;
|
||||
|
||||
import com.antik.DexPatcher.Translation.MethodT.MethodMaker;
|
||||
import com.antik.DexPatcher.Translation.antik.PairipClass;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.immutable.ImmutableClassDef;
|
||||
import org.jf.dexlib2.immutable.ImmutableField;
|
||||
import org.jf.dexlib2.immutable.value.ImmutableBooleanEncodedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TranslationRewriter {
|
||||
private final TranslationData data;
|
||||
|
||||
public TranslationRewriter(TranslationData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public ClassDef rewrite(ClassDef cd) {
|
||||
String type = cd.getType();
|
||||
|
||||
if (PairipClass.APPLICATION.type.equals(type)) {
|
||||
return patchApplication(cd);
|
||||
} else if (PairipClass.STARTUP_LAUNCHER.type.equals(type)) {
|
||||
return patchStartupLauncher(cd);
|
||||
} else if (PairipClass.VM_RUNNER.type.equals(type)) {
|
||||
return patchVMRunner(cd);
|
||||
} else if (PairipClass.LICENSE_CLIENT_V3.type.equals(type)) {
|
||||
return patchLicenseClient(cd);
|
||||
}
|
||||
|
||||
return cd;
|
||||
}
|
||||
|
||||
private ClassDef patchApplication(ClassDef cd) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
for (Method m : cd.getMethods()) {
|
||||
if ("<init>".equals(m.getName())) {
|
||||
methods.add(MethodMaker.createCleanConstructor(m, cd.getSuperclass()));
|
||||
}
|
||||
}
|
||||
methods.add(MethodMaker.createClinit());
|
||||
return new ImmutableClassDef(cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), cd.getStaticFields(), cd.getInstanceFields(), methods, null);
|
||||
}
|
||||
|
||||
private ClassDef patchVMRunner(ClassDef cd) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
methods.add(MethodMaker.createPrivateConstructor(cd.getType(), cd.getSuperclass()));
|
||||
|
||||
for (Method m : cd.getMethods()) {
|
||||
if ("invoke".equals(m.getName())) {
|
||||
methods.add(MethodMaker.createReturnNull(m));
|
||||
}
|
||||
}
|
||||
return new ImmutableClassDef(cd.getType(), AccessFlags.PUBLIC.getValue(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), null, null, null, methods, null);
|
||||
}
|
||||
|
||||
private ClassDef patchLicenseClient(ClassDef cd) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
for (Method m : cd.getMethods()) {
|
||||
if ("<init>".equals(m.getName())) {
|
||||
methods.add(MethodMaker.createCleanConstructor(m, cd.getSuperclass()));
|
||||
} else if ("onActivityCreate".equals(m.getName())) {
|
||||
methods.add(MethodMaker.createReturnVoid(m));
|
||||
}
|
||||
}
|
||||
return new ImmutableClassDef(cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), cd.getStaticFields(), cd.getInstanceFields(), methods, null);
|
||||
}
|
||||
|
||||
private ClassDef patchStartupLauncher(ClassDef cd) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
for (Method m : cd.getMethods()) {
|
||||
if ("<init>".equals(m.getName())) {
|
||||
methods.add(m);
|
||||
}
|
||||
}
|
||||
methods.add(MethodMaker.createLaunch(data.hasMethods));
|
||||
methods.add(MethodMaker.createRestoreString(data));
|
||||
if (data.hasMethods) {
|
||||
methods.add(MethodMaker.createRestoreMethod(data));
|
||||
}
|
||||
|
||||
List<ImmutableField> fields = new ArrayList<>();
|
||||
fields.add(new ImmutableField(cd.getType(), "launchCalled", "Z", AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue(), ImmutableBooleanEncodedValue.FALSE_VALUE, null, null));
|
||||
|
||||
return new ImmutableClassDef(cd.getType(), cd.getAccessFlags(), cd.getSuperclass(), cd.getInterfaces(), cd.getSourceFile(), cd.getAnnotations(), fields, cd.getInstanceFields(), methods, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.antik.DexPatcher.Translation.antik;
|
||||
|
||||
public enum PairipClass {
|
||||
VM_RUNNER("Lcom/pairip/VMRunner;"),
|
||||
APPLICATION("Lcom/pairip/application/Application;"),
|
||||
STARTUP_LAUNCHER("Lcom/pairip/StartupLauncher;"),
|
||||
LICENSE_CLIENT_V3("Lcom/pairip/licensecheck3/LicenseClientV3;");
|
||||
|
||||
public final String type;
|
||||
|
||||
PairipClass(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static boolean contains(String type) {
|
||||
for (PairipClass pc : values()) {
|
||||
if (pc.type.equals(type)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +1,185 @@
|
||||
package com.antik;
|
||||
|
||||
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 java.io.*;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/*
|
||||
* Created by aantik
|
||||
* 3/20/2026 6:20 PM
|
||||
*
|
||||
* ⋆ ႔ ႔
|
||||
* ᠸ^ ^ ⸝⸝
|
||||
* |、˜〵
|
||||
* じしˍ,)⁐̤ᐷ
|
||||
*
|
||||
* Fox Mode 🍺
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @life is butterfly flyfly
|
||||
* I am a professional Android Developer
|
||||
*
|
||||
*/
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] a) {
|
||||
public static void main(String[] args) {
|
||||
|
||||
if (a.length < 2 || !"-i".equals(a[0]))
|
||||
{
|
||||
for (String arg : args) {
|
||||
if ("-p".equals(arg) || "--package".equals(arg)) {
|
||||
com.antik.librarySurgery.LibSurgery.main(args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
help.help();
|
||||
return;
|
||||
}
|
||||
|
||||
String P_ta = a[1];
|
||||
File I_pa = new File(P_ta);
|
||||
String inputPath = null;
|
||||
String translatePath = null;
|
||||
boolean R_mod = false;
|
||||
|
||||
if (!I_pa.exists()) {
|
||||
System.err.println("Input file not found : " + P_ta);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputPath == null) {
|
||||
help.help();
|
||||
return;
|
||||
}
|
||||
|
||||
File inputApk = new File(inputPath);
|
||||
|
||||
if (!inputApk.exists()) {
|
||||
System.err.println("Input file not found : " + inputPath);
|
||||
return;
|
||||
}
|
||||
|
||||
banner.banner();
|
||||
|
||||
File T_Dir = null;
|
||||
File T_dIR = null;
|
||||
|
||||
try {
|
||||
ApkModule module;
|
||||
File mergedApkFile;
|
||||
|
||||
T_Dir = Files.createTempDirectory("antik_merge").toFile();
|
||||
|
||||
|
||||
System.out.println("[MERGE] Extracting APKs...");
|
||||
AntikUtils.ex_apks(I_pa, T_Dir);
|
||||
|
||||
System.out.println("[MERGE] Merging APK...");
|
||||
ApkBundle bd = new ApkBundle();
|
||||
bd.loadApkDirectory(T_Dir);
|
||||
|
||||
|
||||
ApkModule Sp_T = bd.mergeModules();
|
||||
|
||||
|
||||
System.out.println("[INFO] Patching AndroidManifest.xml");
|
||||
|
||||
try {
|
||||
manifestP.patch(Sp_T);
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Manifest patching failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
|
||||
String o = I_pa.getName();
|
||||
|
||||
int d = o.lastIndexOf('.');
|
||||
|
||||
if (d > 0) {
|
||||
o = o.substring(0, d) + "_merged.apk";
|
||||
if (inputPath.endsWith(".apk")) {
|
||||
System.out.println("[INFO] Loading APK ");
|
||||
module = ApkModule.loadApkFile(inputApk);
|
||||
mergedApkFile = inputApk;
|
||||
} else {
|
||||
o = o + "_merged.apk";
|
||||
}
|
||||
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 ");
|
||||
ApkBundle bundle = new ApkBundle();
|
||||
bundle.loadApkDirectory(T_dIR);
|
||||
module = bundle.mergeModules();
|
||||
|
||||
File o_f = new File(output.get_out(I_pa, o));
|
||||
|
||||
int T_tl = Sp_T.getZipEntryMap().size();
|
||||
|
||||
if (Sp_T.hasTableBlock()) {
|
||||
T_tl = T_tl - 1;
|
||||
}
|
||||
|
||||
int[] c = {0};
|
||||
final int T_l = T_tl;
|
||||
Sp_T.writeApk(o_f, new com.reandroid.archive.WriteProgress() {
|
||||
@Override
|
||||
public void onCompressFile(String p, int m, long w) {
|
||||
c[0]++;
|
||||
loading.progress(c[0], T_l);
|
||||
System.out.println("[INFO] Patching AndroidManifest.xml");
|
||||
try {
|
||||
manifestP.patch(module);
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Manifest patching failed : " + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
loading.progress(T_l, T_l);
|
||||
String name = inputApk.getName();
|
||||
int dot = name.lastIndexOf('.');
|
||||
name = (dot > 0 ? name.substring(0, dot) : name) + "_merged.apk";
|
||||
mergedApkFile = new File(output.get_out(inputApk, name));
|
||||
|
||||
System.out.println();
|
||||
System.out.println("[MERGE] APK merged successfully..");
|
||||
|
||||
String pkg = "unknown";
|
||||
try {
|
||||
pkg = Sp_T.getPackageName();
|
||||
System.out.println("[INFO] Package Name: " + pkg);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
String p_n = I_pa.getName();
|
||||
int d2 = p_n.lastIndexOf('.');
|
||||
if (d2 > 0) {
|
||||
p_n = p_n.substring(0, d2) + "_pairip.apk";
|
||||
} else {
|
||||
p_n = p_n + "_pairip.apk";
|
||||
}
|
||||
File Pai_Dir = new File(output.get_out(I_pa, p_n));
|
||||
|
||||
System.out.println("[INFO] Patching classes.dex");
|
||||
try {
|
||||
DexPatcher.patch(Sp_T);
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Patching failed: " + e.getMessage());
|
||||
System.out.println("[BUILD] Writing merged APK ");
|
||||
loading.progress(module, mergedApkFile);
|
||||
System.out.println("[MERGE] APK merged successfully: " + mergedApkFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
System.out.println("[BUILD] Building APK...");
|
||||
Sp_T.writeApk(Pai_Dir);
|
||||
crc32.patch(o_f, Pai_Dir);
|
||||
if (translatePath != null) {
|
||||
File jsonFile = new File(translatePath);
|
||||
if (jsonFile.exists()) {
|
||||
System.out.println("[INFO] Starting Translation Patch ");
|
||||
TranslationPatcher.patch(module, jsonFile);
|
||||
|
||||
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 Translated APK ");
|
||||
output.write(module, transFile);
|
||||
System.out.println("[BUILD] Translated APK built at : " + transFile.getAbsolutePath());
|
||||
} else {
|
||||
System.err.println("[ERROR] Translation file not found : " + translatePath);
|
||||
}
|
||||
} else if (!inputPath.endsWith(".apk")) {
|
||||
System.out.println("[INFO] Patching classes.dex for logging ");
|
||||
try {
|
||||
DexPatcher.patch(module);
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Patching failed : " + e.getMessage());
|
||||
}
|
||||
|
||||
String pn = mergedApkFile.getName();
|
||||
int pd = pn.lastIndexOf('.');
|
||||
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 ");
|
||||
output.write(module, paiFile);
|
||||
crc32.patch(mergedApkFile, paiFile);
|
||||
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] APK built successfully at " + Pai_Dir.getAbsolutePath());
|
||||
System.out.println("[BUILD] Process completed");
|
||||
|
||||
System.out.println("--------------------------------------");
|
||||
System.out.println("⚠️ DO NOT use the pairip app (" + Pai_Dir.getAbsolutePath() + ") for translation.");
|
||||
System.out.println("It is incomplete and intended for logging only.");
|
||||
System.out.println("Please use the merged app instead (" + o_f.getAbsolutePath() + ").");
|
||||
System.out.println("--------------------------------------");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Merge failed: " + e.getMessage());
|
||||
System.err.println("[ERROR] Process failed : " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (T_Dir != null) {
|
||||
deleteDir.del_dir(T_Dir);
|
||||
if (T_dIR != null) {
|
||||
deleteDir.del_dir(T_dIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
package com.antik;
|
||||
|
||||
|
||||
import com.google.common.collect.ImmutableRangeMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.RangeMap;
|
||||
|
||||
import org.jf.dexlib2.Format;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public enum Opcode {
|
||||
/* NOP(0x00, "nop", ReferenceType.NONE, Format.Format10x, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
MOVE(0x01, "move", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_FROM16(0x02, "move/from16", ReferenceType.NONE, Format.Format22x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_16(0x03, "move/16", ReferenceType.NONE, Format.Format32x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_WIDE(0x04, "move-wide", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_FROM16(0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_16(0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_OBJECT(0x07, "move-object", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_FROM16(0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_16(0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT(0x0a, "move-result", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT_WIDE(0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_RESULT_OBJECT(0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MOVE_EXCEPTION(0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
RETURN_VOID(0x0e, "return-void", ReferenceType.NONE, Format.Format10x),
|
||||
RETURN(0x0f, "return", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_WIDE(0x10, "return-wide", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_OBJECT(0x11, "return-object", ReferenceType.NONE, Format.Format11x),
|
||||
CONST_4(0x12, "const/4", ReferenceType.NONE, Format.Format11n, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_16(0x13, "const/16", ReferenceType.NONE, Format.Format21s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST(0x14, "const", ReferenceType.NONE, Format.Format31i, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_HIGH16(0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_WIDE_16(0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_32(0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE(0x18, "const-wide", ReferenceType.NONE, Format.Format51l, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_HIGH16(0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_STRING(0x1a, "const-string", ReferenceType.STRING, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_STRING_JUMBO(0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_CLASS(0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MONITOR_ENTER(0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
MONITOR_EXIT(0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
CHECK_CAST(0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
INSTANCE_OF(0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ARRAY_LENGTH(0x21, "array-length", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
NEW_INSTANCE(0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
NEW_ARRAY(0x23, "new-array", ReferenceType.TYPE, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
FILLED_NEW_ARRAY(0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
FILLED_NEW_ARRAY_RANGE(0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
FILL_ARRAY_DATA(0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
THROW(0x27, "throw", ReferenceType.NONE, Format.Format11x, org.jf.dexlib2.Opcode.CAN_THROW),
|
||||
GOTO(0x28, "goto", ReferenceType.NONE, Format.Format10t),
|
||||
GOTO_16(0x29, "goto/16", ReferenceType.NONE, Format.Format20t),
|
||||
GOTO_32(0x2a, "goto/32", ReferenceType.NONE, Format.Format30t),
|
||||
PACKED_SWITCH(0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
SPARSE_SWITCH(0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
CMPL_FLOAT(0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CMPG_FLOAT(0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CMPL_DOUBLE(0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CMPG_DOUBLE(0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CMP_LONG(0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IF_EQ(0x32, "if-eq", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_NE(0x33, "if-ne", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_LT(0x34, "if-lt", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_GE(0x35, "if-ge", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_GT(0x36, "if-gt", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_LE(0x37, "if-le", ReferenceType.NONE, Format.Format22t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_EQZ(0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_NEZ(0x39, "if-nez", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_LTZ(0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_GEZ(0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_GTZ(0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IF_LEZ(0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
AGET(0x44, "aget", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AGET_WIDE(0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
AGET_OBJECT(0x46, "aget-object", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AGET_BOOLEAN(0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AGET_BYTE(0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AGET_CHAR(0x49, "aget-char", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AGET_SHORT(0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
APUT(0x4b, "aput", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_WIDE(0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_OBJECT(0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_BOOLEAN(0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_BYTE(0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_CHAR(0x50, "aput-char", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
APUT_SHORT(0x51, "aput-short", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IGET(0x52, "iget", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_WIDE(0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT(0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_BOOLEAN(0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_BYTE(0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_CHAR(0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_SHORT(0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IPUT(0x59, "iput", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE(0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT(0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_BOOLEAN(0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_BYTE(0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_CHAR(0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_SHORT(0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
SGET(0x60, "sget", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_WIDE(0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_OBJECT(0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_BOOLEAN(0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_BYTE(0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_CHAR(0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SGET_SHORT(0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT(0x67, "sput", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_WIDE(0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_OBJECT(0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_BOOLEAN(0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_BYTE(0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_CHAR(0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_SHORT(0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
INVOKE_VIRTUAL(0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER(0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT(0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT | org.jf.dexlib2.Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC(0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE(0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_RANGE(0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_RANGE(0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_RANGE(0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT | org.jf.dexlib2.Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC_RANGE(0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE_RANGE(0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
NEG_INT(0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
NOT_INT(0x7c, "not-int", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
NEG_LONG(0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
NOT_LONG(0x7e, "not-long", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
NEG_FLOAT(0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
NEG_DOUBLE(0x80, "neg-double", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_LONG(0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_FLOAT(0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
INT_TO_DOUBLE(0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
LONG_TO_INT(0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
LONG_TO_FLOAT(0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
LONG_TO_DOUBLE(0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_INT(0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
FLOAT_TO_LONG(0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_DOUBLE(0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_INT(0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DOUBLE_TO_LONG(0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_FLOAT(0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
INT_TO_BYTE(0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
INT_TO_CHAR(0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
INT_TO_SHORT(0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_INT(0x90, "add-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SUB_INT(0x91, "sub-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_INT(0x92, "mul-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_INT(0x93, "div-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_INT(0x94, "rem-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AND_INT(0x95, "and-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
OR_INT(0x96, "or-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
XOR_INT(0x97, "xor-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHL_INT(0x98, "shl-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHR_INT(0x99, "shr-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
USHR_INT(0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_LONG(0x9b, "add-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG(0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG(0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG(0x9e, "div-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG(0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG(0xa0, "and-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG(0xa1, "or-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG(0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG(0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG(0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG(0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT(0xa6, "add-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT(0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT(0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT(0xa9, "div-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_FLOAT(0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE(0xab, "add-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE(0xac, "sub-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE(0xad, "mul-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE(0xae, "div-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE(0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_2ADDR(0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SUB_INT_2ADDR(0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_INT_2ADDR(0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_INT_2ADDR(0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_INT_2ADDR(0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AND_INT_2ADDR(0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
OR_INT_2ADDR(0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
XOR_INT_2ADDR(0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHL_INT_2ADDR(0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHR_INT_2ADDR(0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
USHR_INT_2ADDR(0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_LONG_2ADDR(0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG_2ADDR(0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG_2ADDR(0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG_2ADDR(0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG_2ADDR(0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG_2ADDR(0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG_2ADDR(0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG_2ADDR(0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG_2ADDR(0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG_2ADDR(0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG_2ADDR(0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT_2ADDR(0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT_2ADDR(0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT_2ADDR(0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT_2ADDR(0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_FLOAT_2ADDR(0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE_2ADDR(0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE_2ADDR(0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE_2ADDR(0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE_2ADDR(0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE_2ADDR(0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_LIT16(0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
RSUB_INT(0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT16(0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT16(0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT16(0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT16(0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT16(0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT16(0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
ADD_INT_LIT8(0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
RSUB_INT_LIT8(0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT8(0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT8(0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT8(0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT8(0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT8(0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT8(0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHL_INT_LIT8(0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
SHR_INT_LIT8(0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
USHR_INT_LIT8(0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
|
||||
IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_WIDE_VOLATILE(firstApi(0xe8, 9), "iget-wide-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
IPUT_WIDE_VOLATILE(firstApi(0xe9, 9), "iput-wide-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_WIDE_VOLATILE(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
|
||||
THROW_VERIFICATION_ERROR(firstApi(0xed, 5), "throw-verification-error", ReferenceType.NONE, Format.Format20bc, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW),
|
||||
EXECUTE_INLINE(allApis(0xee), "execute-inline", ReferenceType.NONE, Format.Format35mi, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
EXECUTE_INLINE_RANGE(firstApi(0xef, 8), "execute-inline/range", ReferenceType.NONE, Format.Format3rmi, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_EMPTY(lastApi(0xf0, 13), "invoke-direct-empty", ReferenceType.METHOD, Format.Format35c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT | org.jf.dexlib2.Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_OBJECT_INIT_RANGE(firstApi(0xf0, 14), "invoke-object-init/range", ReferenceType.METHOD, Format.Format3rc, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT | org.jf.dexlib2.Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
RETURN_VOID_BARRIER(combine(firstApi(0xf1, 11), lastArtVersion(0x73, 59)), "return-void-barrier", ReferenceType.NONE, Format.Format10x, org.jf.dexlib2.Opcode.ODEX_ONLY),
|
||||
RETURN_VOID_NO_BARRIER(firstArtVersion(0x73, 60), "return-void-no-barrier", ReferenceType.NONE, Format.Format10x, org.jf.dexlib2.Opcode.ODEX_ONLY),
|
||||
IGET_QUICK(combine(allApis(0xf2), allArtVersions(0xe3)), "iget-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_WIDE_QUICK(combine(allApis(0xf3), allArtVersions(0xe4)), "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT_QUICK(combine(allApis(0xf4), allArtVersions(0xe5)), "iget-object-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IPUT_QUICK(combine(allApis(0xf5), allArtVersions(0xe6)), "iput-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE_QUICK(combine(allApis(0xf6), allArtVersions(0xe7)), "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT_QUICK(combine(allApis(0xf7), allArtVersions(0xe8)), "iput-object-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
IPUT_BOOLEAN_QUICK(allArtVersions(0xeb), "iput-boolean-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR),
|
||||
IPUT_BYTE_QUICK(allArtVersions(0xec), "iput-byte-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR),
|
||||
IPUT_CHAR_QUICK(allArtVersions(0xed), "iput-char-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR),
|
||||
IPUT_SHORT_QUICK(allArtVersions(0xee), "iput-short-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR),
|
||||
IGET_BOOLEAN_QUICK(allArtVersions(0xef), "iget-boolean-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_BYTE_QUICK(allArtVersions(0xf0), "iget-byte-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_CHAR_QUICK(allArtVersions(0xf1), "iget-char-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
IGET_SHORT_QUICK(allArtVersions(0xf2), "iget-short-quick", ReferenceType.NONE, Format.Format22cs, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.QUICK_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
|
||||
INVOKE_VIRTUAL_QUICK(combine(allApis(0xf8), allArtVersions(0xe9)), "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_QUICK_RANGE(combine(allApis(0xf9), allArtVersions(0xea)), "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK(lastApi(0xfa, 25), "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK_RANGE(lastApi(0xfb, 25), "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
|
||||
IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE),
|
||||
SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
SPUT_OBJECT_VOLATILE(betweenApi(0xfe, 9, 19), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, org.jf.dexlib2.Opcode.ODEX_ONLY | org.jf.dexlib2.Opcode.VOLATILE_FIELD_ACCESSOR | org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.STATIC_FIELD_ACCESSOR),
|
||||
|
||||
PACKED_SWITCH_PAYLOAD(0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
|
||||
SPARSE_SWITCH_PAYLOAD(0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
|
||||
ARRAY_PAYLOAD(0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
|
||||
|
||||
INVOKE_POLYMORPHIC(firstArtVersion(0xfa, 87), "invoke-polymorphic", ReferenceType.METHOD, ReferenceType.METHOD_PROTO, Format.Format45cc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_POLYMORPHIC_RANGE(firstArtVersion(0xfb, 87), "invoke-polymorphic/range", ReferenceType.METHOD, ReferenceType.METHOD_PROTO, Format.Format4rcc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
|
||||
INVOKE_CUSTOM(firstArtVersion(0xfc, 111), "invoke-custom", ReferenceType.CALL_SITE, Format.Format35c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
INVOKE_CUSTOM_RANGE(firstArtVersion(0xfd, 111), "invoke-custom/range", ReferenceType.CALL_SITE, Format.Format3rc, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_RESULT),
|
||||
|
||||
CONST_METHOD_HANDLE(firstArtVersion(0xfe, 134), "const-method-handle", ReferenceType.METHOD_HANDLE, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER),
|
||||
CONST_METHOD_TYPE(firstArtVersion(0xff, 134), "const-method-type", ReferenceType.METHOD_PROTO, Format.Format21c, org.jf.dexlib2.Opcode.CAN_THROW | org.jf.dexlib2.Opcode.CAN_CONTINUE | org.jf.dexlib2.Opcode.SETS_REGISTER);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.antik.librarySurgery;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static void deleteDirectory(File dir) {
|
||||
if (dir == null || !dir.exists()) return;
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
deleteDirectory(f);
|
||||
} else {
|
||||
if (!f.delete()) {
|
||||
f.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean deleted = dir.delete();
|
||||
if (!deleted) {
|
||||
dir.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.antik.librarySurgery;
|
||||
|
||||
import com.antik.librarySurgery.memory.Memory;
|
||||
import com.antik.librarySurgery.memory.MemoryRegion;
|
||||
import com.antik.librarySurgery.process.ProcessFinder;
|
||||
import com.antik.ui.banner;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LibSurgery {
|
||||
|
||||
public static void process(String packageName, String outDirStr) {
|
||||
banner.banner();
|
||||
|
||||
File outDir = new File(outDirStr);
|
||||
if (!outDir.exists()) {
|
||||
boolean created = outDir.mkdirs();
|
||||
if (!created && !outDir.exists()) {
|
||||
System.err.println("[ERROR] Could not create output directory : " + outDirStr);
|
||||
}
|
||||
}
|
||||
|
||||
File tmpDir = null;
|
||||
try {
|
||||
tmpDir = Files.createTempDirectory("pairip_surgery_").toFile();
|
||||
|
||||
List<File> apks = LibraryExtractor.getInstalledApks(packageName);
|
||||
Map<String, File> extractedLibs = LibraryExtractor.extractArm64Libs(apks, tmpDir);
|
||||
|
||||
String pid = ProcessFinder.waitForProcess(packageName);
|
||||
|
||||
MemoryRegion<Long> rxRegion = Memory.findRxRegion(pid);
|
||||
String rxAddr = rxRegion != null ? Long.toHexString(rxRegion.getStartAddress()) : null;
|
||||
String dumpedPath = rxRegion != null ? rxRegion.getPathname() : "";
|
||||
String dumpedLibName = !dumpedPath.isEmpty() ? new File(dumpedPath).getName() : "";
|
||||
|
||||
byte[] memDumpData = Memory.dumpMemory(pid, rxAddr);
|
||||
|
||||
int patchedCount = 0;
|
||||
for (Map.Entry<String, File> entry : extractedLibs.entrySet()) {
|
||||
String libName = entry.getKey();
|
||||
File libPath = entry.getValue();
|
||||
|
||||
if ("libpairipcore.so".equals(libName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] data = Files.readAllBytes(libPath.toPath());
|
||||
byte[] dumpToUse = (dumpedLibName.isEmpty() || libName.equalsIgnoreCase(dumpedLibName) || dumpedLibName.contains(libName))
|
||||
? memDumpData : null;
|
||||
|
||||
PatchResult res = LibraryPatcher.patchSingleLib(data, dumpToUse);
|
||||
if (res.success()) {
|
||||
String outName = libName.replace(".so", "") + "_patched.so";
|
||||
File outPath = new File(outDir, outName);
|
||||
Files.write(outPath.toPath(), data);
|
||||
System.out.println("[BUILD] " + libName + " -> Saved " + outName + " (" + data.length + " bytes)");
|
||||
patchedCount++;
|
||||
} else {
|
||||
System.out.println("[SKIP] " + libName + " : " + res.message());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("[BUILD] Process completed (" + patchedCount + " libraries patched)");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Process failed : " + e.getMessage());
|
||||
} finally {
|
||||
if (tmpDir != null) {
|
||||
FileUtils.deleteDirectory(tmpDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String packageName = null;
|
||||
String outDir = null;
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (("-p".equals(args[i]) || "--package".equals(args[i])) && i + 1 < args.length) {
|
||||
packageName = args[++i];
|
||||
} else if (("-o".equals(args[i]) || "--outdir".equals(args[i])) && i + 1 < args.length) {
|
||||
outDir = args[++i];
|
||||
}
|
||||
}
|
||||
|
||||
if (packageName == null || outDir == null) {
|
||||
System.out.println("Usage: java -jar RePairip.jar -p <package_name> -o <out_dir>");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
process(packageName, outDir);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.antik.librarySurgery;
|
||||
|
||||
import com.antik.librarySurgery.shell.Shell;
|
||||
import com.antik.librarySurgery.shell.ShellResult;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class LibraryExtractor {
|
||||
|
||||
public static List<File> getInstalledApks(String packageName) {
|
||||
ShellResult result = Shell.runRootCmd("pm path " + packageName);
|
||||
List<File> apks = new ArrayList<>();
|
||||
if (result.isSuccess() && !result.getStdout().isEmpty()) {
|
||||
for (String line : result.getStdout().split("\r?\n")) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.startsWith("package:")) {
|
||||
String apkPath = trimmed.replace("package:", "").trim();
|
||||
File f = new File(apkPath);
|
||||
if (f.exists()) {
|
||||
apks.add(f);
|
||||
} else {
|
||||
File tmpCopy = new File("/data/local/tmp/temp_base.apk");
|
||||
Shell.runRootCmd("cp " + apkPath + " " + tmpCopy.getAbsolutePath() + " && chmod 666 " + tmpCopy.getAbsolutePath());
|
||||
if (tmpCopy.exists()) {
|
||||
apks.add(tmpCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (apks.isEmpty()) {
|
||||
System.err.println("[ERROR] Package not installed or APK paths unavailable : " + packageName);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("[INFO] Found " + apks.size() + " APK path(s) for package : " + packageName);
|
||||
return apks;
|
||||
}
|
||||
|
||||
public static Map<String, File> extractArm64Libs(List<File> apks, File tmpDir) {
|
||||
Map<String, File> extractedLibs = new HashMap<>();
|
||||
for (File apkFile : apks) {
|
||||
try (ZipFile zip = new ZipFile(apkFile)) {
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
String fname = entry.getName();
|
||||
if (fname.endsWith(".so") && fname.contains("arm64")) {
|
||||
String libName = new File(fname).getName();
|
||||
if (!extractedLibs.containsKey(libName)) {
|
||||
File extPath = new File(tmpDir, libName);
|
||||
try (InputStream is = zip.getInputStream(entry);
|
||||
OutputStream os = new FileOutputStream(extPath)) {
|
||||
is.transferTo(os);
|
||||
}
|
||||
extractedLibs.put(libName, extPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Extracting native libs failed : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
System.out.println("[INFO] Extracted " + extractedLibs.size() + " ARM64 native library/libraries");
|
||||
return extractedLibs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.antik.librarySurgery;
|
||||
|
||||
import com.antik.librarySurgery.elf.DynamicEntry;
|
||||
import com.antik.librarySurgery.elf.Elf;
|
||||
import com.antik.librarySurgery.elf.ElfConstants;
|
||||
import com.antik.librarySurgery.elf.ProgramHeader;
|
||||
import com.antik.librarySurgery.io.ByteReader;
|
||||
import com.antik.librarySurgery.io.ByteWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class LibraryPatcher {
|
||||
|
||||
public static PatchResult patchSingleLib(byte[] data, byte[] memDump) {
|
||||
Elf elf = new Elf(data);
|
||||
if (!elf.getHeader().isValid()) {
|
||||
return new PatchResult(false, "Invalid ELF header");
|
||||
}
|
||||
|
||||
ProgramHeader dynamicHeader = elf.getDynamicHeader();
|
||||
if (dynamicHeader == null) {
|
||||
return new PatchResult(false, "No PT_DYNAMIC header found");
|
||||
}
|
||||
|
||||
ByteReader reader = elf.getReader();
|
||||
ByteWriter writer = new ByteWriter(data);
|
||||
|
||||
int dynOff = (int) dynamicHeader.getPOffset();
|
||||
int dynSz = (int) dynamicHeader.getPFileSz();
|
||||
|
||||
Long initVa = null;
|
||||
Long initArrayVa = null;
|
||||
Long initArraySz = null;
|
||||
List<DynamicEntry> newEntries = new ArrayList<>();
|
||||
boolean hasPairip = false;
|
||||
|
||||
for (int j = 0; j < dynSz; j += 16) {
|
||||
int entryPos = dynOff + j;
|
||||
if (entryPos + 16 > reader.length()) break;
|
||||
long tag = reader.readUInt64(entryPos);
|
||||
long val = reader.readUInt64(entryPos + 8);
|
||||
|
||||
if (tag == ElfConstants.DT_NULL) {
|
||||
break;
|
||||
}
|
||||
if (tag == ElfConstants.DT_INIT) {
|
||||
initVa = val;
|
||||
continue;
|
||||
}
|
||||
if (tag == ElfConstants.DT_INIT_ARRAY) {
|
||||
initArrayVa = val;
|
||||
continue;
|
||||
}
|
||||
if (tag == ElfConstants.DT_INIT_ARRAYSZ) {
|
||||
initArraySz = val;
|
||||
continue;
|
||||
}
|
||||
if (tag == ElfConstants.DT_NEEDED) {
|
||||
String name = elf.readStringFromStrtab(val);
|
||||
if (name.toLowerCase().contains("pairip")) {
|
||||
hasPairip = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
newEntries.add(new DynamicEntry(tag, val));
|
||||
}
|
||||
|
||||
int posStr = reader.indexOf(ElfConstants.OLD_EXECUTE_PROGRAM);
|
||||
boolean isPairipProtected = hasPairip || posStr != -1;
|
||||
|
||||
if (!isPairipProtected) {
|
||||
return new PatchResult(false, "Not protected by PairIP");
|
||||
}
|
||||
|
||||
newEntries.add(new DynamicEntry(ElfConstants.DT_NULL, 0L));
|
||||
|
||||
for (int k = 0; k < newEntries.size(); k++) {
|
||||
int pos = dynOff + k * 16;
|
||||
if (pos + 16 <= reader.length()) {
|
||||
writer.writeUInt64(pos, newEntries.get(k).getTag());
|
||||
writer.writeUInt64(pos + 8, newEntries.get(k).getVal());
|
||||
}
|
||||
}
|
||||
|
||||
writer.zeroRange(dynOff + newEntries.size() * 16, dynOff + dynSz);
|
||||
|
||||
if (initVa != null) {
|
||||
for (ProgramHeader ph : elf.getProgramHeaders()) {
|
||||
if (ph.getPType() == ElfConstants.PT_LOAD && ph.getPVaddr() <= initVa && initVa < ph.getPVaddr() + ph.getPMemSz()) {
|
||||
writer.writeUInt32(ph.getHeaderOffset(), ElfConstants.PT_NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initArrayVa != null && initArraySz != null) {
|
||||
Long initArrayOff = elf.vaddrToOffset(initArrayVa);
|
||||
if (initArrayOff != null) {
|
||||
int arrayPos = initArrayOff.intValue();
|
||||
int arraySize = initArraySz.intValue();
|
||||
if (arrayPos >= 0 && arrayPos + arraySize <= reader.length()) {
|
||||
writer.zeroRange(arrayPos, arrayPos + arraySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (posStr != -1) {
|
||||
writer.writeBytes(posStr, ElfConstants.NEW_MEMSET);
|
||||
}
|
||||
|
||||
Long jniOnLoadOffset = elf.findSymbolAddress("JNI_OnLoad");
|
||||
if (jniOnLoadOffset != null) {
|
||||
int stubPos = jniOnLoadOffset.intValue();
|
||||
if (stubPos >= 0 && stubPos + ElfConstants.ARM64_STUB_BYTES.length <= reader.length()) {
|
||||
writer.writeBytes(stubPos, ElfConstants.ARM64_STUB_BYTES);
|
||||
}
|
||||
} else {
|
||||
System.out.println("[INFO] JNI_OnLoad symbol not found in ELF, skipping stub patch safely.");
|
||||
}
|
||||
|
||||
if (memDump != null && memDump.length > 0) {
|
||||
int copyLen = Math.min(reader.length(), memDump.length);
|
||||
writer.writeBytes(0, Arrays.copyOf(memDump, copyLen));
|
||||
}
|
||||
|
||||
return new PatchResult(true, "Successfully patched");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.antik.librarySurgery;
|
||||
|
||||
public record PatchResult(boolean success, String message) {}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.antik.librarySurgery.elf;
|
||||
|
||||
public class DynamicEntry {
|
||||
|
||||
private final long tag;
|
||||
private final long val;
|
||||
|
||||
public DynamicEntry(long tag, long val) {
|
||||
this.tag = tag;
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
public long getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public long getVal() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.antik.librarySurgery.elf;
|
||||
|
||||
import com.antik.librarySurgery.io.ByteReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Elf {
|
||||
|
||||
private final ByteReader reader;
|
||||
private final ElfHeader header;
|
||||
private final List<ProgramHeader> programHeaders = new ArrayList<>();
|
||||
private ProgramHeader dynamicHeader = null;
|
||||
private Long strtabVa = null;
|
||||
private Long strtabOff = null;
|
||||
private Long symtabVa = null;
|
||||
private Long symtabOff = null;
|
||||
|
||||
public Elf(byte[] data) {
|
||||
this.reader = new ByteReader(data);
|
||||
this.header = new ElfHeader(reader);
|
||||
|
||||
if (header.isValid()) {
|
||||
parseProgramHeaders();
|
||||
parseDynamicSection();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseProgramHeaders() {
|
||||
long phOff = header.getPhOff();
|
||||
int phNum = header.getPhNum();
|
||||
for (int i = 0; i < phNum; i++) {
|
||||
int offset = (int) (phOff + i * 56L);
|
||||
if (offset + 56 > reader.length()) break;
|
||||
ProgramHeader ph = new ProgramHeader(reader, offset);
|
||||
programHeaders.add(ph);
|
||||
if (ph.getPType() == ElfConstants.PT_DYNAMIC) {
|
||||
this.dynamicHeader = ph;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseDynamicSection() {
|
||||
if (dynamicHeader == null) return;
|
||||
int dynOff = (int) dynamicHeader.getPOffset();
|
||||
int dynSz = (int) dynamicHeader.getPFileSz();
|
||||
|
||||
for (int j = 0; j < dynSz; j += 16) {
|
||||
int pos = dynOff + j;
|
||||
if (pos + 16 > reader.length()) break;
|
||||
long tag = reader.readUInt64(pos);
|
||||
long val = reader.readUInt64(pos + 8);
|
||||
if (tag == ElfConstants.DT_STRTAB) {
|
||||
this.strtabVa = val;
|
||||
} else if (tag == ElfConstants.DT_SYMTAB) {
|
||||
this.symtabVa = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (strtabVa != null) {
|
||||
this.strtabOff = vaddrToOffset(strtabVa);
|
||||
}
|
||||
if (symtabVa != null) {
|
||||
this.symtabOff = vaddrToOffset(symtabVa);
|
||||
}
|
||||
}
|
||||
|
||||
public Long vaddrToOffset(long vaddr) {
|
||||
for (ProgramHeader ph : programHeaders) {
|
||||
if (ph.getPType() == ElfConstants.PT_LOAD && ph.getPVaddr() <= vaddr && vaddr < ph.getPVaddr() + ph.getPMemSz()) {
|
||||
return ph.getPOffset() + (vaddr - ph.getPVaddr());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long findSymbolAddress(String symbolName) {
|
||||
if (symtabOff == null || strtabOff == null) return null;
|
||||
int symOff = symtabOff.intValue();
|
||||
int maxSyms = 3000;
|
||||
for (int i = 0; i < maxSyms; i++) {
|
||||
int entryPos = symOff + i * 24;
|
||||
if (entryPos + 24 > reader.length()) break;
|
||||
long nameIdx = reader.readUInt32(entryPos);
|
||||
long value = reader.readUInt64(entryPos + 8);
|
||||
if (nameIdx > 0) {
|
||||
String name = reader.readNullTerminatedString((int) (strtabOff + nameIdx));
|
||||
if (symbolName.equals(name)) {
|
||||
return vaddrToOffset(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ByteReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public ElfHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public List<ProgramHeader> getProgramHeaders() {
|
||||
return programHeaders;
|
||||
}
|
||||
|
||||
public ProgramHeader getDynamicHeader() {
|
||||
return dynamicHeader;
|
||||
}
|
||||
|
||||
public Long getStrtabOff() {
|
||||
return strtabOff;
|
||||
}
|
||||
|
||||
public String readStringFromStrtab(long offsetVal) {
|
||||
if (strtabOff == null) return "";
|
||||
return reader.readNullTerminatedString((int) (strtabOff + offsetVal));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.antik.librarySurgery.elf;
|
||||
|
||||
public class ElfConstants {
|
||||
|
||||
public static final byte[] ELF_MAGIC = new byte[]{(byte) 0x7f, 'E', 'L', 'F'};
|
||||
|
||||
public static final int PT_NULL = 0;
|
||||
public static final int PT_LOAD = 1;
|
||||
public static final int PT_DYNAMIC = 2;
|
||||
|
||||
public static final long DT_NULL = 0L;
|
||||
public static final long DT_NEEDED = 1L;
|
||||
public static final long DT_STRTAB = 5L;
|
||||
public static final long DT_SYMTAB = 6L;
|
||||
public static final long DT_INIT = 12L;
|
||||
public static final long DT_INIT_ARRAY = 25L;
|
||||
public static final long DT_INIT_ARRAYSZ = 27L;
|
||||
|
||||
public static final byte[] OLD_EXECUTE_PROGRAM = "ExecuteProgram\0".getBytes(java.nio.charset.StandardCharsets.US_ASCII);
|
||||
public static final byte[] NEW_MEMSET = "memset\0\0\0\0\0\0\0\0\0".getBytes(java.nio.charset.StandardCharsets.US_ASCII);
|
||||
|
||||
public static final byte[] ARM64_STUB_BYTES = new byte[]{
|
||||
(byte) 0xc0, 0x00, (byte) 0x80, 0x52, // mov w0, #0
|
||||
0x20, 0x00, (byte) 0xa0, 0x72, // movk w0, #0x10, lsl #16 (JNI_VERSION_1_6 = 0x00010000)
|
||||
(byte) 0xc0, 0x03, 0x5f, (byte) 0xd6 // ret
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.antik.librarySurgery.elf;
|
||||
|
||||
import com.antik.librarySurgery.io.ByteReader;
|
||||
|
||||
public class ElfHeader {
|
||||
|
||||
private final long phOff;
|
||||
private final int phNum;
|
||||
private final boolean valid;
|
||||
|
||||
public ElfHeader(ByteReader reader) {
|
||||
if (!reader.checkMagic(ElfConstants.ELF_MAGIC)) {
|
||||
this.valid = false;
|
||||
this.phOff = 0L;
|
||||
this.phNum = 0;
|
||||
return;
|
||||
}
|
||||
this.phOff = reader.readUInt64(0x20);
|
||||
this.phNum = reader.readUInt16(0x38);
|
||||
this.valid = true;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public long getPhOff() {
|
||||
return phOff;
|
||||
}
|
||||
|
||||
public int getPhNum() {
|
||||
return phNum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.antik.librarySurgery.elf;
|
||||
|
||||
import com.antik.librarySurgery.io.ByteReader;
|
||||
|
||||
public class ProgramHeader {
|
||||
|
||||
private final int headerOffset;
|
||||
private final int pType;
|
||||
private final long pOffset;
|
||||
private final long pVaddr;
|
||||
private final long pFileSz;
|
||||
private final long pMemSz;
|
||||
|
||||
public ProgramHeader(ByteReader reader, int headerOffset) {
|
||||
this.headerOffset = headerOffset;
|
||||
this.pType = (int) reader.readUInt32(headerOffset);
|
||||
this.pOffset = reader.readUInt64(headerOffset + 8);
|
||||
this.pVaddr = reader.readUInt64(headerOffset + 16);
|
||||
this.pFileSz = reader.readUInt64(headerOffset + 32);
|
||||
this.pMemSz = reader.readUInt64(headerOffset + 40);
|
||||
}
|
||||
|
||||
public int getHeaderOffset() {
|
||||
return headerOffset;
|
||||
}
|
||||
|
||||
public int getPType() {
|
||||
return pType;
|
||||
}
|
||||
|
||||
public long getPOffset() {
|
||||
return pOffset;
|
||||
}
|
||||
|
||||
public long getPVaddr() {
|
||||
return pVaddr;
|
||||
}
|
||||
|
||||
public long getPFileSz() {
|
||||
return pFileSz;
|
||||
}
|
||||
|
||||
public long getPMemSz() {
|
||||
return pMemSz;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.antik.librarySurgery.io;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ByteReader {
|
||||
|
||||
private final byte[] buffer;
|
||||
private final ByteBuffer byteBuffer;
|
||||
|
||||
public ByteReader(byte[] buffer) {
|
||||
this.buffer = buffer;
|
||||
this.byteBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
public int readUInt16(int offset) {
|
||||
if (offset < 0 || offset + 2 > buffer.length) return 0;
|
||||
return byteBuffer.getShort(offset) & 0xFFFF;
|
||||
}
|
||||
|
||||
public long readUInt32(int offset) {
|
||||
if (offset < 0 || offset + 4 > buffer.length) return 0L;
|
||||
return byteBuffer.getInt(offset) & 0xFFFFFFFFL;
|
||||
}
|
||||
|
||||
public long readUInt64(int offset) {
|
||||
if (offset < 0 || offset + 8 > buffer.length) return 0L;
|
||||
return byteBuffer.getLong(offset);
|
||||
}
|
||||
|
||||
public String readNullTerminatedString(int offset) {
|
||||
if (offset < 0 || offset >= buffer.length) return "";
|
||||
int end = offset;
|
||||
while (end < buffer.length && buffer[end] != 0) {
|
||||
end++;
|
||||
}
|
||||
return new String(buffer, offset, end - offset, StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
public int indexOf(byte[] target) {
|
||||
if (target == null || target.length == 0) return -1;
|
||||
outer:
|
||||
for (int i = 0; i <= buffer.length - target.length; i++) {
|
||||
for (int j = 0; j < target.length; j++) {
|
||||
if (buffer[i + j] != target[j]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean checkMagic(byte[] magic) {
|
||||
if (magic == null || buffer.length < magic.length) return false;
|
||||
for (int i = 0; i < magic.length; i++) {
|
||||
if (buffer[i] != magic[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.antik.librarySurgery.io;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class ByteWriter {
|
||||
|
||||
private final byte[] buffer;
|
||||
private final ByteBuffer byteBuffer;
|
||||
|
||||
public ByteWriter(byte[] buffer) {
|
||||
this.buffer = buffer;
|
||||
this.byteBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public void writeUInt16(int offset, int value) {
|
||||
if (offset >= 0 && offset + 2 <= buffer.length) {
|
||||
byteBuffer.putShort(offset, (short) value);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeUInt32(int offset, long value) {
|
||||
if (offset >= 0 && offset + 4 <= buffer.length) {
|
||||
byteBuffer.putInt(offset, (int) value);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeUInt64(int offset, long value) {
|
||||
if (offset >= 0 && offset + 8 <= buffer.length) {
|
||||
byteBuffer.putLong(offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBytes(int offset, byte[] data) {
|
||||
if (data != null && offset >= 0 && offset + data.length <= buffer.length) {
|
||||
System.arraycopy(data, 0, buffer, offset, data.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void zeroRange(int startOffset, int endOffset) {
|
||||
if (startOffset >= 0 && endOffset <= buffer.length && startOffset < endOffset) {
|
||||
for (int i = startOffset; i < endOffset; i++) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.antik.librarySurgery.memory;
|
||||
|
||||
import com.antik.librarySurgery.shell.Shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class Memory {
|
||||
|
||||
public static MemoryRegion<Long> findRxRegion(String pid) {
|
||||
MemoryMap map = MemoryMap.parseFromProcess(pid);
|
||||
return map.findRxRegion();
|
||||
}
|
||||
|
||||
public static byte[] dumpMemory(String pid, String rxAddr) {
|
||||
if (rxAddr == null || rxAddr.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String dumpPath = "/data/local/tmp/pairip_dump.bin";
|
||||
System.out.println("[ROOT] Dumping decrypted .text memory from RAM (Address 0x" + rxAddr + ")");
|
||||
Shell.runRootCmd("dd if=/proc/" + pid + "/mem of=" + dumpPath + " bs=1024 count=576 skip=$((0x" + rxAddr + "/1024))");
|
||||
|
||||
File dumpFile = new File(dumpPath);
|
||||
byte[] data = null;
|
||||
if (dumpFile.exists() && dumpFile.length() > 0) {
|
||||
try {
|
||||
data = Files.readAllBytes(dumpFile.toPath());
|
||||
System.out.println("[ROOT] Captured " + data.length + " bytes decrypted memory dump");
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Reading memory dump failed : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
Shell.runRootCmd("rm -f " + dumpPath);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.antik.librarySurgery.memory;
|
||||
|
||||
import com.antik.librarySurgery.shell.Shell;
|
||||
import com.antik.librarySurgery.shell.ShellResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MemoryMap {
|
||||
|
||||
private final List<MemoryRegion<Long>> regions = new ArrayList<>();
|
||||
|
||||
public static MemoryMap parseFromProcess(String pid) {
|
||||
MemoryMap map = new MemoryMap();
|
||||
ShellResult result = Shell.runRootCmd("grep 'r-xp' /proc/" + pid + "/maps");
|
||||
if (!result.getStdout().isEmpty()) {
|
||||
for (String line : result.getStdout().split("\r?\n")) {
|
||||
String trimmed = line.trim();
|
||||
String[] parts = trimmed.split("\\s+");
|
||||
if (parts.length >= 1) {
|
||||
String[] addrs = parts[0].split("-");
|
||||
if (addrs.length == 2) {
|
||||
try {
|
||||
long start = Long.parseUnsignedLong(addrs[0], 16);
|
||||
long end = Long.parseUnsignedLong(addrs[1], 16);
|
||||
String perms = parts.length > 1 ? parts[1] : "";
|
||||
String path = parts.length > 5 ? parts[5] : "";
|
||||
map.regions.add(new MemoryRegion<>(start, end, perms, path));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public List<MemoryRegion<Long>> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
public MemoryRegion<Long> findRxRegion() {
|
||||
for (MemoryRegion<Long> region : regions) {
|
||||
String path = region.getPathname();
|
||||
if (region.isExecutable() && (path.contains("apk") || path.contains("lib"))) {
|
||||
return region;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String findRxAddressString() {
|
||||
MemoryRegion<Long> region = findRxRegion();
|
||||
if (region != null) {
|
||||
return Long.toHexString(region.getStartAddress());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.antik.librarySurgery.memory;
|
||||
|
||||
public class MemoryRegion<T extends Number> {
|
||||
|
||||
private final T startAddress;
|
||||
private final T endAddress;
|
||||
private final String permissions;
|
||||
private final String pathname;
|
||||
|
||||
public MemoryRegion(T startAddress, T endAddress, String permissions, String pathname) {
|
||||
this.startAddress = startAddress;
|
||||
this.endAddress = endAddress;
|
||||
this.permissions = permissions;
|
||||
this.pathname = pathname;
|
||||
}
|
||||
|
||||
public T getStartAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public T getEndAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public String getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public String getPathname() {
|
||||
return pathname;
|
||||
}
|
||||
|
||||
public boolean isExecutable() {
|
||||
return permissions != null && permissions.contains("x");
|
||||
}
|
||||
|
||||
public boolean isReadable() {
|
||||
return permissions != null && permissions.contains("r");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.antik.librarySurgery.process;
|
||||
|
||||
import com.antik.librarySurgery.shell.Shell;
|
||||
import com.antik.librarySurgery.shell.ShellResult;
|
||||
|
||||
public class ProcessFinder {
|
||||
|
||||
public static String getPidForPackage(String packageName) {
|
||||
ShellResult result = Shell.runRootCmd("ps -A | grep " + packageName);
|
||||
if (!result.isSuccess() || result.getStdout().isEmpty()) {
|
||||
result = Shell.runRootCmd("pidof " + packageName);
|
||||
if (result.isSuccess() && !result.getStdout().isEmpty()) {
|
||||
String[] pids = result.getStdout().trim().split("\\s+");
|
||||
return pids[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String line : result.getStdout().split("\r?\n")) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.contains(packageName)) {
|
||||
String[] parts = trimmed.split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String waitForProcess(String packageName) {
|
||||
System.out.println("[INFO] Waiting for package : " + packageName);
|
||||
System.out.println("[INFO] Please launch the app on your device ");
|
||||
|
||||
while (true) {
|
||||
String pid = getPidForPackage(packageName);
|
||||
if (pid != null && !pid.isEmpty()) {
|
||||
System.out.println("[ROOT] App process detected with PID : " + pid);
|
||||
return pid;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.antik.librarySurgery.shell;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class Shell {
|
||||
|
||||
public static ShellResult runRootCmd(String command) {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", command});
|
||||
BufferedReader outReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
|
||||
StringBuilder outSb = new StringBuilder();
|
||||
for (String line = outReader.readLine(); line != null; line = outReader.readLine()) {
|
||||
outSb.append(line).append("\n");
|
||||
}
|
||||
|
||||
StringBuilder errSb = new StringBuilder();
|
||||
for (String line = errReader.readLine(); line != null; line = errReader.readLine()) {
|
||||
errSb.append(line).append("\n");
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
return new ShellResult(exitCode, outSb.toString(), errSb.toString());
|
||||
} catch (Exception e) {
|
||||
return new ShellResult(-1, "", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static ShellResult runCmd(String command) {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
|
||||
BufferedReader outReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
StringBuilder outSb = new StringBuilder();
|
||||
for (String line = outReader.readLine(); line != null; line = outReader.readLine()) {
|
||||
outSb.append(line).append("\n");
|
||||
}
|
||||
int exitCode = process.waitFor();
|
||||
return new ShellResult(exitCode, outSb.toString(), "");
|
||||
} catch (Exception e) {
|
||||
return runRootCmd(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.antik.librarySurgery.shell;
|
||||
|
||||
public class ShellResult {
|
||||
|
||||
private final int exitCode;
|
||||
private final String stdout;
|
||||
private final String stderr;
|
||||
|
||||
public ShellResult(int exitCode, String stdout, String stderr) {
|
||||
this.exitCode = exitCode;
|
||||
this.stdout = stdout != null ? stdout.trim() : "";
|
||||
this.stderr = stderr != null ? stderr.trim() : "";
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public String getStdout() {
|
||||
return stdout;
|
||||
}
|
||||
|
||||
public String getStderr() {
|
||||
return stderr;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return exitCode == 0;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user