首页
社区
课程
招聘
[原创]由一道CTF对10种反调试的探究
发表于: 2020-4-24 09:14 10405

[原创]由一道CTF对10种反调试的探究

2020-4-24 09:14
10405

参考文章:

https://blog.csdn.net/hgy413/article/details/7996652
https://blog.csdn.net/yiyefangzhou24/article/details/6242459
https://www.52pojie.cn/thread-883664-1-1.html

https://bbs.pediy.com/thread-223857.htm

http://bbs.pediy.com/showthread.php?t=31447

https://blog.csdn.net/qq_32400847/article/details/52798050

经过逆向分析首先经过10次反调试,只有在不被调试的情况下才能组成正确的str1,然后对flag普通base64加密,再

进行奇偶位分别与str1与[0ff_404018^3]比较。反调试的函数主要在下面分析。

脚本如下:

看一下反汇编(下面截取了关键的部分)

7C85AA3C    50              push eax                           //eax里是 hProcess

7C85AA3D    6A 07           push 7                            // 这里的7定义是 ProcessDebugPort

7C85AA3F    FF75 08        push dword ptr SS:[ebp+8]                    //hProcess

7C85AA42    FF15 AC10807C  call dword ptr DS:[<&ntdll.NtQueryInform> ]           //ntdll.NTQueryInformationProcess

7C85AA48    85C0            test eax,eax                       //判断

可以看出实际调了NtQueryInformationProcess,它可以将指定类型的进程信息拷贝到某个缓冲,

函数原型如下:

其中ProcessInformationClass中的ProcessDebugPort,它来检索调试器的端口号,只要是非0则有调试器。

Windows在执行异常处理时,无论是内核异常还是用户异常,在进行异常信息的“包装”后,都会到KiDispatchException进行异常的分发,下面逆了此函数的一部分:

内核异常的分发(部分):

用户异常的分发(部分):

​​

可以看出来,无论是用户异常还是内核异常,再进行VEH,SEH之前都会先判断是否用调试器,利用该特征可判断进程是正常运行还是调试运行,然后根据不同的结果执行不同来判断程序是否被调试。

先看一下这个函数

DebugActiveProcess会使调试器附加到获取的进程上并且调试它。此函数的唯一参数是进程的PID。

关键是这个这个句柄在3环时放哪了。我们重新回到ntdll里的DbgUiConnectToDbg()。

发现是存到了TEB+0xF24的地方,此时,DebugObject与调试器建立起了关系。

做反调试的话,可以遍历所有TEB+F24h的位置,如果有值,那肯定在被调试(嘴角疯狂上扬)

​​

OK,差不多了,我们重新回到梦开始的地方DebugActiveProcess,往下看

用到了传进去的参数PID,在下面转换成了被调试进程的句柄存到esi里,紧接着传入了 kernel32!DbgUiDebugActiveProcess(被调试进程句柄),此时,调试器和被调试建立起了联系。

​​之后同样进入了ntdll里UiDebugActiveProcess(被调试进程句柄)

​​

惊喜来了,创造对象--DebugObject 的句柄和被调试进程的句柄都传入了ntdll!NtDebugActiveProcess

跟进去之后同样通过SystemCall进NtDebugActiveProcess(0环),

以下是NtDebugActiveProcess的逆向结果:

​​

进入 _DbgkpSetProcessDebugObject

​​

建立上了调试关系.

在使用 CreateProcess 创建进程时,需要传递 
STARTUPINFO 的结构的指针,
而常常我们并不会一个一个设置其结构的值,
连把其他不用的值清0都会忽略,
而 ollydbg 也这样做了,
我们可以使用 GetStartupInfo 检查启动信息,
如果很多值为"不可理解"的,那么就说明自己不是由 explorer 来创建的.(explorer.exe 使用 shell32 中 ShellExecute 的来运行程序, ShellExecute 会清不用的值)

还有一点 ollydbg 会向 STARTUPINFO 中的   dwFlags 设置 STARTF_FORCEOFFFEEDBACK,而 explorer 不会

