Android应用方法隐藏及反调试技术浅析

记一次app爬虫sign破解 此APP正常情况下fd是无法抓到包。开启了justtrustme插件也抓不到。但是可以在模拟器中登录app,使用Http Debugger抓到提交的数据包。从提交的数据包初步分析,只有sign需要解密。 数据包有了,接下来进行初步的静态分析,使用jadx搜索"sign",比较方便的找到了sign的出处。 一步步跟踪方法调用后发现,是在so里面进行了加密工作: ... 阅读详情

原文地址: http://www.freebuf.com/articles/terminal/75944.html


0x00 前言


Android应用的加固和对抗不断升级,单纯的静态加固效果已无法满足需求,所以出现了隐藏方法加固,运行时动态恢复和反调试等方法来对抗,本文通过实例来分析有哪些对抗和反调试手段。

0x01 对抗反编译


首先使用apktool进行反编译,发现该应用使用的加固方式会让apktool卡死,通过调试apktool源码(如何调试apktool可参见前文《Android应用资源文件格式解析与保护对抗研究》),发现解析时抛出异常,如下图:

根据异常信息可知是readSmallUint出错,调用者是getDebugInfo,查看源码如下:

可见其在计算该偏移处的uleb值时得到的结果小于0,从而抛出异常。 在前文《Android程序的反编译对抗研究》中介绍了DEX的文件格式,其中提到与DebugInfo相关的字段为DexCode结构的debugInfoOff字段。猜测应该是在此处做了手脚,在010editor中打开dex文件,运行模板DEXTemplate.bt,找到debugInfoOff字段。果然,该值被设置为了0xFEEEEEEE。

接下来修复就比较简单了,由于debugInfoOff一般情况下是无关紧要的字段,所以只要关闭异常就行了。

为了保险起见,在readSmallUint方法后面添加一个新方法readSmallUint_DebugInfo,复制readSmallUint的代码,if语句内result赋值为0并注释掉抛异常代码。

然后在getDebugInfo中调用readSmallUint_DebugInfo即可。

重新编译apktool,对apk进行反编译,一切正常。

然而以上只是开胃菜,虽然apktool可以正常反编译了,但查看反编译后的smali代码,发现所有的虚方法都是native方法,而且类的初始化方法中开头多了2行代码,如下图:

其基本原理是在dex文件中隐藏虚方法,运行后在第一次加载类时通过在方法(如果没有方法,则会自动添加该方法)中调用ProxyApplication的init方法来恢复被隐藏的虚方法,其中字符串"aHcuaGVsbG93b3JsZC5NYWluQWN0aXZpdHk="是当前类名的base64编码。

ProxyApplication类只有2个方法,clinit和init,clinit主要是判断系统版本和架构,加载指定版本的so保护模块(X86或ARM);而init方法也是native方法,调用时直接进入了so模块。

那么它是如何恢复被隐藏的方法的呢?这就要深入SO模块内部一探究竟了。

0x02 动态调试so模块

如何使用IDA调试android的SO模块,网上有很多教程,这里简单说明一下。

1. 准备工作

1.1准备好模拟器并安装目标APP。

1.2 将IDA\dbgsrv\目录下的android_server复制到模拟器里,并赋予可执行权限。

adb push d:\IDA\dbgsrv\android_server /data/data/sv
adb shell chmod 755 /data/data/sv

1.3 运行android_server,默认监听23946端口。

adb shell /data/data/sv

1.4 端口转发。

adb forward tcp:23946 tcp:23946

2 以调试模式启动APP,模拟器将出现等待调试器的对话框。

adb shell am start -D -n hw.helloworld/hw.helloworld.MainActivity

3 启动IDA,打开debugger->attach->remote Armlinux/andoid debugger,设置hostname为localhost,port为23946,点击OK;然后选择要调试的APP并点击OK。

这时,正常状态下会断下来:

然后设置在模块加载时中断:

