一、xposed 的使用:
hook一个方法,
需要首先实现一个XC_MethodHook 用于,定义调用 方法之前与之后执行的操作。
找到要hook的类,然后调用hookAllMethods 去hook 对应方法,代码为:
XC_MethodHook callback = new XC_MethodHook() {
//调用原方法之前调用
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
.......
}
//调用原方法之后调用
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
......
}
};
Class<?> clazz = findClass("class", null);
Method method = clazz.getDeclaredMethod("method", parameterTypes);
method.setAccessible(true);
XposedBridge.hookMethod(m, callback);
二、hook的实现为:
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
..........
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
..........
return callback.new Unhook(hookMethod);
}
hookMethodNative 在xposed 框架初始化时在 onVmCreated 方法内 已经注册到系统中,在 libxposed_art.cpp 为其natvie 实现:
void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
jobject, jint, jobject javaAdditionalInfo) {
ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);
// hook 指定方法。
artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}
在Android 中,系统在native层 将java的类方法的描述为ArtMethod,调用其EnableXposedHook,对当前方法进行hook 操作
开始hook 指定方法
void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) {
//创建原方法的备份,backup_method
auto* cl = Runtime::Current()->GetClassLinker();
ArtMethod* backup_method = cl->AllocArtMethodArray(soa.Self(), 1);
backup_method->CopyFrom(this, cl->GetImagePointerSize());
//添加设置 backup_method的flags 为kAccXposedOriginalMethod,表示其为hook方法的原方法
backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod);
mirror::AbstractMethod* reflect_method;
if (IsConstructor()) {
reflect_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method);
} else {
reflect_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method);
}
reflect_method->SetAccessible<false>(true);
//创建hookinfo,用于保存,hook的原方法,与其 before && after 方法
XposedHookInfo* hookInfo = reinterpret_cast<XposedHookInfo*>(calloc(1, sizeof(XposedHookInfo)));
hookInfo->reflectedMethod = soa.Vm()->AddGlobalRef(soa.Self(), reflect_method);
//添加hook的方法(before && after 方法)
hookInfo->additionalInfo = soa.Env()->NewGlobalRef(additional_info);
//添加 backup_method(原方法)
hookInfo->originalMethod = backup_method;
//通过setEntryPointFromJni 将hookinfo 的写入当前 函数
SetEntryPointFromJni(reinterpret_cast<uint8_t*>(hookInfo));
// 重新设置 EntryPointFromQuickCompiledCode 值为GetQuickProxyInvokeHandler 的返回值art_quick_proxy_invoke_handler
//以此达到hook的目的
SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
//设置当前方法以机器指令执行。
SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
// TODO将此方法设置为 非native(~kAccNative) 方法,且为xposed hook 的方法(kAccXposedHookedMethod)
SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod);
}
//GetQuickProxyInvokeHandler 用于获取xposed 框架下,函数以本地机器码执行时的首地址(即java层函数的执行从这个C 函数还是的)
static inline const void* GetQuickProxyInvokeHandler() {
return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
}
EnableXposedHook,会创建一个hookInfo 将original、before、after 方法存入,然后通过 SetEntryPointFromJni 方法将hookinfo 保存到ArtMethod 中。
在之后 通过SetEntryPointFromInterpreter 方法强制设置 java 方法为,本地机器码执行
通过SetEntryPointFromQuickCompiledCode 改变机器码执行时的函数入口为 art_quick_proxy_invoke_handler
到此函数的整个hook 流程就已经完成。
三、在方法调用时,hook的实现为:
在调用到hook 方法时,他的大概逻辑为:
art_method.Invoke()--> art_quick_invoke_stub--> EntryPointFromQuickCompiledCode(art_quick_proxy_invoke_handler) --> artQuickProxyInvokeHandler--->InvokeXposedHandleHookedMethod-->soa.Env()->CallStaticObjectMethodA-->XposedBridge.handleHookedMethod
-->开始处理hook 逻辑
详细为:
art在执行java 方法是通过调用 art_method.Invoke() 来进行执行的:
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) {
//如果是本地机器码 执行,会走:
if (!IsStatic()) {
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
} else {
(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
}
}
在art_method.Invoke() 中会 判断方法类型进行区别调用,假设其为非静态方法,那么会调用到汇编中的 art_quick_invoke_stub方法:
ENTRY art_quick_invoke_stub
.........
INVOKE_STUB_CALL_AND_RETURN
END art_quick_invoke_stub
在此汇编指令中会调用 INVOKE_STUB_CALL_AND_RETURN 宏方法,实现为:
.macro INVOKE_STUB_CALL_AND_RETURN
// load method-> METHOD_QUICK_CODE_
ldr x9, [x0, #ART_METHOD_QUICK_CODE
// Branch to method.
blr x9
.endm
通过着两端汇编可知,系统在调用 art_quick_invoke_stub ,会找到ArtMethod的ART_METHOD_QUICK_CODE(即:entry_point_from_quick_compiled_code_)字段,这个就是EntryPointFromQuickCompiledCode,从而进入OAT函数执行。
#define ART_METHOD_QUICK_CODE_OFFSET_64 48
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_64,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
而EntryPointFromQuickCompiledCode 我们在之前hook时,已经通过SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()) 方法强制性改为自己的 art_quick_proxy_invoke_handler 方法了。
那么可知,在调用art_quick_invoke_stub 时,会自动调用到我们设置的 art_quick_proxy_invoke_handler 函数中去。
ENTRY art_quick_proxy_invoke_handler
bl artQuickProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP)
END art_quick_proxy_invoke_handler
从art_quick_proxy_invoke_handler 的汇编中,可以看出他会直接调用的到artQuickProxyInvokeHandler 方法,
extern "C" uint64_t artQuickProxyInvokeHandler(
ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
const bool is_xposed = proxy_method->IsXposedHookedMethod();//判断 GetAccessFlags() 的kAccXposedHookedMethod 字段
......
if (is_xposed) {
jmethodID proxy_methodid = soa.EncodeMethod(proxy_method);
self->EndAssertNoThreadSuspension(old_cause);
JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args);
local_ref_visitor.FixupReferences();
return result.GetJ();
}
......
}
在artQuickProxyInvokeHandler方法中,系统会通过的 kAccXposedHookedMethod 字段判断当前 ArtMethod 是否被hook,如被hook那么会调用 InvokeXposedHandleHookedMethod 走他的hook调用逻辑。
InvokeXposedHandleHookedMethod 方法会从 ArtMethod 中获取 之前设置的 hookinfo,然后调用到 xposed 的java 层XposedBridge.java 去处理他的 调用逻辑
JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, jobject rcvr_jobj, jmethodID method, std::vector<jvalue>& args) {
//获取ArtMethod 的hookinfo 信息
const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo();
//将hookinfo 转为一个数组,以便和java 层进行通信调用
jvalue invocation_args[5];
invocation_args[0].l = hookInfo->reflectedMethod;
invocation_args[1].i = 1;
invocation_args[2].l = hookInfo->additionalInfo;
invocation_args[3].l = rcvr_jobj;
invocation_args[4].l = args_jobj;
//通过CallStaticObjectMethodA 调用 xposed_callback_method 类里面 xposed_callback_method 的方法
//xposed_callback_method: XposedBridge.java
//xposed_callback_method: handleHookedMethod 方法
//ArtMethod 的这两个值,在系统开机时 在 onVmCreated 进行赋值的
jobject result = soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class,
ArtMethod::xposed_callback_method,
invocation_args);
}
到此会调用到 java层 XposedBridge.java 类的handleHookedMethod 方法,真正去处理 before、Original、after 这三个方法的调用关系了:
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable {
AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
//如果xposed 时关闭状态,或者callbacksSnapshot 长度为0, 那么将会走原逻辑,不会hook
......
// 从callbacksSnapshot 中取出“before method“ 并调用。
int beforeIdx = 0;
//使用循环以防有多个before 方法。
do {
((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
if (param.returnEarly) {
// skip remaining "before" callbacks and corresponding "after" callbacks
beforeIdx++;
break;
}
} while (++beforeIdx < callbacksLength);
//调用正常的逻辑
if (!param.returnEarly) {
try {
param.setResult(invokeOriginalMethodNative(method, originalMethodId,
additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
} catch (InvocationTargetException e) {
param.setThrowable(e.getCause());
}
}
// 同理调用 "after method" 的方法。
}
四、应用的启动,与hook
app 进程的启动见:
https://blog.csdn.net/xiaolli/article/details/72869106
app的进程启动,首先走的是
core/java/android/app/ActivityThread.java----->main方法
public static void main(String[] args) {
thread.attach(false, startSeq);
}
private void attach(boolean system, long startSeq) {
final IActivityManager mgr = ActivityManager.getService();
try {
//通过attachApplication 将自身进程attach到 AMS, 建立app和AMS 的连接
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
AMS的attachApplication 方法会调用thread.bindApplication, 在走回ActivityThread 的 bindApplication方法
而bindApplication方法, 在zygote进程中xposed框架初始化时已经被hook了见:
https://blog.csdn.net/xiaolli/article/details/107506138
XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java
/*package*/ static void initForZygote() throws Throwable {
findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
......
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);
});
....
}
在app进程执行handleBindApplication 之前会执行 beforeHookedMethod 方法,此方法内,会加载相关类, 并hook 他们的方法
a. hook 信息的加载:XposedBridge.sLoadedPackageCallbacks
hook 信息的加载也是在zygote进程内完成:
XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedBridge.java ---->main方法
XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java--->loadModules方法
XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java---->loadModule 方法
private static void loadModule(String apk, ClassLoader topClassLoader) {
ZipFile zipFile = null;
InputStream is;
zipFile = new ZipFile(apk);
ZipEntry zipEntry = zipFile.getEntry("assets/xposed_init");
ClassLoader mcl = new PathClassLoader(apk, XposedBridge.BOOTCLASSLOADER);
BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is));
try {
String moduleClassName;
while ((moduleClassName = moduleClassesReader.readLine()) != null) {
moduleClassName = moduleClassName.trim();
try {
Class<?> moduleClass = mcl.loadClass(moduleClassName);
final Object moduleInstance = moduleClass.newInstance();
if (XposedBridge.isZygote) {
if (moduleInstance instanceof IXposedHookLoadPackage)
XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
if (moduleInstance instanceof IXposedHookInitPackageResources)
XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
}
}
} catch (IOException e) {
Log.e(TAG, " Failed to load module from " + apk, e);
} finally {
closeSilently(is);
closeSilently(zipFile);
}
}
XposedBridge.hookLoadPackage 方法会 将此callback 添加进sLoadedPackageCallbacks中
public static void hookLoadPackage(XC_LoadPackage callback) {
synchronized (sLoadedPackageCallbacks) {
sLoadedPackageCallbacks.add(callback);
}
}
跳回initForZygote 方法他会将sLoadedPackageCallbacks 封装到XC_LoadPackage.LoadPackageParam中的callback中之后调用XC_LoadPackage 的callAll函数
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);
XposedBridge-art/app/src/main/java/de/robv/android/xposed/callbacks/XCallback.java
public static void callAll(Param param) {
if (param.callbacks == null)
throw new IllegalStateException("This object was not created for use with callAll");
for (int i = 0; i < param.callbacks.length; i++) {
try {
((XCallback) param.callbacks[i]).call(param);
} catch (Throwable t) { XposedBridge.log(t); }
}
}
XposedBridge-art/app/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java
protected void call(Param param) throws Throwable {
if (param instanceof LoadPackageParam)
handleLoadPackage((LoadPackageParam) param);
}
loadModule 方法中创建的为Wrapper 对象,那么会走到
XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
final class Wrapper extends XC_LoadPackage {
private final IXposedHookLoadPackage instance;
public Wrapper(IXposedHookLoadPackage instance) {
this.instance = instance;
}
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
instance.handleLoadPackage(lpparam);
}
}
然后就会走到 开发者实现的handleLoadPackage 方法中取hook方法
http://www.infocool.net/kb/OtherMobile/201611/208851.html
http://www.cnblogs.com/lanrenxinxin/p/5207174.html