Framework定制-集成Dobby模块

前言

上次分享了,源码集成YAHFA,有个同学私信,怎么Hook so层,作为一个爱分享的人,今天给你解决方案。这次就接着把上次YAHFA框架集成的代码稍微改一下,不用大动,不明白的人,先看一下上次的YAHFA框架集成。

先在Application类,插入加载so的代码

- frameworks/base/core/java/android/app/Application.java

在上次的基础上,插几行代码即可
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
        // add start
        try{
            JLog.print_info("Process-Info", "Application->attach ProcessName:" + getProcessName());

            if (isHook == false){
                AntiyHookManager hook = (AntiyHookManager)context.getSystemService(Context.ANTIY_HOOK_SERVICE);
                hook.getConfig();
                if (hook.checkHookDex(getProcessName())){
                    int flags = context.getApplicationInfo().flags;
                    if (flags > 0 && ((flags&ApplicationInfo.FLAG_SYSTEM)!=1)){
                        String apkPath = "/data/data/" + context.getPackageName() + "/AntiyPlugin.apk";
                        String soPath = "/data/data/" + context.getPackageName() + "/AntiyPlugin.so";
                        AntiyHook.moveApkToData(hook.hookdex.pluginPath, apkPath);
                        AntiyHook.unZipApkSoToData(hook.hookdex.pluginPath, soPath);
                        AntiyHook.startHook(context.getClassLoader(), apkPath, soPath);
                        isHook = true;
                    }
                }
            }

        } catch(Exception e){
            Log.e("AntiyHook_Application", e.toString());
        }
        // add end
    }

AntiyHook类在上次帖子的基础上,稍加修改,加一个函数即可(Antiy暴露了我的东家)

package android.app;


import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import dalvik.system.DexClassLoader;
import android.os.FileUtils;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


public class AntiyHook {
	public static final String TAG = "AntiyHook";

    static {
    	init(Build.VERSION.SDK_INT); //android 9 = 28
    }


