Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SandHook]first add sandhook #142

Merged
merged 1 commit into from
Mar 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Bridge/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ tasks.preBuild.dependsOn fixIml
dependencies {
compileOnly files("libs/framework-stub.jar")
compileOnly project(':dexmaker')
compileOnly project(':sandhook')
}

afterEvaluate {
Expand Down
2 changes: 2 additions & 0 deletions Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
@SuppressLint("DefaultLocale")
public class Main implements KeepAll {

public volatile static boolean useSandHook = true;

public static String appDataDir = "";
public static String appProcessName = "";
private static String forkAndSpecializePramsStr = "";
Expand Down
10 changes: 8 additions & 2 deletions Bridge/src/main/java/de/robv/android/xposed/XposedBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import android.annotation.SuppressLint;
import android.util.Log;

import com.elderdrivers.riru.xposed.Main;
import com.elderdrivers.riru.xposed.core.HookMain;
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
import com.elderdrivers.riru.xposed.dexmaker.MethodInfo;
import com.swift.sandhook.xposedcompat.XposedCompat;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -62,7 +64,7 @@ public final class XposedBridge {
private static final Object[] EMPTY_ARRAY = new Object[0];

// built-in handlers
private static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new HashMap<>();
public static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new HashMap<>();
public static final CopyOnWriteSortedSet<XC_LoadPackage> sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>();
/*package*/ static final CopyOnWriteSortedSet<XC_InitPackageResources> sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>();

Expand Down Expand Up @@ -399,7 +401,11 @@ public static void hookInitPackageResources(XC_InitPackageResources callback) {
*/
private synchronized static void hookMethodNative(final Member method, Class<?> declaringClass,
int slot, final Object additionalInfoObj) {
DynamicBridge.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
if (Main.useSandHook) {
XposedCompat.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
} else {
DynamicBridge.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
}
}

private static Object invokeOriginalMethodNative(Member method, int methodId,
Expand Down
60 changes: 54 additions & 6 deletions dexmaker/src/main/java/external/com/android/dx/DexMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ TypeDeclaration getTypeDeclaration(TypeId<?> type) {
* Modifier#FINAL} and {@link Modifier#ABSTRACT}.
*/
public void declare(TypeId<?> type, String sourceFile, int flags,
TypeId<?> supertype, TypeId<?>... interfaces) {
TypeId<?> supertype, TypeId<?>... interfaces) {
TypeDeclaration declaration = getTypeDeclaration(type);
int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT
| AccessFlags.ACC_SYNTHETIC;
Expand Down Expand Up @@ -471,6 +471,21 @@ private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader
}
}

public ClassLoader loadClassDirect(ClassLoader parent, File dexCache, String dexFileName) {
File result = new File(dexCache, dexFileName);
// Check that the file exists. If it does, return a DexClassLoader and skip all
// the dex bytecode generation.
if (result.exists()) {
return generateClassLoader(result, dexCache, parent);
} else {
return null;
}
}

public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException {
return generateAndLoad(parent, dexCache, generateFileName());
}

/**
* Generates a dex file and loads its types into the current process.
*
Expand All @@ -497,7 +512,7 @@ private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader
* dex files will be written. If null, this class will try to guess the
* application's private data dir.
*/
public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException {
public ClassLoader generateAndLoad(ClassLoader parent, File dexCache, String dexFileName) throws IOException {
if (dexCache == null) {
String property = System.getProperty("dexmaker.dexcache");
if (property != null) {
Expand All @@ -511,11 +526,12 @@ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOE
}
}

File result = new File(dexCache, generateFileName());
// Check that the file exists. If it does, return a DexClassLoader and skip all
// the dex bytecode generation.
File result = new File(dexCache, dexFileName);

if (result.exists()) {
return generateClassLoader(result, dexCache, parent);
try {
deleteOldDex(result);
} catch (Throwable throwable) {}
}

byte[] dex = generate();
Expand All @@ -527,6 +543,12 @@ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOE
*
* TODO: load the dex from memory where supported.
*/

File parentDir = result.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}

result.createNewFile();
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result));
JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME);
Expand All @@ -538,6 +560,32 @@ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOE
return generateClassLoader(result, dexCache, parent);
}

public void deleteOldDex(File dexFile) {
dexFile.delete();
String dexDir = dexFile.getParent();
File oatDir = new File(dexDir, "/oat/");
File oatDirArm = new File(oatDir, "/arm/");
File oatDirArm64 = new File(oatDir, "/arm64/");
if (!oatDir.exists())
return;
String nameStart = dexFile.getName().replaceAll(".jar", "");
doDeleteOatFiles(oatDir, nameStart);
doDeleteOatFiles(oatDirArm, nameStart);
doDeleteOatFiles(oatDirArm64, nameStart);
}

private void doDeleteOatFiles(File dir, String nameStart) {
if (!dir.exists())
return;
File[] oats = dir.listFiles();
if (oats == null)
return;
for (File oatFile:oats) {
if (oatFile.isFile() && oatFile.getName().startsWith(nameStart))
oatFile.delete();
}
}

DexFile getDexFile() {
if (outputDex == null) {
DexOptions options = new DexOptions();
Expand Down
1 change: 1 addition & 0 deletions sandhook/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
34 changes: 34 additions & 0 deletions sandhook/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 28



defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

}

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
implementation 'com.swift.sandhook:hooklib:3.0.1'
compileOnly project(':dexmaker')
compileOnly project(':xposedapi')
}

186 changes: 186 additions & 0 deletions sandhook/genhookstubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/python

import os

STUB_FILE_NAME = "MethodHookerStubs"

TEMP_STUB_CLASS_WRAPPER = """package com.swift.sandhook.xposedcompat.hookstub;

import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.hookBridge;
import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.getMethodId;
import static com.swift.sandhook.xposedcompat.hookstub.HookStubManager.originMethods;
import static com.swift.sandhook.xposedcompat.utils.DexLog.printCallOriginError;

/**
* this file is auto gen by genhookstubs.py
* it is for sandhook internal hooker & backup methods
**/
public class MethodHookerStubs%d {
%s
}
"""

TEMP_STUB_HOOK_METHOD_NAME = """stub_hook_%d"""
TEMP_STUB_HOOK_BACKUP_NAME = """stub_backup_%d"""
TEMP_STUB_CALL_ORIGIN_NAME = """call_origin_%d_%d"""

TEMP_STUB_GET_METHOD_ID_NAME = """getMethodId(%d, %d)"""

JAVA_TYPE_INT = "int"
JAVA_CAST_INT = "(int)"
JAVA_TYPE_LONG = "long"

TEMP_STUB_HOOK_METHOD = """
public static %s %s(%s) throws Throwable {
return %s hookBridge(%s, %s %s);
}
"""

TEMP_STUB_BACKUP_METHOD = """
public static %s %s(%s) throws Throwable {
try {
printCallOriginError(originMethods[%s]);
} catch (Throwable throwable) {}
return 0;
}
"""

TEMP_STUB_CALL_ORIGIN_CLASS = """
static class %s implements CallOriginCallBack {
@Override
public long call(long... args) throws Throwable {
return %s(%s);
}
}
"""

TEMP_STUB_INFO = """
public static boolean hasStubBackup = %s;
public static int[] stubSizes = {%s};
"""


STUB_SIZES = [10,20,30,30,30,30,30,20,10,10,5,5,3]
HAS_BACKUP = False;


def getMethodId(args, index):
return TEMP_STUB_GET_METHOD_ID_NAME % (args, index)

def getMethodHookName(index):
return TEMP_STUB_HOOK_METHOD_NAME % index

def getMethodBackupName(index):
return TEMP_STUB_HOOK_BACKUP_NAME % index

def getCallOriginClassName(args, index):
return TEMP_STUB_CALL_ORIGIN_NAME % (args, index)


def genArgsList(is64Bit, isDefine, length):
args_list = ""
for i in range(length):
if (i != 0):
args_list += ", "
if isDefine:
if (is64Bit):
args_list += (JAVA_TYPE_LONG + " " + "a" + str(i))
else:
args_list += (JAVA_TYPE_INT + " " + "a" + str(i))
else:
args_list += ("a" + str(i))
return args_list


def genArgsListForCallOriginMethod(is64Bit, length):
arg_name = """args[%s]"""
args_list = ""
for i in range(length):
if (i != 0):
args_list += ", "
if (is64Bit):
args_list += arg_name % i
else:
args_list += (JAVA_CAST_INT + arg_name % i)
return args_list


def genHookMethod(is64Bit, args, index):
java_type = JAVA_TYPE_LONG if is64Bit else JAVA_TYPE_INT
cast = "" if is64Bit else JAVA_CAST_INT
args_list_pre = ", " if args > 0 else ""
args_list = genArgsList(is64Bit, False, args)
args_list_def = genArgsList(is64Bit, True, args)
call_origin_obj = ("new " + getCallOriginClassName(args, index) + "()") if HAS_BACKUP else "null"
method = TEMP_STUB_HOOK_METHOD % (java_type, getMethodHookName(index), args_list_def, cast, getMethodId(args, index), call_origin_obj, args_list_pre + args_list)
return method


def genBackupMethod(is64Bit, args, index):
java_type = JAVA_TYPE_LONG if is64Bit else JAVA_TYPE_INT
args_list_def = genArgsList(is64Bit, True, args)
method = TEMP_STUB_BACKUP_METHOD % (java_type, getMethodBackupName(index), args_list_def, getMethodId(args, index))
return method

def genCallOriginClass(is64Bit, args, index):
method = TEMP_STUB_CALL_ORIGIN_CLASS % (getCallOriginClassName(args, index), getMethodBackupName(index), genArgsListForCallOriginMethod(is64Bit, args))
return method

def genStubInfo():
hasStub = "true" if HAS_BACKUP else "false"
stubSizes = ""
for args in range(len(STUB_SIZES)):
if (args != 0):
stubSizes += ", "
stubSizes += str(STUB_SIZES[args])
return TEMP_STUB_INFO % (hasStub, stubSizes)

def gen32Stub(packageDir):
class_content = genStubInfo()
class_name = STUB_FILE_NAME + "32"
for args in range(len(STUB_SIZES)):
for index in range(STUB_SIZES[args]):
class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index)
class_content += genHookMethod(False, args, index)
if HAS_BACKUP:
class_content += "\n"
class_content += genCallOriginClass(False, args, index)
class_content += "\n"
class_content += genBackupMethod(False, args, index)
class_content += "\n"
class_str = TEMP_STUB_CLASS_WRAPPER % (32, class_content)
javaFile = open(os.path.join(packageDir, class_name + ".java"), "w")
javaFile.write(class_str)
javaFile.close()


def gen64Stub(packageDir):
class_content = genStubInfo()
class_name = STUB_FILE_NAME + "64"
for args in range(len(STUB_SIZES)):
for index in range(STUB_SIZES[args]):
class_content += """\n\n\t//stub of arg size %d, index %d""" % (args, index)
class_content += genHookMethod(True, args, index)
if HAS_BACKUP:
class_content += "\n"
class_content += genCallOriginClass(True, args, index)
class_content += "\n"
class_content += genBackupMethod(True, args, index)
class_content += "\n"
class_str = TEMP_STUB_CLASS_WRAPPER % (64, class_content)
javaFile = open(os.path.join(packageDir, class_name + ".java"), "w")
javaFile.write(class_str)
javaFile.close()


def genStub(packageDir):
for fileName in os.listdir(packageDir):
if fileName.startswith(STUB_FILE_NAME):
os.remove(os.path.join(packageDir, fileName))
gen32Stub(packageDir)
gen64Stub(packageDir)


if __name__ == "__main__":
genStub(os.path.join(os.path.dirname(os.path.realpath(__file__)),
"src/main/java/com/swift/sandhook/xposedcompat/hookstub"))
21 changes: 21 additions & 0 deletions sandhook/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Loading