点击OK,按F9运行。

然后打开DDMS并执行以下命令,模拟器就会自动断下来:

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

(如果出现如下无法附加到目标VM的错误,可尝试端口8600)

此时,可在IDA中正常下断点调试,这里我们断JNI_OnLoad和init函数。

由于IDA调试器还不够完善,单步调试的时候经常报错,最好先做一个内存快照,然后分析关键点的函数调用,在关键点下断而不是单步调试。

0x03 反调试初探


一般反调试在JNI_OnLoad中执行,也有的是在INIT_ARRAY段和INIT段中早于JNI_OnLoad执行。可通过readelf工具查看INIT_ARRAY段和INIT段的信息,定位到对应代码进行分析。

INIT_ARRAY如下:

其中函数sub_80407A88的代码如下,通过检测时间差来检测是否中间有被单步调试执行:

sub_8040903C函数里就是脱壳了,首先读取/proc/self/maps找到自身模块基址,然后解析ELF文件格式,从程序头部表中找到类型为PT_LOAD,p_offset!=0的程序头部表项,并从该程序段末尾读取自定义的数组,该数组保存了被加密的代码的偏移和大小,然后逐项解密。

函数check_com_android_reverse里检测是否加载了com.android.reverse,检测到则直接退出。

JNI_OnLoad函数中有几个关键的函数调用:

call_system_property_get检测手机上的一些硬件信息,判断是否在调试器中。

checkProcStatus函数检测进程的状态,打开/proc/$PID/status,读取第6行得到TracerPid,发现被跟踪调试则直接退出。

通过命令行查询进程信息,一共有3个同名进程,创建顺序为33->415->430->431。其中415和431处于调试状态:

进程415被进程405(即IDA的android_server)调试:

进程431被其父进程430调试:

要过这种反调试可在调用点直接修改跳转指令,让代码在检测到被调试后继续正常的执行路径,或者干脆nop掉整个函数即可。 检测调试之后,就是调用ptrace附加自身,防止其他进程再一次附加,起到反调试作用。

修改跳转指令BNE(0xD1)为B(0xE0),直接返回即可。

当然,更加彻底的方法是修改android源码中bionic中的libc中的ptrace系统调用。检测到一个进程试图附加自身时直接返回0即可。

上面几处反调试点在检测到调试器后都直接调用exit()退出进程了,所以直接nop掉后按F9执行。然后就断在了init函数入口,顺利过掉反调试:

init函数在每个类加载的时候被调用,用于恢复当前类的被隐藏方法.首次调用时解密dex文件末尾的附加数据,得到事先保存的所有类的方法属性,然后根据传入的类名查找该类的被隐藏方法,并恢复对应属性字段。 执行完init函数,当前类的方法已经恢复了。然后转到dex文件的内存地址

dump出dex文件,保存为dump.dex。

0x04 恢复隐藏方法


对比一下原始dex文件,发现dex文件末尾的附加数据被解密出来了:

仔细分析一下附加数据的数据结构可以发现,它是一个数组,保存了所有类的所有方法的method_idx、access_flags、code_off、debug_info_off属性,解密后的这些属性都是uint类型的,如下图:

其中黄色框里的就是MainActivity的各方法的属性,知道这些就可以修复dex文件,恢复出被隐藏的方法了。下图就是恢复后的MainActivity类:

0x05 总结


以上就是通过实例分析展示出来的对抗和反调试手段。so模块中的反调试手段比较初级,可以非常简单的手工patch内存指令过掉,而隐藏方法的这种手段对art模式不兼容,不推荐使用这种方法加固应用。总的来说还是过于简单。预计未来通过虚拟机来加固应用将是一大发展方向。

