关闭页面特效

[Android 逆向整理笔记] Xposed

终于在暑假的尾巴闲下来了🤧但是驾照还是没拿到,啥 b 教练给我拖了 2 个月了还没考科目三,私募了😅

好久没更 blog 了(居然已经一年半了,悲)期间写的一些零零散散的 wp/笔记啥的也懒得整理发出来了,直接开新坑吧

以前 Android 方面都是碰到啥就学点啥,比较杂碎,开个坑从基础的部分开始做一下系统性的整理总结,顺便在这个过程中查漏补缺学点新东西。然后立个 flag,以后会经常看 Google 推的安全补丁然后写 blog 记录下来(希望真不是 flag 吧)

这篇先写一下 Xposed 的,后面 frida 还有一坨,估计得写一会了。抓包也要写一下,以前一直是 wireshark + fiddler 用到死,遇到不走代理的就寄,趁此机会看看有没有其它优雅的方法能学一学。然后看有没有空学一学一直声称实际上一点没碰过的 flutter 逆向

机:Google Pixel 6 Pro, Android 13

1|0简介


Xposed 框架是一套开放源代码的、在Android高权限模式下运行的框架服务,可以在不修改APK文件的情况下修改程序的运行,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。比较常用的就是用 Xposed 来搞 Hook。

Xposed 用自己实现的 app_process 模块替换了系统本身的 app_process,加载了一个新的 jar 包,这样就劫持了 zygote 进程,替换成 Xposed 自己的虚拟机,这就是大概的原理。

2|0配置


放一个 xml 板子

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xposeddemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.XposedDemo"> <meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposeddescription" android:value="This is my Xposed module" /> <meta-data android:name="xposedminversion" android:value="89" /> </application> </manifest>

libs内导入 XposedBridgeAPI,build.gradle 要把这个 libs 的 implementation 改成 compileOnly,不参与打包

然后在 main 下新建一个 assets 目录,创建文件 xposed_init,用它去声明入口,就一行代码,比如说包名是com.example.xposeddemo,那就写一行 com.example.xposeddemo.hook

hook 类的框架板子

import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class Hook implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { if (!loadPackageParam.packageName.equals("包名")) { return; } } }

3|0基本的 Hook


就按这个板子,套在之前那个框架里面就行

XposedHelpers.findAndHookMethod("类名", loadPackageParam.classLoader, "方法名", String.class, new XC_MethodHook() { // String.class 是参数类型,就是说有一个 String 类型参数 @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // Hook 的方法执行之前,这里一般会对传入的参数修改 super.beforeHookedMethod(param); XposedBridge.log("修改前的参数: " + param.args[0].toString()); // 修改前正常传入的参数是什么 String a = "自定义参数"; param.args[0] = a; // 修改参数 XposedBridge.log("修改后的参数: " + param.args[0].toString()); // 检查修改后的参数是什么 // 用 log.e 也行,在 logcat 里面好像还更好看一些 } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // Hook 的方法执行之后,这里一般就是修改一下返回值 super.afterHookedMethod(param); XposedBridge.log("修改前的返回值: " + param.getResult().toString()); // 修改前正常的返回值 param.setResult("自定义返回值"); XposedBridge.log("修改后的返回值: " + param.getResult().toString()); // 修改后的返回值 } }); });

注意一下,XposedBridge 的 filter 标识是 LSPosed-Bridge,logcat 里面用这个过滤即可

4|0不用填参数类型的写法


如果参数不是普通的 int, String 这种,而是比较复杂的玩意,可以用这个板子

Class a = loadPackageParam.classLoader.loadClass("类名"); XposedBridge.hookAllMethods(a, "方法名", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); XposedBridge.log(param.args[0].toString()); } });

其实就是用了下 hookAllMethods 这个 api,不用填参数类型了,区别不大,后续修改操作和之前一样的

5|0Hook 函数


Class a = loadPackageParam.classLoader.loadClass("类名"); XposedBridge.hookAllMethods(a, "方法名", new XC_MethodReplacement() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); } @Override protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable { return ""; } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } });

在这里面写就行

然后如果是构造函数的话也差不多,就换了个 api

无参的

XposedHelpers.findAndHookConstructor("类名", loadPackageParam.classLoader, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } });

有参的加个参数类型即可

XposedHelpers.findAndHookConstructor("类名", classLoader, String.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } });

6|0简单加固的处理


XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Context context = (Context) param.args[0]; ClassLoader classLoader = context.getClassLoader(); // 然后是 Hook 逻辑 } });

简单来讲就是尝试把 ClassLoader 的点找到,然后再用这个 ClassLoader 去进行后续的操作。当然大部分情况下还是得自己去处理前面加固的逻辑,这个只能说是一个可能的例子,不能当板子用

7|0Hook 变量


如果是静态变量,static,val 这种

final Class temp = XposedHelpers.findClass("类名", loadPackageParam.classLoader); XposedHelpers.setStaticIntField(temp, "变量名", 233);

然后,由于没有 String 的 api,所以如果是改 string 这里就用 setStaticObjectField,反正都是对象

