安卓逆向之对抗Anti-Frida学习

基础补充

什么是 Anti-Frida 保护?

Anti-Frida保护是指在移动应用或程序中采用的一种安全技术或防护机制,旨在防止或干扰Frida等动态分析工具的注入与使用。

Anti-Frida保护常见技术 有哪些?

  • 检测frida-agent.so的注入 :

Frida通过注入一个名为frida-agent.so的共享库到目标进程中。这是Frida工作原理的一部分,任何应用如果加载了这个库,就意味着它可能正在被Frida控制。Anti-Frida保护可以检测到这个库的加载,并通过终止进程、使其崩溃或显示警告来防止分析

  • 检查/proc/[pid]/maps文件

如前所述,Frida注入的frida-agent.so库会出现在进程的/proc/[pid]/maps文件中。防护机制可能通过伪造或监控这个文件,阻止Frida的注入信息显现。或者,应用程序可以定期检查这个文件,以检测是否有Frida的注入

  • 检测Frida的特定行为

Frida通过其API来修改应用程序的内存、执行代码等。某些Anti-Frida保护机制会分析进程的行为,监控是否存在类似Frida的异常行为(例如,动态修改内存、重定向函数调用等),从而判断是否有Frida工具的干预

  • 反调试技术

反调试技术可以通过识别调试器或动态分析工具(如Frida)来干扰其运行。常见的反调试技术包括检查ptrace系统调用、检测特定的调试标记或使用某些系统调用(如getppidsysctl)来检查是否存在调试器

什么是maps?

在Linux系统中,/proc/[pid]/maps文件记录了一个进程的内存映射情况。它列出了进程如何将虚拟内存地址空间映射到实际的物理内存地址、文件或共享内存区域。每一行都表示一个内存区域的映射,包括区域的起始和结束地址、权限(如读写执行权限)、偏移量、设备ID、inode号、以及该区域关联的文件名或共享内存名。

定位检测逻辑

要判断该壳是如何对抗frida的就要先定位他检测frida的代码逻辑在哪个so里

安卓中通常用来加载so的函数有 lopen 和 android_dlopen_ext ,那么可以使用frida-trace给着两个函数进行挂钩 先分析dlopen

frida-trace -U -f appname -i dlopen

默认只会显示调用了dlopen不会显示进行加载的so在哪个位置,dlopen的第一个参数就是所要加载的so的路径 可以修改dlopen.js的脚步即可

onEnter(log,args,state){log('dlopen():'+args[0].readCString());
}

结果都是系统的so而不是这个app的so

再去hook另外一个函数android_dlopen_ext 看看 这里加载的so就是app的so了 甚至可以看到app的名字

可以看到当load到libDexHelper.so的时候,frida被杀掉了,所以我们初步可以判定做检测的位置在 第三个so中,检测到了frida

对抗anti-frida

maps检测

通常检测frida的地方会检测“frida”这个字符串,那么程序代码很有可能就会用到比较字符串的函数 如strstr和strcmp等函数 ,那么就尝试hook这种函数来判断对抗逻辑

通过这些字符串的特征,可以知道它们来自maps , 而Frida的一大特征就是在注入到app中后,app的 maps中会有frida-agent.so的内存分布。所以这里我们可以通过伪造maps来绕过这里的检测

什么是伪造maps?

伪造maps指的是通过编程手段修改或模拟/proc/[pid]/maps文件中的内容,使得其呈现出不同的内存映射情况,从而绕过某些安全检测或者防护机制。在本段话中,伪造maps的目的是为了绕过检测Frida注入的迹象

如何绕过maps检测?

第一种方式 通过hookstrstrstrcmp 来防止它们返回与"REJECT""frida"相关的匹配结果。

