<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/7488

            官方教程:https://github.com/rovo89/XposedBridge/wiki/Development-tutorial

            官網:http://repo.xposed.info/module/de.robv.android.xposed.installer

            apk:http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk

            源碼:https://github.com/rovo89/XposedInstaller

            模塊基本開發流程


            1.創建工程android4.0.3(api15,測試發現其他版本也可以),可以不用activity

            2.修改AndroidManifest.xml

            <?xml version="1.0" encoding="utf-8"?>
            <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                package="de.robv.android.xposed.mods.tutorial"
                android:versionCode="1"
                android:versionName="1.0" >
                <uses-sdk android:minSdkVersion="15" />
                <application
                    android:icon="@drawable/ic_launcher"
                    android:label="@string/app_name" >
                    <meta-data
                        android:name="xposedmodule"
                        android:value="true" />
                    <meta-data
                        android:name="xposeddescription"
                        android:value="Easy example" />
                    <meta-data
                        android:name="xposedminversion"
                        android:value="54" />
                </application>
            </manifest>
            

            3.在工程目錄下新建一個lib文件夾,將下載好的XposedBridgeApi-54.jar包放入其中.

            eclipse 在工程里 選中XposedBridgeApi-54.jar 右鍵–Build Path–Add to Build Path.

            IDEA 鼠標右鍵點擊工程,選擇Open Module Settings,在彈出的窗口中打開Dependencies選項卡.把XposedBridgeApi這個jar包后面的Scope屬性改成provided.

            4.模塊實現接口

            #!java
            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;
            
            public class Tutorial implements IXposedHookLoadPackage {
                public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
                    XposedBridge.log("Loaded app: " + lpparam.packageName);
                }
            }
            

            5.入口assets/xposed_init配置,聲明需要加載到 XposedInstaller 的入口類:

            #!java
            de.robv.android.xposed.mods.tutorial.Tutorial  //完整類名:包名+類名
            

            6.定位要hook的api

            7.XposedBridge to hook it

            示例如下:

            #!java
            package de.robv.android.xposed.mods.tutorial;
            
            import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC\_MethodHook; import de.robv.android.xposed.callbacks.XC\_LoadPackage.LoadPackageParam;
            
            public class Tutorial implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { if (!lpparam.packageName.equals("com.android.systemui")) return;
            
                    findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            // this will be called before the clock was updated by the original method
                        }
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            // this will be called after the clock was updated by the original method
                        }
                });
                }
            
            
            }
            

            重寫XC_MethodHook的兩個方法beforeHookedMethod和afterHookedMethod,這兩個方法會在原始的方法的之前和之后執行.您可以使用beforeHookedMethod 方法來打印/篡改方法調用的參數(通過param.args) ,甚至阻止調用原來的方法(發送自己的結果).afterHookedMethod 方法可以用來做基于原始方法的結果的事情.您還可以用它來操縱結果 .當然,你可以添加自己的代碼,它將會準確地在原始方法的前或后執行.

            關鍵API

            IXposedHookLoadPackage

            handleLoadPackage : 這個方法用于在加載應用程序的包的時候執行用戶的操作

            調用示例

            #!java
            public class XposedInterface implements IXposedHookLoadPackage {
            public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
            XposedBridge.log("Kevin-Loaded app: " + lpparam.packageName); }
            }
            

            參數說明|final LoadPackageParam lpparam 這個參數包含了加載的應用程序的一些基本信息。

            XposedHelpers

            findAndHookMethod ;這是一個輔助方法,可以通過如下方式靜態導入:

            #!java
            import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
            

            使用示例

            #!java
            findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "handleUpdateClock", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            // this will be called before the clock was updated by the original method }
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            // this will be called after the clock was updated by the original method }
            });
            

            參數說明

            findAndHookMethod(Class<?>clazz, //需要Hook的類名
            ClassLoader, //類加載器,可以設置為 null 
            String methodName, //需要 Hook 的方法名 
            Object... parameterTypesAndCallback
            

            該函數的最后一個參數集,包含了:

            (1)Hook 的目標方法的參數,譬如:

            "com.android.internal.policy.impl.PhoneWindow.DecorView"
            

            是方法的參數的類。

            (2)回調方法:

            a.XC_MethodHook 
            b.XC_MethodReplacement
            

            模塊開發中的一些細節


            1.Dalvik 孵化器 Zygote (Android系統中,所有的應用程序進程以及系統服務進程SystemServer都是由Zygote進程孕育/fork出來的)進程對應的程序是/system/bin/app_process. Xposed 框架中真正起作用的是對方法的 hook。

            #!java
            
            因為 Xposed 工作原理是在/system/bin 目錄下替換文件,在 install 的時候需要 root 權限,但是運行時不需要 root 權限。
            

            2.log 統一管理,tag 顯示包名

            #!java
            
                Log.d(MYTAG+lpparam.packageName, "hello" + lpparam.packageName);
            

            3.植入廣播接收器,動態執行指令

            #!java
            
                    findAndHookMethod("android.app.Application", lpparam.classLoader, "onCreate", new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            
                            Context context = (Context) param.thisObject;
                            IntentFilter filter = new IntentFilter(myCast.myAction);
                            filter.addAction(myCast.myCmd);
                            context.registerReceiver(new myCast(), filter);
            
                        }
            
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                        }
                    });
            

            4.context 獲取

            #!java
            
                fristApplication = (Application) param.thisObject;
            

            5.注入點選擇 application oncreate 程序真正啟動函數而是 MainActivity 的 onCreate (該類有可能被重寫,所以通過反射得到 oncreate 方法)

            #!java
            
                String appClassName = this.getAppInfo().className;
                        if (appClassName == null) {
                            Method hookOncreateMethod = null;
                            try {
                                hookOncreateMethod = Application.class.getDeclaredMethod("onCreate", new Class[] {});
                            } catch (NoSuchMethodException e) {
                                e.printStackTrace();
                            }
                            hookhelper.hookMethod(hookOncreateMethod, new ApplicationOnCreateHook());
            

            6.排除系統 app,排除自身,確定主線程

            #!java
            
                if(lpparam.appInfo == null || 
                                (lpparam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) !=0){
                            return;
                        }else if(lpparam.isFirstApplication && !ZJDROID_PACKAGENAME.equals(lpparam.packageName)){
            

            7.hook method

            #!java
            
                Only methods and constructors can be hooked,Cannot hook interfaces,Cannot hook abstract methods
                只能 hook 方法和構造方法,不能 hook 接口和抽象方法
                抽象類中的非抽象方法是可以 hook的, 接口中的方法不能 hook (接口中的method默認是public abstract 抽象的.field 必須是public static final)
            

            8.參數中有 自定義類

            #!java
            
                public void myMethod (String a, MyClass b) 
            
            
            通過反射得到自定義類,也可以用[xposedhelpers](https://github.com/rovo89/XposedBridge/wiki/Helpers#class-xposedhelpers)封裝好的方法findMethod/findConstructor/callStaticMethod....
            

            9.注入后反射自定義類

            #!java
            
                Class<?> hookMessageListenerClass = null;
            
                hookMessageListenerClass = lpparam.classLoader.loadClass("org.jivesoftware.smack.MessageListener");
            
                findAndHookMethod("org.jivesoftware.smack.ChatManager", lpparam.classLoader, "createChat", String.class , hookMessageListenerClass ,new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            
                        String sendTo = (String) param.args[0];
                        Log.i(tag , "sendTo : + " + sendTo );
            
                    }
            
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        super.afterHookedMethod(param);
                    }
                });
            

            10.hook 一個類的方法,該類是子類并且沒有重寫父類的方法,此時應該 hook 父類還是子類.(hook 父類方法后,子類若沒重寫,一樣生效.子類重寫方法需要另外 hook) (如果子類重寫父類方法時候加上 spuer ,hook 父類依舊有效)

            例如 java.net.HttpURLConnection extends URLConnection ,
            

            方法在父類

            #!java
            
                public OutputStream getOutputStream() throws IOException {
                       throw new UnknownServiceException("protocol doesn't support output");
                   }
            
            
            org.apache.http.impl.client.AbstractHttpClient extends CloseableHttpClient ,
            

            方法在父類(注意,android的繼承的 AbstractHttpClient implements org.apache.http.client.HttpClient)

            #!java
            
                public CloseableHttpResponse execute(
                        final HttpHost target,
                        final HttpRequest request,
                        final HttpContext context) throws IOException, ClientProtocolException {
                    return doExecute(target, request, context);
                }
            
            
            android.async.http復寫HttpGet導致zjdroid hook org.apache.http.impl.client.AbstractHttpClient execute 無法獲取到請求 url和method
            

            11.hook 構造方法

            #!java
            
                public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {
                    return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback);
                }
            

            12.承接4,application 的onCreate 方法被重寫,例如阿里的殼,重寫為原生 native 方法.

            解1:通過反射到 application 類重寫后的 onCreate 方法再對該方法進行hook

            解2:hook 構造方法(構造方法被重寫,繼續解1)

            13.native 方法可以 hook,不過是在 java 層調用時hook而不是 hook 動態鏈接庫.

            實戰Hook android 中可能的出現 HTTP 請求


            首先確定http 請求的 api,大致分為:

            不太了解 java 的 hook 前可以先看下基礎的代碼HttpClient以及HttpURLConnection的基本使用

            對 HttpClient的 hook 可以參考 賈志軍大牛的Zjdroid

            #!java
                Method executeRequest = RefInvoke.findMethodExact("org.apache.http.impl.client.AbstractHttpClient", ClassLoader.getSystemClassLoader(),
                        "execute", HttpHost.class, HttpRequest.class, HttpContext.class);
            
                hookhelper.hookMethod(executeRequest, new AbstractBahaviorHookCallBack() {
                    @Override
                    public void descParam(HookParam param) {
                        // TODO Auto-generated method stub
                        Logger.log_behavior("Apache Connect to URL ->");
                        HttpHost host = (HttpHost) param.args[0];
            
                        HttpRequest request = (HttpRequest) param.args[1];
                        if (request instanceof org.apache.http.client.methods.HttpGet) {
                            org.apache.http.client.methods.HttpGet httpGet = (org.apache.http.client.methods.HttpGet) request;
                            Logger.log_behavior("HTTP Method : " + httpGet.getMethod());
                            Logger.log_behavior("HTTP GET URL : " + httpGet.getURI().toString());
                            Header[] headers = request.getAllHeaders();
                            if (headers != null) {
                                for (int i = 0; i < headers.length; i++) {
                                    Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
                                }
                            }
                        } else if (request instanceof HttpPost) {
                            HttpPost httpPost = (HttpPost) request;
                            Logger.log_behavior("HTTP Method : " + httpPost.getMethod());
                            Logger.log_behavior("HTTP URL : " + httpPost.getURI().toString());
                            Header[] headers = request.getAllHeaders();
                            if (headers != null) {
                                for (int i = 0; i < headers.length; i++) {
                                    Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
                                }
                            }
                            HttpEntity entity = httpPost.getEntity();
                            String contentType = null;
                            if (entity.getContentType() != null) {
                                contentType = entity.getContentType().getValue();
                                if (URLEncodedUtils.CONTENT_TYPE.equals(contentType)) {
            
                                    try {
                                        byte[] data = new byte[(int) entity.getContentLength()];
                                        entity.getContent().read(data);
                                        String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
                                        Logger.log_behavior("HTTP POST Content : " + content);
                                    } catch (IllegalStateException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    } catch (IOException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }
            
                                } else if (contentType.startsWith(HTTP.DEFAULT_CONTENT_TYPE)) {
                                    try {
                                        byte[] data = new byte[(int) entity.getContentLength()];
                                        entity.getContent().read(data);
                                        String content = new String(data, contentType.substring(contentType.lastIndexOf("=") + 1));
                                        Logger.log_behavior("HTTP POST Content : " + content);
                                    } catch (IllegalStateException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    } catch (IOException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }
            
                                }
                            }else{
                                byte[] data = new byte[(int) entity.getContentLength()];
                                try {
                                    entity.getContent().read(data);
                                    String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
                                    Logger.log_behavior("HTTP POST Content : " + content);
                                } catch (IllegalStateException e) {
                                    // TODO Auto-generated catch block
                                    e.printStackTrace();
                                } catch (IOException e) {
                                    // TODO Auto-generated catch block
                                    e.printStackTrace();
                                }
            
                            }
            
                        }
                    }
            
                    @Override
                    public void afterHookedMethod(HookParam param) {
                        // TODO Auto-generated method stub
                        super.afterHookedMethod(param);
                        HttpResponse resp = (HttpResponse) param.getResult();
                        if (resp != null) {
                            Logger.log_behavior("Status Code = " + resp.getStatusLine().getStatusCode());
                            Header[] headers = resp.getAllHeaders();
                            if (headers != null) {
                                for (int i = 0; i < headers.length; i++) {
                                    Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
                                }
                            }
            
                        }
                    }
                });
            

            對 HttpURLConnection 的 hook Zjdroid 未能提供完美的解決方案,想要取得除了 URL 之外的 data 字段必須對I/O流操作.

            #!java
                Method openConnectionMethod = RefInvoke.findMethodExact("java.net.URL", ClassLoader.getSystemClassLoader(), "openConnection");
                hookhelper.hookMethod(openConnectionMethod, new AbstractBahaviorHookCallBack() {
                    @Override
                    public void descParam(HookParam param) {
                        // TODO Auto-generated method stub
                        URL url = (URL) param.thisObject;
                        Logger.log_behavior("Connect to URL ->");
                        Logger.log_behavior("The URL = " + url.toString());
                    }
                });
            

            我采取的臨時解決方法是對I/O 進行正則匹配,類似 url 的 data 字段就打印出來,代碼如下(這段代碼只能解決前文 HttpUtils而且會有誤報,大家有啥好想法歡迎指點一二)

            #!java
                findAndHookMethod("java.io.PrintWriter", lpparam.classLoader, "print",String.class, new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        String print = (String) param.args[0];
                        Pattern pattern = Pattern.compile("(\\w+=.*)");
                        Matcher matcher = pattern.matcher(print);
                        if (matcher.matches())
                            Log.i(tag+lpparam.packageName,"data : " + print);
                        //Log.d(tag,"A :" + print);
                    }
            
                });
            

            因為Android-async-http重寫了 HttpGet 導致 Zjdroidhook 失敗(未進入 HttpGet 和 HttpPost 的判讀),加入一個else 語句就可以解決這個問題

            #!java
            else {
                                HttpEntityEnclosingRequestBase httpGet = (HttpEntityEnclosingRequestBase) request;
                                HttpEntity entity = httpGet.getEntity();
                                Logger.log_behavior("HttpRequestBase URL : " + httpGet.getURI().toString());
                                Header[] headers = request.getAllHeaders();
                                if (headers != null) {
                                    for (int i = 0; i < headers.length; i++) {
                                        Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
                                    }
                                }
            
                                if(entity!= null){
                                            try {
                                                String content = EntityUtils
                                                        .toString(entity);
                                                Logger.log_behavior("HTTP entity Content : "
                                                        + content);
                                            } catch (IllegalStateException e) {
                                                // TODO Auto-generated catch block
                                                e.printStackTrace();
                                            } catch (IOException e) {
                                                // TODO Auto-generated catch block
                                                e.printStackTrace();
                                            }
                                }
            
                                }
            

            一些常用工具


            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线