吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 24813|回复: 106
上一主题 下一主题
收起左侧

[调试逆向] 【原创】反调试实战系列一 x64dbg+IDA 过IsDebuggerPresent

    [复制链接]
跳转到指定楼层
楼主
lyl610abc 发表于 2021-5-3 21:02 回帖奖励
本帖最后由 lyl610abc 于 2021-8-8 20:34 编辑

系列

【原创】反调试实战系列一 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)进程环境块 中的BeingDebuggedProcessHeap

除了检测PEB外,还可以检测 软件断点、硬件断点代码校验等等

除了检测,之前提到的插入花指令进行干扰也属于Ring3中的反调试手段

在Ring3下的反调试手段 五花八门,这里仅列举出了一小部分


Ring0

在Ring0下的反调试保护,TenProtect不可谓不强,在先前的【原创】TP驱动保护分析系列一 定位TenProtect保护中已经提及

这里限于篇幅原因不再赘述


反调试实战

前面稍微补充了一点关于反调试的知识,接下来正式进入实战环节

要调试的程序

为了更好地起到学习作用,这次要调试的程序是我自己写的一个小demo,是一个MFC程序(练习了一波MFC)

image-20210503173142596

界面比较简陋,毕竟是个小demo,不要介意

关于这个demo的代码之后也会放在后面的附件里,有需要的可以自行取用(* ̄3 ̄)╭


实战流程

查壳

首先使用PE工具:DIE(Detect It Easy) 查壳

image-20210503182249040

可以看到,程序并没有加壳,并且是64位程序、用MFC编写


关闭ASLR

使用MFC编译出的64位程序默认是开启ASLR的,不利于调试,需要先关闭


什么是ASLR

ASLR全称Address Space Layout Randomization,又称地址空间配置随机化地址空间布局随机化


ASLR的作用

地址空间配置随机加载利用随机方式配置数据地址空间,使某些敏感数据配置到一个恶意程序无法事先获知的地址,令攻击者难以进行攻击

粗俗地说,就是使得每次调试工具(如OD、x64dbg等)加载程序后,地址是随机动态分配的,无法使用固定的地址进行定位


ASLR的体现

上面纯粹的说明可能不是很直观,接下来使用x64dbg载入程序

image-20210503184317064

可以看到,x64dbg默认是中断在了系统断点,我们需要它运行到OEP(程序入口点)

使用快捷键:ALT+F9 运行到OEP(程序没有加壳,所以可以运行到OEP),或者 调试→运行到用户代码


到达OEP


得到了:

image-20210503185325655

可以看到此时的EntryPoint为:

00007FF6950D14F1 | E9 CADE0000              | jmp <crackme.wWinMainCRTStartup>              |

记录下此时的地址为:00007FF6950D14F1

如果学习过PE就会知道 EntryPoint的地址 = EntryPoint + ImageBase

(不了解这个知识点的可以回顾:PE文件笔记五 PE文件头之扩展PE头)

image-20210503185622558

从前面的DIE工具的查看中可以得到:

正常的 EntryPoint的地址 = EntryPoint + ImageBase  = 0x114F1 + 0x140000000 = 0x1400114F1

但是此时的地址很明显不等于0x1400114F1,这就是ASLR的体现


使用PE工具关闭ASLR

知道了ASLR会干扰调试,于是要使用PE工具关闭ASLR

关闭ASLR

ASLR由 扩展PE头中的DllCharacteristics决定,关于DllCharacteristics可参考:DllCharacteristics


验证ASLR的关闭

关闭完ASLR,再使用x64dbg载入程序,查看此时的OEP:

image-20210503191219952

可以发现,此时的EntryPoint地址就和前面计算出来的地址一致,为:0x1400114F1


x64dbg定位反调试

载入程序以后,要先让程序跑起来再设置相关的API断点,于是先运行

使用快捷键:F9 使得程序运行起来

但是当运行F9后,会发现程序直接退出了(不使用调试工具时程序是可以正常运行的);这也就是本帖的关键了:反调试

通过上面的操作,可以推测出:程序检测当前是否正在被调试,如是是则直接退出程序

推断出大致的流程后,可以写出伪代码:

void AntiDebug(){                //反调试函数
    bool IsBeingDebugged=checkIsDebug();//通过某种方式判断当前程序是否正在被调试
    if (IsBeingDebugged){        //如果正在被调试
        exit();                                //退出程序
    }
}

可以得出调用情况为:AntiDebug→exit

于是可以从exit(退出程序)入手,开始定位

退出程序一般会使用到ExitProcess()这个Windows API,于是对这个函数下断点

image-20210503192924459

在底下的命令行输入:

bp ExitProcess

然后可以得到:

image-20210503193044899


确定设置完断点后,按F9让程序运行起来,然后断点断下:

image-20210503193240187


注意堆栈中调用情况:

image-20210503193317550

找到调用的该程序的函数,可以怀疑这个函数相当于AntiDebug函数(用来反调试)


image-20210503193514900

选中这一行,然后回车,查看其对应的反汇编


得到:

image-20210503193606227

不难发现在调用exit函数的前面调用了IsDebuggerPresent来检测是否被调试