android native 反编译,Android逆向从入门到入土(smali修改,so修改) 闲来无事,学习了下安卓的逆向工程,有助于观摩学习他人的优秀代码,查询了解后发现大体包括两部分:1.dex反编译,即smali语法学习;2.Native层,arm汇编学习。反编译dex,Smali语法工具java环境不多说1.apktool.jar;2.jd-gui.jar;3.dex2jar.zip;这里就不提供了,毕竟自己动手,丰衣足食,建议百度下载最新的版本,旧版本可能会有bug。查看java... 阅读详情

相关推荐

安卓APK加固实战:基于IO流操作的Dex文件加密与动态加载方案

在移动应用开发中,代码保护是保障应用安全与知识产权的核心环节。其基本原理在于通过技术手段防止核心逻辑被轻易逆向分析。从技术价值看,有效的代码保护能直接抵御外挂、破解和山寨应用,维护开发者收益。常见的应用场景包括金融、游戏等高价值应用的安全加固。其中,Dex文件作为安卓应用的代码载体,其保护尤为关键。本文聚焦于通过自定义IO流操作对Dex文件进行AES加密,并设计外壳程序实现运行时动态解密与加载。该方案能有效应对静态分析,并结合密钥管理、反调试等策略,为安卓应用提供一套可落地的自制加固方案,兼顾安全性与工程实

weixin_33796177的博客 352

Linux几种检测rootkit隐藏进程的方法

Rootkit通常会隐藏进程,隐藏文件和网络连接。这里主要记录几种对隐藏进程的检测方法 一. 隐藏进程的方法 1.1 用户级Rootkit 通过LD_PRELOADER来hook libc库实现,从而过滤/proc/pid目录 1.2 内核级rootkit 通过hook系统调用getdents/getdents64或者hook 文件file_operation的iterate 1.3 内核...

bin_linux96的专栏 4883

IDA教程-隐藏IDA调试器

IDA教程-隐藏IDA调试器,教你如何隐藏IDA,调试软件

android 系统内存检查

1.     Introduction Android对内存的使用包括内存泄漏和内存越界,内存泄漏会导致系统内存减少,最终分配不到内存,这样大的程序就不能运行,甚至系统没有内存而崩溃。Android中kernel和应用程序都可能会有内存泄漏和越界。对于Java代码,在越界的时候虚拟机会加以检查并抛出异常。而对于C/C++代码,越界的时候就悄无声息地让程序出错或crash 2.     

u011279649的专栏 2146

Android应用方法native ida so隐藏反调试技术浅析

转自:http://www.freebuf.com/articles/terminal/80996.html 0×00、前言 Android应用的加固和对抗不断升级,单纯的静态加固效果已无法满足需求,所以出现了隐藏方法加固,运行时动态恢复和反调试方法来对抗,本文通过实例来分析有哪些对抗和反调试手段。 0×01、对抗反编译 首先使用apktool进行反编译,发现该应用使

云守护的专栏 2982

android拦截native方法,Android native反调试方式及使用IDA绕过反调试

0x00为了避免我们的so文件被动态分析,我们通常在so中加入一些反调试代码,常见的Android native反调试方法有以下几种。1、直接调用ptrace(PTRACE_TRACEME, 0, 0, 0),参考Android Native反调试。2、根据上面说的/proc/$pid/status中TracerPid行显示调试程序的pid的原理, 可以写一个方法检查下这个值, 如果!=0就退出程...

weixin_30332669的博客 1133

JNI 反调试介绍

Author: Crystal 0X01 jni 反调试介绍 为了避免我们的so文件被动态分析,我们通常在so中加入一些反调试代码,常见的Java native反调试方法有以下几种。 1、直接调用ptrace(PTRACE_TRACEME, 0, 0, 0)。 2、根据上面说的/proc/$pid/status中TracerPid行显示调试程序的pid的原理, 可以写一个方法检查下这个值, 如...

u014715599的博客 1119

【学习记录】各种反调试手段总结

