-
咱们好,我叫 Jack Darren,现在主要负责国内游戏发行 Android SDK 开发
-
自从前次发布的两篇关于 Android 逆向的文章(Android 逆向入门保姆级教程 和 Android 逆向之脱壳实战篇)火了之后,从中感觉出来咱们对这个系列的文章仍是比较感兴趣的,所以续写了这个系列的文章,这次给咱们带来的是 Xposed 开发相关的文章。
目录
-
Xposed 介绍
-
集成 Xposed
-
运用 Xposed
-
Xposed 完结原理
-
Xposed 疑问回答
Xposed 介绍
-
Xposed 结构是一个强壮的 Android 逆向工程工具,它允许开发者在不修正运用程序源代码的状况下,动态地注入和修正Android 运用程序的行为。这使得开发者能够履行各种使命,包含修正运用程序的行为、禁用广告、添加新功能、提高隐私保护等。
-
Xposed 不只能够 Hook 方针运用的 API,还能够 Hook 方针运用调用体系的 API,Xposed 能够监控和操控到方针运用的一切 Java 层的操作。
-
运用 Xposed 的前提条件
-
手机有必要 Root:这儿引荐运用 Magisk(面具)
-
手机有必要装 xp 结构:这儿引荐运用 LSPosed,原因也很简单,由于 XPosed Installer 和 EdXposed 现已弃更了,现在只有 LSPosed 还在更新,讲到这儿,许多同学应该都懵逼了,这三个到底是啥?有什么关系?这三个其实都是 xp 结构,只不过 XPosed Installer 不保护了,后边就有大神根据这个版别保护了 EdXposed 结构,仅仅 EdXposed 结构后边也不保护了,又有大神根据 EdXposed 保护了 LSPosed,前史总是在重蹈覆辙。
-
集成 Xposed
- 第一步:在项目主模块下的
build.gradle
文件中加入远程依赖
dependencies {
// XP 结构:https://github.com/rovo89/Xposed
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
}
- 第二步:在
AndroidManifest.xml
中加入装备
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.xposed.demo">
<application>
<!-- 当时运用是否为 Xposed 模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- Xposed 模块描绘 -->
<meta-data
android:name="xposeddescription"
android:value="我是模块的描绘" />
<!-- 最小要求 Xposed 版别号 -->
<meta-data
android:name="xposedminversion"
android:value="53" />
</application>
</manifest>
- 第三步:创建一个 Hook 进口类,示例这儿创建了一个名为 XposedHookMain 类
package com.android.xposed.demo;
public class XposedHookMain implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(LoadPackageParam loadPackageParam) throws Throwable {
// 打印方针运用的包名
XposedBridge.log("Loaded app: " + loadPackageParam.packageName);
}
}
- 然后在主模块中的
src/main/assets/
创建一个名为xposed_init
文件,并加入刚刚创建的 Hook 类
com.android.xposed.demo.XposedHookMain
- 第四步:打开 Xposed 结构中启用 Xposed 运用,并且选择这个 Xposed 模块对哪些方针运用收效(效果域)
- 至此,Xposed 结构运用环境现已搭建完结,能够进行下一步 Hook 操作了
运用 Xposed
- 我假如想 Hook 方针运用的 Application 类的 onCreate 办法的话,能够添加以下 Hook 代码
public class XposedHookMain implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(LoadPackageParam loadPackageParam) throws Throwable {
// 打印方针运用的包名
XposedBridge.log("Loaded app: " + loadPackageParam.packageName);
XposedHelpers.findAndHookMethod("android.app.Application", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
// Hook 到办法履行前,能够在此处理履行一些代码逻辑,一般常用于修正办法传入的参数
XposedBridge.log("Hook 到 Application.onCreate 履行前");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
// Hook 到办法履行后,能够在此处理履行一些代码逻辑,一般常用于修正办法回来参数
XposedBridge.log("Hook 到 Application.onCreate 履行前");
}
});
}
}
- MethodHookParam 类运用介绍
// 当时 Hook 办法的方针实例,假如 Hook 的办法是静态办法便是 null
Object thisObject = param.thisObject;
// 当时 Hook 办法的参数值
Object[] args = param.args;
// 当时 Hook 办法的信息(办法的名称、办法所在类名、办法修饰符、是否为同步办法)
Member method = param.method;
// 获取办法的回来值
Object result = param.getResult();
// 修正办法的回来值
param.setResult(result);
// 判断办法履行是否呈现了反常
param.hasThrowable();
// 获取办法调用呈现的反常
Throwable throwable = param.getThrowable();
// 修正办法调用呈现的反常
param.setThrowable(throwable);
// 回来办法调用的成果,这个成果可能是正常的成果,也可能是一个反常方针
Object resultOrThrowable = param.getResultOrThrowable();
-
除了 Hook 办法,Xposed 还提供了其他办法,例如:
-
XposedHelpers.findAndHookConstructor:Hook 构建函数
-
XposedHelpers.findField:Hook 字段
-
……
-
-
这些仅仅 API 调用,这儿就不展开细讲了
Xposed 完结原理
- 其实这个 Xposed 结构 wiki 上面现已写了,由所以英文的,咱们可能不太好了解,所以这儿我跟咱们做一下翻译
Android 体系发动时,有一个名为“Zygote”的进程,它是Android运转时的核心。每个运用程序都是作为它的一个副本(“分支”)发动的。当手机发动时,经过/init.rc脚本发动了这个进程。进程的发动是经过/system/bin/app_process完结的,它加载所需的类并调用初始化办法。
-
每个运用程序发动的时分的,都会经过 Zygote 进程 fork 出来一个新的进程,那么 Zygote 进程是怎样来的呢?当 Android 体系开机时,会经过
/init.rc
脚本来开启,Zygote 进程的发动是经过/system/bin/app_process
来完结,在这儿会加载所需求的类并进行初始化。 -
Xposed 的原理其实很简单,便是搞了一个扩展版的
app_process
,并进行狸猫换太子,这个扩展版的app_process
会在发动过程中会将 Xposed Jar 包添加到类加载器中,并初始化 XposedBridge main 函数进口,main 函数其实就干了两件事-
initNative:初始化钩子,注册办法、字段等监听
-
loadModules:加载 xp 模块,便是读取
assets/xposed_init
注册的类名,然后进行反射创建和加载
-
-
涉及到的核心代码有以下这些:
XposedBridge#main
public final class XposedBridge {
@SuppressWarnings("deprecation")
protected static void main(String[] args) {
// Initialize the Xposed framework and modules
try {
SELinuxHelper.initOnce();
SELinuxHelper.initForProcess(null);
runtime = getRuntime();
if (initNative()) {
XPOSED_BRIDGE_VERSION = getXposedVersion();
if (isZygote) {
startsSystemServer = startsSystemServer();
initForZygote();
}
loadModules();
} else {
log("Errors during native Xposed initialization");
}
} catch (Throwable t) {
log("Errors during Xposed initialization");
log(t);
disableHooks = true;
}
// Call the original startup code
if (isZygote)
ZygoteInit.main(args);
else
RuntimeInit.main(args);
}
private static void loadModules() throws IOException {
final String filename = BASE_DIR + "conf/modules.list";
BaseService service = SELinuxHelper.getAppDataFileService();
if (!service.checkFileExists(filename)) {
Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
return;
}
InputStream stream = service.getFileInputStream(filename);
BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
String apk;
while ((apk = apks.readLine()) != null) {
loadModule(apk);
}
apks.close();
}
}
runtime#InitNativeMethods
void Runtime::InitNativeMethods() {
VLOG(startup) << "Runtime::InitNativeMethods entering";
Thread* self = Thread::Current();
JNIEnv* env = self->GetJniEnv();
// Must be in the kNative state for calling native methods (JNI_OnLoad code).
CHECK_EQ(self->GetState(), kNative);
// First set up JniConstants, which is used by both the runtime's built-in native
// methods and libcore.
JniConstants::init(env);
// Then set up the native methods provided by the runtime itself.
RegisterRuntimeNativeMethods(env);
// Initialize classes used in JNI. The initialization requires runtime native
// methods to be loaded first.
WellKnownClasses::Init(env);
// Then set up libjavacore / libopenjdk, which are just a regular JNI libraries with
// a regular JNI_OnLoad. Most JNI libraries can just use System.loadLibrary, but
// libcore can't because it's the library that implements System.loadLibrary!
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
{
constexpr const char* kOpenJdkLibrary = kIsDebugBuild
? "libopenjdkd.so"
: "libopenjdk.so";
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
// Initialize well known classes that may invoke runtime native methods.
WellKnownClasses::LateInit(env);
VLOG(startup) << "Runtime::InitNativeMethods exiting";
}
runtime#RegisterRuntimeNativeMethods
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_dalvik_system_DexFile(env);
register_dalvik_system_VMDebug(env);
register_dalvik_system_VMRuntime(env);
register_dalvik_system_VMStack(env);
register_dalvik_system_ZygoteHooks(env);
register_java_lang_Class(env);
register_java_lang_DexCache(env);
register_java_lang_Object(env);
register_java_lang_ref_FinalizerReference(env);
register_java_lang_reflect_AbstractMethod(env);
register_java_lang_reflect_Array(env);
register_java_lang_reflect_Constructor(env);
register_java_lang_reflect_Field(env);
register_java_lang_reflect_Method(env);
register_java_lang_reflect_Proxy(env);
register_java_lang_ref_Reference(env);
register_java_lang_String(env);
register_java_lang_StringFactory(env);
register_java_lang_System(env);
register_java_lang_Thread(env);
register_java_lang_Throwable(env);
register_java_lang_VMClassLoader(env);
register_java_util_concurrent_atomic_AtomicLong(env);
register_libcore_util_CharsetUtils(env);
register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(env);
register_sun_misc_Unsafe(env);
}
Xposed 疑问回答
运用 Xposed 结构有什么需求注意的点或许坑吗?
- 假如你用的是旧版别的 Android Studio,需求在设置中禁用 Instant Run,否则编译时类不会直接包含在 apk 中,会导致 hook 失败
项目依赖 Xposed 结构为什么用的是 compileOnly,而不是用 implementation 或许 api?
- 由于没有必要,由于装了 Xposed 模块的体系上面,是存在 Xposed 的调用相关类的,所以没有必要打到包里边去。
Hook 了方针运用之后,没有收效该怎样办?
- 呈现这种问题大概率是 Xposed 模块晚于方针运用履行,这样就会导致 Hook 不收效,重启一下方针运用即可。
Xposed 能够 Hook native 层的办法吗?
- 不行,Xposed 只能用于 Java 层代码的 Hook,现在不支持 Native 层代码的 Hook。
Hook 到办法后,当时代码环境是在方针运用的进程中仍是 Xposed 运用的进程中?
- 是在方针进程中,而不是在 Xposed 运用进程中,由于 Xposed 模块要依赖方针运用才能收效,本质上是寄生在方针的运用上做监控和修正,所以进程不会独立于方针运用。
在没有装 Xposed 结构的手机运转莫非不会崩溃吗?
- 理论上不会的,由于 Xposed 会经过读取
src/main/assets/xposed_init
文件中的类名,咱们在这个类里边调用 Xposed 的 API,假如用户的手机没有装 Xposed 结构,那么自然也不会去进行这一操作,当然有一种状况在外,假如是经过其他进口的加载的 Hook 类,那就另当别论了。
在项目中运用 XposedBridge.log
打印日志和运用 Log 打印日志有什么区别吗?
- 区别在于 XposedBridge.log 打印的日志,不只能够在 Logcat 操控台看到,也能够在 Xposed 结构上看到,这儿以 LSPosed 为例,能够在此处看到。