在“汇编,让你更拉风”那片文章中我稍微提到了下fs:[0]这个玩意,这里再重复下,实际上fs:[0]这个地址保存着一个很重要的结构体,TEB ( Thread Evirment Block ) 线程块,SEH异常处理,线程的一些相关信息都和这个结构体紧紧相关,而我要说的IsDebuggerPresent实际上也是从这个结构体中获取信息来判断当前线程所在进程是否处于被调试状态,这实际上和TEB中的一个子结构PEB有关,这个api的效率很高,因为它的实现就像这样:
mov eax, fs:[30h]
mov eax, [eax+0Ch] // 取fs段偏移0x30的值村放进eax,实际上偏移0x30就是PEB子结构的开始地址
mov eax, [eax+10h] // 取到标示进程是否被调试的标志变量
只是几句简单的语句,所以你可以在你要反调试的程序中每个一段时间调用一次这个函数来检测进程是否被调试了,但是调用这个api实在是不安全,因为很可能这个函数已经被hook了,你的调用结果将完全被hook这个函数的人控制了,然后我们可以进一步想,我们可以不通过调用这个函数来获取进程是否被调试的信息吗?当然可以,上面那段汇编代码实际上就能做到,但是别以为这样就安全了,调试者一样有办法,既然你都可以直接通过汇编代码来做,调试者当然也就可以,他可能会写出下面这样的代码来设置这个标志标量:
mov eax, fs:[30h]
mov eax, [eax+0Ch]
mov [eax+10h], 0x0
这样即使你不调用他hook掉的IsDebuggerPresent,他一样可以让你获取到的东西无效。那么想想吧,我们的程序在开发期被调试器调试是很正常的,真正发布出去了如果被调试了,说明就有问题了,所以我们可以在发布版中这么做:那么调试者们上面的手段都会失效,我们可以每隔一段时间就判断一下那个标志变量的状态,如果是被调试,就直接退出程序。当然这样也不是就安全了,调试者可能会根据代码的特征找到你相关的代码,然后直接修改,为了防止调试者找到你的代码,你可以使用VMProtect技术,作用就是把你的代码弄的很乱,让人根本看不明白,这样一来,调试者可能得花上很长一段时间来跟踪你的代码了。
完毕!其实在IsDebuggerPresent这种ring3级别的函数上花多心思效果是不大的,真正要做到反调试得考虑在驱动层做手脚,这个就以后再说了。
PS:早上起来就写了这篇日志,早饭都没吃,大家不顶简直对不起我的肚子哈!