epic的使用和简单介绍

  • Epic 使用
  1. gradle 中引入依赖: com.github.tiann:epic:0.12
  2. 使用方法

class ThreadMethodHook extends XC_MethodHook{
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
        Thread t = (Thread) param.thisObject;
        Log.i(TAG, "thread:" + t + ", started..");
    }

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Thread t = (Thread) param.thisObject;
        Log.i(TAG, "thread:" + t + ", exit..");
    }
}

DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Thread thread = (Thread) param.thisObject;
        Class<?> clazz = thread.getClass();
        if (clazz != Thread.class) {
            Log.d(TAG, "found class extend Thread:" + clazz);
            DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
        }
        Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() +  " is created.");
    }
});
DexposedBridge.hookAllMethod(Thread.class, "run", new ThreadMethodHook());

3.  注意事项

不支持armeabi架构,如果项目仅支持armeabi架构,需要从armeabi-v7进行so库的复制。

  • 简介

基于dynamic dispatch的dynamic callee-side rewriting来实现hook

其实就是修改entrypoint 所指向的代码.

 

1)保证要Hook的method完成compile,也就是运行时要执行其compiled_code。

2)根据要Hook的method对应的art::mirror::ArtMethod找到compiled_code入口点。

3)在compiled_code的开始位置放置一段很短的跳转代码,称为“一段跳板”,作用是跳转到二段跳板。之所以弄一个一段跳板,是怕二段跳板太长,原方法的compiled_code区域放不下。

4)二段跳板会将一些必要的参数打包,调用Java-Bridge方法,并将打包在一起的参数,通过r3传递给Java-Bridge。

5)Java-Bridge方法取出传递进来的参数,然后根据r1、r2、r3以及sp(以Thumb2为例,除了r0~r3,剩余的参数会通过sp传递),构造出原方法的参数,最后调用DexposedBridge.handleHookedArtMethod。

6)由DexposedBridge.handleHookedArtMethod调用beforeHookedMethod、原方法和afterHookedMethod。

DexposedBridge.hookAllMethods入口跟踪

public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
   Set<XC_MethodHook.Unhook> unhooks = new HashSet<XC_MethodHook.Unhook>();
   for (Member method : hookClass.getDeclaredMethods())
      if (method.getName().equals(methodName))
         unhooks.add(hookMethod(method, callback));
   return unhooks;
}

对传入的类进行遍历,然后匹配和传入的方法相同的方法mothod,取callbackhemothed调用hookMethod进行hook的操作.

    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
        if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {
            throw new IllegalArgumentException("only methods and constructors can be hooked");
        }

        boolean newMethod = false;
        CopyOnWriteSortedSet<XC_MethodHook> callbacks;
        synchronized (hookedMethodCallbacks) {
            callbacks = hookedMethodCallbacks.get(hookMethod);
            if (callbacks == null) {
                callbacks = new CopyOnWriteSortedSet<XC_MethodHook>();
                hookedMethodCallbacks.put(hookMethod, callbacks);
                newMethod = true;
            }
        }

        Logger.w(TAG, "hook: " + hookMethod + ", newMethod ? " + newMethod);

        callbacks.add(callback);
        if (newMethod) {
            if (Runtime.isArt()) {
                if (hookMethod instanceof Method) {
                    Epic.hookMethod(((Method) hookMethod));
                } else {
                    Epic.hookMethod(((Constructor) hookMethod));
                }
            } else {
                ...
            }
        }
        return callback.new Unhook(hookMethod);
    }

已经hook过得方法和回调会被存储在hookedMethodCallbacks中,如果方法已经被hook过的,会直接从map中取出来.Epic.hookMethod就是hook方法的入口.

public static boolean hookMethod(Method origin) {
    ArtMethod artOrigin = ArtMethod.of(origin);
    return hookMethod(artOrigin);
}

