12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 275|回复: 0

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

[复制链接]
发表于 2022-1-28 21:45:28 | 显示全部楼层 |阅读模式
系列

【原创】反调试实战系列一 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)

界面比力简陋,毕竟是个小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);                }}留一份副本在论坛里,土豪专享( &#8226;&#768; .&#811; &#8226;&#769; )&#10023;
CrackMe.zip
67.28 KB, 下载次数: 59, 下载积分: 吾爱币 -1 CB

土豪专享,已更新

来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-4-24 03:56 , Processed in 0.125000 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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