首页
社区
课程
招聘
[原创]MFC逆向之CrackMe Level3 过反调试 + 写注册机(二)
发表于: 2023-3-26 19:13 6229

[原创]MFC逆向之CrackMe Level3 过反调试 + 写注册机(二)

2023-3-26 19:13
6229
前言

各位大佬早上好,我是你们好朋友旺仔,今天我们接着分析CrackMe Level3,上一篇文章中是使用的静态分析,借助IDA F5功能还原算法,今天我们来动态分析,并过反调试,在动态分析之前先做一些铺垫

应用级别反调试

先聊聊反调试吧,反调试有两类,一类是应用级别的,一类是内核级别的,今天我们只探讨应用级别的

反调试就是根据一些调试特征做一些检测

例如

1.有一些标志,在正常的运行状态的值跟调试状态的值不一样,或者就是一些API在正常的运行下跟调试状态运行下的结果或返回值不一样,我们就可以拿来进行检测

2.根据运行时间,在正常情况下,我们的一个函数到下一个函数的运行时间也就是0.0000001秒 但是在调试状态下由于单步运行就会让这个运行时间增加,我们可以在第一个函数中获取当前时间,在第二个函数中也获取当前时间,进行计算得到执行的时间,根据执行的时间来检测

3.基于调试器实现的原理做一些手脚,例如调试器在做软件断点的时候 会有CC指令写入,我们就可以对CC进行检测,调试器在做硬件断点的时候会对调试寄存器进行操作,我们可以检测调试寄存器中的值,调试器在做内存访问断点的时候,会修改内存属性,我可以对内存属性进行检测

4.基于异常的机制,我们可以在程序里面制造一些异常,编写异常处理函数,在异常处理函数里面进行检测,根据结果进行不同的异常处理


下面我会简单的介绍一下调试器的实现原理和异常处理,要想详细了解可以看看加密解密4 我记得里面有讲解 不行就看张银傀的软件调试

异常处理

赛选器异常

设置赛选器异常

https://blog.csdn.net/qq_35426012/article/details/102688898


SEH异常

设置SEH异常处理

https://blog.csdn.net/musilintan/article/details/115622182

https://www.cnblogs.com/yilang/p/11233935.html

https://www.cnblogs.com/iBinary/p/7596199.html


在程序当中如果发现有Fs[0] 这种代码 就说明有SEH的存在,如果有SetUnhandledExceptionFilter这种函数 就说明有赛选器的存在


下面是异常处理的顺序

1.系统首先发送给调试器 调试器是优先级最高的

2.如果没有调试器,系统会继续查找线程相关的异常处理

3.每个线程相关的异常处理 例程,可以处理或者不处理这个异常,如果不处理,并且安装了多个线程相关的处理例程,可以交给连起来的其它例程处理

4.都不处理这个异常,在判断程序是否在调试状态,如果在就接着给调试器

5.如果没有的话,或者不处理,那么操作系统就会调用筛选器异常

6.如果没有,那么系统会调用默认的异常处理,也就是崩溃的页面

7.在终结之前,对其展开操作,然后依次调用设置的SEH链表中的回调函数,给予一次最后的清理的机会


上面的处理顺序是我之前学习的时候记录的,我现在已经看不懂了,大概是这个样子

有调试器的时候 先给调试器 如果调试器不处理给SEH SEH不处理给调试器,调试器不处理给赛选器,赛选器不处理给操作系统,操作系统不处理程序崩溃

就是说 调试器优先级最高 应用程序第二 操作系统第三


调试器的部分功能实现原理

整个调试框架是基于异常实现的,要想实现一个简单的调试器非常简单,其实就是调用API,熟练运用这些API则可以进行软件调试


创建调试进程

创建调试进程也就是我们在程序还没有运行起来的时候,拖到调试器里面去运行

使用的API是CreateProcess  创建标志是DEBUG_ONLY_THIS_PROCESS


附加

附加就是在程序运行起来,我们再使用调试器去附加

使用的API是DebugActiveProcess


等待调试器事件

创建调试进程或者附加调试进程完成后都会建立起调试会话,这样被调试程序所发生的所有异常都会通过调试会话发送到我们的调试器进程中,然后我们就可以接收到异常事件,进行处理异常事件

使用的API是WaitForDebugEvent


硬件断点

硬件断点是基于调试器寄存器来进行实现的,有8个调试寄存器 分别是Dr0 - Dr7

Dr0到Dr3 这四个寄存器 它们的作用是存放断点的地址

Dr4到Dr5 这俩个寄存器 暂时不用管

Dr6到Dr7 这俩个寄存器的作用是用来记录你在Dr0-Dr3中下断的地址的属性


软件断点

软件断点是CC异常,也就是把代码修改为0xCC,有一些指令会触发80000003异常,也就是断点异常,常见的指令就是CC 不常见的有一些特权指令


单步异常

在浮点寄存器中有一个标志叫单步标志,设置为1的时候,执行指令就会触发80000004异常,也就是单步异常


内存访问异常

内存访问异常就是通过修改内存属性,让其不可访问,这时候就会触发C05异常,调试器接收到异常后进行处理


介绍反反调试插件和驱动级别调试器工具