这种反调试在ctf中很常见。

首先CreateToolhelp32Snapshot照下快照,记录当前进程,线程信息

利用process32First函数来获得第一个进程的句柄。

可以看出本题比较的是szExeFile进程名称这一参数,来判断是否被调试。

除了本题中查找进程信息,还可以查找窗体信息,和查找调试器引用的注册表项

查找调试器引用的注册表项:

下面是调试器在注册表中的一个常用位置。
SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug(32位系统)
SOFTWARE\Wow6432Node\Microsoft\WindowsNT\CurrentVersion\AeDebug(64位系统)
该注册表项指定当应用程序发生错误时,触发哪一个调试器。默认情况下,它被设置为Dr.Watson。如果该这册表的键值被修改为OllyDbg,则恶意代码就可能确定它正在被调试。

查找窗体信息:

FindWindow函数检索处理顶级窗口的类名和窗口名称匹配指定的字符串。

EnumWindows函数枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数

本题采用的是__rdtsc进行的检测。

rdtsc指令将时间标签计数器读入 EDX:EAX。

(CTF的原题稍后添加到附件)




  最近做的有些ctf中总是出现一些反动态调试的情况。由次对一些常见的反动态调试进行一些总结。既然是调试,趁着这个机会探究了一下调试器如何与被调试进程建立联系的过程。
先运行一下
​​到ida里看一下

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 13
支持
分享
最新回复 (14)
雪    币: 8764
活跃值: (5240)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
学习了。
2020-4-24 13:23
0
雪    币: 8223
活跃值: (6439)
能力值: ( LV12,RANK:207 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2020-4-24 13:56
0
雪    币: 161
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2020-4-24 16:26
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
学习到了感谢分享
2020-4-24 16:59
0
雪    币: 75
活跃值: (156)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
疯狂学习中……
2020-4-25 00:35
0
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
very good
2020-4-26 10:10
0
雪    币: 2677
活跃值: (5340)
能力值: ( LV10,RANK:177 )
在线值:
发帖
回帖
粉丝
8
大牛,我看其他博客说fs指向的是teb,这里为什么要加0x18呢。。另外我做题有碰到个这个*(*(__readfsdword(0x30u) + 0x18) + 12) != 2,能解释一波,谢谢大牛
2020-4-28 14:51
0
雪    币: 1034
活跃值: (227)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
YenKoc 大牛,我看其他博客说fs指向的是teb,这里为什么要加0x18呢。。另外我做题有碰到个这个*(*(__readfsdword(0x30u) + 0x18) + 12) != 2,能解释一波,谢谢大牛
第一fs寄存器在3环时指向teb,在0环时指向KPCR,当3环指向teb时,teb+0x18的位置指向teb的开头,所以fs+0x18也指向teb的开头。
第二问题,我记得没错的话应该是peb—>Process Heap这个位置+12应该是heap. flag这个成员,当没有被调试时这个成员是2,记得不太清了,你可以再查一下
2020-4-28 16:54
0
雪    币: 2677
活跃值: (5340)
能力值: ( LV10,RANK:177 )
在线值:
发帖
回帖
粉丝
10
谢谢大佬,明白了
2020-4-28 19:31
0
雪    币: 141
活跃值: (318)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
可以。
2020-5-7 18:49
0
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
感谢分享!
2020-5-8 10:40
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
13
公众号来的,支持支持
2020-5-9 06:02
0
雪    币: 425
活跃值: (2886)
能力值: ( LV11,RANK:185 )
在线值:
发帖
回帖
粉丝
14
我差点以为是我写的cm,整那么多anti
2020-5-10 18:34
0
雪    币: 344
活跃值: (922)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
BIX
15
师傅,请问一下。第三个反调试,没有处于调试状态的话,OutputDebugStringA调用失败,那会重置错误码,那GetLastError()函数的返回值将不等于12345, 这样的话,哪里正确字符不应该是 A 嘛。
2020-9-22 21:31
0
游客
登录 | 注册 方可回帖
返回
//