private static boolean hookMethod(ArtMethod artOrigin) {

    MethodInfo methodInfo = new MethodInfo();
    methodInfo.isStatic = Modifier.isStatic(artOrigin.getModifiers());
    final Class<?>[] parameterTypes = artOrigin.getParameterTypes();
    if (parameterTypes != null) {
        methodInfo.paramNumber = parameterTypes.length;
        methodInfo.paramTypes = parameterTypes;
    } else {
        methodInfo.paramNumber = 0;
        methodInfo.paramTypes = new Class<?>[0];
    }
    methodInfo.returnType = artOrigin.getReturnType();
    methodInfo.method = artOrigin;
    // 存储方法的地址和新的methodInfo
    originSigs.put(artOrigin.getAddress(), methodInfo);

    // 取消权限检查
    if (!artOrigin.isAccessible()) {
        artOrigin.setAccessible(true);
    }
    // 静态方法主动调用
    artOrigin.ensureResolved();

    long originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
    if (originEntry == ArtMethod.getQuickToInterpreterBridge()) {
        Logger.i(TAG, "this method is not compiled, compile it now. current entry: 0x" + Long.toHexString(originEntry));
        // 未编译主动调用进行编译
        boolean ret = artOrigin.compile();
        if (ret) {
            originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
            Logger.i(TAG, "compile method success, new entry: 0x" + Long.toHexString(originEntry));
        } else {
            Logger.e(TAG, "compile method failed...");
            return false;
            // return hookInterpreterBridge(artOrigin);
        }
    }
    // 备份
    ArtMethod backupMethod = artOrigin.backup();

    Logger.i(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress()));
    Logger.i(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));

    ArtMethod backupList = getBackMethod(artOrigin);
    if (backupList == null) {
        setBackMethod(artOrigin, backupMethod);
    }

    final long key = originEntry;
    final EntryLock lock = EntryLock.obtain(originEntry);
    //noinspection SynchronizationOnLocalVariableOrMethodParameter
    synchronized (lock) {
        if (!scripts.containsKey(key)) {
            scripts.put(key, new Trampoline(ShellCode, originEntry));
        }

        // dynamic callee-side rewriting 实现hook
        Trampoline trampoline = scripts.get(key);
        boolean ret = trampoline.install(artOrigin);
        // Logger.d(TAG, "hook Method result:" + ret);
        return ret;
    }
}

安装跳板,实现hook

public boolean install(ArtMethod originMethod){
    boolean modified = segments.add(originMethod);
    if (!modified) {
        // Already hooked, ignore
        Logger.d(TAG, originMethod + " is already hooked, return.");
        return true;
    }
    // 创建 Trampoline
    byte[] page = create();
    // Trampoline 写到一块新的内存中
    EpicNative.put(page, getTrampolineAddress());

    int quickCompiledCodeSize = Epic.getQuickCompiledCodeSize(originMethod);
    int sizeOfDirectJump = shellCode.sizeOfDirectJump();
    if (quickCompiledCodeSize < sizeOfDirectJump) {
        // complied_code 太短,无法安装一段跳板,complied_code 入口改为二段跳板
        Logger.w(TAG, originMethod.toGenericString() + " quickCompiledCodeSize: " + quickCompiledCodeSize);
        originMethod.setEntryPointFromQuickCompiledCode(getTrampolinePc());
        return true;
    }
    // 这里是绝对不能改EntryPoint的,碰到GC就挂(GC暂停线程的时候,遍历所有线程堆栈,如果被hook的方法在堆栈上,那就GG)
    // 安装一段跳板
    return activate();
}
private boolean activate() {
    long pc = getTrampolinePc();
    Logger.d(TAG, "Writing direct jump entry " + Debug.addrHex(pc) + " to origin entry: 0x" + Debug.addrHex(jumpToAddress));
    synchronized (Trampoline.class) {
        return EpicNative.activateNative(jumpToAddress, pc, shellCode.sizeOfDirectJump(),
                shellCode.sizeOfBridgeJump(), shellCode.createDirectJump(pc));
    }
}

1)创建Trampoline(包括“二段跳板”BridgeJump,和CallOrigin)

2)创建和安装“一段跳板”,完成Hook。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值