Xposed框架是一款可以在不修改APK的情況下影響程序運(yùn)行(修改系統(tǒng))的框架服務(wù),可謂喜歡搗鼓的人的必備神器。而且使用上也極為簡(jiǎn)單,下面我就來(lái)看看如何在Android Studio上開發(fā)xposed模塊。
環(huán)境設(shè)置就不再贅述了,需要以下前提準(zhǔn)備工作。
| 電腦端 | 手機(jī)端 |
|---|---|
| Android studio | root |
| Android sdk | 安裝了xposed installer |
| XposedBridgeApi jar包 | 已安裝并激活xposed框架 |
一、創(chuàng)建xposed模塊
1、新建Android工程
其實(shí)就是一路next,選擇Empty Activity,創(chuàng)建工程完成后等待gradle加載完畢。
2、導(dǎo)入XposedBridgeApi jar包
導(dǎo)入完成后,修改下app/build.gradle中的依賴聲明。將XposedBridgeApi的依賴由implementation改成provided。改完后記得sync一下gradle。
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
provided files('lib/XposedBridgeApi-54.jar')
}
3、修改AndroidManifest
<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/AppTheme">
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="這里填寫xposde說(shuō)明" />
<meta-data
android:name="xposedminversion"
android:value="54" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
xposedmodule: 表示這是一個(gè)xposed模塊
xposeddescription: 描述該模塊的用途,可以引用string.xml中的字符串
xposedminversion:要求支持的Xposed Framework最低版本
4、模塊實(shí)現(xiàn)
創(chuàng)建一個(gè)或者幾個(gè)類,并實(shí)現(xiàn)IXposedHookLoadPackage,IXposedHookZygoteInit或者其他IXposedMod的子接口。
package de.robv.android.xposed.mods.tutorial;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import android.util.Log;
public class TestDemo implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Loaded app: " + lpparam.packageName);
Log.d("YOUR_TAG", "Loaded app: " + lpparam.packageName )
}
}
XposedBridge.log會(huì)將日志輸出到logcat,并寫入日志文件
也可以用 android.util.Log輸出到logcat。
5、聲明xposed入口
我們需要新建一個(gè)assets文件夾,并創(chuàng)建文件xposed_init,在里面填上xposed模塊的入口.
這里我們聲明自己的類“com.johnhao.testdemo.TestDemo”

到這里,這個(gè)簡(jiǎn)單的模塊就算開發(fā)完了。雖然沒有界面,沒有任何實(shí)際的功能,但這也是一個(gè)確確實(shí)實(shí)的xposed模塊??梢员话惭b到設(shè)備上,也可以被xposed installer檢測(cè)到。
二、實(shí)踐技巧
前面只是簡(jiǎn)單的搭出了框子,并沒有任何實(shí)際功能,接下來(lái)我們往里添加一些內(nèi)容。
技巧1:檢測(cè)模塊是否啟動(dòng)
如果你使用過(guò)xposed插件,那么一定有概率碰上模塊未啟動(dòng)的情況,即便是你已經(jīng)勾選激活該模塊。那么有沒有什么方法來(lái)分辨模塊是否真的啟動(dòng)了呢?當(dāng)然有,而且很簡(jiǎn)單。原理就是:
在MainActivity實(shí)現(xiàn)一個(gè)boolean方法,然后用xposed hook自己Activity里的函數(shù)。能hook成功,自然代表模塊成功啟動(dòng)了,反之亦然。
// MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!isModuleActive()){
Toast.makeText(this, "模塊未啟動(dòng)", LENGTH_LONG).show();
}
else {
Toast.makeText(this, "模塊已啟動(dòng)", LENGTH_LONG).show();
}
}
private boolean isModuleActive(){
return false;
}
hook自己
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
// Xposed模塊自檢測(cè)
if (loadPackageParam.packageName.equals("your.package.name")){
XposedHelpers.findAndHookMethod("your.package.name.MainActivity", loadPackageParam.classLoader, "isModuleActive", XC_MethodReplacement.returnConstant(true));
}
}
這樣,如果模塊成功激活,就會(huì)在啟動(dòng)app的時(shí)候彈一個(gè)模塊已啟動(dòng)的toast,如果沒有啟動(dòng)則會(huì)彈模塊未啟動(dòng)的toast。
技巧2:hook帶殼應(yīng)用
隨著人們安全意識(shí)越來(lái)越高,很多應(yīng)用開發(fā)者都選擇給應(yīng)用加殼來(lái)保護(hù)自己。從最開始簡(jiǎn)單的dex加密到現(xiàn)在的onCreate函數(shù)虛擬化技術(shù),逆向的難度也越來(lái)越高。如何脫殼和修復(fù)onCreate函數(shù)不是本文的所要講的內(nèi)容。
這里以去年某款答題app為例子講解,我們期望獲得答題的題目和選項(xiàng)。

拿到app反編譯,明顯發(fā)現(xiàn)使用了某廠商的加密技術(shù)。第一步是脫殼,然后拿到smali后開始分析(這里我們不去修復(fù)onCreate方法)。
我們通過(guò)socket中的關(guān)鍵詞showQuestion字段,最終在Lcom/chongdingdahui/app/socket/MessageManager$7類里面發(fā)現(xiàn)了處理數(shù)據(jù)的代碼。


找到了關(guān)鍵的方法,接下來(lái)便是如何hook帶殼的應(yīng)用。如果直接hook的話,會(huì)發(fā)現(xiàn)找不到目標(biāo)class,所以我們這里可以采用四哥的方法。
// 應(yīng)用被加殼,采用這種方式加載類
try {
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
context = (Context) param.args[0];
ClassLoader loader = context.getClassLoader();
// 獲取題目和答案
try {
Class clazz = loader.loadClass("com.chongdingdahui.app.socket.MessageManager$7");
if (clazz != null){
XposedHelpers.findAndHookMethod(clazz, "call", Object[].class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] obj = (Object[]) param.args[0];
String content = obj[0].toString();
Log.d(TAG, content);
question = Util.getQuestion(content);
answers = Util.getAnswer(content);
}
});
}
}catch (Exception e){
Log.e(TAG, "socket.MessageManager$7 clazz not found" + Log.getStackTraceString(e));
}
這樣就能得到題目和答案了。因?yàn)槟壳按痤}類應(yīng)用早已規(guī)范化了,所以展示的代碼僅供方法參考,并不能實(shí)際應(yīng)用,但原理都是一樣的。
感興趣的話可以在我的微信公眾號(hào)回復(fù)xposed,獲取以上樣例的源碼??吹竭@兒,伙伴們有沒有g(shù)et到新技能呢?