Android安全指南 之 Xposed Hook实战

0.jpg

本篇博文主要是介绍如何使用Xposed 框架Hook应用,使用的案例是OWASP提供的UNCRACKABLE1.apk,目的是经过Xposed Hook 获取到应用内部的flag字符串。java

1.png

咱们发现UNCRACKABLE1这个应用在启动时会检测手机是否被root,当探知到手机出于root状态时会强制中止运行。那么首先使用JEB来分析如下示例的代码逻辑。android


1、JEB 分析apkapi

使用JEB打开apk文件,因为root检测是在启动的时候进行的,所以咱们先来看 onCreate方法:安全

2.png

很明显MainActivity中的a方法执行了对root的检测,同时也对调试环境进行检测,看看a方法的实现:网络

3.png

知道了检测在哪一部分,接下来就去找哪里进行了输入字符串的检测。看到MainActivity下的verify方法:架构

4.png

verify方法经过a.a()方法返回的结果判断输入字符串的正确性,这就是咱们的目标:app

5.png

分析a.a()的代码逻辑,咱们可知变量v1中保存的字符串是flag字符串加密后的结果,arg5是咱们传入的字符串,并且咱们能看到方法并无对arg5进行任何的处理,由此可知校验的方式是对flag字符串解密后的明文对比,而flag明文就保存在v0_2变量中,解密的方法是sg.vantagepoint.a.a.a。框架


2、Xposed 模块的安装ide

Xposed框架的安装与运行是须要root权限的,前往Xposed官网下载安装包:http://xposed.appkg.com/nav。安装包下载完毕后放在设备的sdcard中,运行安装。安装完毕后打开Xposed installer:函数

6.png

设备或模拟器取得Root权限后,点击安装/更新,等待一段时间后设备会重启,显示如下界面则安装成功。

7.png


3、xposed程序的编写与运行

从本质上来说,Xposed 模块也是一个 Android 程序。但与普通程序不一样的是,想要让写出的Android程序成为一个Xposed 模块,须要完成几个工做:

  • 1) 让手机上的xposed框架知道咱们安装的这个程序是个xposed模块。

  • 2) 模块里要包含有xposed的API的jar包,以实现下一步的hook操做。

  • 3) 这个模块里面要有对目标程序进行hook操做的方法。

  • 4) 要让手机上的xposed框架知道,咱们编写的xposed模块中,哪个方法是实现hook操做的。

针对上面四个任务,咱们分别进行处理:

(1) 新建项目并编辑AndroidManifest.xml:

首先咱们须要建立一个Android项目,这个项目有没有Activity取决于须要,咱们这个Hook模块不须要Activity,因此建立一个无Activity项目。

8.png

建立完毕后咱们来修改AndroidManifest.xml文件,来讲明这个程序是一个xposed模块。插入如下代码:

<meta-data android:name="xposedmodule" android:value="true" /> 
<meta-data android:name="xposeddescription" android:value="xposed for CTF" /> 
<meta-data android:name="xposedminversion" android:value="53" />

9.png

插入以上代码后,Xposed框架就能将咱们这个android应用识别成一个Xposed模块了:

10.png

(2) 导入Xposed API

让Xposed框架识别出咱们编写的模块是第一步,要让这个模块具备Xposed的功能,咱们须要导入Xposed的API,也就是 XposedBridgeApi.jar 。在Android Studio3.0以上的版本中,只须要在build.gradle中进行配置,Android Studio就会去下载XposedBridgeApi.jar并构建到项目中去。

咱们找到在项目app目录下面的build.gradle文件,添加上两段代码:

repositories { 
    jcenter() 
} 
dependencies { 
    compileOnly 'de.robv.android.xposed:api:82' 
    compileOnly 'de.robv.android.xposed:api:82:sources' 
}

build.gradle文件被修改后,Android Studio会弹出提示,此时咱们点击sync now,完成同步。Android Studio会自行下载XposedBridgeApi.jar。若是因为网络问题XposedBridgeApi.jar没法下载,则须要从网上手动下载XposedBridgeApi.jar,放到项目的libs目录下,右键 Add As Library添加这个jar包。

(3) 实现Hook操做

导入Xposed API后咱们即可以编写hook代码了。在Hook模块的MainActivity的同一目录下新建了一个Hook类文件,Hook类实现了IXposedHookLoadPackage:

public class Hook implements IXposedHookLoadPackage { 
    @Override 
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable{ 
        ...... 
    } 
}

具体的模块编写咱们放在后面,下面接着看第四个须要完成的工做。

(4) 设置模块的入口点

