首页
论坛
专栏
课程

[原创]Xposed注入实现分析及免重启定制

2018-1-3 11:10 16456

[原创]Xposed注入实现分析及免重启定制

2018-1-3 11:10
16456

Xposed的实现解析

Xposed简介

Xposed已经作为Android的Java层Hook大哥好多年了,不仅仅是安全研究者和逆向工程师手中的神器,还拥有着很多的插件开发者,在官方仓库收录的插件也已经超过了1000+个,而其服务的用户以各搞机论坛用户为主,也是不尽其数。
Xposed的代码是完全开源的,并且代码量也不算很大,对于安全研究者来说呢,我觉得研究一下Xposed的原理还是很有必要的。
Xposed的github地址:
https://github.com/rovo89/Xposed
https://github.com/rovo89/XposedBridge

 

本文使用Android4.4的源码进行分析

如何实现全局注入

简单的讲,Xposed是通过替换修改过的app_process来注入Zygote以实现的全局注入。

Android的启动过程

想要知道Xposed是如何实现的全局注入,首先要分析一下Android系统的启动过程,就可以很方便的理解Xposed选择的注入点。
我画了张图,Android系统的启动大致如下:
Android系统启动流程
从图中可以很方便的理解从BootLoader到显示系统界面的过程。

应用孵化器 - Zygote

Zygote介绍

Zygote,中文是"受精卵"...实际上他就是一个孵化器,所有应用程序的进程都是由它fork出来的。

USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
root      155   1     509740 41876 ffffffff b75761e0 S zygote
drm       156   1     18508  4064  ffffffff b7599ff6 S /system/bin/drmserver
media     157   1     69296  15656 ffffffff b755bff6 S /system/bin/mediaserver
install   158   1     6580   1228  c04d1048 b7507bb6 S /system/bin/installd
keystore  159   1     10172  2088  c03f4a25 b7561ff6 S /system/bin/keystore
root      160   1     1096   4     c044881c 080ddd03 S /system/xbin/su
system    187   1     81040  3860  ffffffff b75b0ff6 S /system/bin/surfaceflinger
root      343   2     0      0     c01cfbd5 00000000 S flush-8:16
root      414   62    6732   1412  c02f4452 b74c6bb6 S /system/bin/sh
root      418   62    6688   1464  c01c0a90 b752e1e0 S logcat
system    424   155   629276 46384 ffffffff b7575ff6 S system_server
u0_a44    506   155   564252 75032 ffffffff b75779eb S com.android.systemui
u0_a0     551   155   525484 25696 ffffffff b75779eb S android.process.acore
wifi      565   1     10776  2912  c01c0a90 b741d1e0 S /system/bin/wpa_supplicant
system    566   155   526468 21732 ffffffff b75779eb S com.android.settings
u0_a22    606   155   521276 24932 ffffffff b75779eb S com.android.inputmethod.latin
radio     623   155   539356 28428 ffffffff b75779eb S com.android.phone
u0_a23    637   155   557680 45252 ffffffff b75779eb S com.android.launcher
u0_a29    673   155   520300 20324 ffffffff b75779eb S com.android.music
u0_a16    693   155   521532 25876 ffffffff b75779eb S android.process.media
u0_a49    719   155   517580 18600 ffffffff b75779eb S com.android.smspush
u0_a15    806   155   521632 19920 ffffffff b75779eb S com.android.dialer
bluetooth 821   155   522828 22284 ffffffff b75779eb S com.android.bluetooth
dhcp      845   1     6656   1452  c01c0a90 b74e6ab6 S /system/bin/dhcpcd
u0_a6     916   155   527828 22036 ffffffff b75779eb S com.android.calendar
u0_a7     943   155   519636 23176 ffffffff b75779eb S com.android.providers.calendar
u0_a12    967   155   521156 21960 ffffffff b75779eb S com.android.deskclock
u0_a17    1019  155   528300 24912 ffffffff b75779eb S com.android.email
u0_a18    1078  155   524976 20436 ffffffff b75779eb S com.android.exchange
u0_a28    1168  155   530852 23100 ffffffff b75779eb S com.android.mms
u0_a32    1215  155   517576 19140 ffffffff b75779eb S com.android.onetimeinitializer
u0_a47    1251  155   517732 19072 ffffffff b75779eb S com.android.voicedialer

