|
系列
【原创】反调试实战系列一 x64dbg+IDA 过IsDebuggerPresent
【原创】反调试实战系列二 TLS反调试+CheckRemoteDebuggerPresent原理
前言
挺久之前就想开一个反调试系列的坑,而且恰好可以用这个实战系列来巩固所学
这次分析的程序为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 |记录下此时的地址为: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检察伪代码:

得到:

到了这里其实基本上就已经水落石出了,已经有人解出了暗码,详见置顶楼
总结
该篇是反调试实战这个系列的开篇之作,此次实战只涉及了一个最简单的反调试:IsDebuggerPresent
后续还会不断更新其它反调试实战内容(应该会吧?咕咕咕(づ ̄ 3 ̄)づ),盼望各人多多支持(。・∀・)ノ゙
稍微总结一下此篇的内容:
- 反调试的手段可以大致归纳为:检测、干扰、权限清零 三种
- 分析程序时,可以采取x64dbg搭配IDA Pro 动静结合的方式举行分析
- ASLR可以使用PE工具关闭,之后调试起来更方便
- 反调试的手段五花八门,重点在于怎样定位,一般都是以某个相关函数作为突破口举行分析
此次的实战确实很简单,重点在于分享反调试相关的思绪和x64dbg共同IDA Pro分析的操纵,大佬勿喷
帖子中可能会有不妥之处,欢迎各人指出
附件
CrackMe下载地址
CrackMe.zip
CrackMe源代码
(由于MFC项目还挺大的,这里截取出关键的代码)
反调试代码
unsigned int __stdcall ThreadFun(PVOID pM) //反调试线程{ if (IsDebuggerPresent()) { //使用IsDebuggerPresent判断是否被调试 exit(0); //被调试则退出 return 0; } Sleep(100); //休眠,防止线程创建过多,导致资源占用 HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); //继承创建反调试线程 return 0;}HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); //创建线程暗码验证相关代码
void encodeCString(CString str) { //简单的字符串加密函数 for (int i=0;i 25 || str.GetLength() < 15) { //字符串长度判断 flag = FALSE; encodeCString(errorStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr); } else { //password //610 - 520 - 666 - 233 CString sToken = _T(""); //用来吸收每次分割的字符串 int i = 0; // substring index to extract while (AfxExtractSubString(sToken, str, i, '-')) //以-举行分割 { //.. //work with sToken //.. strList = sToken.Trim(); //字符串去空格 i++; if (i > 4) { //如果分割大于4,则不满足条件 flag = FALSE; encodeCString(errorStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr); break; } } if (i != 4) { //如果分割不即是4,不满足条件 flag = FALSE; encodeCString(errorStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr); } else { for (i = 0; i < 4; i++) { //比力字符串 if(strList.CompareNoCase(correctList.MakeReverse())==-1){ //注意这里的MakeReverse() flag = FALSE; encodeCString(errorStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr); break; } } } } //判断标记 if (flag) { encodeCString(correctStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(correctStr); } Sleep(500); long t2 = GetTickCount64(); //获取结束时间 if (t2 - t1 >= 560) { //如果时间差大于即是560则超时,是被调试的情况 encodeCString(debugStr); GetDlgItem(IDC_STATIC)->SetWindowTextW(debugStr); }}留一份副本在论坛里,土豪专享( •̀ .̫ •́ )✧
CrackMe.zip
67.28 KB, 下载次数: 59, 下载积分: 吾爱币 -1 CB
土豪专享,已更新
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|