一、原理
Android運行的核心是zygote進程,所有app的進程都是通過zygote fork出來的。通過替換system/bin/下面的app_process等文件,相當於替換了zygote進程,實現了控制手機上的所有APP。基本原理是修改了ART/Davilk虛擬機,將需要hook的函數注冊為Native層函數,當執行到該函數時,虛擬機會先執行Native層函數,然后執行Java層函數,這樣完成hook。
更詳細的可以參考:https://blog.csdn.net/wxyyxc1992/article/details/17320911
二、Xposed安裝
環境:網易mumu、Android Studio3.3.1
github地址:https://github.com/rovo89/XposedInstaller
Xposedinstaller的apk:https://repo.xposed.info/module/de.robv.android.xposed.installer
在網易mumu中安裝好xposedinstaller apk后,關閉應用兼容性(不關閉的話安裝xposed框架會出錯),進去之后點擊小雲彩即可安裝完成。

三、Xposed代碼編寫
新建項目,選擇
empty activity,創建成功后,在AndroidManifest.xml中添加如下代碼
<meta-data
android:name="xposedmodule"
android:value="true" /> <!--告訴xposed框架這是一個xposed模塊-->
<meta-data
android:name="xposeddescription"
android:value="這是一個Xposed例程" /> <!--模塊描述-->
<meta-data
android:name="xposedminversion" <!--模塊支持的最低版本-->
android:value="30" />
在gradle中配置XposedbridgeApi,build.gradle中配置
repositories {
jcenter()
}
dependencies {
...
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
...
}
這是在網絡通暢的情況下進行的, 網絡不通暢的話,可以手動下載XposedBridgeApi-82.jar,拖動到/app/libs中,刪除上述gradle中配置的
jcenter,右鍵"Add As Library"添加這個jar包。
在界面上畫個按鈕,並在
MainAcitiviy中編寫如下代碼(單純寫hook的話前面新建項目的時候可以add no activity)
package com.example.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;
public class MainActivity2 extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity2.this, toastMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public String toastMessage() {
return "我未被劫持";
}
}
編寫Hook代碼,在
MainActivity同級目錄下新建HookTest.java,並且繼承接口IXposedHookLoadPackage和重寫handleLoadPackage方法
package com.example.myapplication;
import java.lang.reflect.Array;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Map;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class HookTest implements IXposedHookLoadPackage {
private static final String HOOK_APP_NAME = "APP名字";
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
//性能優化,避免操作無關app
if (!lpparam.packageName.equals(HOOK_APP_NAME))
return;
if (lpparam.packageName.equals("HOOK_APP_NAME")) {
XposedBridge.log(" 劫持成功!!!");
XposedBridge.log("XposedMainInit handleLoadPackage 執行");
XposedBridge.log("Loaded app: " + lpparam.packageName); XposedHelpers.findAndHookMethod("APP名字.MainActivity",//hook的類
lpparam.classLoader,
"toastMessage", // 被Hook的函數
//Map.class, 被Hook函數的第一個參數 (此處沒有,只是舉個例子)
//String.class, 被Hook函數的第二個參數String
new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
// 參數獲取
XposedBridge.log("入口函數執行");
//參數1
XposedBridge.log("beforeHookedMethod map:" + param.args[0]);
//參數2
XposedBridge.log("beforeHookedMethod hash_key:" + param.args[1]);
//函數返回值
XposedBridge.log("beforeHookedMethod result:" + param.getResult());
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("afterHookedMethod result:" + param.getResult());
param.setResult("你已被劫持");
}
});
}
}
}
在src/mian目錄下添加一個assets目錄,目錄下添加一個xposed_init文件,里面的代碼是你的Hook類的包名+類名。
com.example.myapplication.HookTest
最后選擇禁用 Instant Run: 單擊 File -> Settings -> Build, Execution, Deployment -> Instant Run,把勾全部去掉。

這個時候鈎子已經執行了,具體想鈎什么,就看自己的需求了。
注:實際操作中,需要對APP先進行反編譯(反編譯了才能知道要鈎那個函數),反編譯工具有很多,這里就不細說了。
四、面對加了殼的APP
直接用反編譯工具打開apk,查看加的是哪種殼,尋找對應的函數,類似
attachBaseContext這樣的方法。
public class EncryptHook implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam loadPackageParam) throws Throwable {
if (!loadPackageParam.packageName.equals("app包名")) { return; }
XposedBridge.log("Start hook " + loadPackageParam.packageName);
XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader,
//com.stub.StubApp 加殼的類
"attachBaseContext", Context.class, new XC_MethodHook() {
// attachBaseContext 加殼的方法
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
XposedBridge.log("Enter stubApp");
XposedHelpers.findAndHookMethod("com.huijiemanager.utils.t", classLoader,
"a", byte[].class, PublicKey.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("rsa before params: " + new String(
(byte[]) param.args[0]) + "," + param.args[1]);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("rsa after params: " + new String(
(byte[]) param.args[0]) + "," + param.args[1]);
}
});
}
});
}
}
注:反編譯的代碼不一定准確,逆向的時候最好對每個關鍵函數都掛上鈎子,查看參數是否正確。
附上xposedAPI文檔:https://api.xposed.info/reference/packages.html
