安全矩阵

 找回密码
 立即注册
搜索
查看: 2590|回复: 0

Frida与Android CTF

[复制链接]

249

主题

299

帖子

1391

积分

金牌会员

Rank: 6Rank: 6

积分
1391
发表于 2022-3-21 17:11:57 | 显示全部楼层 |阅读模式
原文链接:Frida与Android CTF

前言
最近练习了下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


  1. var CONTEXT = null;

  2. function getObjClassName(obj) {
  3.     if (!jclazz) {
  4.         var jclazz = Java.use("java.lang.Class");
  5.     }
  6.     if (!jobj) {
  7.         var jobj = Java.use("java.lang.Object");
  8.     }
  9.     return jclazz.getName.call(jobj.getClass.call(obj));
  10. }

  11. function hookReturn() {
  12.     Java.perform(function () {
  13.         Java.use("com.kanxue.pediy1.VVVVV").VVVV.implementation = function (context, str) {
  14.             var result = this.VVVV(context, str)
  15.             console.log("context,str,result => ", context, str, result);
  16.             console.log("context className is => ", getObjClassName(context));
  17.             CONTEXT = context;
  18.             return true;
  19.         }
  20.     })
  21. }
  22. function invoke() {
  23.     Java.perform(function () {
  24.         //console.log("CONTEXT IS => ",CONTEXT)
  25.         var MainActivity = null;
  26.         Java.choose("com.kanxue.pediy1.MainActivity", {
  27.             onMatch: function (instance) {
  28.                 MainActivity = instance;
  29.             },
  30.             onComplete: function () { }
  31.         })
  32.         var CONTEXT2 = Java.use("com.kanxue.pediy1.MainActivity$1").$new(MainActivity);
  33.         var javaString = Java.use("java.lang.String").$new("12345");
  34.         for (var x = 0; x < (99999 + 1); x++) {
  35.             var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(CONTEXT2, String(x));
  36.             console.log("now x is => ", String(x))
  37.             if (result) {
  38.                 console.log("found result is => ", String(x))
  39.                 break;
  40.             }
  41.         }
  42.     })

  43. }

  44. function main() {
  45.     hookReturn()
  46. }
复制代码
验证成功

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

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


所以可以编写frida脚本,通过枚举ClassLoader选择正确的classLoader再对函数进行主动调用爆破得到flag。
  1. function invoke2() {
  2.     Java.perform(function () {
  3.         Java.enumerateClassLoaders({
  4.             onMatch: function (loader) {
  5.                 try {
  6.                     if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
  7.                         console.log("Successfully found loader")
  8.                         console.log(loader);
  9.                         Java.classFactory.loader = loader;
  10.                     }
  11.                 }
  12.                 catch (error) {
  13.                     console.log("find error:" + error)
  14.                 }
  15.             },
  16.             onComplete: function () {
  17.                 console.log("end1")
  18.             }
  19.         })
  20.         var javaString = Java.use("java.lang.String").$new("12345");
  21.         for (var x = 0; x < (99999 + 1); x++) {
  22.             var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(x));         
  23.             console.log("now x is => ", String(x))
  24.             if (result) {
  25.                 console.log("found result is => ", String(x))
  26.                 break;
  27.             }
  28.         }
  29.     })
  30. }

  31. function main() {

  32. }
  33. setImmediate(main)
复制代码
不过测试发现flag不对
继续分析发现还对stringFromJNI还做了一层处理,这个函数是native函数,所以要进行两层主动调用。
  1. function invoke2() {
  2.   Java.perform(function () {
  3.     var MainActivity = null;
  4.     Java.choose("com.kanxue.pediy1.MainActivity",{
  5.       onMatch:function(instance){
  6.         MainActivity = instance;
  7.       },
  8.       onComplete:function(){}
  9.     })
  10.     var loader1 = null;
  11.     var loader2 = null;
  12.     Java.enumerateClassLoaders({
  13.       onMatch: function (loader) {
  14.         try {
  15.           if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
  16.             console.log("Successfully found loader")
  17.             console.log(loader);
  18.             loader2 = loader;
  19.             Java.classFactory.loader = loader2;
  20.           }else if(loader.findClass("com.kanxue.pediy1.MainActivity")){console.log("Successfully found loader")
  21.                                                                        console.log(loader);
  22.                                                                        loader1 = loader;
  23.                                                                       }else{
  24.                                                                       }
  25.         }
  26.         catch (error) {
  27.           console.log("find error:" + error)
  28.         }
  29.       },
  30.       onComplete: function () {
  31.         console.log("end1")
  32.       }
  33.     })
  34.     var javaString = Java.use("java.lang.String").$new("12345");
  35.     for (var x = 0; x < (99999 + 1); x++) {
  36.       var result1 = MainActivity.stringFromJNI(String(100000 - x));
  37.       var result2 = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(result1));         
  38.       console.log("now x is => ", String(x))
  39.       if (result2) {
  40.         console.log("found result2 is => ", String(100000 - x))
  41.         break;
  42.       }
  43.     }
  44.   })
  45. }
  46. function main() {
  47. }
  48. 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正常运行来绕过反调试。编写相关脚本如下:

  1. frida -U -f com.kanxue.pediy1 -l /Users/tale/Downloads/20220317/111.js  --no-pause
复制代码
  1. function replaceKill(){
  2.     var kill_addr = Module.findExportByName("libc.so", "kill");
  3.     Interceptor.replace(kill_addr,new NativeCallback(function(arg0,arg1){
  4.         console.log("arg0=> ",arg0)
  5.         console.log("arg1=> ",arg1)
  6.     },"int",['int','int']))
  7. }

  8. function main() {
  9.     replaceKill();
  10. }
复制代码



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


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

参考资料
https://bbs.pediy.com/thread-260550.htm











本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-24 14:56 , Processed in 0.016103 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表