// 定义一个函数anti_maps,用于阻止特定字符串的搜索匹配,避免检测到敏感内容如"Frida"或"REJECT"
function anti_maps() {// 查找libc.so库中strstr函数的地址,strstr用于查找字符串中首次出现指定字符序列的位置var pt_strstr = Module.findExportByName("libc.so", 'strstr');// 查找libc.so库中strcmp函数的地址,strcmp用于比较两个字符串var pt_strcmp = Module.findExportByName("libc.so", 'strcmp');// 使用Interceptor模块附加到strstr函数上,拦截并修改其行为Interceptor.attach(pt_strstr, {// 在strstr函数调用前执行的回调onEnter: function (args) {// 读取strstr的第一个参数(源字符串)和第二个参数(要查找的子字符串)var str1 = args[0].readCString();var str2 = args[1].readCString();// 检查子字符串是否包含"REJECT"或"frida",如果包含则设置hook标志为trueif (str2.indexOf("REJECT") !== -1  || str2.indexOf("frida") !== -1) {this.hook = true;}},// 在strstr函数调用后执行的回调onLeave: function (retval) {// 如果之前设置了hook标志,则将strstr的结果替换为0(表示未找到),从而隐藏敏感信息if (this.hook) {retval.replace(0);}}});// 对strcmp函数做类似的处理,防止通过字符串比较检测敏感信息Interceptor.attach(pt_strcmp, {onEnter: function (args) {var str1 = args[0].readCString();var str2 = args[1].readCString();if (str2.indexOf("REJECT") !== -1  || str2.indexOf("frida") !== -1) {this.hook = true;}},onLeave: function (retval) {if (this.hook) {// strcmp返回值为0表示两个字符串相等,这里同样替换为0以避免匹配成功retval.replace(0);}}});
}

第二种方式去hook open函数 如果open的路径包含"/proc/""maps",这表明当前正在尝试打开/proc/[pid]/maps文件 也就是在检查maps,此时我们就创建一个假的maps,再去open一个假的maps返回结果,当然事先肯定会对这个假的maps做处理来隐藏frida的特征

// 定义一个函数,用于重定向并修改maps文件内容,以隐藏特定的库和路径信息
function mapsRedirect() {// 定义伪造的maps文件路径var FakeMaps = "/data/data/com.zj.wuaipojie/maps";// 获取libc.so库中'open'函数的地址const openPtr = Module.getExportByName('libc.so', 'open');// 根据地址创建一个新的NativeFunction对象,表示原生的'open'函数const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);// 查找并获取libc.so库中'read'函数的地址var readPtr = Module.findExportByName("libc.so", "read");// 创建新的NativeFunction对象表示原生的'read'函数var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', "int"]);// 分配512字节的内存空间,用于临时存储从maps文件读取的内容var MapsBuffer = Memory.alloc(512);// 创建一个伪造的maps文件,用于写入修改后的内容,模式为"w"(写入)var MapsFile = new File(FakeMaps, "w");// 使用Interceptor替换原有的'open'函数,注入自定义逻辑Interceptor.replace(openPtr, new NativeCallback(function(pathname, flag) {// 调用原始的'open'函数,并获取文件描述符(FD)var FD = open(pathname, flag);// 读取并打印尝试打开的文件路径var ch = pathname.readCString();if (ch.indexOf("/proc/") >= 0 && ch.indexOf("maps") >= 0) {console.log("open : ", pathname.readCString());// 循环读取maps内容,并写入伪造的maps文件中,同时进行字符串替换以隐藏特定信息while (parseInt(read(FD, MapsBuffer, 512)) !== 0) {var MBuffer = MapsBuffer.readCString();MBuffer = MBuffer.replaceAll("/data/local/tmp/re.frida.server/frida-agent-64.so", "FakingMaps");MBuffer = MBuffer.replaceAll("re.frida.server", "FakingMaps");MBuffer = MBuffer.replaceAll("frida-agent-64.so", "FakingMaps");MBuffer = MBuffer.replaceAll("frida-agent-32.so", "FakingMaps");MBuffer = MBuffer.replaceAll("frida", "FakingMaps");MBuffer = MBuffer.replaceAll("/data/local/tmp", "/data");// 将修改后的内容写入伪造的maps文件MapsFile.write(MBuffer);}// 为返回伪造maps文件的打开操作,分配UTF8编码的文件名字符串var filename = Memory.allocUtf8String(FakeMaps);// 返回打开伪造maps文件的文件描述符return open(filename, flag);}// 如果不是目标maps文件,则直接返回原open调用的结果return FD;}, 'int', ['pointer', 'int']));
}

task检测

扫描task目录下所有/task/pid/status中的Name字段寻找是否存在frida注入的特征,具体线程名为 gmain 、 gdbus 和 gum-js- loop ,一般情况下这三个线程在第11--13的位置,此外在frida运行脚本过 程中,还会存在一个Name字段为 pool-frida 的线程。

可以通过以上伪造maps的方法 去伪造task

