Commit 3be93512 authored by swift_gan's avatar swift_gan

add hook wrapper

parent 13f17d10
...@@ -20,7 +20,7 @@ namespace SandHook { ...@@ -20,7 +20,7 @@ namespace SandHook {
} }
} }
switch (SDK_INT) { switch (SDK_INT) {
} }
return getParentSize() + 1; return getParentSize() + 1;
} }
......
...@@ -128,7 +128,7 @@ Size getAddressFromJava(JNIEnv* env, char* className, char* fieldName) { ...@@ -128,7 +128,7 @@ Size getAddressFromJava(JNIEnv* env, char* className, char* fieldName) {
printf("find class error !"); printf("find class error !");
return 0; return 0;
} }
jfieldID id = env -> GetStaticFieldID(clazz, fieldName, "Ljava/lang/Long;"); jfieldID id = env -> GetStaticFieldID(clazz, fieldName, "J");
if (id == NULL){ if (id == NULL){
printf("find field error !"); printf("find field error !");
return 0; return 0;
......
package com.swift.sandhook;
import android.app.Activity;
import android.os.Bundle;
import com.swift.sandhook.wrapper.HookClass;
import com.swift.sandhook.wrapper.HookMethod;
import com.swift.sandhook.wrapper.HookMethodBackup;
import com.swift.sandhook.wrapper.MethodParams;
@HookClass(Activity.class)
public class ActivityHooker {
@HookMethod("onCreate")
@MethodParams(Bundle.class)
public static void onCreate(Activity thiz, Bundle bundle) {
onCreateBackup(thiz, bundle);
}
@HookMethodBackup("onCreate")
@MethodParams(Bundle.class)
public static void onCreateBackup(Activity thiz, Bundle bundle) {
}
}
...@@ -10,6 +10,9 @@ import android.view.View; ...@@ -10,6 +10,9 @@ import android.view.View;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import com.swift.sandhook.wrapper.HookErrorException;
import com.swift.sandhook.wrapper.HookWrapper;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -36,6 +39,12 @@ public class MainActivity extends AppCompatActivity { ...@@ -36,6 +39,12 @@ public class MainActivity extends AppCompatActivity {
} }
}); });
try {
HookWrapper.addHookClass(ActivityHooker.class);
} catch (HookErrorException e) {
e.printStackTrace();
}
// Example of a call to a native method // Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text); TextView tv = (TextView) findViewById(R.id.sample_text);
try { try {
...@@ -46,18 +55,18 @@ public class MainActivity extends AppCompatActivity { ...@@ -46,18 +55,18 @@ public class MainActivity extends AppCompatActivity {
Object dexCache = field.get(ArtMethodSizeTest.class); Object dexCache = field.get(ArtMethodSizeTest.class);
Field resolvedMethodsField = dexCache.getClass().getDeclaredField("resolvedMethods"); Field resolvedMethodsField = dexCache.getClass().getDeclaredField("resolvedMethods");
resolvedMethodsField.setAccessible(true); resolvedMethodsField.setAccessible(true);
long[] methods = (long[]) resolvedMethodsField.get(dexCache); // long[] methods = (long[]) resolvedMethodsField.get(dexCache);
//
//
Field dexMethodIndexField = getField(Method.class, "dexMethodIndex"); // Field dexMethodIndexField = getField(Method.class, "dexMethodIndex");
dexMethodIndexField.setAccessible(true); // dexMethodIndexField.setAccessible(true);
int dexMethodIndex = (int) dexMethodIndexField.get(method1); // int dexMethodIndex = (int) dexMethodIndexField.get(method1);
//
Field artMethodField = getField(Method.class, "artMethod"); // Field artMethodField = getField(Method.class, "artMethod");
artMethodField.setAccessible(true); // artMethodField.setAccessible(true);
long artMethod = (long) artMethodField.get(method1); // long artMethod = (long) artMethodField.get(method1);
//
methods[dexMethodIndex] = artMethod; // methods[dexMethodIndex] = artMethod;
initHook(); initHook();
// SandHook.init(); // SandHook.init();
......
package com.swift.sandhook; package com.swift.sandhook;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -21,6 +24,10 @@ public class SandHook { ...@@ -21,6 +24,10 @@ public class SandHook {
return true; return true;
} }
public static void hook(@NonNull Method target, @NonNull Method hook, @Nullable Method backup) {
}
private static void initTestOffset() { private static void initTestOffset() {
// make test methods sure resolved! // make test methods sure resolved!
......
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HookClass {
Class<?> value();
}
package com.swift.sandhook.wrapper;
public class HookErrorException extends Exception {
public HookErrorException(String s) {
super(s);
}
public HookErrorException(String message, Throwable cause) {
super(message, cause);
}
}
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HookMethod {
String value() default "<init>";
}
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HookMethodBackup {
String value() default "<init>";
}
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HookReflectClass {
String value();
}
package com.swift.sandhook.wrapper;
import com.swift.sandhook.SandHook;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class HookWrapper {
public static void addHookClass(Class<?>... classes) throws HookErrorException {
for (Class clazz:classes) {
addHookClass(clazz);
}
}
public static void addHookClass(Class<?> clazz) throws HookErrorException {
Class targetHookClass = getTargetHookClass(clazz);
if (targetHookClass == null)
throw new HookErrorException("error hook wrapper class :" + clazz.getName());
Map<Method,HookEntity> hookEntityMap = getHookMethods(targetHookClass, clazz);
for (HookEntity entity:hookEntityMap.values()) {
if (entity.target != null && entity.hook != null) {
SandHook.hook(entity.target, entity.hook, entity.backup);
}
}
}
private static Map<Method, HookEntity> getHookMethods(Class targetHookClass, Class<?> hookWrapperClass) throws HookErrorException {
Map<Method,HookEntity> hookEntityMap = new HashMap<>();
Method[] methods = hookWrapperClass.getDeclaredMethods();
if (methods == null && methods.length == 0)
throw new HookErrorException("error hook wrapper class :" + targetHookClass.getName());
for (Method method:methods) {
HookMethod hookMethodAnno = method.getAnnotation(HookMethod.class);
HookMethodBackup hookMethodBackupAnno = method.getAnnotation(HookMethodBackup.class);
String methodName;
Method foundMethod;
Class[] pars;
if (hookMethodAnno != null) {
methodName = hookMethodAnno.value();
pars = parseMethodPars(method);
try {
foundMethod = targetHookClass.getDeclaredMethod(methodName, pars);
} catch (NoSuchMethodException e) {
throw new HookErrorException("can not find target method: " + methodName, e);
}
checkSignature(foundMethod, method, pars);
HookEntity entity = hookEntityMap.get(foundMethod);
if (entity == null) {
entity = new HookEntity(foundMethod);
hookEntityMap.put(foundMethod, entity);
}
entity.hook = method;
} else if (hookMethodBackupAnno != null) {
methodName = hookMethodBackupAnno.value();
pars = parseMethodPars(method);
try {
foundMethod = targetHookClass.getDeclaredMethod(methodName, pars);
} catch (NoSuchMethodException e) {
throw new HookErrorException("can not find target method: " + methodName, e);
}
checkSignature(foundMethod, method, pars);
HookEntity entity = hookEntityMap.get(foundMethod);
if (entity == null) {
entity = new HookEntity(foundMethod);
hookEntityMap.put(foundMethod, entity);
}
entity.backup = method;
} else {
continue;
}
}
return hookEntityMap;
}
private static Class[] parseMethodPars(Method method) throws HookErrorException {
MethodParams methodParams = method.getAnnotation(MethodParams.class);
MethodReflectParams methodReflectParams = method.getAnnotation(MethodReflectParams.class);
if (methodParams != null) {
return methodParams.value();
} else if (methodReflectParams != null) {
if (methodReflectParams.value().length == 0)
return null;
Class[] pars = new Class[methodReflectParams.value().length];
for (int i = 0;i < methodReflectParams.value().length; i++) {
try {
pars[i] = Class.forName(methodReflectParams.value()[i]);
} catch (ClassNotFoundException e) {
throw new HookErrorException("hook method pars error: " + method.getName(), e);
}
}
return pars;
} else {
return null;
}
}
private static Class getTargetHookClass(Class<?> hookWrapperClass) {
HookClass hookClass = hookWrapperClass.getAnnotation(HookClass.class);
HookReflectClass hookReflectClass = hookWrapperClass.getAnnotation(HookReflectClass.class);
if (hookClass != null) {
return hookClass.value();
} else if (hookReflectClass != null) {
try {
return Class.forName(hookReflectClass.value());
} catch (ClassNotFoundException e) {
return null;
}
} else {
return null;
}
}
public static void checkSignature(Method origin, Method fake, Class[] originPars) throws HookErrorException {
if (!Modifier.isStatic(fake.getModifiers()))
throw new HookErrorException("hook method must static! - " + fake.getName());
if (origin.getReturnType() != fake.getReturnType() && !origin.getReturnType().isAssignableFrom(origin.getReturnType()))
throw new HookErrorException("error return type! - " + fake.getName());
Class[] fakePars = fake.getParameterTypes();
if (fakePars == null)
fakePars = new Class[0];
if (originPars == null)
originPars = new Class[0];
if (originPars.length == 0 && fakePars.length == 0)
return;
int parOffset = 0;
if (!Modifier.isStatic(origin.getModifiers())) {
parOffset = 1;
if (fakePars.length == 0)
throw new HookErrorException("first par must be this! " + fake.getName());
if (fakePars[0] != origin.getDeclaringClass() && !fakePars[0].isAssignableFrom(origin.getDeclaringClass()))
throw new HookErrorException("first par must be this! " + fake.getName());
if (fakePars.length != originPars.length + 1)
throw new HookErrorException("hook method pars must match the origin method! " + fake.getName());
} else {
if (fakePars.length != originPars.length)
throw new HookErrorException("hook method pars must match the origin method! " + fake.getName());
}
for (int i = 0;i < originPars.length;i++) {
if (fakePars[i + parOffset] != originPars[i] && !fakePars[i + parOffset].isAssignableFrom(originPars[i]))
throw new HookErrorException("hook method pars must match the origin method! " + fake.getName());
}
}
public static class HookEntity {
public Method target;
public Method hook;
public Method backup;
public HookEntity(Method target) {
this.target = target;
}
}
}
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodParams {
Class<?>[] value();
}
\ No newline at end of file
package com.swift.sandhook.wrapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodReflectParams {
String[] value();
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment