Frida与Android CTF
2023-3-15 07:28:34 Author: 白帽子(查看原文) 阅读量:13 收藏

前言

最近练习了下CTF中Android相关题目,发现三题分别考察了三个点:
1、Frida Java Hook与静态函数的主动调用
2、Frida遍历ClassLoader从而Hook动态加载的Ddex的函数
3、Frida Native Hook去反调试

第一题 Frida静态函数的主动调用

首先安装第一款APP运行发现提示说需要输入纯数字,另外当输入错误时会调试”请继续加油“。

将该APK文件拖入jadx中进行静态分析发现,输入的用户名和密码的字符串拼接作为参数,然后传入vvvv方法。


然后查看VVVV函数,发现input参数限制长度为5位,并且经过eeeee方法得到的结果和p一致,需要同时满足这两个条件,才可以获得flag


继续分析eeeee方法,发现sssss方法在获取字符串的Sha-1值,ccccc方法将sha-1的摘要转化成了16进制字符串的形式,该算法不可逆。


通过对以上分析得知,想要获取flag有两种思路:
1、根据flag长度为5位数字,进行爆破
2、通过frida hook函数返回值强制返回true


但如果使用第二种方法,发现该check失效,输入任何五位数字都可提示成功,也就失去了题目的意义。接下来我们编写frida脚本进行第一种方法爆破解题。
因反编译的结果不能完全相信,首先使用Objection分析,因两段逻辑判断,所以在APP中输入5位纯数字,发现objection中VVVV调用了eeeee,因此验证了Jadx反编译的内容确实没错。


对com.kanxue.pediy1.VVVV类中的方法eeeee进行主动调用。
编写frida脚本,爆破拿到flag为66888


var CONTEXT = null;

function getObjClassName(obj) {
if (!jclazz) {
var jclazz = Java.use("java.lang.Class");
}
if (!jobj) {
var jobj = Java.use("java.lang.Object");
}
return jclazz.getName.call(jobj.getClass.call(obj));
}

function hookReturn() {
Java.perform(function () {
Java.use("com.kanxue.pediy1.VVVVV").VVVV.implementation = function (context, str) {
var result = this.VVVV(context, str)
console.log("context,str,result => ", context, str, result);
console.log("context className is => ", getObjClassName(context));
CONTEXT = context;
return true;
}
})
}
function invoke() {
Java.perform(function () {
//console.log("CONTEXT IS => ",CONTEXT)
var MainActivity = null;
Java.choose("com.kanxue.pediy1.MainActivity", {
onMatch: function (instance) {
MainActivity = instance;
},
onComplete: function () { }
})
var CONTEXT2 = Java.use("com.kanxue.pediy1.MainActivity$1").$new(MainActivity);
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(CONTEXT2, String(x));
console.log("now x is => ", String(x))
if (result) {
console.log("found result is => ", String(x))
break;
}
}
})

}

function main() {
hookReturn()
}

验证成功


第二题 Frida Hook动态加载Dex

通过Jadx分析发现了Dex的动态加载以及Native函数的引入。


动态加载dex并调用动态加载的dex中的VVVV函数,首先解压该APK包,使用Jadx打开classes.dex文件发现本题算法和第一题一样


所以可以编写frida脚本,通过枚举ClassLoader选择正确的classLoader再对函数进行主动调用爆破得到flag。


function invoke2() {
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(x));
console.log("now x is => ", String(x))
if (result) {
console.log("found result is => ", String(x))
break;
}
}
})
}

function main() {

}
setImmediate(main)

不过测试发现flag不对


继续分析发现还对stringFromJNI还做了一层处理,这个函数是native函数,所以要进行两层主动调用。


function invoke2() {
Java.perform(function () {
var MainActivity = null;
Java.choose("com.kanxue.pediy1.MainActivity",{
onMatch:function(instance){
MainActivity = instance;
},
onComplete:function(){}
})
var loader1 = null;
var loader2 = null;
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
console.log("Successfully found loader")
console.log(loader);
loader2 = loader;
Java.classFactory.loader = loader2;
}else if(loader.findClass("com.kanxue.pediy1.MainActivity")){console.log("Successfully found loader")
console.log(loader);
loader1 = loader;
}else{
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result1 = MainActivity.stringFromJNI(String(100000 - x));
var result2 = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(result1));
console.log("now x is => ", String(x))
if (result2) {
console.log("found result2 is => ", String(100000 - x))
break;
}
}
})
}
function main() {
}
setImmediate(main)

测试发现flag66998时正确


当然也可以使用IDA查看so文件,分析stringFromJNI函数,发现将输入的数字字符串转换为int然后加了一进行返回,所以正确的flag应该为66999 - 1 = 66998


第三题 Native层去反调试

该题在第二题的基础上加入了native层对Frida的反调试。
反调试逻辑:
通过一直循环创建Socket连接,遍历端口,检查端口是否被占用,收到“REJECT”时,说明frida-server正在运行,然后直接kill掉进程。
测试也发现当通过frida进行hook时,APP会崩掉。


绕过思路:
可以通过将系统中的kill进行替换,从而达到程序无法执行kill命令,保持APP正常运行来绕过反调试。编写相关脚本如下:

frida -U -f com.kanxue.pediy1 -l /Users/tale/Downloads/20220317/111.js  --no-pause

 function replaceKill(){
var kill_addr = Module.findExportByName("libc.so", "kill");
Interceptor.replace(kill_addr,new NativeCallback(function(arg0,arg1){
console.log("arg0=> ",arg0)
console.log("arg1=> ",arg1)
},"int",['int','int']))
}

function main() {
replaceKill();
}

发现frida-server和APP正常运行,从而成功绕过了对frida的反调试。
接下来将反调试代码加入到题目二中进行爆破可成功得到该题目flag99998。


验证成功


总结

这三道题主要逻辑就是输入flag验证对错,并且flag的求解都是通过爆破长度为5位的数字,程序本身利用hash加密后与程序已有密文进行对比,总体来说可以使用Frida爆破一把梭。

参考资料

https://bbs.pediy.com/thread-260550.htm

E

N

D

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章370余篇,自研平台达到26个,目有15个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246575&idx=2&sn=17fe157d9b113025d627c4bb08e145a0&chksm=82ea5606b59ddf1002d85e0559f901bd38bb294b599c627c25feb98d88078e3d44fb60db6b7c#rd
如有侵权请联系:admin#unsafe.sh