0.前言
本篇所有内容,以学习为目的进行探讨,1是对xposed原理更加了解,第二个也验证了,xposed模块确实比较危险,来历不明的模块不要使用,最后是不得用于违法用途。
是这样,手上有一个app,想在登录的时候得到用户输入的用户名和密码,我们可以通过xposed来实现。
前提:
- 你知道这个app在哪个类中存储了账号密码
- 你知道这个类中某个具体的方法
- xposed的基本常识
- 一台安装了xposed的android(也可以是模拟器)
1.逻辑分析
现在,我已经拿到了一个app,并且我对它进行了反编译,得到了以下信息。
-
登录界面所在的activity
这个部分可以直接分析,或者反编译得到清单文件
-
找到了存储账号密码的viewmodel
首先找到上面所说的LoginActivity,这个界面必定存储了账号和密码
查看它的代码:
发现,他在按下按钮之后,执行了viewmodel的login方法,找到这个viewmodel的login方法:
可以发现,这里它直接调用自身的username 和 password对象的get方法,就得到了用户名和密码。
看下这两个是什么属性:
发现是ObservableField属性。
好了,到这里,我们就找到了用户名和密码所存储的类
-
劫持ViewModel中的方法
只要能劫持到该类中的任意一个方法,就可以直接拿到这个类的对象。(当然也可以直接通过类名找到class,再通过反射获取这个类的对象)
对象都拿到了,对象里面的方法和属性不是随便拿?
-
通过反射获取到对象中的属性
首先,我们通过反射,拿到username和password对象。这两个是ObservableField对象,反射得到的是Object类型。
此时还不能直接得到账号密码,需要执行ObservableField中的get方法来返回。
同样的,又是通过反射,调用 ObservableField对象的get方法,又会得到一个Object对象,我们把他强转成String(从源码得知的,这个ObserveField存储的是String类型)。
这样就拿到了账号密码
2.xposed项目搭建
-
创建一个空项目
通过as创建一个空项目,什么也不需要
-
添加依赖
在app级别的gradle文件中,加上
compileOnly 'de.robv.android.xposed:api:82' compileOnly 'de.robv.android.xposed:api:82:sources'
-
创建一个xposed入口类
名字随意,包名随意,创建一个普通的类,然后继承IXposedHookLoadPackage,实现handleLoadPackage方法
-
创建xposed_init
在src/main下,创建assets文件夹,在里面创建一个xposed_init,然后写上你的入口类名
-
清单文件加上模块声明
这个是必须加上的,否则xposed框架不识别
<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.HookDemo2" > <meta-data android:name="xposedmodule" android:value="true"/>- <meta-data android:name="xposeddescription" android:value="@string/description"/> <meta-data android:name="xposedminversion" android:value="53"/> </application>
具体内容查看官方教程。大体就是,xposed模块用途,xposedsdk版本
-
在handleLoadPackage中编写代码
这部分就是重点。
逻辑查看注释:
@Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { //判断当前应用是不是我的目标应用 if (!lpparam.packageName.equals("com.xxx.xxx")) return; //打印日志 XposedBridge.log("HookDEmo 找到app " + lpparam.packageName); //监听app启动 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("要拦截的类名,此处应当是viewmodel的包名"); } catch (Exception e) { XposedBridge.log("寻找xxxx报错" + e); return; } XposedBridge.log( "寻找xxxx成功"); XposedHelpers.findAndHookMethod(hookclass, "login", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //进行hook操作 XposedBridge.log("执行了LoginViewModel的 login方法"); //1.x得到的是viewModel自身 Object viewModel = param.thisObject; //2.1 获取用户名 //得到ObservableField真实对象 Object userNameObserveObj = XposedHelpers.getObjectField(viewModel,"userName"); //通过xposed的callMethod方法,调用Object中的属性 String userNameStr = (String) XposedHelpers.callMethod(userNameObserveObj, "get"); //2.2 获取密码 //得到ObservableField真实对象 Object passwordObserveObj = XposedHelpers.getObjectField(viewModel,"password"); //通过xposed的callMethod方法,调用Object中的属性 String passwordStr = (String) XposedHelpers.callMethod(passwordObserveObj, "get"); //3.x 打印 XposedBridge.log("最终得到的username是:"+userNameStr); XposedBridge.log("最终得到的password是:"+passwordStr); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } }); } });
-
安装模块并激活模块
到xposed中安装并激活模块,然后重启生效
3.效果
首先打开目标应用的登录界面,输入账号密码,点击登录
到xposed中查看日志:
到此获得账号密码。
4.总结
虽然能实现修改第三方app的效果,但是说起来比较麻烦,还要先反编译再写模块。
如果能从系统级别进行修改,反正app安装到了我的系统上,最终还得让我的系统来执行。是不是直接可以在内存中取得class,就不用反编译了。
发送请求时也可以在系统层面进行拦截,不必通过各种抓包工具。