IDA Pro分析反调试

为了更清晰地分析代码,使用x64dbg定位到了关键函数后,可以搭配IDA Pro进行分析

使用IDA Pro载入程序后,按G键弹出:

image-20210503194315804


这里要跳转的地址为前面得到的地址:这里填14001A1D8

image-20210503194407263


跳转后得到:

image-20210503194442007


选中函数的头部,按快捷键:F5查看其对应的伪代码:

image-20210503194618513


得到:

image-20210503194802393


即:

__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:

keyPatch


Patch完结果如下:

image-20210503200226681


接下来要将Patch完的结果导出到文件:

Edit→ Patch Program → Apply patches to input file

image-20210503200440658


image-20210503200549048

确定导出即可(导出的时候,记得要先关闭x64dbg,不然程序被占用会无法导出)

image-20210503200642669


验证反调试的处理

此时再使用x64dbg载入程序 并让程序运行起来,可以发现此时就可以正常运行了:

image-20210503201037198


x64定位Crack相关函数

摆脱了反调试的干扰后,终于可以正式开始Crack了

一般来说,对于没有加壳的程序直接搜索字符串即可,但这里字符串是被加密的,于是不能通过字符串直接定位

换个角度,可以通过对相关的API函数下断进行定位

这里可以发现当输入了错误的密码后,等待Crack中变成了密码错误

image-20210503201507775


这很显然是对标签文本的修改,可以尝试对SetWindowTextW下断点:

image-20210503201638038

bp SetWindowTextW

image-20210503201719965


设置完断点后,再点击确定来触发断点:

image-20210503201900612


观察此时的堆栈情况:

image-20210503202152598

可以看到,堆栈中有输入的密码:610 和 要设置的文本:密码错误

于是可以以此为突破口,继续分析


选中 L"密码错误"上面的函数,按回车查看其对应的反汇编

image-20210503202426839


image-20210503202501821


翻看附近的代码,可以看到在代码下方不远处可以找到:

image-20210503203628172

这里就算定位到了关键的函数处,接下来使用IDA Pro进行分析


IDA Pro分析Crack相关函数

在IDA Pro中 按G弹出跳转地址窗口,然后填入要跳转的地址:140019272

image-20210503203721623


得到:

image-20210503203853910


接下来故技重施,选中函数的头部,然后按F5查看伪代码:

image-20210503204015648


得到:

image-20210503204046921

到了这里其实基本上就已经水落石出了,已经有人解出了密码,详见置顶楼


总结

该篇是反调试实战这个系列的开篇之作,此次实战只涉及了一个最简单的反调试: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<str.GetLength();i++)
        {
                str.SetAt(i, -str[i]);                                                //简单的加密
        }
}