为了躲避杀软了检测,病毒会使用各种骚气的手段来隐藏自身 反调试 反虚拟机 遇到的几种比较骚气的 1.拿到设备信息,检测里面是否有某些软件,否则环境不运行 2.它通过向远程地址发出某种认证请求,返回地址,ip等信息。如果检测到是特定的组织机构名称与以下JSON文件中的任何机构字符串匹配,恶意软件将发生异常并停止运行。当然,上述列表中的机构名称在代码中是经过混淆的: http://bbs.pediy.

Tesi1a的充电桩 2410

Godot游戏安全加固实战:代码混淆、资源加密与反调试集成方案

在游戏开发领域,代码保护与资源安全是保障开发者核心资产和商业利益的关键环节。其基本原理是通过混淆、加密等技术手段,增加代码和资源的逆向工程难度,从而防止核心逻辑被窃取、篡改或资源被盗用。这项技术的核心价值在于显著提高攻击成本,有效抵御常见的破解行为,保护开发者的知识产权。在应用场景上,尤其对于使用开源引擎(如Godot)开发的、计划上架各平台的独立游戏或商业项目,构建一套纵深防御体系至关重要。本文聚焦于Godot引擎,深入探讨如何整合GDScript代码混淆、自定义资源加密以及跨平台反调试等热词技术,形成一

weixin_30386713的博客 366

最简单有效的隐藏文件方法

注:本文提供的方法仅在Windows下有效。 方法一:把文件隐藏到图片里。 1) 准备好两个文件:image.jpg(图片文件) 和 secret.rar(包含有私密内容的各种文件,可以是.rar或.zip)。 2) 把他们放到某个文件夹里,如 C:\hello\ 3) 开始--运行--cmd,打开命令行,输入 cd C:\hello\ 4) 输入 copy /b image....

whusl的专栏 410

Rootkit隐藏进程和端口检测

Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用。 例如:inetd或者login,为攻击者提供后门;隐藏攻击者的目录和进程的程序,ps、netstat等常见命令。 rootkit检测也成为主机安全一项重要功能,针对rootkit中最常见隐藏进程、端口检测,主要分为两种检测思路...

手牵手科技 2902

Python编程基础:第三十节 文件检测File Detection

检测指定路径下是否存在该文件

wzy628810的博客 1096

linux隐藏文件导出,看我如何通过Linux Rootkit实现文件隐藏

预估稿费:180RMB投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿前言一直以来,我希望能深入了解Linux内核内部是如何工作的。为实现这一点,有一个比较好的思路是写一个小的Rootkit PoC。大家可以在这里找到相关Rootkit代码。这是一个非常简单的Rootkit。其功能是,隐藏特定前缀的文件使其不可见。然而,假如我们知道这些文件或文件夹的位置,就仍然可以访问它们。但...

weixin_36296063的博客 872

如何实现文件隐藏

方法: 1. 劫持系统中用于读取文件列表的系统调用 2. 劫持文件系统中的库函数,实现对于文件的隐藏 一、如何通过劫持库函数实现: 通过preload 预加载库机制进行 1. 编写注入劫持函数,并编译成动态链接库 gcc -fPIC -shared parock.c -o libmycmp.so 2. 加载预加载库 ...

G_Super_Mouse的博客 1355

libc_hidden_def、libc_hidden_weak、libc_hidden_proto

libc_hidden_def、libc_hidden_weak、libc_hidden_proto 在阅读glibc源码的时候,遇见了几个没见过的宏,几乎所有的函数都会使用这几个宏:libc_hidden_def、libc_hidden_weak、libc_hidden_proto 因为我比较好奇,所以特地去找了一下有关这些宏的定义(主要也是想多学一点) Linux下学习源码,最方便的地方就是它的各种命令 使用find ./ | grep -r "define libc_hidden_def",发现在in

私のBookShelf 2392
上一篇: Android应用资源文件格式解析与保护对抗研究
下一篇: Android应用安全开发之源码安全
maspchen
博客等级 码龄12年 21粉丝 1原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值