<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

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

                    原文:http://d3adend.org/blog/?p=589

                    0x00 前言


                    一個最近關于檢測native hook框架的方法讓我開始思考一個Android應用如何在Java層檢測Cydia Substrate或者Xposed框架。

                    聲明:

                    下文所有的anti-hooking技巧很容易就可以被有經驗的逆向人員繞過,這里只是展示幾個檢測的方法。在最近DexGuard和GuardIT等工具中還沒有這類anti-hooking檢測功能,不過我相信不久就會增加這個功能。

                    0x01 檢測安裝的應用


                    一個最直接的想法就是檢測設備上有沒有安裝Substrate或者Xposed框架,可以直接調用PackageManager顯示所有安裝的應用,然后看是否安裝了Substrate或者Xposed。

                    #!java
                    PackageManager packageManager = context.getPackageManager();
                    List applicationInfoList  = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
                    
                    for(ApplicationInfo applicationInfo : applicationInfoList) {
                        if(applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
                            Log.wtf("HookDetection", "Xposed found on the system.");
                        }
                        if(applicationInfo.packageName.equals("com.saurik.substrate")) {
                            Log.wtf("HookDetection", "Substrate found on the system.");
                        }
                    }       
                    

                    0x02 檢查調用棧里的可疑方法


                    另一個想到的方法是檢查Java調用棧里的可疑方法,主動拋出一個異常,然后打印方法的調用棧。代碼如下:

                    #!java
                    public class DoStuff {
                        public static String getSecret() {
                            try {
                                throw new Exception("blah");
                            }
                            catch(Exception e) {
                                for(StackTraceElement stackTraceElement : e.getStackTrace()) {
                                    Log.wtf("HookDetection", stackTraceElement.getClassName() + "->" + stackTraceElement.getMethodName());
                                }
                            }
                            return "ChangeMePls!!!";
                        }
                    }
                    

                    當應用沒有被hook的時候,正常的調用棧是這樣的:

                    #!bash
                    com.example.hookdetection.DoStuff->getSecret
                    com.example.hookdetection.MainActivity->onCreate
                    android.app.Activity->performCreate
                    android.app.Instrumentation->callActivityOnCreate
                    android.app.ActivityThread->performLaunchActivity
                    android.app.ActivityThread->handleLaunchActivity
                    android.app.ActivityThread->access$800
                    android.app.ActivityThread$H->handleMessage
                    android.os.Handler->dispatchMessage
                    android.os.Looper->loop
                    android.app.ActivityThread->main
                    java.lang.reflect.Method->invokeNative
                    java.lang.reflect.Method->invoke
                    com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run
                    com.android.internal.os.ZygoteInit->main
                    dalvik.system.NativeStart->main
                    

                    但是假如有Xposed框架hook了com.example.hookdetection.DoStuff.getSecret方法,那么調用棧會有2個變化:

                    所以如果hook了getSecret方法,調用棧就會如下:

                    #!bash
                    com.example.hookdetection.DoStuff->getSecret
                    
                    de.robv.android.xposed.XposedBridge->invokeOriginalMethodNative
                    de.robv.android.xposed.XposedBridge->handleHookedMethod
                    
                    com.example.hookdetection.DoStuff->getSecret
                    com.example.hookdetection.MainActivity->onCreate
                    android.app.Activity->performCreate
                    android.app.Instrumentation->callActivityOnCreate
                    android.app.ActivityThread->performLaunchActivity
                    android.app.ActivityThread->handleLaunchActivity
                    android.app.ActivityThread->access$800
                    android.app.ActivityThread$H->handleMessage
                    android.os.Handler->dispatchMessage
                    android.os.Looper->loop
                    android.app.ActivityThread->main
                    java.lang.reflect.Method->invokeNative
                    java.lang.reflect.Method->invoke
                    com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run
                    com.android.internal.os.ZygoteInit->main
                    
                    de.robv.android.xposed.XposedBridge->main
                    
                    dalvik.system.NativeStart->main
                    

                    下面看下Substrate hook com.example.hookdetection.DoStuff.getSecret方法后,調用棧會有什么變化:

                    所以如果hook了getSecret方法,調用棧就會如下:

                    #!bash
                    com.example.hookdetection.DoStuff->getSecret
                    
                    com.saurik.substrate._MS$MethodPointer->invoke
                    com.saurik.substrate.MS$MethodPointer->invoke
                    com.cigital.freak.Freak$1$1->invoked
                    com.saurik.substrate.MS$2->invoked
                    
                    com.example.hookdetection.DoStuff->getSecret
                    com.example.hookdetection.MainActivity->onCreate
                    android.app.Activity->performCreate
                    android.app.Instrumentation->callActivityOnCreate
                    android.app.ActivityThread->performLaunchActivity
                    android.app.ActivityThread->handleLaunchActivity
                    android.app.ActivityThread->access$800
                    android.app.ActivityThread$H->handleMessage
                    android.os.Handler->dispatchMessage
                    android.os.Looper->loop
                    android.app.ActivityThread->main
                    java.lang.reflect.Method->invokeNative
                    java.lang.reflect.Method->invoke
                    com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run
                    com.android.internal.os.ZygoteInit->main
                    
                    com.android.internal.os.ZygoteInit->main
                    
                    dalvik.system.NativeStart->main
                    

                    在知道了調用棧的變化之后,就可以在Java層寫代碼進行檢測:

                    #!java
                    try {
                        throw new Exception("blah");
                    }
                    catch(Exception e) {
                        int zygoteInitCallCount = 0;
                        for(StackTraceElement stackTraceElement : e.getStackTrace()) {
                            if(stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
                                zygoteInitCallCount++;
                                if(zygoteInitCallCount == 2) {
                                    Log.wtf("HookDetection", "Substrate is active on the device.");
                                }
                            }
                            if(stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") && 
                                    stackTraceElement.getMethodName().equals("invoked")) {
                                Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate.");
                            }
                            if(stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 
                                    stackTraceElement.getMethodName().equals("main")) {
                                Log.wtf("HookDetection", "Xposed is active on the device.");
                            }
                            if(stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 
                                    stackTraceElement.getMethodName().equals("handleHookedMethod")) {
                                Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed.");
                            }
                    
                        }
                    }
                    

                    0x03 檢測并不應該native的native方法


                    Xposed框架會把hook的Java方法類型改為"native",然后把原來的方法替換成自己的代碼(調用hookedMethodCallback)。可以查看XposedBridge_hookMethodNative的實現,是修改后app_process里的方法。

                    利用Xposed改變hook方法的這個特性(Substrate也使用類似的原理),就可以用來檢測是否被hook了。注意這不能用來檢測ART運行時的Xposed,因為沒必要把方法的類型改為native。

                    假設有下面這個方法:

                    #!java
                    public class DoStuff {
                        public static String getSecret() {
                            return "ChangeMePls!!!";
                        }
                    }
                    

                    如果getSecret方法被hook了,在運行的時候就會像下面的定義:

                    #!java
                    public class DoStuff {
                            // calls hookedMethodCallback if hooked using Xposed
                        public native static String getSecret(); 
                    }
                    

                    基于上面的原理,檢測的步驟如下:

                    下面的Java展示了這個技巧。這里假設了應用本身沒有通過JNI調用本地代碼,大多數應用都不需要調用本地方法。不過如果有JNI調用的話,只需要把這些native方法添加到一個白名單中即可。理論上這個方法也可以用于檢測Java庫或者第三方庫,不過需要把第三方庫的native方法添加到一個白名單。檢測代碼如下:

                    #!java
                    for (ApplicationInfo applicationInfo : applicationInfoList) {
                        if (applicationInfo.processName.equals("com.example.hookdetection")) {      
                            Set classes = new HashSet();
                            DexFile dex;
                            try {
                                dex = new DexFile(applicationInfo.sourceDir);
                                Enumeration entries = dex.entries();
                                while(entries.hasMoreElements()) {
                                    String entry = entries.nextElement();
                                    classes.add(entry);
                                }
                                dex.close();
                            } 
                            catch (IOException e) {
                                Log.e("HookDetection", e.toString());
                            }
                            for(String className : classes) {
                                if(className.startsWith("com.example.hookdetection")) {
                                    try {
                                        Class clazz = HookDetection.class.forName(className);
                                        for(Method method : clazz.getDeclaredMethods()) {
                                            if(Modifier.isNative(method.getModifiers())){
                                                Log.wtf("HookDetection", "Native function found (could be hooked by Substrate or Xposed): " + clazz.getCanonicalName() + "->" + method.getName());
                                            }
                                        }
                                    }
                                    catch(ClassNotFoundException e) {
                                        Log.wtf("HookDetection", e.toString());
                                    }
                                }
                            }
                        }
                    }
                    

                    0x04 通過/proc/[pid]/maps檢測可疑的共享對象或者JAR


                    /proc/[pid]/maps記錄了內存映射的區域和訪問權限,首先查看Android應用的映像,第一列是起始地址和結束地址,第六列是映射文件的路徑。

                    #!bash
                    #cat /proc/5584/maps
                    
                    40027000-4002c000 r-xp 00000000 103:06 2114      /system/bin/app_process
                    4002c000-4002d000 r--p 00004000 103:06 2114      /system/bin/app_process
                    4002d000-4002e000 rw-p 00005000 103:06 2114      /system/bin/app_process
                    4002e000-4003d000 r-xp 00000000 103:06 246       /system/bin/linker
                    4003d000-4003e000 r--p 0000e000 103:06 246       /system/bin/linker
                    4003e000-4003f000 rw-p 0000f000 103:06 246       /system/bin/linker
                    4003f000-40042000 rw-p 00000000 00:00 0 
                    40042000-40043000 r--p 00000000 00:00 0 
                    40043000-40044000 rw-p 00000000 00:00 0 
                    40044000-40047000 r-xp 00000000 103:06 1176      /system/lib/libNimsWrap.so
                    40047000-40048000 r--p 00002000 103:06 1176      /system/lib/libNimsWrap.so
                    40048000-40049000 rw-p 00003000 103:06 1176      /system/lib/libNimsWrap.so
                    40049000-40091000 r-xp 00000000 103:06 1237      /system/lib/libc.so
                    ... Lots of other memory regions here ...
                    

                    因此可以寫代碼檢測加載到當前內存區域中的可疑文件:

                    #!java
                    try {
                        Set libraries = new HashSet();
                        String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
                        BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
                        String line;
                        while((line = reader.readLine()) != null) {
                            if (line.endsWith(".so") || line.endsWith(".jar")) {
                                int n = line.lastIndexOf(" ");
                                libraries.add(line.substring(n + 1));
                            }
                        }
                        for (String library : libraries) {
                            if(library.contains("com.saurik.substrate")) {
                                Log.wtf("HookDetection", "Substrate shared object found: " + library);
                            }
                            if(library.contains("XposedBridge.jar")) {
                                Log.wtf("HookDetection", "Xposed JAR found: " + library);
                            }
                        }
                        reader.close();
                    }
                    catch (Exception e) {
                        Log.wtf("HookDetection", e.toString());
                    }
                    

                    Substrate會用到幾個so:

                    #!bash
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libAndroidBootstrap0.so
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libAndroidCydia.cy.so
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libDalvikLoader.cy.so
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libsubstrate.so
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libsubstrate-dvm.so
                    Substrate shared object found: /data/app-lib/com.saurik.substrate-1/libAndroidLoader.so
                    

                    Xposed會用到一個Jar:

                    #!bash
                    Xposed JAR found: /data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar
                    

                    0x05 繞過檢測的方法


                    上面討論了幾個anti-hooking的方法,不過相信也會有人提出繞過的方法,這里對應每個檢測方法如下:

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频