可以看到,这些包名格式的进程的PPID(父进程PID)都是155,即zygote进程。
Zygote启动之后会建立一个Socket Server,然后fork自身成为一个新的进程——system_server,并让它成为"前端代言人",当system_server接收到打开进程的请求时,它就会通过Socket跟Zygote通讯,Zygote就再fork自身称为新的进程。

Zygote的启动

在init.rc中可以看到,Zygote是通过app_process启动的,

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

这时app_process的任务也很简单,就是把自己的进程名变成Zygote,然后反射调用Zygote的主方法,即com.android.internal.os.ZygoteInit的main()。之后Zygote做的事情就是在介绍中说过的了。

Hook Zygote

那么Xposed就是通过修改app_process的方式来实现注入Zygote的。
项目地址:https://github.com/rovo89/Xposed 即是修改app_process的源码,在app_main.cpp的main函数中可以看到

    isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);
    if (zygote) {
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    }

可以看到,Xposed首先尝试加载自己的库,如果成功的话,就将runtime.start的类名替换成自己的类:de.robv.android.xposed.XposedBridge,因为app_processs是采用反射main()的方法来调用加载方法,所以只要在这个类中定义一个main()方法,就可以为所欲为了,最后只需在末尾调用一下com.android.internal.os.ZygoteInit的main()让系统继续跑下去就可以实现对Zygote的Hook。

注入应用进程

既然已经拿到了Zygote的所有权了,但是现在Application还没跑起来呢,如果这个时候就执行Hook模块,肯定是无法Hook到相关代码的。那么这时候就有必要提一下从Zygote接受到请求之后做了什么,我又简单的画了张图:

值得注意的是,因为fork之后的子进程是会继承父进程的状态继续往下跑,所以到handleChildProc的时候,此时已经是子进程在执行程序了,而ActivityThread也是在新进程中loop。
在Activity的loop中,可以接收的消息也包括了Appcalition的生命周期BIND_APPLICATIONEXIT_APPLICATION。处理BIND_APPLICATION的时候是直接调用了handleBindApplication方法,Xposed便是在Zygote中Hook了这个方法,在这里面执行用户的Hook模块,因为在这个时候,已经可以拿到应用的classLoader了,然后将此classLoader封装成一个LoadPackageParam再传给各个模块的handleLoadPackage即可。
在XposedBridge的main中可看到其调用了initForZygote()

protected static void main(String[] args) {
    ...
        if (isZygote) {
            XposedInit.hookResources();
            XposedInit.initForZygote();
        }
    ...
}

initForZygote中又Hook了handleBindApplication

static void initForZygote() throws Throwable {
    ...
    findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            ActivityThread activityThread = (ActivityThread) param.thisObject;
            ApplicationInfo appInfo = (ApplicationInfo) getObjectField(param.args[0], "appInfo");
            String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
            SELinuxHelper.initForProcess(reportedPackageName);
            ComponentName instrumentationName = (ComponentName) getObjectField(param.args[0], "instrumentationName");
            if (instrumentationName != null) {
                Log.w(TAG, "Instrumentation detected, disabling framework for " + reportedPackageName);
                XposedBridge.disableHooks = true;
                return;
            }
            CompatibilityInfo compatInfo = (CompatibilityInfo) getObjectField(param.args[0], "compatInfo");
            if (appInfo.sourceDir == null)
                return;

            setObjectField(activityThread, "mBoundApplication", param.args[0]);
            loadedPackagesInProcess.add(reportedPackageName);
            LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
            XResources.setPackageNameForResDir(appInfo.packageName, loadedApk.getResDir());

            XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
            lpparam.packageName = reportedPackageName;
            lpparam.processName = (String) getObjectField(param.args[0], "processName");
            lpparam.classLoader = loadedApk.getClassLoader();
            lpparam.appInfo = appInfo;
            lpparam.isFirstApplication = true;
            XC_LoadPackage.callAll(lpparam);

            if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME))
                hookXposedInstaller(lpparam.classLoader);
        }
    });
    ...
}