function replace_str() {var pt_strstr = Module.findExportByName("libc.so", 'strstr');var pt_strcmp = Module.findExportByName("libc.so", 'strcmp');Interceptor.attach(pt_strstr, {onEnter: function (args) {var str1 = args[0].readCString();var str2 = args[1].readCString();if (str2.indexOf("tmp") !== -1 ||str2.indexOf("frida") !== -1 ||str2.indexOf("gum-js-loop") !== -1 ||str2.indexOf("gmain") !== -1 ||str2.indexOf("gdbus") !== -1 ||str2.indexOf("pool-frida") !== -1||str2.indexOf("linjector") !== -1) {//console.log("strcmp-->", str1, str2);this.hook = true;}}, onLeave: function (retval) {if (this.hook) {retval.replace(0);}}});Interceptor.attach(pt_strcmp, {onEnter: function (args) {var str1 = args[0].readCString();var str2 = args[1].readCString();if (str2.indexOf("tmp") !== -1 ||str2.indexOf("frida") !== -1 ||str2.indexOf("gum-js-loop") !== -1 ||str2.indexOf("gmain") !== -1 ||str2.indexOf("gdbus") !== -1 ||str2.indexOf("pool-frida") !== -1||str2.indexOf("linjector") !== -1) {//console.log("strcmp-->", str1, str2);this.hook = true;}}, onLeave: function (retval) {if (this.hook) {retval.replace(0);}}})}

inlinehook检测

检测原理就是通过对比 一个api再hook之前与hook之后的字节码,来判断是否被frida给inlinehook了

检查的时候 程序会用到fread函数 那么我们就hook他 给他返回没有hook的函数字节码


function hook_memcmp_addr(){//hook反调试var memcmp_addr = Module.findExportByName("libc.so", "fread");if (memcmp_addr !== null) {console.log("fread address: ", memcmp_addr);Interceptor.attach(memcmp_addr, {onEnter: function (args) {this.buffer = args[0];   // 保存 buffer 参数this.size = args[1];     // 保存 size 参数this.count = args[2];    // 保存 count 参数this.stream = args[3];   // 保存 FILE* 参数},onLeave: function (retval) {// 这里可以修改 buffer 的内容,假设我们知道何时 fread 被用于敏感操作console.log(this.count.toInt32());if (this.count.toInt32() == 8) {// 模拟 fread 读取了预期数据,伪造返回值Memory.writeByteArray(this.buffer, [0x50, 0x00, 0x00, 0x58, 0x00, 0x02, 0x1f, 0xd6]);retval.replace(8); // 填充前8字节console.log(hexdump(this.buffer));}}});} else {console.log("Error: memcmp function not found in libc.so");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/35068.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

安全架构评审

安全架构评审 1.概述2.安全设计原则3.美团安全架构评审模型安全需求分析架构review攻击面分析和威胁建模攻击面分析威胁列表 1.概述 完整的安全评审会包含安全架构评审、安全代码审核和安全测试三个手段 安全架构评审聚焦于探寻安全设计中的漏洞,以宏观视野全面考…

迎接国庆,我上线了第一款小程序

最近花了些时间,写了一个 “国庆头像” 小程序。正好快国庆节了,于是分享一下我的这个 “Starstick星点贴纸” 小程序,顺便简单讲讲以及其中的设计、开发、上线过程。 小程序的界面是这样的: 🔮背景 今年中秋前夕&am…

房产销售系统

文末获取源码和万字论文,制作不易,感谢点赞支持。 摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于房产销售系统当然也不能排除在外,随着网络技术的不…

开发一套ERP 第二个生产版本

库存管理第一个生产版本 Okay 进入测试 嘿嘿,搞完了,剩下的就是细测慢调了 下一版本可以内置sqlite 数据库来操作这些数据表,sqlite 也支持 trigger 的功能

Weiss 机器人电动夹爪,重塑工业自动化精密操作

在当今的自动化进程里,Weiss高精密电动夹爪扮演着重要的角色。Weiss公司位于德国,其精心研制的高精密电动夹爪专为工业机器人与协作机器人打造。 Weiss 高精密电动夹爪的控制方式独具特色,与传统的一些夹爪相比,Weiss电动夹爪在处…

【JVM虚拟机】面试经典八股文(应届生必看)

目录 1.JDK、JRE、JVM三者关系? 2.谈谈JVM的理解? 3.JVM执行字节码的过程?(执行方式) 4.JVM的组成是什么? 5.什么是类加载机制? 6.什么是双亲委派模型? 7.JVM内存模型 8.堆区的…

Android 实现中英文切换

在开发海外项目的时候,需要实现app内部的中英文切换功能,所有的英文都是内置的,整体思路为: 创建一个sp对象,存储当前系统的语言类型,然后在BaseActivity中对语言进行判断; //公共Activitypubl…

Qt-练习1(事件循环)

一、练习要求 1、使用QT的IO读取文件; 2、以QSlider为基础,写一个进度条,显示文件读取的进度; 3、给QSlider布置qss样式,qss文件像css文件一样,分离出来。 二、源码 全部源码 源码目录: 1.1 、m…

解决Clang 18+不能链接GCC14编译的库的问题

前面笔者的博文MSYS2 MinGW64使用Protobuf新版本踩坑,有提到使用最新的Clang 19.1.4版本在MinGW下无法正常链接使用了Protobuf 28.3的项目,因为MinGW下的项目默认都是使用GCC来编译的,所以如果开发者使用Clang编译器,必然会出现链…

城市内涝监测预警系统解决方案-智慧排水

成因分析 气候变化 受全球气候变暖导致大气水循环加快,环流不稳定性增加,进一步引发极端降雨事件受城市热岛效应、雨岛效应的影响,强降雨常集中于城市且雨量多,强度大且雨量多。 排水系统不完善 城市发展快,城市防洪排涝工程&a…

Python:使用随机森林分类器进行模型评估:ROC 曲线与 AUC 指标计算

前言 这段代码的目标是使用 随机森林分类器(Random Forest Classifier) 来进行二分类任务,并基于每个数据子集计算 ROC 曲线(Receiver Operating Characteristic Curve)以及 AUC(Area Under Curve&#xf…

【JAVA练习】力扣1232.缀点成线

题目: 解题思路: 直线上面的点满足公式:, 如果所有点在一条直线上,即任意两点的斜率相同,由于计算斜率存在精度的问题,可以将除法等价于乘法。 等价于 class Solution {public boolean che…

【SKFramework框架核心模块】3-12、网络请求模块

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群:398291828小红书小破站 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【Unity3D框架】SKFramework框架完全教程《全…

C# 反射(Reflection)

文章目录 前言一、反射的优缺点(一)优点(二)缺点 二、反射的用途(一)查看特性(attribute)信息(二)审查集合中的各种类型并实例化(三)实…

Jdk配置、maven配置、gradle配置、Java开发常用的依赖库

一、配置JDK环境变量: 1. 安装 JDK 首先确保已经安装了 JDK。如果还没有安装,可以从 Oracle 官网 或者 AdoptOpenJDK 下载并安装合适的 JDK 版本。 我建议下载这个:https://adoptium.net/zh-CN/temurin/releases/?oswindows&archx64&…

【AI系统】FBNet 系列

FBNet 系列 本文主要介绍 FBNet 系列,在这一章会给大家带来三种版本的 FBNet 网络,从基本 NAS 搜索方法开始,到 v3 版本的独特方法。在本节中读者会了解到如何用 NAS 搜索出最好的网络和训练参数。 FBNet V1 模型 FBNetV1:完全基于 NAS 搜…

二十三、Linux可视管理之webmin和bt运维工具

1、webmin 基本介绍 Webmin 是功能强大的基于 Web 的 Unix/linux 系统管理工具。管理员通过浏览器访问 Webmin 的各种管理功能并完成相应的管理操作。除了各版本的 linux 以外还可用于:AIX、HPUX、Solaris、Unixware、Irix 和 FreeBSD 等系统安装webmin&配置 …

聚合支付系统/官方个人免签系统/三方支付系统稳定安全高并发 附教程

聚合支付系统/官方个人免签系统/三方支付系统稳定安全高并发 附教程 系统采用FastAdmin框架独立全新开发,安全稳定,系统支持代理、商户、码商等业务逻辑。 针对最近一些JD,TB等业务定制,子账号业务逻辑API 非常详细,方便内置…

Transformer真的是机器人技术的基础吗?

生成式预训练Transformer(GPT)被吹捧为将彻底改变机器人技术。但实际应用中,GPT需要庞大且昂贵的计算资源、冗长的训练时间以及(通常)非机载无线控制,诸多限制之下,GPT技术真的实用吗&#xff1…

使用脚本语言实现Lumerical官方案例——闪耀光栅(Blazed grating)(纯代码)(2)

接《使用脚本语言实现Lumerical官方案例——闪耀光栅(Blazed grating)(纯代码)(1)》 一、添加分析组 1.1 代码实现 #添加分析组 addanalysisgroup(); set("name", "grating_R"); set("x", 0); set("y", 2.5*um); addanalysisgrou…