咱们编写完模块后须要告诉Xposed框架那个类实现了Hook操做。在main文件夹下新建assets文件夹:

11.png

在assets文件夹下建立xposed_init,在文件内写上Hook类的完整包名:

12.png

以上四步完成后,一个Hook模块就编写完毕了,接下来选择 no Activity启动,而后运行,在Xposed框架中选中模块便可:

13.png


4、编写 xposed 模块

通过上面的分析,咱们知道须要完成两个工做才能拿到正确的密码。一是绕过root与debug检测,二是hook flag的解密函数。对于环境检测,咱们的思路是无论它检测的结果如何,只要不结束程序运行就能够,所以咱们能够Hook MainActivity内的a方法,替换它的实现,让它不执行system.exit(),下面是Xposed模块。

try{ 
    XposedBridge.log("Hook start"); 
    Class mainActivityClass = loadPackageParam.classLoader
         .loadClass("sg.vantagepoint.uncrackable1.MainActivity"); 
    XposedHelpers.findAndHookMethod(mainActivityClass, "a",java.lang.String.class, 
               new XC_MethodReplacement() { 
        @Override 
        protected Object replaceHookedMethod(XC_MethodReplacement 
               .MethodHookParam param) throws Throwable { 
               return null; 
         } 
    }); 
}catch(Throwable e){ 
    XposedBridge.log(e); 
}

当咱们要修改被Hook方法的逻辑时,经常使用replaceHookedMethod,方法体内部是咱们想要被Hook方法完成的工做,这里咱们直接让MainActivity.a(),什么都不作直接结束执行。跳过检测后咱们再来Hook解密方法sg.vantagepoint.a.a.a,截取它的返回值并经过log打印:

try{ 
    XposedBridge.log("Hook start"); 
    XposedHelpers.findAndHookMethod("sg.vantagepoint.a.a", loadPackageParam.classLoader, "a", byte[].class, byte [].class, 
new XC_MethodHook() { 
        @Override 
        protected void beforeHookedMethod(MethodHookParam param) 
throws Throwable {} 
        protected void afterHookedMethod(XC_MethodHook 
                .MethodHookParam methodHookParam) throws Throwable { 
                byte[] flag_byte = (byte[]) methodHookParam.getResult(); 
                String flag = new String(flag_byte); 
                XposedBridge.log("Flag: " + flag) 
        } 
    }); 
}catch(Throwable e){ 
    XposedBridge.log(e); 
}

完整的Hook类:

public class Hook implements IXposedHookLoadPackage { 
    @Override 
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) 
        throws Throwable{ 
        if(loadPackageParam.packageName.equals("owasp.mstg.uncrackable1")){ 
            try{ 
                XposedBridge.log("Hook start"); 
                Class mainActivityClass = loadPackageParam.classLoader 
                        .loadClass("sg.vantagepoint.uncrackable1.MainActivity"); 
                XposedHelpers.findAndHookMethod(mainActivityClass, "a", 
                        new XC_MethodReplacement() { 
                    @Override 
                    protected Object replaceHookedMethod(MethodHookParam param) 
                        throws Throwable { 
                        return null; 
                    } 
                }); 
        }catch(Throwable e){ 
            XposedBridge.log(e); 
        } 
        try{ 
            XposedBridge.log("Hook start"); 
            XposedHelpers.findAndHookMethod("sg.vantagepoint.a.a", 
                    loadPackageParam.classLoader, "a", byte [].class, byte [].class, 
                new XC_MethodHook() { 
                @Override 
                protected void beforeHookedMethod(MethodHookParam param) 
                    throws Throwable { 
                } 
                protected void afterHookedMethod(XC_MethodHook 
                    .MethodHookParam methodHookParam) throws Throwable { 
                    byte[] flag_byte = (byte[]) methodHookParam.getResult(); 
                    String flag = new String(flag_byte); 
                    XposedBridge.log("Flag: " + flag);
                } 
        }); 
    }catch (Throwable e){ 
        XposedBridge.log(e); 
    } 
} } 
}


5、获取flag

编写完毕后安装到设备中,在xposed里面勾选并重启生效,而后启动UNCRACKABLE1,随机输入一串密码,而后去看输出的log:

14.png

咱们能够看到log中打印出了正确的flag,将flag填入输入框中,能够看到这就是正确结果。

15.png


做者简介:叶绍琛,Unix/Linux/Android操做系统内核技术专家,大中华区前50位RHCA系统架构师,曾任网易互娱云计算平台技术负责人。


《Android安全指南》系列文章,未完待续。

相关文章
相关标签/搜索