Xposed在Zygote进程中执行完这些之后,每次fork出来的都是handleBindApplication已经被Hook过的进程,所以当每个应用进程被打开的时候,都会最开始就先执行Xposed的模块插件,然后才开始执行本身的代码。这样就实现了注入到每个应用程序来进行Hook。

如何实现Hook

findAndHookMethod

findAndHookMethod想必是大家写Xposed插件最常用及最熟悉的一个方法,我们就通过这个方法来分析Xposed是如何实现的Java Hook。

public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
    if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_MethodHook))
        throw new IllegalArgumentException("no callback defined");

    XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];
    Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));

    return XposedBridge.hookMethod(m, callback);
}

大家知道这个方法的参数是个可变长参数列表,然后它会取最后一个参数来作为callback,即hook时的注入的代码。然后通过反射的方法来得到原本的Method。最后将这两个传给hookMethod。

public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
    ...
    if (newMethod) {
        Class<?> declaringClass = hookMethod.getDeclaringClass();
        int slot;
        Class<?>[] parameterTypes;
        Class<?> returnType;
        if (runtime == RUNTIME_ART) {
            slot = 0;
            parameterTypes = null;
            returnType = null;
        } else if (hookMethod instanceof Method) {
            slot = getIntField(hookMethod, "slot");
            parameterTypes = ((Method) hookMethod).getParameterTypes();
            returnType = ((Method) hookMethod).getReturnType();
        } else {
            slot = getIntField(hookMethod, "slot");
            parameterTypes = ((Constructor<?>) hookMethod).getParameterTypes();
            returnType = null;
        }

        AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
        hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
    }
    ...
}

可以看到hookMethod实际上时调用了一个native函数hookMethodNative,其他参数都很好理解,但是取的这个slot是个什么东西?翻了一下百度,在Dalvik的源码中dalvik/vm/reflect.c:

static int methodToSlot(const Method* meth)
{
    ClassObject* clazz = meth->clazz;
    int slot;

    if (dvmIsDirectMethod(meth)) {
        slot = meth - clazz->directMethods;
        slot = -(slot+1);
    } else {
        slot = meth - clazz->virtualMethods;
    }

    return slot;
}

原来这个slot就是此method在ClassObject的methods中的偏移,directMethods为负,virtualMethods为正。

偷梁换柱

hookMethodNative对应C函数XposedBridge_hookMethodNative

void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
            jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {
    // Usage errors?
    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {
        dvmThrowIllegalArgumentException("method and declaredClass must not be null");
        return;
    }

    // Find the internal representation of the method
    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
    Method* method = dvmSlotToMethod(declaredClass, slot);
    if (method == NULL) {
        dvmThrowNoSuchMethodError("Could not get internal representation for method");
        return;
    }

    if (isMethodHooked(method)) {
        // already hooked
        return;
    }

    // Save a copy of the original method and other hook info
    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));
    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));
    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // Replace method with our own code
    SET_METHOD_FLAG(method, ACC_NATIVE);
    method->nativeFunc = &hookedMethodCallback;
    method->insns = (const u2*) hookInfo;
    method->registersSize = method->insSize;
    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {
        // reset JIT cache
        char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));
        if (currentValue == 0 || currentValue == 1) {
            MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;
        } else {
            ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);
        }
    }
}

首先是通过slot获取到Method对象在内存中的Method结构体指针,然后将它设置为一个native方法,再将他的nativeFunc即执行时对应的函数地址设置为hookedMethodCallback的指针,将保存着原method信息的hookInfo暂时存放在本是dalvik字节码的insnsreflectedMethod是原method的java对象,additionalInfo是AdditionalHookInfo对象。
然后当调用到这个方法的时候,执行的就变成了hookedMethodCallback函数了。