反反调试插件和驱动级别的调试器有很多,Github上面也有很多开源的,大多反调试插件都是开源的,下面介绍几个

1.海风月影的StrongOD

2.ScyllaHide

3.CE的DBVM 是一款内核级别的调试器

4.DbgPlugin 是一款内核级别的调试器

使用反反调试插件和驱动级别调试器过反调试

1.我们先来使用OD + StrongOD 附加结果直接闪退 无任何异常提示

2.我们使用x32 + ScyllaHide  附加结果跟OD一样 直接闪退 无任何异常提示

3.我们使用CE的DBVM 附加结果 完美

4.我们使用x32 + DbgPlugin 附加 结果不行 会异常

反调试手段介绍

反调试的手段太多了,下面是我收集的一些文章

https://xz.aliyun.com/t/5408

https://blog.csdn.net/xiao_yi_xiao/article/details/121762629

https://blog.csdn.net/liuhaidon1992/article/details/103888101

分析反调试(一)

我们先来看看这个使用x32 + DbgPlugin 附加 会出现异常的情况 是为什么

这种情况通常都是修改了Eip 使其让代码跑飞,能修改EIP的指令有 CALL JMP RET

我们先通过栈回溯来看看,先回溯一层 来这里下个断点,看看程序能不能正常运行到这里,如果程序能正常运行到这里 那就说明修改EIP的代码就在下面函数中

程序确实会正常运行到这里,说明问题就出现在这俩函数里面,先F8 如果崩溃就说明这个CALL有问题 如果没崩溃就没问题

奔溃之后我们进入崩溃的函数,接着F8 继续排查 反复这种操作 就可以找到修改的位置

我们先F8 看看到底是哪个函数出现了问题

我在运行第一个函数的时候就直接跳飞了的,说明问题就出现在第一个函数里面,F7进入 一直F8 继续排查,结果是把返回地址给改了

我们来看看上面的代码 哪里修改了反回地址呢,发现是Call Next的代码 将返回地址加 60

这个位置会一直来 估计是个定时器吧 然后把它改一下jmp 让它一直走正确的流程即可 正常运行

接着我们来看看它是检测了什么 我们在IDA中定位到刚刚的代码 F5查看 发现有4个函数

下面是函数的实现,都是检测标志的

点击注册会来到这里,这是上一篇文章中的验证函数

我们打开IDA来看看这个异常,这是一个C++异常 下面我用图的方式来给大家看看这个异常

 (2024年4月28号 这是编译器拓展异常 FFFFFFFFE 表示没有上一层)

如果没有看懂没关系 我们自己写一个C++异常来看一下 对着源码来看这个异常

看完这个异常,并没有发现有什么反调试的地方,而且我们的程序已经可以正常调试了的

除了使用x32 + DbgPlugin 我还使用了ScyllaHide 去掉标志检测 好像没什么太大的效果

分析反调试(二)

接着我们来看看为什么使用x32和OD接收不到异常呢,原因是这样的,有一种反调试的方法是隐藏调试器

在入口断点搜索查看模块 搜索这个函数,我是没有找到,没关系我们可以去NTDLL里面去下断点

下完断点后运行,结果各种SEH异常

有很多SEH异常,我没有去管它,让它自己去处理,直接一直运行,到这个地方发现,运行不了的,我们栈回溯看一下哪里调过来的

是这这里调用的,在这个函数下断点,我们来看看

断点来了的,我们F7进入,结果直接就是这代码

我困惑了好久,后面我看了上个函数的逻辑和这个函数的逻辑,发现它是在NTDLL里面复制过来的,我在NTDLL里面下断点后,它直接复制过来 就是INT 3

它这个代码是直接调用三环的最后一个环节 Syscall 这样做可以绕过一些HOOK

我们对它进行打个补丁 直接nop掉 不让他调用这个隐藏线程的API

nop完成后 运行程序 附加 完美接收异常,接下来就是把之前的哪个jmp也打个补丁 即可

现在程序已经可以运行到这里了 是没有问题的 接下来看看验证函数 是否正常

验证函数中发现 402750这个函数 是有问题的 会触发异常,因为在还原算法的文章中 没有用到这个函数 所以直接RET 然后打个补丁

这里进去会崩溃 不能直接Ret 4 如果直接RET会导致程序 无法运行 需要F7进去看看为什么 

结果发现是使用的之前的套路 直接强制改jmp 然后打个补丁

这个函数也是会崩溃 跟402750一样都是在算法当中没有用到的 直接RET 然后打个补丁

程序在非调试状态正常

程序在调试状态 正常

结尾

本文就到此结束,感谢各位大佬观看,没有接触过反调试的朋友可以动手试试,这个程序学习反调试还是非常不错的

另外我这是使用了插件把一些标志给去掉了,要不然分析起来的难度会增加,感兴趣的可以去掉插件分析

我也是刚刚学习逆向的,所以在文章中肯定会有一些错误的地方,希望大佬发现后纠正


文章中用到的软件都是可以在网上下载到的,CrackMeDemo 在上一篇文字中有下载地址


2024年4月28 本篇文章有大量的错误



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2024-6-21 23:19 被旺仔_小可爱编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//