xposed模塊開發(fā)入門與實(shí)踐技巧

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到新技能呢?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容