作者简介:叶绍琛,网络安全攻防技术专家,极牛网技术委员会主席,CTFWAR网络安全攻防对抗联赛发起人,《网安观察》社群期刊总顾问,大中华区前50位RHCA系统架构师,曾任网易互娱云计算平台技术负责人,曾获国家科技部和教育部联合颁发的教育信息化发明创新奖。
本篇博文主要是介绍如何使用Xposed 框架Hook应用,使用的案例是OWASP提供的UNCRACKABLE1.apk,目的是通过Xposed Hook 获取到应用内部的flag字符串。
我们发现UNCRACKABLE1这个应用在启动时会检测手机是否被root,当探知到手机出于root状态时会强制停止运行。那么首先使用JEB来分析以下示例的代码逻辑。
一、JEB 分析apk
使用JEB打开apk文件,由于root检测是在启动的时候进行的,因此我们先来看 onCreate方法:
很明显MainActivity中的a方法执行了对root的检测,同时也对调试环境进行检测,看看a方法的实现:
知道了检测在哪一部分,接下来就去找哪里进行了输入字符串的检测。看到MainActivity下的verify方法:
verify方法通过a.a()方法返回的结果判断输入字符串的正确性,这就是我们的目标:
分析a.a()的代码逻辑,我们可知变量v1中保存的字符串是flag字符串加密后的结果,arg5是我们传入的字符串,而且我们能看到方法并没有对arg5进行任何的处理,由此可知校验的方式是对flag字符串解密后的明文对比,而flag明文就保存在v0_2变量中,解密的方法是sg.vantagepoint.a.a.a。
二、Xposed 模块的安装
Xposed框架的安装与运行是需要root权限的,前往Xposed官网下载安装包:http://xposed.appkg.com/nav。安装包下载完毕后放在设备的sdcard中,运行安装。安装完毕后打开Xposed installer:
设备或模拟器取得Root权限后,点击安装/更新,等待一段时间后设备会重启,显示以下界面则安装成功。
三、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项目。
创建完毕后我们来修改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″ />
插入以上代码后,Xposed框架就能将我们这个android应用识别成一个Xposed模块了:
(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文件夹:
在assets文件夹下创建xposed_init,在文件内写上Hook类的完整包名:
以上四步完成后,一个Hook模块就编写完毕了,接下来选择 no Activity启动,然后运行,在Xposed框架中选中模块即可:
四、编写 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);
}
}
}
}
五、获取flag
编写完毕后安装到设备中,在xposed里面勾选并重启生效,然后启动UNCRACKABLE1,随机输入一串密码,然后去看输出的log:
我们可以看到log中打印出了正确的flag,将flag填入输入框中,可以看到这就是正确结果。
《Android安全指南》系列文章,未完待续。
极牛网精选文章《Android安全指南 之 Xposed Hook实战》文中所述为作者独立观点,不代表极牛网立场。如若转载请注明出处:https://geeknb.com/12683.html
评论列表(2条)
牛逼!好文推荐
评论好少