Xposed開發淺談探尋篇

2019-06-21 +8 47024人圍觀 ,發現 1 個不明物體 工具

前言

Xposed作為一個著名的Hook框架,早已經在移動安全行業家喻戶曉。今天寫這篇文章主要也是想下手玩玩這個框架,至于框架的安裝,雖然也會碰見很多問題,但是今天暫不附上教程,因為最近精力有限,等有時間我會附上安裝教程,以及遇見的問題解決方法!

知識前導

Hook技術

Hook英文翻譯為“鉤子”,而鉤子就是在事件傳送到終點前截獲并監控事件的傳輸,像個鉤子鉤上事件一樣,并且能夠在鉤上事件時,處理一些自己特定的事件;

Hook使它能夠將自己的代碼“融入”被勾住(Hook)的進程中,成為目標進程的一部分;

在Andorid沙箱機制下,Hook是我們能通過一個程序改變其他程序某些行為得以實現;

Hook原理

Hook技術本質是函數調用,由于處于Linux用戶狀態,每個進程有自己獨立的進程控件,所以必須先注入所要Hook的進程空間,修改其內存中進程代碼,替換過程表的符號地址,通過ptrace函數附加進程,向遠程進程注入so庫,從而達到監控以及遠程進程關鍵函數掛鉤;

Xposed原理分析

Xposed框架的原理是修改系統文件,替換了/system/bin/app_process可執行文件,在啟動Zygote時加載額外的jar文件(/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar),并執行一些初始化操作(執行XposedBridge的main方法)。然后我們就可以在這個Zygote上下文中進行某些hook操作。

在Android中,zygote是整個系統創建新進程的核心進程。zygote進程在內部會先啟動Dalvik虛擬機,繼而加載一些必要的系統資源和系統類,最后進入一種監聽狀態。

在之后的運作中,當其他系統模塊(比如AMS)希望創建新進程時,只需向zygote進程發出請求,zygote進程監聽到該請求后,會相應地fork出新的進程,于是這個新進程在初生之時,就先天具有了自己的Dalvik虛擬機以及系統資源。

zygote進程是由init進程啟動起來,由init.rc腳本中關于zygote的描述可知:zygote對應的可執行文件就是/system/bin/app_process,也就是說系統啟動時會執行到這個可執行文件的main()函數里。

正文

Xposed的技術實現是通過系統進程Hook技術,所以必須要有root權限才行。一般通過刷機來實現,這里我找了個兼容模擬器的Xposed來操作。這里我通過三個簡單的Hook插件開發,來介紹下Xposed插件開發的基本方法。當然我會附上完整源碼來供大家學習研究。接下來,讓我們開啟上帝模式!

Hook裝載的apk程序包名

新建一個入口類并繼承并實現IXposedHookLoadPackage接口

如下操作,我們新建了一個Hook的類,并實現IXposedHookLoadPackage接口中的handleLoadPackage方法,然后對當前裝載的apk程序包名進行打印。

package com.example.test;?import de.robv.android.xposed.IXposedHookLoadPackage;import de.robv.android.xposed.XposedBridge;import de.robv.android.xposed.callbacks.XC_LoadPackage;/** * Created by Ethan on 2018/11/18\. */public class  Hook implements IXposedHookLoadPackage{    @Override    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {        // 打印裝載的apk程序包名        XposedBridge.log("Launch app: " + loadPackageParam.packageName);        XposedBridge.log("Hook已經成功了");    }}

這是一個最簡單的一個Xposed插件,實現的功能就是對當前安卓虛擬機裝載的apk程序的報名進行Hook,并且打印出來。1560071502_5cfccd4e2cb6b.png!small

按鈕劫持Hook

首先自己寫一個粗糙的apk,實現的功能就是點擊界面的按鈕,就會彈出消息你未被劫持的消息!具體完整代碼如下:

MainActivity:

package com.example.ceshi;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.Toast;?public class MainActivity extends AppCompatActivity {    private Button button;?    public 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(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show();            }        });    }?    public String toastMessage() {        return "我未被劫持";    }}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.ceshi.MainActivity">?    <Button        android:id="@+id/button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Button" />?</android.support.constraint.ConstraintLayout>

