===============================
函数的原型可以从连接http://msdn.microsoft.com/en-us/library/windows/desktop/ms680345(v=vs.85).aspx或者,如下所示:
BOOL WINAPI IsDebuggerPresent( void );
这个函数的用途就是用来检查程序是否在ring 3调试器中运行。关于这个函数google出来一大把的文章,这个函数很被鄙视~~~~
我自己写了一个hello world的小程序来调试调试,程序代码如下:
#include <windows.h>
#include <stdio.h>
#define _WIN32_WINNT 0x0500
int main( void )
{
bool isDebugged;
while ( true ) {
isDebugged = IsDebuggerPresent();
if ( isDebugged ) {
printf( "[-] kao, go away!\n" );
exit( -1 );
}
printf( "[+] Hello World!\n" );
}
return 0;
}
这个程序的目的是在console下输出hello world,如果程序没有在调试器中运行,使用了一个死循环,一直在hello world,
如果检查到程序在调试器中,程序就会退出。
接下来,我们的任务就是娱乐下可怜的hello world,让它以为没有人在调戏它。
=======================================
调试过程:
=========
第一步:先让cmd下让hello world跑起来,可以在控制台下看到它不知疲倦的一直在hello world。
第二步:打开windbg attach到这个程序
这个时候,程序会被断下来:
ntdll!DbgBreakPoint:
7c92120e cc int 3
现在可以给IsDebuggerPresent函数下断点了;
bp Kernel32!IsDebuggerPresent
现在可以用bl查看下断点,应该设置好了断点。
输入g,程序会在IsDebuggerPresent的入口处停下来
查看汇编代码:
7c813132 90 nop
kernel32!IsDebuggerPresent:
7c813133 64a118000000 mov eax,dword ptr fs:[00000018h] fs:003b:00000018=7ffdf000
7c813139 8b4030 mov eax,dword ptr [eax+30h]
7c81313c 0fb64002 movzx eax,byte ptr [eax+2]
7c813140 c3 ret
7c813141 90 nop
7c813142 90 nop
红色的代码就是这个函数的实现了。
我们现在可以用p单步步过这几条指令,会到用户空间。
0040102f 7448 je antidebug!main+0x69 (00401079)
00401031 8bf4 mov esi,esp
00401033 ff153c514200 call dword ptr [antidebug!_imp__IsDebuggerPresent (0042513c)]
00401039 3bf4 cmp esi,esp
现在可以查看下eax的值为1,说明程序是在被调试的。
接下来,我们可以选择修改eax的值,这样这一轮循环,可以正常的输出hello world了,但下一次又需要重复这个过程了。
另一选择就是在内存中抹掉对这个函数的调用,从上面蓝色的指令行可以知道内存0x401033地址处开始的连续6个字节中存储了对这个函数调用的opcode。
我们可以用以下指令修改这6个字节
ew 0x401033 0xc031 修改内存
ew 0x401035 0xc031
ew 0x401037 0xc031
0xc031 为xor eax, eax的opcode。
再用reax=0修改eax的值。
bc 0 去掉断点,现在g一下,就可以看到永不知疲倦的hello world了。
这个方法虽然管用,但是很野蛮、暴力,我们还有另一个更好的选择。
现在我们再回来看看IsDebuggerPresent函数的实现。
7c813133 64a118000000 mov eax,dword ptr fs:[00000018h] fs:003b:00000018=7ffdf000
7c813139 8b4030 mov eax,dword ptr [eax+30h]
7c81313c 0fb64002 movzx eax,byte ptr [eax+2]
7c813140 c3 ret
Windows为每一个运行的进程在用户空间建立了一个进程环境块peb,我们可以用dt _peb来查看下这个数据结构,
每个进程默认有一个主线程,在用户空间为每一个线程建立一个线程环境块teb,同样也可以用dt _teb来查看下下这个数据结构。
其实teb据说以前叫tib,后来改名为teb了,但在数据结构的定义中teb的第一个元素就是tib,tib应该是win98时候的名称,
后来可能扩展了,改名为teb,但teb第一个包含了tib,使得它跟以前的tib兼容。
tib便宜0x18的地方有一个指针 self,这个指针指向了tib的起始地址,其实tib的起始地址就是teb的起始地址,所有从
这个指针我们就可以知道teb的起始地址了,从teb的数据结构中可以知道teb便宜0x30的地方也有一个指针,这个指针说明了
该线程是属于哪一个进程拥有的,指向了peb的起始地址,在从peb的数据结构中可以知道peb便宜0下的地方有一个变量,
:001> dt _peb 7ffd7000
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0x1 ''
+0x003 SpareBool : 0 ''
这个标红的变量指明了进程是否是出于被调试状态。本质上IsDebuggerPresent就是检查了这个变量。fs总是指向当前运行的线程的teb,现在应该很好理解IsDebuggerPresent的实现了,接下来我们就可以用eb直接修改BeingDebugged变量了。
到次关于IsDebuggerPresent的讨论应该可以暂时告一段落了。