为帮助童鞋们更有节奏感地学习,本文分为Demo篇和实战篇来作叙述。
先简单介绍Xposed框架(下文简称:Xposed)
关于Xposed
简介
Xposed是一个很强大的android平台上的hook工具,其可以在不修改APK文件的情况下影响程序运行的框架服务,且在功能不冲突的情况下同时运作。开发者可以基于Xposed制作出很多功能强大的模块,用于android的逆向破解,应用美化等方面,现在市面上很多抢红包,消息防撤回,应用破解收费模块应用也是基于此。
原理
刷入Xposed时会向系统写入并替换掉系统一些关键文件,下面是安装包文件结构:
META-INF/ 里面有flash-script.sh脚本文件,用于配置各个文件安装位置
system/bin/ 主要为替换系统的Zygote进程执行文件对应的xposed版文件,如app_process
system/framework/XposedBridge.jar jar包位置
system/lib so文件所在位置
system/lib64 so(适用于64架构)文件所在位置
system/xposed.prop xposed版本说明文件
- 我们都知道Zygote进程是Android的核心,所有的应用以及系统服务进程都是由Zygote进程fork出来的。
- 系统启动时会开启此进程,对应的执行文件是/system/bin/app_process,由于app_process文件被替换,所以启动了Xposed版的Zygote进程。
- 该进程会加载Xposed相关类库和函数,其中包括XposedBridge.jar库。
- XposedBridge.jar中的XposedBridge.main函数完成对系统的一些关键函数进行hook以及Xposed模块的初始化。
- Xposed在进行hook java方法时,利用修改过的虚拟机将方法注册为native方法,JVM调用java函数时,如果该函数为native的,就调用它的nativeFunc,这样方法在调用时,就会调用到这个native方法。
- 在这个native方法中,Xposed直接调用了一个java方法,这个java方法里面对原方法进行了调用,并在调用前后插入了钩子,于是就hook住了这个方法。
简要流程如下:
graph LR
A(替换系统文件)-->B[创建Xposed版Zygote进程]
B-->C[加载XposedBridge.jar库]
C-->D[Hook系统关键函数]
C-->F[加载Xposed模块]
D-->G[目标java方法注册为native方法]
F-->G
G-->H(native方法调用目标java方法并前后插钩)
你可能没有完全看懂,没有关系,不是本文重点,简单了解下有助于对下文的理解。
劫持登录Demo
编写Xposed模块
-
Android Studio(下文简称:AS)新建Android项目工程,本文名为“XposedDemo”,然后新建module(模块),本文名为“HookLoginModule”。
-
在HookLoginModule下的build.gradle中添加依赖(更多版本依赖点此访问):
compileOnly 'de.robv.android.xposed:api:82'
==注意: 一定为“compileOnly”,老版本AS请用“provided”,不能为“implementation”或“compile”==
-
在模块中的AndroidManifest.xml下的application标签里添加如下内容:
<!--模块申明,true表示申明为xposed模块--> <meta-data android:name="xposedmodule" android:value="true" /> <!--模块说明,一般为模块的功能描述--> <meta-data android:name="xposeddescription" android:value="这个模块是用来劫持登录的" /> <!--模块兼容版本--> <meta-data android:name="xposedminversion" android:value="54" />
-
新建Hook入口类HookLogin实现xposed的接口IXposedHookLoadPackage并重写方法handleLoadPackage:
public class HookLogin implements IXposedHookLoadPackage { public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { } }
-
在模块的src/main/assets(若没有assets目录请新建个)下新建文件xposed_init并将HookLogin类的完整类名写进去以完成Hook入口类的注册:
cn.icheny.xposed.HookLogin
好了,Xposed模块已完成了基本的编写,接下来写Demo项目
编写Demo项目
- 本文为了简化直接使用XposedDemo下的“app”模块,更名为“LoginDemo”
- 新建登录Activity为“LoginActivity”并编写如下代码:
public class LoginActivity extends AppCompatActivity {
EditText mTvUsername, mTvPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mTvUsername = findViewById(R.id.tv_username);
mTvPassword = findViewById(R.id.tv_password);
}
/**
* 登录
*/
public void login(View view) {
String username = mTvUsername.getText().toString();
String password = mTvPassword.getText().toString();
if (TextUtils.isEmpty(username)) {
Toast.makeText(this, "请输入用户名", Toast.LENGTH_SHORT).show();
return;
} else if (TextUtils.isEmpty(password)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
if (checkLogin(username, password)) {
startActivity(new Intent(this, LoginSuccessActivity.class));
}
}
/**
* 模拟后台服务器校验登录
*
* @param username 用户名
* @param password 密码
*/
public boolean checkLogin(String username, String password) {
if ("admin".equals(username) && "admin123".equals(password)) {
return true;
}
Toast.makeText(this, "用户名或密码不正确!", Toast.LENGTH_SHORT).show();
return false;
}
}
- 在对应的布局中编辑登录界面代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="30dp">
<EditText
android:id="@+id/tv_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:hint="请输入用户名..."
android:imeOptions="actionNext"
android:textSize="18dp" />
<EditText
android:id="@+id/tv_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:hint="请输入密码..."
android:imeOptions="actionDone"
android:inputType="textPassword"
android:textSize="18dp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:onClick="login"
android:text="登录"
android:textSize="18dp" />
</LinearLayout>
通过LoginActivity中的代码可以了解用户名必须为“admin”密码为“admin123”才能完成登录。那么我们运行LoginDemo验证下:
演示图成功验证了代码逻辑。
Hook劫持
- 编辑HookLogin下的handleLoadPackage方法:
......
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam == null) {
return;
}
Log.e(TAG, "Load app packageName:" + lpparam.packageName);
/**
* 过滤非目标应用,本文目标应用即LoginDemo,包名为:cn.icheny.logindemo
*/
if (!"cn.icheny.logindemo".equals(lpparam.packageName)) {
return;
}
//固定格式
XposedHelpers.findAndHookMethod(
"cn.icheny.logindemo.LoginActivity", // 需要hook的方法所在类的完整类名
lpparam.classLoader, // 类加载器,固定这么写就行了
"checkLogin", // 需要hook的方法名,checkLogin(username,password)
String.class, // 第一个参数,用户名
String.class, // 第二个参数,密码
// Hook回调
new XC_MethodHook() {
@Override
/**
* checkLogin被hook前执行下面的方法
*/
protected void beforeHookedMethod(MethodHookParam param) {
Log.e(TAG, "劫持开始了↓↓↓↓↓↓");
}
/**
* checkLogin被hook后执行下面的方法
*/
protected void afterHookedMethod(MethodHookParam param) {
// hook 用户名和密码
String username = (String) param.args[0];
String password = (String) param.args[1];
Log.e(TAG, "用户名:" + username + " 密码:" + password);
// 被hook后返回自己指定的值(true,表示方法checkLogin调用返回值为true)
param.setResult(true);
Log.e(TAG, "劫持结束了↑↑↑↑↑↑");
}
}
);
}
......
代码中我们对LoginActivity下的“checkLogin(String username, String password)”方法进行hook,获取用户名和密码并做log打印,最后让其返回结果为true,即输入任意字符的用户名和密码都能登录成功。beforeHookedMethod和afterHookedMethod即前文所述“前后插钩”的实现方法。
- 代码已经注释的很清晰,如果你还没看懂,可以下方评论帮助我优化文章,接下我们将写好的Xposed模块HookLoginModule编译apk安装到手机中,在Xposed Installer中勾选使用该模块,然后重启手机。
- 接下来再次打开LoginDemo应用来验证Hook代码:
演示图成功验证了Hook代码逻辑,这里贴下log日志:
2019-05-08 22:08:06.614 24617-24617/? E/HookLogin: 劫持开始了↓↓↓↓↓↓
2019-05-08 22:08:06.621 24617-24617/? E/HookLogin: 用户名:11 密码:111
2019-05-08 22:08:06.621 24617-24617/? E/HookLogin: 劫持结束了↑↑↑↑↑↑
---------------------------------------------------------------------------
2019-05-08 22:08:14.855 24617-24617/? E/HookLogin: 劫持开始了↓↓↓↓↓↓
2019-05-08 22:08:14.863 24617-24617/? E/HookLogin: 用户名:114554 密码:1114545
2019-05-08 22:08:14.863 24617-24617/? E/HookLogin: 劫持结束了↑↑↑↑↑↑
成功获取到了用户名和密码,这样我们的Xposed模块就大工造成了,Demo篇到此结束!
XposedDemo下载:https://github.com/ausboyue/XposedDemo
暂无评论内容