检查进程是否被调试
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了检查进程是否被调试,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6292字,纯文字阅读大概需要9分钟。
内容图文
![检查进程是否被调试](/upload/InfoBanner/zyjiaocheng/981/9e645c60e7404d888e8c1a0a1f358505.jpg)
转自:http://www.cnblogs.com/this-543273659/archive/2013/03/04/2943380.html
在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术,首先我们来看看反调试技术。 ? 一、Windows API方法 ? Win32提供了两个API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用来检测当前进程是否正在被调试,以IsDebuggerPresent函数为例,例子如下: ? BOOL ret = IsDebuggerPresent(); printf("ret = %d\n", ret); ? 破解方法很简单,就是在系统里将这两个函数hook掉,让这两个函数一直返回false就可以了,网上有很多做hook API工作的工具,也有很多工具源代码是开放的,所以这里就不细谈了。 ? ? 二、查询进程PEB的BeingDebugged标志位 ? 当进程被调试器所附加的时候,操作系统会自动设置这个标志位,因此在程序里定期查询这个标志位就可以了,例子如下: ? bool PebIsDebuggedApproach() { ? ? ? ?char result = 0; ? ? ? ?__asm ? ? ? ?{ // 进程的PEB地址放在fs这个寄存器位置上 ? ? ? ? ? ? ? mov eax, fs:[30h] // 查询BeingDebugged标志位 ? ? ? ? ? ? ? mov al, BYTE PTR [eax + 2]? ? ? ? ? ? ? ? mov result, al ? ? ? ?} ? ? ? ? ?return result != 0; } ? ? 三、查询进程PEB的NtGlobal标志位? ? 跟第二个方法一样,当进程被调试的时候,操作系统除了修改BeingDebugged这个标志位以外,还会修改其他几个地方,其中NtDll中一些控制堆(Heap)操作的函数的标志位就会被修改,因此也可以查询这个标志位,例子如下: ? bool PebNtGlobalFlagsApproach() { ? ? ? ?int result = 0; ? ? ? ? ?__asm ? ? ? ?{ ??// 进程的PEB ? ? ? ? ? ? ? mov eax, fs:[30h] ??// 控制堆操作函数的工作方式的标志位 ? ? ? ? ? ? ? mov eax, [eax + 68h] ??// 操作系统会加上这些标志位FLG_HEAP_ENABLE_TAIL_CHECK,? ??// FLG_HEAP_ENABLE_FREE_CHECK and FLG_HEAP_VALIDATE_PARAMETERS, ??// 它们的并集就是x70 ??// ??// 下面的代码相当于C/C++的 ??// ? ? eax = eax & 0x70 ? ? ? ? ? ? ? and eax, 0x70 ? ? ? ? ? ? ? mov result, eax ? ? ? ?} ? ? ? ? ?return result != 0; } ? ? 四、查询进程堆的一些标志位 ? 这个方法是第三个方法的变种,只要进程被调试,进程在堆上分配的内存,在分配 的堆的头信息里,ForceFlags这个标志位会被修改,因此可以通过判断这个标志位的方式来反调试。因为进程可以有很多的堆,因此只要检查任意一个堆 的头信息就可以了,所以这个方法貌似很强大,例子如下: ? bool HeapFlagsApproach() { ? ? ? ?int result = 0; ? ? ? ? ?__asm ? ? ? ?{ ?? ? ?// 进程的PEB ? ? ? ? ? ? ? mov eax, fs:[30h] ?? ? ?// 进程的堆,我们随便访问了一个堆,下面是默认的堆 ? ? ? ? ? ? ? mov eax, [eax + 18h] ??// 检查ForceFlag标志位,在没有被调试的情况下应该是 ? ? ? ? ? ? ? mov eax, [eax + 10h] ? ? ? ? ? ? ? mov result, eax ? ? ? ?} ? ? ? ? ?return result != 0; } ? ? 五、使用NtQueryInformationProcess函数 ? NtQueryInformationProcess函数是一个未公开的 API,它的第二个参数可以用来查询进程的调试端口。如果进程被调试,那么返回的端口值会是-1,否则就是其他的值。由于这个函数是一个未公开的函数,因 此需要使用LoadLibrary和GetProceAddress的方法获取调用地址,示例代码如下: ? // 声明一个函数指针。 typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)( ? ? ? ?HANDLE processHandle, ? ? ? ?PROCESSINFOCLASS processInformationClass, ? ? ? ?PVOID processInformation, ? ? ? ?ULONG processInformationLength, ? ? ? ?PULONG returnLength); ? bool NtQueryInformationProcessApproach() { ? ? ? ?int debugPort = 0; ? ? ? ?HMODULE hModule = LoadLibrary(TEXT("Ntdll.dll ")); ? ? ? ?NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess"); ? ? ? ?if ( NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)7, &debugPort, sizeof(debugPort), NULL) ) ? ? ? ? ? ? ? printf("[ERROR NtQueryInformationProcessApproach] NtQueryInformationProcess failed\n"); ? ? ? ?else ? ? ? ? ? ? ? return debugPort == -1; ? ? ? ? ?return false; } ? ? 六、NtSetInformationThread方法 ? 这个也是使用Windows的一个未公开函数的方法,你可以在当前线程里调用 NtSetInformationThread,调用这个函数时,如果在第二个参数里指定0x11这个值(意思是 ThreadHideFromDebugger),等于告诉操作系统,将所有附加的调试器统统取消掉。示例代码: ? // 声明一个函数指针。 typedef NTSTATUS (*NtSetInformationThreadPtr)(HANDLE threadHandle, ? ? ? ?THREADINFOCLASS threadInformationClass, ? ? ? ?PVOID threadInformation, ? ? ? ?ULONG threadInformationLength); ? void NtSetInformationThreadApproach() { ? ? ? HMODULE hModule = LoadLibrary(TEXT("ntdll.dll")); ? ? ? NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hModule, "NtSetInformationThread"); ? ?? ? ? ? NtSetInformationThread(GetCurrentThread(), (THREADINFOCLASS)0x11, 0, 0); } ? ? 七、触发异常的方法 ? 这个技术的原理是,首先,进程使用 SetUnhandledExceptionFilter函数注册一个未处理异常处理函数A,如果进程没有被调试的话,那么触发一个未处理异常,会导致操 作系统将控制权交给先前注册的函数A;而如果进程被调试的话,那么这个未处理异常会被调试器捕捉,这样我们的函数A就没有机会运行了。 ? 这里有一个技巧,就是触发未处理异常的时候,如果跳转回原来代码继续执行,而 不是让操作系统关闭进程。方案是在函数A里修改eip的值,因为在函数A的参数_EXCEPTION_POINTERS里,会保存当时触发异常的指令地 址,所以在函数A里根据这个指令地址修改寄存器eip的值就可以了,示例代码如下: ? // 进程要注册的未处理异常处理程序A LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei) { ? ? ? ?SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) ? ? ? ? ? ? ? pei->ContextRecord->Eax); ? ? ? ?// 修改寄存器eip的值 ? ? ? ?pei->ContextRecord->Eip += 2; ? ? ? ?// 告诉操作系统,继续执行进程剩余的指令(指令保存在eip里),而不是关闭进程 ? ? ? ?return EXCEPTION_CONTINUE_EXECUTION; } ? bool UnhandledExceptionFilterApproach() { ? ? ? ?SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); ? ? ? ?__asm ? ? ? ?{ ? ? ? ? ? ? ? // 将eax清零 ? ? ? ? ? ? ? xor eax, eax ? ? ? ? ? ? ? // 触发一个除零异常 ? ? ? ? ? ? ? div eax ? ? ? ?} ? ? ? ? ?return false; } ? 八、调用DeleteFiber函数 ? 如果给DeleteFiber函数传递一个无效的参数的 话,DeleteFiber函数除了会抛出一个异常以外,还是将进程的LastError值设置为具体出错原因的代号。然而,如果进程正在被调试的话,这 个LastError值会被修改,因此如果调试器绕过了第七步里讲的反调试技术的话,我们还可以通过验证LastError值是不是被修改过来检测调试器 的存在,示例代码: ? bool DeleteFiberApproach() { ? ? ? ?char fib[1024] = {0}; ? ? ? ?// 会抛出一个异常并被调试器捕获 ? ? ? ?DeleteFiber(fib); ? ? ? ? ?// 0x57的意思是ERROR_INVALID_PARAMETER ? ? ? ?return (GetLastError() != 0x57); }内容总结
以上是互联网集市为您收集整理的检查进程是否被调试全部内容,希望文章能够帮你解决检查进程是否被调试所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。