这里我突然比较好奇:这静态变量 hook 是怎么能生效的,按理来说应该编译的时候就定死了这个静态变量永不改变了。后面有空可以试着读读源码看是怎么操作的

如果是实例变量

final Class temp = XposedHelpers.findClass("类名", loadPackageParam.classLoader); XposedBridge.hookAllConstructors(temp, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); // param.thisObject 获取当前所属的对象 Object ob = param.thisObject; XposedHelpers.setIntField(ob, "变量名", 2333); } });

就是先找到字节码,然后在函数构造完毕后再去 Hook

8|0Hook (多个) Dex 文件


和前面那个对加固的处理差不多的过程,遍历找到了后改就行

XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { ClassLoader cl = ((Context) param.args[0]).getClassLoader(); Class<?> hookclass = null; try { hookclass = cl.loadClass("类名"); } catch (Exception e) { Log.e("filter", "未找到类 ", e); return; } XposedHelpers.findAndHookMethod(hookclass, "方法名", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } }); } });

9|0主动调用方法


静态和实例方法其实差不多,只不过一个用的是 callStaticMethod 一个是 callMethod

Class temp = XposedHelpers.findClass("类名", loadPackageParam.classLoader); XposedHelpers.callMethod(temp.newInstance(), "方法名"); // 有参数的话在方法名后面继续写参数,就 String.class 那种

也可以试一下反射的手法

Class temp = XposedHelpers.findClass("类名", loadPackageParam.classLoader); Class temp_class = Class.forName("类名", false, loadPackageParam.classLoader); // 第一步找到类 Method temp_method = temp_class.getDeclaredMethod("方法名"); // 找到方法 temp_method.setAccessible(true); // 如果是私有方法就要 setAccessible 设置访问权限 temp_method.invoke(temp.newInstance()); // invoke 主动调用,或者 set 修改值(变量)

10|0Hook 内部类


XposedHelpers.findAndHookMethod("类名$内部类名", loadPackageParam.classLoader, "内部方法名", String.class, new XC_MethodHook() { // 放了个 String 类型的参数的例子 @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); } });

其实就是多一个 $ 符号再把内部类的名字放上去,剩下的和之前一样

11|0遍历方法


XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); Class temp = (Class) param.getResult(); String name = temp.getName(); // 选择该包名的类 if (name.contains("包名")) { Method[] arr = temp.getDeclaredMethods(); for (int i = 0; i < arr.length; ++i) { final Method md = arr[i]; int mod = arr[i].getModifiers(); // 排除抽象、native、接口方法,这几个一般不 Hook if (!Modifier.isAbstract(mod) && !Modifier.isNative(mod) && !Modifier.isAbstract(mod)) { XposedBridge.hookMethod(arr[i], new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); XposedBridge.log("Find Method: " + md.toString()); } }); } } } } });

这个可以一边操作一边看,每次操作的时候用到了的方法都会打印出来。把 log 写细致一点可以辅助 debug,并且它的打印顺序就是堆栈顺序,总体来说还是挺好用的

12|0trick


12|1字符串赋值的定位


XposedHelpers.findAndHookMethod("android.widget.TextView", loadPackageParam.classLoader, "setText", CharSequence.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); Log.d("StackTrace", param.args[0].toString()); if (param.args[0].equals("写你想看的变量的值")) { printStackTrace(); } } }); private static void printStackTrace() { Throwable ex = new Throwable(); StackTraceElement[] stackElements = ex.getStackTrace(); for (int i = 0; i < stackElements.length; i++) { StackTraceElement element = stackElements[i]; Log.d("StackTrace", "at " + element.getClassName() + "." + element.getMethodName() + "(" + element.getFileName() + ":" + element.getLineNumber() + ")"); } }

这个可以打印堆栈,然后去看什么时候进行字符串赋值了(setText)

12|2监听点击事件


Class temp = XposedHelpers.findClass("android.view.View", loadPackageParam.classLoader); XposedBridge.hookAllMethods(temp, "performClick", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); Object listenerInfoObject = XposedHelpers.getObjectField(param.thisObject, "mListenerInfo"); Object mOnClickListenerObject = XposedHelpers.getObjectField(listenerInfoObject, "mOnClickListener"); String callbackType = mOnClickListenerObject.getClass().getName(); Log.d("test", callbackType); } });

这个会打印出来你点击按钮后该按钮触发了哪些方法,也是跟踪逻辑的好板子

12|3改写布局


XposedHelpers.findAndHookMethod("类名", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); View img = (View)XposedHelpers.callMethod(param.thisObject, "findViewById", 控件的 16 进制值); img.setVisibility(View.GONE); } });

强制改控件的布局,可以去掉一些图片啥的

剩下的大概可能也许是读一下 Xposed 的源码...?以前还没读过这个的源码,前面那个改静态变量的实现方法还挺感兴趣的,然后就是找一个能用上 Xposed 的逆向题再练练手。

先把后面的部分整理一下再议,frida 还有一坨东西要写


__EOF__

作  者iPlayForSG
出  处https://www.cnblogs.com/Here-is-SG/p/18363274
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!

posted @   iPlayForSG  阅读(228)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示