反调试实战系列一 x64dbg+IDA 过 IsDebuggerPresent
前言
挺久之前就想开一个反调试系列的坑,而且正好可以用这个实战系列来巩固所学
这次分析的程序为 64 位,用到的调试工具有:IDA Pro (64-bit)
和 x64dbg
这次的 CrackMe 并不难,有兴趣的可以尝试先自己动手破解看看
反调试介绍
在实战之前,先大致介绍一下反调试
什么是反调试
反调试技术,顾名思义就是用来防止被调试的一种技术
简单的反调试往往是识别是否被调试,如果是则退出程序,封禁账号等等 (检测)
再复杂些可以在反汇编代码中插入花指令,使调试器的反汇编引擎无法正确解析反汇编指令(干扰)
门槛较高的反调试则可以是从驱动层将调试权限清零,使得调试器失效等等 (权限清零)
反调试的手段可以大致归纳为:检测、干扰、权限清零 三种
反调试的常见手段
反调试手段层出不穷,可以分为两类:
- Ring0(内核层反调试)
- Ring3(应用层反调试)
Ring3
Windows API 中提供了两个用于检测是否被调试的函数:
- IsDebuggerPresent
- CheckRemoteDebuggerPresent
Windows API | 作用 |
---|---|
IsDebuggerPresent | 确定调用进程是否由用户模式调试器进行调试 |
CheckRemoteDebuggerPresent | 确定是否正在调试指定的进程 |
除了使用上述两个 Windows API 外 还有不少在 Ring3 下的反调试手段:
如检测 PEB(Process Environment Block)进程环境块 中的 BeingDebugged、ProcessHeap 等
除了检测 PEB 外,还可以检测 软件断点、硬件断点,代码校验等等
除了检测,之前提到的插入花指令进行干扰也属于 Ring3 中的反调试手段
在 Ring3 下的反调试手段 五花八门,这里仅列举出了一小部分
Ring0
在 Ring0 下的反调试保护,TenProtect 不可谓不强,在先前的【原创】TP 驱动保护分析系列一 定位 TenProtect 保护中已经提及
这里限于篇幅原因不再赘述
反调试实战
前面稍微补充了一点关于反调试的知识,接下来正式进入实战环节
要调试的程序
为了更好地起到学习作用,这次要调试的程序是我自己写的一个小 demo,是一个 MFC 程序(练习了一波 MFC)
界面比较简陋,毕竟是个小 demo,不要介意
关于这个 demo 的代码之后也会放在后面的附件里,有需要的可以自行取用 (* ̄3 ̄)╭
实战流程
查壳
首先使用 PE 工具:DIE(Detect It Easy) 查壳
可以看到,程序并没有加壳,并且是 64 位程序、用 MFC 编写
关闭 ASLR
使用 MFC 编译出的 64 位程序默认是开启 ASLR 的,不利于调试,需要先关闭
什么是 ASLR
ASLR 全称 Address Space Layout Randomization,又称地址空间配置随机化、地址空间布局随机化
ASLR 的作用
地址空间配置随机加载利用随机方式配置数据地址空间,使某些敏感数据配置到一个恶意程序无法事先获知的地址,令攻击者难以进行攻击
粗俗地说,就是使得每次调试工具(如 OD、x64dbg 等)加载程序后,地址是随机动态分配的,无法使用固定的地址进行定位
ASLR 的体现
上面纯粹的说明可能不是很直观,接下来使用 x64dbg 载入程序
可以看到,x64dbg 默认是中断在了系统断点,我们需要它运行到 OEP(程序入口点)
使用快捷键:ALT+F9 运行到 OEP(程序没有加壳,所以可以运行到 OEP),或者 调试→运行到用户代码
得到了:
可以看到此时的 EntryPoint 为:
复制代码 隐藏代码
00007FF6950D14F1 | E9 CADE0000 | jmp <crackme.wWinMainCRTStartup> |
记录下此时的地址为:00007FF6950D14F1
如果学习过 PE 就会知道 EntryPoint 的地址 = EntryPoint + ImageBase
(不了解这个知识点的可以回顾:PE 文件笔记五 PE 文件头之扩展 PE 头)
从前面的 DIE 工具的查看中可以得到:
正常的 EntryPoint 的地址 = EntryPoint + ImageBase = 0x114F1 + 0x140000000 = 0x1400114F1
但是此时的地址很明显不等于 0x1400114F1,这就是 ASLR 的体现
使用 PE 工具关闭 ASLR
知道了 ASLR 会干扰调试,于是要使用 PE 工具关闭 ASLR
ASLR 由 扩展 PE 头中的 DllCharacteristics 决定,关于 DllCharacteristics 可参考:DllCharacteristics
验证 ASLR 的关闭
关闭完 ASLR,再使用 x64dbg 载入程序,查看此时的 OEP:
可以发现,此时的 EntryPoint 地址就和前面计算出来的地址一致,为:0x1400114F1
x64dbg 定位反调试
载入程序以后,要先让程序跑起来再设置相关的 API 断点,于是先运行
使用快捷键:F9 使得程序运行起来
但是当运行 F9 后,会发现程序直接退出了(不使用调试工具时程序是可以正常运行的);这也就是本帖的关键了:反调试
通过上面的操作,可以推测出:程序检测当前是否正在被调试,如是是则直接退出程序
推断出大致的流程后,可以写出伪代码:
复制代码 隐藏代码
void AntiDebug(){ //反调试函数
bool IsBeingDebugged=checkIsDebug();//通过某种方式判断当前程序是否正在被调试
if (IsBeingDebugged){ //如果正在被调试
exit(); //退出程序
}
}
可以得出调用情况为:AntiDebug→exit
于是可以从 exit (退出程序) 入手,开始定位
退出程序一般会使用到 ExitProcess () 这个 Windows API,于是对这个函数下断点
在底下的命令行输入:
复制代码 隐藏代码
bp ExitProcess
然后可以得到:
确定设置完断点后,按 F9 让程序运行起来,然后断点断下:
注意堆栈中调用情况:
找到调用的该程序的函数,可以怀疑这个函数相当于 AntiDebug 函数(用来反调试)
选中这一行,然后回车,查看其对应的反汇编
得到:
不难发现在调用 exit 函数的前面调用了 IsDebuggerPresent 来检测是否被调试
IDA Pro 分析反调试
为了更清晰地分析代码,使用 x64dbg 定位到了关键函数后,可以搭配 IDA Pro 进行分析
使用 IDA Pro 载入程序后,按 G 键弹出:
这里要跳转的地址为前面得到的地址:这里填 14001A1D8
跳转后得到:
选中函数的头部,按快捷键:F5 查看其对应的伪代码:
得到:
即:
复制代码 隐藏代码
__int64 StartAddress_0()
{
__int64 *v0; // rdi
__int64 i; // rcx
__int64 v3; // [rsp+0h] [rbp-30h] BYREF
v0 = &v3;
for ( i = 70i64; i; --i )
{
*(_DWORD *)v0 = -858993460;
v0 = (__int64 *)((char *)v0 + 4);
}
sub_140011C12(&unk_14004201F);
if ( IsDebuggerPresent() ) //通过IsDebuggerPresent判断是否被调试
exit(0); //如果检测到被调试则退出程序
Sleep(0x64u); //为防止线程占用过高,使用Sleep
beginthreadex(0i64, 0, StartAddress, 0i64, 0, 0i64); //启动检测线程
return 0i64;
}
到这里其实就已经十分清晰了,接下来开始处理反调试
IDA Pro 处理反调试
分析出了该函数就是个反调试函数,于是可以直接在函数头部使其返回,让反调试函数无效
这里要用到 IDA Pro 的 KeyPatch 功能:
选中函数的头部,然后右键 → Key Patch → Patch:
Patch 完结果如下:
接下来要将 Patch 完的结果导出到文件:
Edit→ Patch Program → Apply patches to input file
确定导出即可(导出的时候,记得要先关闭 x64dbg,不然程序被占用会无法导出)
验证反调试的处理
此时再使用 x64dbg 载入程序 并让程序运行起来,可以发现此时就可以正常运行了:
x64 定位 Crack 相关函数
摆脱了反调试的干扰后,终于可以正式开始 Crack 了
一般来说,对于没有加壳的程序直接搜索字符串即可,但这里字符串是被加密的,于是不能通过字符串直接定位
换个角度,可以通过对相关的 API 函数下断进行定位
这里可以发现当输入了错误的密码后,等待 Crack 中变成了密码错误
这很显然是对标签文本的修改,可以尝试对 SetWindowTextW 下断点:
复制代码 隐藏代码
bp SetWindowTextW
设置完断点后,再点击确定来触发断点:
观察此时的堆栈情况:
可以看到,堆栈中有输入的密码:610 和 要设置的文本:密码错误
于是可以以此为突破口,继续分析
选中 L"密码错误" 上面的函数,按回车查看其对应的反汇编
翻看附近的代码,可以看到在代码下方不远处可以找到:
这里就算定位到了关键的函数处,接下来使用 IDA Pro 进行分析
IDA Pro 分析 Crack 相关函数
在 IDA Pro 中 按 G 弹出跳转地址窗口,然后填入要跳转的地址:140019272
得到:
接下来故技重施,选中函数的头部,然后按 F5 查看伪代码:
得到:
到了这里其实基本上就已经水落石出了,为了留点悬念,接下来的步骤等有人 Crack 出来后再放出 ( ̄︶ ̄*))
Crack 出来的要求:逆推出正确的密码(该密码唯一)
总结
该篇是反调试实战这个系列的开篇之作,此次实战只涉及了一个最简单的反调试:IsDebuggerPresent
后续还会不断更新其它反调试实战内容(应该会吧?咕咕咕 (づ ̄ 3 ̄) づ),希望大家多多支持 (。・∀・)ノ゙
稍微总结一下此篇的内容:
- 反调试的手段可以大致归纳为:检测、干扰、权限清零 三种
- 分析程序时,可以采取 x64dbg 搭配 IDA Pro 动静结合的方式进行分析
- ASLR 可以使用 PE 工具关闭,之后调试起来更方便
- 反调试的手段五花八门,重点在于如何定位,一般都是以某个相关函数作为突破口进行分析