CString correctStr = L"密码正确";
CString errorStr = L"密码错误";
CString debugStr = L"检测到被调试";
void CMFCApplication2Dlg::OnBnClickedButton1()                //按钮"确定"的响应事件
{

        // TODO: 在此添加控件通知处理程序代码
        //获取到edit1的内容 然后给edit2赋值
                CString str;
                edit1.GetWindowTextW(str);                                        //获取输入的密码

                WCHAR out[1024];
                CString strList[4];
                CString correctList[4];                                                //用来存放正确的密码,后面拿来比较
                BOOL flag = TRUE;                                                        //标志,用来标记密码是否正确
                correctList[0] = "016";
                correctList[1] = "025";
                correctList[2] = "666";
                correctList[3] = "332";

                encodeCString(correctStr);                                        //简单的加密
                encodeCString(errorStr);                                        //简单的加密
                encodeCString(debugStr);                                        //简单的加密

                long t1 = GetTickCount64();                                        //获取开始时间

                if (str.GetLength() > 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[i] = 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[i].CompareNoCase(correctList[i].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, 下载次数: 147, 下载积分: 吾爱币 -1 CB

土豪专享,已更新

免费评分

参与人数 46威望 +2 吾爱币 +165 热心值 +43 收起 理由
wanao2008 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
11987GENIUS + 1 我很赞同!
junjia215 + 1 + 1 热心回复!
中山小小熊 + 2 + 1 用心讨论,共获提升!
niucaidi + 2 + 1 用心讨论,共获提升!
chengjiu + 1 + 1 谢谢@Thanks!
小马奔腾2 + 1 热心回复!
sam喵喵 + 1 谢谢@Thanks!
shuiyu + 1 + 1 谢谢@Thanks!
kelvar + 1 + 1 谢谢@Thanks!
yixiong201 + 1 + 1 虽然看得不是很懂但是是个好教程啊!多出一些,加油!已关注
wei5383079 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
brosqin114514 + 1 + 1 用心讨论,共获提升!
努力加载中 + 1 + 1 谢谢@Thanks!
chenjingyes + 1 + 1 谢谢@Thanks!
gogobn + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
UniqueLegend + 1 + 1 这篇文章写的相当不错,文内图文结合相当生动
FuSu_ChunQiu + 1 + 1 热心回复!
河南彭于晏 + 1 + 1 用心讨论,共获提升!
hrf321hrf + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
忆魂丶天雷 + 2 + 1 我很赞同!
gaosld + 1 + 1 谢谢@Thanks!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
MasterW + 1 + 1 用心讨论,共获提升!
kwq1995 + 1 + 1 我很赞同!
ming1332236 + 1 + 1 我很赞同!
nmy124 + 1 + 1 谢谢@Thanks!
debug_cat + 2 + 1 大佬v5
一口猪头肉 + 1 + 1 我很赞同!
零夜夜 + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
shiina0831 + 1 + 1 谢谢@Thanks!
Chenda1 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
这个游戏不太行 + 1 + 1 认真的高质量帖子!共同提升!
爱你小吉君 + 1 热心回复!
阿锋01 + 1 + 1 谢谢@Thanks!
涛之雨 + 16 + 1 老咕咕咕怪了
PrincessSnow + 1 + 1 我很赞同!
游叶子明 + 1 + 1 热心回复!
领悟者的涂鸦笔 + 1 一看就会一做就废
正己 + 3 + 1 前排出售楼主大佬的丝袜
zhuzhuxia111 + 1 + 1 我很赞同!
qaz003 + 1 + 1 难得有空收藏好慢慢看
qianshang666 + 1 + 1 我很赞同!
Ps出来的小赵 + 3 + 1 前排出售楼主大佬的丝袜

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

来自 2#
ZJevon 发表于 2021-5-4 10:14
不知道是我理解有问题还是题目本身问题,只要满足  15<=长度=<25 ,4个 - 分隔密码,在分隔符之间数字依次大于610,520,666,233 就能密码正确

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
fengbolee + 1 + 1 用心讨论,共获提升!
lyl610abc + 1 + 1 正解

查看全部评分

来自 #
 楼主| lyl610abc 发表于 2021-5-5 11:56 |楼主
fxinyi 发表于 2021-5-5 08:35
先感谢大佬分享经验,能者为师,请教一下, 也许是用的版本不同, 我用的是PE 3.01 去掉ASLR以后, 重新加载虽然 ...

第一个问题:为什么没有直接跳到一堆JMP的地方?
我解说的版本是Debug版本,后来改成了Release版本,所以反汇编代码会有所差异,但思路没有问题,不影响学习
第二个问题:下断找不到EXITPROCESS
下断要区分大小写,API函数为ExitProcess
第三个问题:直接OD修改可以吗?
可以通过OD直接修改
第四个问题:OD隐藏就可以过掉
因为这里演示的反调试手段十分常见,OD隐藏就可以过掉,毕竟是系列开篇,先拿个简单的演示
来自 #
 楼主| lyl610abc 发表于 2021-5-13 12:22 |楼主
FuSu_ChunQiu 发表于 2021-5-12 23:29
我的ida版本问题吗,怎么是这样的?

我演示的版本为debug版本
因为有人出现需要安装支持库的原因,后续修改为了release版本
所以反汇编代码会有差异
但分析的思路和方法还是可以运用的
推荐
这个游戏不太行 发表于 2021-5-3 23:58
[ 不懂就问 ]
打开/调试CrackMe.exe的时候报错
请问这种情况如何解决(网上说是软件从Visual Studio做出来但没有Release的结果...mfc140ud.dll是VS内部调试工具)
我应该补哪个运行库?

批注 2021-05-03 224944.png (16.8 KB, 下载次数: 2)

错误界面

错误界面

批注 2021-05-03 225115.png (10.09 KB, 下载次数: 2)

批注 2021-05-03 225115.png

批注 2021-05-03 233141.png (36.1 KB, 下载次数: 1)

已安装的运行库

已安装的运行库

免费评分

参与人数 2吾爱币 +3 热心值 +2 收起 理由
fengbolee + 1 用心讨论,共获提升!
lyl610abc + 3 + 1 已经修改为Release版本,感谢指正

查看全部评分

头像被屏蔽
推荐
zhangderek123 发表于 2021-5-4 12:32
提示: 作者被禁止或删除 内容自动屏蔽
推荐
不苦小和尚 发表于 2021-11-28 11:05
本帖最后由 不苦小和尚 于 2021-11-28 12:56 编辑

好像有bug啊,输入1234-5678-1234-5678,也提示成功,知道原因了。CompareNoCase这函数,Unicode环境下,ATL中的CString(包含头文件atlstr.h时)比较结果当双方不等于时将不再返回1或-1,而是返回两者第一处不同点的差值。

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
lyl610abc + 3 + 1 感谢您的宝贵建议,我们会努力争取做得更好!

查看全部评分

5#
浅╮念 发表于 2021-5-3 22:37
太牛了  压根看不懂
6#
qaz003 发表于 2021-5-3 22:50
谢谢分享。。。
难得有空收藏好慢慢看
7#
lock6969 发表于 2021-5-3 23:13
非常不错的讲解,学到了很多!
8#
正己 发表于 2021-5-3 23:30
大佬好可怕
9#
Pwqi 发表于 2021-5-4 05:02
非常不错的讲解,学到了很多!
10#
Christia 发表于 2021-5-4 08:38
牛啊牛啊
11#
love514415 发表于 2021-5-4 08:45
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 10:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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