    public static void unZipApkSoToData(String srcApkPath, String dstSoPath){
        try {
            File zipFile = new File(srcApkPath);
            if(!zipFile.exists()) {
                return ;
            }
            ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));
            ZipEntry entry;
            while ( (entry = zin.getNextEntry()) != null){
                if ( entry.getName().contains(".so")){
                    if(System.getProperty("os.arch").indexOf("64") >= 0) {
                        if ( entry.getName().contains("arm64-v8a")){
                            Log.i(TAG, "copy so file :" + entry.getName());
                            writeSoFile(zin, dstSoPath);
                        }
                    }else{
                        if ( entry.getName().contains("armeabi-v7a")){
                            Log.i(TAG, "copy so file :" + entry.getName());
                            writeSoFile(zin, dstSoPath);
                        }
                    }
                }
            }
            zin.closeEntry();
        }
        catch (Exception e) {
            Log.e(TAG, e.toString());
        }
    }

    public static void writeSoFile(ZipInputStream zin, String path){
        FileOutputStream out = null;
        try{
            File fi = new File(path);
            if (!fi.exists()) {
                fi.getParentFile().mkdirs();
                fi.createNewFile();
            }
            out = new FileOutputStream(fi);
            int len;
            byte[] buffer = new byte[2048];
            while ((len = zin.read(buffer)) != -1) {
                out.write(buffer, 0, len);
                out.flush();
            }
        } catch (Exception e){
            Log.e(TAG, e.toString());
        } finally {
            try{
                if (out != null){
                    out.close();
                }
            } catch (Exception e){
                Log.e(TAG, e.toString());
            }
            setPermission(path);
        }
    }


    public static void moveApkToData(String srcFileName, String dstFileName){
        deleteDataFile(dstFileName);
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(srcFileName);
            out = new FileOutputStream(dstFileName);
            byte[] bytes = new byte[1024];
            int i;
            while ((i = in.read(bytes)) != -1)
                out.write(bytes, 0, i);
        } catch (IOException e) {
            Log.e(TAG, e.toString());
        } finally {
            try {
                if (in != null)
                    in.close();
                if (out != null){
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                Log.e(TAG, e.toString());
            }
            setPermission(dstFileName);
        }

    }


    public static void setPermission(String path){
        try{
            File file = new File(path);
            if (file.exists()){
                int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;
                FileUtils.setPermissions(path, perm, -1, -1);//将权限改为777   
            }
  
        }catch (Exception e) {
            Log.e(TAG, e.toString());
        }

    }


    public static void deleteDataFile(String fileName){
        try {
            File file = new File(fileName);
            if (file.exists()){
                file.delete();
            }
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        } 
    }

    public static void loadPluginSo(String soPath){
        try{
            File file = new File(soPath);
            if (file.exists()){
                Log.i(TAG, "load so src:" + soPath);
                System.load(soPath);
                file.delete();//用完就删否则不会更新
            }
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        } 

    }

    public static void startHook(ClassLoader originLoader, String apkPath, String soPath){
    	try{
            Log.i(TAG, "in AntiyHook startHook");
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
	            DexClassLoader pluginLoader = new DexClassLoader(
	                    apkPath, null, null, originLoader);
            	doHookDefault(pluginLoader, originLoader);
            }
            loadPluginSo(soPath);
        }  catch (Exception e) {
            Log.e(TAG, e.toString());
        }

    }


    public static void doHookDefault(ClassLoader pluginLoader, ClassLoader originLoader) {
        try {
            Log.i(TAG, "in AntiyHook doHookDefault");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
                Class<?> hookInfoClass = Class.forName("com.antiy.HookInfo", true, pluginLoader);
                String[] hookClassNames = (String[]) hookInfoClass.getField("hookClassNames").get(null);
                for (String hookClassName : hookClassNames) {
                    doHookItemDefault(pluginLoader, hookClassName, originLoader);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "doHookDefault:" + e.toString());
        }
    }

    public static void doHookItemDefault(ClassLoader patchClassLoader, String hookItemName, ClassLoader originClassLoader) {
        try {
            Log.i(TAG, "in AntiyHook doHookItemDefault:" + hookItemName);
            Class<?> hookItem = Class.forName(hookItemName, true, patchClassLoader);

            String className = (String) hookItem.getField("className").get(null);
            String methodName = (String) hookItem.getField("methodName").get(null);
            String methodSig = (String) hookItem.getField("methodSig").get(null);

            if (className == null || className.equals("")) {
                Log.w(TAG, "No target class. Skipping...");
                return;
            }

            Class<?> clazz = Class.forName(className, true, originClassLoader);
            if (clazz == null){
                Log.e(TAG, "Cannot find class " + className);
                return;
            }

            Method hook = null;
            Method backup = null;
            for (Method method : hookItem.getDeclaredMethods()) {
                if (method.getName().equals("hook") && Modifier.isStatic(method.getModifiers())) {
                    hook = method;
                } else if (method.getName().equals("backup") && Modifier.isStatic(method.getModifiers())) {
                    backup = method;
                }
            }
            if (hook == null) {
                Log.e(TAG, "Cannot find hook for " + className + ":" + methodName);
                return;
            }

            // has to visibly init the classes
            // see the comment for function Utils.initClass()
            if(initClass() != 0) {
                Log.e(TAG, "Utils.initClass failed");
            }

            findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
        } catch (Exception e) {
            Log.e(TAG, "doHookItemDefault:" + e.toString());
        }
    }

    public static void findAndHook(Class targetClass, String methodName, String methodSig, Method hook) {
        hook(findMethod(targetClass, methodName, methodSig), hook);
    }

    public static void findAndBackupAndHook(Class targetClass, String methodName, String methodSig,
                                            Method hook, Method backup) {
        Log.i(TAG, "in AntiyHook findAndBackupAndHook:" + methodName);
        backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup);
    }

    public static void hook(Object target, Method hook) {
        backupAndHook(target, hook, null);
    }

    public static void backupAndHook(Object target, Method hook, Method backup) {
        if (target == null) {
            throw new IllegalArgumentException("null target method");
        }
        
        if (hook == null) {
            throw new IllegalArgumentException("null hook method");
        }

        if (!Modifier.isStatic(hook.getModifiers())) {
            throw new IllegalArgumentException("Hook must be a static method: " + hook);
        }
        checkCompatibleMethods(target, hook, "Original", "Hook");
        if (backup != null) {
            if (!Modifier.isStatic(backup.getModifiers())) {
                throw new IllegalArgumentException("Backup must be a static method: " + backup);
            }
            checkCompatibleMethods(target, backup, "Original", "Backup");
        }

        if(initClass() != 0) {
            Log.e(TAG, "initClass failed");
        }

        if (!backupAndHookNative(target, hook, backup)) {
            throw new RuntimeException("Failed to hook " + target + " with " + hook);
        }
    }

    private static Object findMethod(Class cls, String methodName, String methodSig) {
        if (cls == null) {
            throw new IllegalArgumentException("null class");
        }
        if (methodName == null) {
            throw new IllegalArgumentException("null method name");
        }
        if (methodSig == null) {
            throw new IllegalArgumentException("null method signature");
        }
        return findMethodNative(cls, methodName, methodSig);
    }

    private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) {
        ArrayList<Class<?>> originalParams;
        if (original instanceof Method) {
            originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
        } else if (original instanceof Constructor) {
            originalParams = new ArrayList<>(Arrays.asList(((Constructor<?>) original).getParameterTypes()));
        } else {
            throw new IllegalArgumentException("Type of target method is wrong");
        }

        ArrayList<Class<?>> replacementParams = new ArrayList<>(Arrays.asList(replacement.getParameterTypes()));

        if (original instanceof Method
                && !Modifier.isStatic(((Method) original).getModifiers())) {
            originalParams.add(0, ((Method) original).getDeclaringClass());
        } else if (original instanceof Constructor) {
            originalParams.add(0, ((Constructor<?>) original).getDeclaringClass());
        }


        if (!Modifier.isStatic(replacement.getModifiers())) {
            replacementParams.add(0, replacement.getDeclaringClass());
        }

        if (original instanceof Method
                && !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
            throw new IllegalArgumentException("Incompatible return types. " + originalName + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
        } else if (original instanceof Constructor) {
            if (replacement.getReturnType().equals(Void.class)) {
                throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
            }
        }

        if (originalParams.size() != replacementParams.size()) {
            throw new IllegalArgumentException("Number of arguments don't match. " + originalName + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
        }

        for (int i = 0; i < originalParams.size(); i++) {
            if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
                throw new IllegalArgumentException("Incompatible argument #" + i + ": " + originalName + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
            }
        }
    }


    private static native boolean backupAndHookNative(Object target, Method hook, Method backup);

    public static native Object findMethodNative(Class targetClass, String methodName, String methodSig);

    private static native void init(int sdkVersion);

    public static int initClass() {
        if(shouldVisiblyInit()) {
            long thread = getThread();
            return visiblyInit(thread);
        }
        else {
            return 0;
        }
    }

    private static native boolean shouldVisiblyInit();
    private static native int visiblyInit(long thread);
    private static native long getThread();
}


然后编译源码刷机

这样在应用启动初期,就会加载含有Dobby框架的so,现在来写插件,在插件中集成Dobby

1 修改CMakefile文件把so名称设置为AntiyPlugin.so

2 新建libs文件夹放入dobby.a静态库(参考:Dobby集成 - 简书

3 在cpp文件夹下放入dobby头文件

4 编写代码hook  open试试



定义函数指针
typedef int(*pFunc_Open)(const char *, int , ...);
pFunc_Open Hook::orgin_open = nullptr;
 
定义用于替换的函数
int fake_open(const char *path, int flag, ...){
    va_list args;
    va_start(args, flag);
    int mode = va_arg(args, int);
    va_end(args);
    LOGI("open hook path -> %s ", path);
    return orgin_open(path, flag, mode); //调用原函数
}
 
对open函数进行hook
void hook_open() {
    void* addr = DobbySymbolResolver("libc.so", "open");
    if (addr != nullptr){
        if (DobbyHook((void*)addr, (void*)(fake_open), (void**)&orgin_open) == RT_SUCCESS){
            LOGI("open hook success");
        }else{
            LOGI("open hook failed");
        }
    }
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
	
	//获取JNIEnv环境
    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
        printf("JavaVm fail to get JNIEnv\n");
        return -1;
    }

    ...
    hook_open();
    ...

}

其他就跟上次帖子集成YAHFA时的操作一致,放入config.js, push 插件到/data/local/tmp/就行了

现在来看一下效果:

 (上面这个应用会检测调试信息,通过对open函数的hook,也能发现部分踪迹, /proc/self/status文件可以查看是否被调试)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值