實現功能如圖

1560071514_5cfccd5a85f29.png!small

接下來我們要對這個apk的按鈕的方法進行Hook,并且修改方法。

package com.example.xposed;import de.robv.android.xposed.IXposedHookLoadPackage;import de.robv.android.xposed.XC_MethodHook;import de.robv.android.xposed.XposedHelpers;import de.robv.android.xposed.callbacks.XC_LoadPackage;?public class HookToast implements IXposedHookLoadPackage {?//Module繼承了IXposedHookLoadPackage接口,當系統加載應用包的時候回回調 handleLoadPackage;    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {        //過濾包名,定位要Hook的包名        if (loadPackageParam.packageName.equals("com.example.ceshi")) {?            //定位要Hook的具體的類名            Class clazz = loadPackageParam.classLoader.loadClass("com.example.ceshi.MainActivity");            //Hook的方法為toastMessage,XposedHelpers的靜態方法 findAndHookMethod就是hook函數的的方法,其參數對應為   類名+loadPackageParam.classLoader(照寫)+方法名+參數類型(根據所hook方法的參數的類型,即有多少個寫多少個,加上.class)+XC_MethodHook回調接口;            XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() {                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                    super.beforeHookedMethod(param);                }?                protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {                    //param.setResult("你已被劫持")將返回的結果設置成了你已被劫持                    param.setResult("你已被劫持");                }            });        }    }}

經過以上操作,當apk執行時會調用我們的Hook類,然后執行就會對apk對應的進程方法進行修改,達到Hook的效果。

Hook后效果如圖:

1560071524_5cfccd6416f7e.png!small

登陸劫持

上面進行的按鈕劫持的效果也許還不夠明顯或者說是有趣。大家可能跟我一樣喜歡登陸劫持密碼這樣的操作,首先我們還是自己寫一個簡單的登陸程序。

MainActivity:

package com.example.xposedtest;?import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;?public class MainActivity extends AppCompatActivity {    EditText Name;   //定義Plain Test控件第一個輸入框的名字    EditText Pass;   //定義Plain Test控件第二個輸入框的名字?    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Name = (EditText) findViewById(R.id.TEXT_NAME); //通過findViewById找到輸入框控件對應的id并給它起一個名字        Pass = (EditText) findViewById(R.id.TEST_PASS);//通過findViewById找到輸入框控件對應的id并給它起一個名字        Button Login = (Button) findViewById(R.id.BTN_Login);//通過findViewById找到按鈕控件對應的id并給它起一個名字        Login.setOnClickListener(new View.OnClickListener() {  //監聽有沒有點擊按鈕控件 如果點擊了就會執行onClick函數            @Override            public void onClick(View view) {                check(Name.getText().toString().trim(),Pass.getText().toString().trim()); //調用check函數            }        });    }    public void check(String name,String pass)   //自定義函數check 這里用來檢查用戶名和密碼是否是cck和1234    {        if(name.equals("cck")&&pass.equals("1234"))        {            Toast.makeText(MainActivity.this,"登錄成功", Toast.LENGTH_SHORT).show();//彈框        }        else            Toast.makeText(MainActivity.this,"登錄失敗", Toast.LENGTH_SHORT).show();//彈框    }}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"    tools:context="com.example.xposedtest.MainActivity">?    <TextView        android:text="用戶名:"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true"        android:layout_marginLeft="63dp"        android:layout_marginStart="63dp"        android:layout_marginTop="77dp"        android:id="@+id/textView"/>?    <TextView        android:text="密碼:"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="77dp"        android:id="@+id/textView2"        android:layout_below="@+id/textView"        android:layout_alignLeft="@+id/textView"        android:layout_alignStart="@+id/textView"/>?    <EditText        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:inputType="textPersonName"        android:ems="10"        android:layout_alignBottom="@+id/textView"        android:layout_toRightOf="@+id/textView"        android:layout_toEndOf="@+id/textView"        android:id="@+id/TEXT_NAME"/>?    <EditText        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:inputType="textPassword"        android:ems="10"        android:layout_alignBottom="@+id/textView2"        android:layout_toRightOf="@+id/textView2"        android:layout_toEndOf="@+id/textView2"        android:id="@+id/TEST_PASS"/>?    <Button        android:text="登錄"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@+id/TEST_PASS"        android:layout_toRightOf="@+id/textView"        android:layout_toEndOf="@+id/textView"        android:layout_marginLeft="13dp"        android:layout_marginStart="13dp"        android:layout_marginTop="130dp"        android:id="@+id/BTN_Login"/>?</RelativeLayout>

實現效果如圖:

1560071535_5cfccd6fab716.png!small

1560071543_5cfccd77f0390.png!small

只有當我們輸入正確的用戶名cck和密碼1234時才會彈出登陸成功的消息。

這里的我們要Hook的效果為不管輸入什么,都會顯示登陸成功,實現的手段就是Hook對應的方法,并對相應的參數進行修改,還是使用上面的回調方法來實現

package com.example.xposeddeluhook;?import de.robv.android.xposed.IXposedHookLoadPackage;import de.robv.android.xposed.XC_MethodHook;??import de.robv.android.xposed.XposedBridge;import de.robv.android.xposed.callbacks.XC_LoadPackage;?import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;public class Hookdenglu implements IXposedHookLoadPackage {?    /**     * 包加載時候的回調     */    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {?        // 將包名不是 com.example.xposedtest 的應用剔除掉,可以減少管理的類        if (!lpparam.packageName.equals("com.example.xposedtest"))            return;        XposedBridge.log("Loaded app: " + lpparam.packageName);?        //第一個參數是className,表示被注入的方法所在的類        //第二個參數是類加載器,照抄就行        //第三個參數是被注入的方法名        //第四五個參數是第三個參數的兩個形參的類型        //最后一個參數是匿名內部類        findAndHookMethod("com.example.xposedtest.MainActivity", lpparam.classLoader, "check", String.class,                String.class, new XC_MethodHook() {?            /**             * 該方法在check方法調用之前被調用,我們輸出一些日志,并且捕獲參數的值。             * 最后兩行的目的是改變參數的值。也就是說無論參數是什么值,都會被替換為name為cck,pass為1234             * @param param             * @throws Throwable             */                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                        XposedBridge.log("開始劫持了~");                        XposedBridge.log("參數1 = " + param.args[0]);                        XposedBridge.log("參數2 = " + param.args[1]);                        param.args[0] = "cck";                        param.args[1] = "1234";                    }             /**             * 該方法在check方法調用之后被調用             * @param param             * @throws Throwable             */?                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {                        XposedBridge.log("劫持結束了~");                        XposedBridge.log("參數1 = " + param.args[0]);                        XposedBridge.log("參數2 = " + param.args[1]);?                    }                });    }?}

上述代碼我們通過對方法的參數進行了重賦值,來達到了我們想要的結果!

實現效果如下:

1560071552_5cfccd8046b44.png!small

Xposed日志如下:

1560071561_5cfccd8905917.png!small

Think one Think

Xposed可以在不修改APK源碼的情況下,通過自己編寫的模塊來影響程序運行的框架服務,采用了插件機制,通過替換/system/bin/app_process程序控制zygote進程,使得app_process在啟動過程中會加載XposedBridge.jar這個jar包,從而完成對Zygote進程及其創建的Dalvik虛擬機的劫持,從而可以使我們開啟上帝模式,從原理上講,只要你對要操作的方法,參數的個數,類型了如指掌,那你就可以實現任意應用的任意方法的Hook。這也是Xposed插件開發的初衷!

*本文作者:cck,轉載請注明來自FreeBuf.COM

這些評論亮了

發表評論

已有 1 條評論

取消
Loading...

特別推薦

推薦關注

填寫個人信息

姓名
電話
郵箱
公司
行業
職位
css.php 重庆百变王牌走势图