void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
    ...
    dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,
        originalReflected, (int) original, additionalInfo, thisObject, argsArray);
    ...
}

主要就是又调了一个java方法methodXposedBridgeHandleHookedMethod,解释一下各个参数,originalReflected就是上面hookInfo的reflectedMethodoriginal是原method结构体指针,后面的不必解释了吧。

private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
            Object thisObject, Object[] args) throws Throwable {
        ...
        int beforeIdx = 0;
        do {
            try {
                ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
            } catch (Throwable t) {
                XposedBridge.log(t);

                // reset result (ignoring what the unexpectedly exiting callback did)
                param.setResult(null);
                param.returnEarly = false;
                continue;
            }

            if (param.returnEarly) {
                // skip remaining "before" callbacks and corresponding "after" callbacks
                beforeIdx++;
                break;
            }
        } while (++beforeIdx < callbacksLength);

        // call original method if not requested otherwise
        if (!param.returnEarly) {
            try {
                param.setResult(invokeOriginalMethodNative(method, originalMethodId,
                        additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
            } catch (InvocationTargetException e) {
                param.setThrowable(e.getCause());
            }
        }

        // call "after method" callbacks
        int afterIdx = beforeIdx - 1;
        do {
            Object lastResult =  param.getResult();
            Throwable lastThrowable = param.getThrowable();

            try {
                ((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);
            } catch (Throwable t) {
                XposedBridge.log(t);

                // reset to last result (ignoring what the unexpectedly exiting callback did)
                if (lastThrowable == null)
                    param.setResult(lastResult);
                else
                    param.setThrowable(lastThrowable);
            }
        } while (--afterIdx >= 0);
    ...
}

那么methodXposedBridgeHandleHookedMethod做的事情也很好理解,就是先把所有的beforeHookedMethod给调用一遍,然后再还原原来的method调用一遍,最后再把所有的afterHookMethod的给调用一遍,就完成了。

还原执行

执行原来的方法调用的是native方法invokeOriginalMethodNative。

void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult,
            const Method* method, ::Thread* self) {
    Method* meth = (Method*) args[1];
    if (meth == NULL) {
        meth = dvmGetMethodFromReflectObj((Object*) args[0]);
        if (isMethodHooked(meth)) {
            meth = (Method*) meth->insns;
        }
    }
    ArrayObject* params = (ArrayObject*) args[2];
    ClassObject* returnType = (ClassObject*) args[3];
    Object* thisObject = (Object*) args[4]; // null for static methods
    ArrayObject* argList = (ArrayObject*) args[5];

    // invoke the method
    pResult->l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true);
    return;
}

emm..就是通过拿到备份的method结构体,然后直接调用dvmInvokeMethod即可。

改造更新免重启的Xposed

大家在写Xposed模块的时候是不是有一个很不爽的地方,就是每次改两行代码之后又得重启,非常的烦,那么清楚了注入的原理之后,发现这点好像不是必须的!只是rovo89可能是考虑到性能的问题,所以才采用这种方案。实际上,只要轻轻的改动两行代码,就可以实现免重启更新!

Xposed原加载机制

Xposed原本是在app_process的时候一次性将模块列表读取,Load之后将其hook类放到一个Set里面,在de.robv.android.xposed.XposedBridgemain()中可以看到

protected static void main(String[] args) {
    ...
        if (isZygote) {
            XposedInit.hookResources();
            XposedInit.initForZygote();
        }

        XposedInit.loadModules();
    ...
}

待到应用启动的时候再分别调用每个handleLoadPackage,之后就不再读取插件。因为这样,所以每次fork之后,插件已经被加载在内存中了,就算更新插件也不会被读取加载,所以必须得重启才能使得插件生效。

处理办法

之前说到,Xposed通过HookhandleBindApplication来进入每个应用进程,那只要在这个时候再loadModules,那不就ojbk了吗?
loadModule中,进行加载不仅有handleLoadPackage,还有hook资源及hook命令行程序的包,

private static void loadModule(String apk, ClassLoader topClassLoader) {
        ...
                        if (moduleInstance instanceof IXposedHookZygoteInit) {
                            IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
                            param.modulePath = apk;
                            param.startsSystemServer = startsSystemServer;
                            ((IXposedHookZygoteInit) moduleInstance).initZygote(param);
                        }
                        if (moduleInstance instanceof IXposedHookLoadPackage)
                            XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
                        if (moduleInstance instanceof IXposedHookInitPackageResources)
                            XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
                    } else {
                        if (moduleInstance instanceof IXposedHookCmdInit) {
                            IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
                            param.modulePath = apk;
                            param.startClassName = startClassName;
                            ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
        ...
    }

但是我的需求就是只需要处理handleLoadPackage就可以了,所以给这个参数加个开关isLoadHookLoadPackage,我的代码如下

if (XposedBridge.isZygote) {
        if (moduleInstance instanceof IXposedHookZygoteInit) {
            IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
            param.modulePath = apk;
            param.startsSystemServer = startsSystemServer;
            ((IXposedHookZygoteInit) moduleInstance).initZygote(param);
        }

        if (moduleInstance instanceof IXposedHookLoadPackage)
            if(isLoadHookLoadPackage)
            {
                XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
            }
        if (moduleInstance instanceof IXposedHookInitPackageResources)
            XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
    } else {
        if (moduleInstance instanceof IXposedHookCmdInit) {
            IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
            param.modulePath = apk;
            param.startClassName = startClassName;
            ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
        }
    }

实际上就是简单的加了一个if而已,然后在main的地方改成

XposedInit.loadModules(false);

最后就是在hook handleBindApplication的地方加上XposedInit.loadModules(true);

findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        XposedInit.loadModules(true);
...

嗯,炒鸡简单,改源码不超过2分钟。

编译

改源码两分钟,编译五小时..
rovo89还提供了另外一个项目:https://github.com/rovo89/XposedTools
因为Xposed编译需要依赖AOSP来进行编译,这个工具就是专门用来编译Xposed源码的,使用方法也很简单,填好build.conf然后一把梭就可以了,具体的可以查看百度。

 

需要注意的是,上叙的源码是新版的xposed,就是支持5.0+art的,而我使用的是4.4.4的源码,模拟器也是4.4,所以clone源码的时候要-b master来切换分支。然后修改的代码也大同小异,就是loadmodule多了个startClassName参数,可以通过getStartClassName()方法获取,就是这样:
XposedBridge.loadModules(getStartClassName(),true);
因为这个老工程是个Eclipse工程,XposedTool是依赖于Gradle,所以无法使用XposedTool编译。直接使用Android Studio - Build Apk最后把文件名改成XposedBridge.jar即可,
最后你可以选择直接修改Xposed Installer将其中的XposedBridge.jar替换再安装,或者在手机上安装好Xposed,然后将修改好的XposedBridge.jar替换/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar,重启,就可以愉快的调插件玩耍了。

 

 




[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最后于 2018-2-24 01:40 被葫芦娃编辑 ,原因:
最新回复 (41)
zylyy 2018-1-3 13:34
2
0
,我也一直在用xposed,虽然不清楚具体原理
铭天星 2018-1-3 19:19
3
0
唯一的缺点就是initZygote重复调用
铭天星 2018-1-3 19:58
4
0
铭天星 唯一的缺点就是initZygote重复调用
也非常感谢楼主的思路
聖blue 2018-1-3 20:35
5
0
葫芦娃 1 2018-1-3 21:14
6
0
铭天星 唯一的缺点就是initZygote重复调用
不需要的哦,仔细看文
sudami 25 2018-1-3 21:35
7
0
牛鼻学习了!
铭天星 2018-1-3 21:53
8
0



Hoimk

不需要的哦,仔细看文
我分析的结果:第二次调用loadModules(true)的时候也会再调用一次initZygote。
如果改成initZygote加上条件!isLoadHookLoadPackage的话,就会导致调用initZygote,handleLoadPackage在不同的实例。
如有分析不正确的地方,望楼主一同交流核实
葫芦娃 1 2018-1-4 01:51
9
0
铭天星 Hoimk 不需要的哦,仔细看文 我分析的结果:第二次调用loadModules(true)的时候也会再调用一次initZygote。如果改成initZ ...
loadModules里不会调用initZygot的。他只是把xposed模块一个个加载进当前进程而已。initZygote从开机到启动进程只会执行一次,即开机通过app_process启动的那一次。
铭天星 2018-1-4 08:26
10
0
考查以下模块:

public class TestModule implements IXposedHookZygoteInit, IXposedHookLoadPackage {

private String modulePath;

@Override
public void initZygote(StartupParam startupParam) throws Throwable {
this.modulePath = startupParam.modulePath;
XposedBridge.log("initZygote modulePath=" + this.modulePath);
}

@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.isFirstApplication && lpparam.appInfo != null &&
(lpparam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 0) {
XposedBridge.log("handleLoadPackage modulePath=" + this.modulePath + ", processName=" + lpparam.processName + ", dataDir=" + lpparam.appInfo.dataDir);
}
}
}

官方的xposed开机调用一次initZygote,每次启动App调用handleLoadPackage时modulePath不为null

测试模块见附件
上传的附件:
葫芦娃 1 2018-1-4 08:57
11
0
铭天星 考查以下模块: public class TestModule implements IXposedHookZygoteInit, IXposedHookLoadPackage { priva ...
噢好吧,你说的是这个,这个没做处理,我一直以为你说的是XposedInit.initForZygote();  ....
lykseek 2018-1-4 10:16
12
0
先去赞一个
tangsilian 2018-1-4 16:10
13
0
赞,葫芦娃  文章读着很爽 
darmao 1 2018-1-4 16:44
14
0
支持楼主,顶顶顶
钱八斤 2018-1-4 20:51
15
0
支持楼主,有更快的方法,这是另一种思路
http://blog.csdn.net/u011956004/article/details/78612502
Lucaks 1 2018-1-6 22:43
16
0
膜  葫芦娃
supersun 2018-1-11 01:59
17
0
很棒,能否把编译好的发一份,自己编译太麻烦了
又见飞刀z 2018-1-18 11:50
18
0
如何hook的进程是系统服务中的代码,不知可否免重启呢?
又见飞刀z 2018-1-18 11:50
19
0
钱八斤 支持楼主,有更快的方法,这是另一种思路http://blog.csdn.net/u011956004/article/details/78612502
你这个应该不支持hook系统服务的代码
葫芦娃 1 2018-1-18 15:12
20
0
又见飞刀z 如何hook的进程是系统服务中的代码,不知可否免重启呢?
需要重启服务
gttiankai 2018-1-27 15:57
21
0
帖子写的非常详细,感谢.
WXAdamancy 2018-1-30 18:14
22
0
你好,我们公司在做防作弊相关的这方面东西,可以交流下吗?QQ:646241923
葫芦娃 1 2018-1-31 23:36
23
0
WXAdamancy 你好,我们公司在做防作弊相关的这方面东西,可以交流下吗?QQ:646241923
有什么问题可站内信私我
WXAdamancy 2018-2-1 11:14
24
0
谢谢,通过反作弊手段识别设备,比如通过设备id,imsi,imei,root, hook,模拟器检测能识别真机的百分比高吗?反作弊一期应该采集哪些设备信息(主要针对设备标别)
葫芦娃 1 2018-2-1 17:27
25
0
WXAdamancy 谢谢,通过反作弊手段识别设备,比如通过设备id,imsi,imei,root, hook,模拟器检测能识别真机的百分比高吗?反作弊一期应该采集哪些设备信息(主要针对设备标别)
不高,很多模拟器都对大部分API、文件、系统函数进行了处理,通常情况下获取到的很多信息都是模拟器伪造的。没什么特别精准的公开方案
WXAdamancy 2018-2-2 16:15
26
0
好的,如果把模拟器识别,xposed检测  ,root检测,然后尽量采集多的设备标识信息,这样的话能够识别反作弊的概率能提高大概多少呀?java层和c层都要做吗?
恭敬 2018-2-8 18:14
27
0
这个方案适用开发的时候用,免重启调试很棒,惭愧一直想省,一直没去做
liandafp 2018-2-8 18:35
28
0
有毕业学习一下
virjar 1 2018-4-1 11:44
29
0
哈哈楼主,我有另一种方案。可以不编译源码。  我们可以在插件加载入口偷梁换柱,用新的classLoader加载最新的插件代码。我稍后写一篇帖子吧
葫芦娃 1 2018-4-2 20:55
30
0
virjar 哈哈楼主,我有另一种方案。可以不编译源码。 我们可以在插件加载入口偷梁换柱,用新的classLoader加载最新的插件代码。我稍后写一篇帖子吧
不编译的方案也是有的..比如你自己可以用Xposed来Hook  Xposed或者是Zygote,最终目的只是再调用一次loadModules。
最后于 2018-4-2 20:57 被葫芦娃编辑 ,原因:
网络小男孩 2018-6-27 16:30
31
0
葫芦娃,么么
leehero 2018-6-28 11:09
32
0
66 现在好像有些其他的方案 免重启的。https://github.com/githubwing/HotXposed  https://github.com/shuihuadx/XposedHook
qwldcl 2018-8-27 16:59
33
0
你好,我下载源码编译也通过了,编译成功,但是刷到手机里系统就启动不了了,我用的测试手机是google 的N5,报错信息如下:
I Xposed  : Added Xposed (/system/framework/XposedBridge.jar) to CLASSPATH
D AndroidRuntime: >>>>>> START de.robv.android.xposed.XposedBridge uid 0 <<<<<<
D AndroidRuntime: CheckJNI is OFF
F DEBUG   :     #01 pc 00060ccf  /system/lib/libandroid_runtime.so (android::AndroidRuntime::startVm(_JavaVM**, _JNIEnv**, bool)+2358)
 F DEBUG   :     #02 pc 000610b3  /system/lib/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+250)


就开始一直循环这个动作,无法进入到系统中去。本来给你发消息的,奈何只支持140个字符
葫芦娃 1 2018-8-29 19:52
34
0
qwldcl 你好,我下载源码编译也通过了,编译成功,但是刷到手机里系统就启动不了了,我用的测试手机是google 的N5,报错信息如下: I Xposed : Added Xposed (/system/fr ...
这个日志里也看不出原因,不过你要是想要Hook调试APP的话可以给你推荐一下Frida,连APP都不用重启了。
airnow 2018-10-26 17:15
35
0
mark
老skr江 2018-10-26 18:08
36
0
厉害厉害
又见飞刀z 2018-12-1 17:41
37
0
在Android 6.0.1 好像会出现 modules.list未发现的问题

zhengzemao 2019-3-15 09:39
38
0
葫芦娃,你要改的是4.4 的,用art来表示,能不能发一份4.4的改好的apk或源码都行
Vn小帆 2019-3-15 09:45
39
0
WXAdamancy 谢谢,通过反作弊手段识别设备,比如通过设备id,imsi,imei,root, hook,模拟器检测能识别真机的百分比高吗?反作弊一期应该采集哪些设备信息(主要针对设备标别)
你说的这些 我都能过掉
miyuecao 2019-7-11 10:42
40
0
写的很详细了,学习了
xia0 1 2019-8-4 22:44
41
0
葫芦娃牛皮,学习了!
gqm 2019-8-19 02:09
42
0
葫芦娃大神,可以加您微信吗
游客
登录 | 注册 方可回帖
返回