|
[原创]Visual studio 2008+DDK搭建驱动开发环境(一)
顶一个。。表示懒人....一般用easysys或者visual ddk.... |
|
[求助]windbg 条件断点如何设置?
ba w4 0x******** "p; .if(poi(0x********) == 0 {} . else{gc;})"...类似与这样了吧。。应该。。s 是单步一下运行完这条指令。。ba w4 是硬件写入断点。。。0x********就是你要检测的地址。没有试。。就是个思路。。你按照网介绍windbg条件断点的帖子和 windbg的文档自己想想吧。。 |
|
[求助]windbg 条件断点如何设置?
bp 0x******* ".if(poi(0x12e138) == 0) {;}.else{gc;}";0x******是断点语句的地址。。。 |
|
[求助]《0day2》第12章ret2libc之利用virtualprotect指令查找问题
两句之间是可以有别的指令的。。不一定非得是紧密连在一起的。。但是之间的指令一定不能影响程序的正常功能。 |
|
[求助]自学—病毒分析书籍
PracticalMalwareAnalysis....正在看。。比较新。所以实用 |
|
[分享]终于熬到这一天 50Kx到手 转正了
还差4K的飘过。。。。 |
|
[求助][求助]Winodws平台的Rootkit技术资料……
去编程技术区 逛逛你会发现好多的介绍的 还有一个rootkit专题的东西 kssd里面有 也可以下载chm版本的。。。如果楼主没有驱动开发的知识的话。。建议先看看驱动开发的技术。。可以从<<windows驱动开发技术详解>> 开始。。。 |
|
[求助]为什么找不到壳呢
PEID的特征码可能比较老了,,试试exeinfo pe 0.0.3.0这个特征还算比较新。。。有时候也可以从区段上看出来。。是什么壳。。。 |
|
怎么设置或者有插件可以实现od里的反汇编代码的着色,就像vc+visual assist那样的
不是有那个call jmp je 什么的高亮 和push pop 代码的颜色么。。。我感觉这些就够了。。黑白的 省眼睛啊。。。 |
|
怎么设置或者有插件可以实现od里的反汇编代码的着色,就像vc+visual assist那样的
设置选项->界面选项 的 高亮 和 颜色 , 在反汇编区右键菜单->界面选项 里面选择你设置的方案就可以了。。。不过觉得 od原版的 配置就是最简洁的。。。改的过于花哨更不便与长时间调试。。 |
|
[求助]为什么重新开机,基址不会变的?
这个你的意思是外挂中的机制这个意思?还是ImageBase啊? 咱们说的这个内存地址其实是线性地址。是虚拟映射的不是实际的地址。ImageBase是硬编码在exe中的。每个exe在32位windows中都是独享2G的用户模式的线性内存地址的。多个程序不会冲突。只是映射到内存的时候不会在同样的地址。这个是操作系统和硬件完成的在编程。对用户是透明的。 |
|
[求助]为什么重新开机,基址不会变的?
这个基址我理解就是一个全局变量似的东西。。这个东西除非exe版本更改否则是不改的。。。 |
|
[分享]Anti-Debugging-A Developer's View学习笔记
基于异常处理的检测 依赖于 调试器会内部消化异常不会适当的把他们交给被调试程序处理的事实。 一些调试器中可能会提供设置选择忽略异常还是把异常交给程序处理。但是多数情况下这个功能不是默认开启的。如果调试器不适当的将异常交回给程序,程序可以通过异常处理机制检测到。 M$ Windows用异常链设计来在异常导致程序或者操作系统崩溃之前处理异常。异常链由1个或者多个异常处理程序向量构成,后面紧跟着SEH链 最后是用来处理用其他方式处理失败的异常的未处理异常过滤器(Unhandled exception filter)。 A.INT 3 Exception(0xCC) 通用调试器最基本的操作是用INT 3指令触发一个软件断点。前面已经提到了的硬件断点用另一个中断来触发.Int 3的机器码是0xCC当进程被调试时0xCC会导致调试器捕捉到异常。如果调试器不存在异常会被交给一个异常处理结构来处理。利用这个原理可以检测调试器的存在。 int flag = 0; __try { __asm { int 3; } } __except(EXCEPTION_EXECUTE_HANDLER) { flag = 1; //No Debugger } if (flag == 0) //Detected Debugger; B INT 2D INT 2D 被用来访问核心调试系统。这个指令会创建一个异常记录然后激发一个被核心调试器处理的异常。这个指令最终会被内核或者R3层的调试器截获。我们可以把上个例子的int 3 替换为int 2dh来检测调试器。 C ICE 断点 另一种思路是用ICE断点,这种思路用了一个undocumented操作码我们一般叫做ICE断点来导致一个和int 3一样的中断。我们可以用操作码0xF1来触发这个中断。 例如: flag = 0; __try { __asm { __emit 0xf1 } } __except(EXCEPTION_EXECUTE_HANDLER) { flag = 1; //No Debugger } if (flag == 0) { MessageBox(NULL, L"Detected!", NULL, MB_OK); } D 单步检测 当程序运行时,我们可以告诉线程每运行一条指令后触发一个单步中断。修改EFLAGS寄存器的单步标志可以达到这个目的。我们可以通过pushfd指令把EFLAGS寄存器的值压栈。用or 操作改变标志位。最终用popfd指令存入EFLAGS寄存器。这些操作之后单步中断就被打开了。如果我们的进程正在被调试,调试器会拦截这个异常跳过我们的处理函数。 int flag = 0; __try { __asm { pushfd; or BYTE PTR [esp+1], 1; popfd; nop; } } __except (EXCEPTION_EXECUTE_HANDLER) { flag = 1; //No Debugger Detected } if (flag == 0) { //Debugger Detected } E Unhandle Exception Filter 当在M$ windows操作系统上运行的程序发生异常的时候,会有一些预先定义的异常处理函数。如果如果没有自定义的异常处理函数可以处理发生的异常。最终处理会调用未处理异常过滤器。这个过滤器会接收所有其他异常处理机制不能处理的异常。运用M$提供的API函数我们可以改变未处理异常过滤器的回调函数。 当有调试器存在时。他会在未处理异常过滤器之前插入自己的函数在其之前运行。调试器会在我们的任务回调函数允许我们判断是否有调试器存在之前拦截异常。在例子中我们创建一个当调试器存在时会导致程序崩溃的异常。因为调试器不会处理异常而是允许其运行。当代码在没有调试的情况下运行的时候。未处理异常过滤器会捕获异常然后安全的运行程序。 利用这个反调试方法的第一个步是设置回调函数。这个回调函数在例子中用SetUnhandledExceptionFilter()函数来注册。紧接着这个函数调用的内嵌汇编会导致一个除0异常。 int flag = 0; SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) exHandler); _asm { xor eax,eax; div eax; } //No Debugger Detected 我们的回调函数只是 简单的通过重置默认的未处理异常过滤器然后继续运行处理异常 LONG WINAPI exHandler(PEXCEPTION_POINTERS pExecpPointers) { SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)pExecpPointers-ContextRecord->Eax); pExecpPointers->ContextRecord->Eip += 2; return EXCEPTION_CONTINUE_EXECUTION; } F CloseHandle 另一个琐碎的方法来创建会导致调试器拦截的异常。引发这个异常可以通过调用CloseHandle()关闭一个不合理的句柄。这个调用会直接运行系统调用ZwClose() 引发一个异常。像以上介绍的那些检测方法。我们用__try 和 __except 使得我们的程序可以安全的处理异常。这个特殊的异常机制在程序被调试的情况下会有稍微的不同。 如果调试器存在,这个调用会产生一个异常。 如果没有调试器存在,这个调用只是返回一个错误代码然后继续运行。 基于这样的原理。如果我们的except区的代码被执行过说明调试器存在 __try { CloseHandle((HANDLE)0x12345678); } __except (EXCEPTION_EXECUTE_HANDLER) { flag = 1; //No Debugger Detected } if (flag == 0) { //Detected } Control-C Vectored(向量?) 异常 Vectored 异常处理 是M$操作系统最近(这篇文章为2008年)增加的异常处理机制。Vectored 异常处理首先在异常链中被执行 任意数量的VEH处理可以被链在一起。Vectored异常处理部分可以直接加入到代码中不需要__try和__except 区块。当创建vectored异常处理时 会用到一个链接表结构 允许程序在SEH和最终的unhandled exception filter 之间安装无限制数量的异常处理程序. 当一个命令行模式的应用程序在调试模式下运行键入Crtl-C可以创建一个可以被vectored 异常处理程序检测到并处理的异常。一般命令行模式的应用程序会使用一个处理程序来适当处理这个异常。 如果程序没有在调试器情况下运行,这个信号处理程序会被运行。 通过创建信号处理程序和异常处理程序。我们可以判断是信号处理还是异常处理被运行。 在Visual Studio debugger 异常会在我们的程序中被抛出和处理 AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exhandler); SetConsoleCtrlHandler((PHANDLER_ROUTINE)sighandler, TRUE); success = GenerateConsoleCrlEvent(CTRL_C_EVENT, 0); 以上的代码中有3个函数调用。第一个调用我们增加了一个vectored异常处理程序。然后增加了一个信号处理程序。最后我们触发了一个control-C信号. 在信号处理函数中我们只是简单的处理信号然后继续运行。在异常处理函数中我们假设调试器被发现。代码在被调试的情况下异常处理函数会首先被调用。如果VEH没有运行说明没有调试器存在我们的信号处理函数处理了ctrl-c信号。 具体的异常处理函数由于我以前没有接触过。。我就不实现了。。感兴趣的可以自己查资料实现。。原理和以前的异常检测机制是一样的 H Prefix 处理 当程序中内嵌汇编了 prefix 指令 一些有趣的情况会发生。在一些调试器中prefixes指令可能不会正常的执行。当rep指令紧跟着prefix指令时一些调试器可能只是简单的步过这些指令和接下来的一条指令,而不会真正运行指令refix 和 rep后面紧跟的指令。这会导致我们例子中导致中断发生的代码不会运行,进而导致异常处理函数不会运行。这种情况下我们可以检测到调试器的存在。在用这种方法达到反调试目的时候我们应该确定我们需要检测的调试器可以被这种方法检测到(OllyDbg会被检测到)。 int flag = 0; __try { __asm { __emit 0xf3; //0xf3 0x64 is REFIX REP: __emit 0x64; __emit 0xF1; //Break that gets skipped if debugged } } __except(EXCEPTION_EXECUTE_HANDLER) { flag = 1; // No Debugger Detected } if (flag == 0) // Detected I CMPXCHG8B 和 LOCK LOCK prefix指令 被用来设置一个特殊的标志 在运行接下来的指令。这个特殊的表示被用来确保只有一个处理器可以访问共享的内存区域。LOCK prefix 被用来在多核系统中的同步问题。 CMPXCHG8B指令 比较一个 EDX:EAX 中的64位值与目的操作数。如果相等则把ECX:EBX中的64位值存储到目的操作数中否则目的操作数被存入EDX:EAX中。目的操作数是一个8字节的内存区域 如果这两个指令不适当的组合会造成无效指令错误。这段代码在调试器中运行时调试器会捕获无效指令异常然后终止程序运行。如果没有调试器存在我们可以自己处理这个异常后让程序继续运行。 我们设置一个unhandled exception filter然后用内嵌汇编构造一个这样的异常来达到这个目的。 void error() { //No Debugger Detected return; } ... SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)error); _asm { __emit 0xf0; __emit 0xf0; __emit 0xc7; __emit 0xc8; } J OllyDbg 内存断点 OllyDbg 系统会自动处理一些特殊的异常。如果我们把一个内存页面设置成PAGE_GUARD然后尝试运行这个页面上的代码,OllyDbg会把这个当成自己的内存断点.如果在OllyDbg调试的情况下 我们的代码运行到被保护的内存区域 一个内存断点会发生,这样的话我们的异常处理函数不会被运行。如果没有被OllyDbg调试的话我们的异常处理函数会被运行。 构造这样的代码第一步是需要申请一块内存区域。然后用Return的机器指令填充这块区域。这个可以用VirtualAlloc()和RtlFillMemory 用参数0xc3(ret的机器码)来完成.用ret指令填充的话OllyDbg在处理好断点继续运行时代码会从被设置保护的目标内存区域返回原来的代码区域。 memRegion = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_READWRITE); RtlFillMemory(memRegion, 0x10, 0xc3); 然后我们用VirtualProtect()为申请到的内存区域加上PAGE_GUARD限制。 success = VirtualProtect(memRegion, 0x10, PAGE_EXECUTE_READ | PAGE_GUARD, &oldProt); 然后建立我们的异常处理函数和一个指向申请区域的函数指针.我们在__try部分调用这个函数指针然后在异常处理函数部分放入代表我们的函数没有在OllyDbg中运行的代码 myproc = (FARPROC) memRegion; success = 1; __try { myproc(); } __except (EXCEPTION_EXECUTE_HANDLER) { success = 0; //No Olly&Dbg Detected } 最后我们检测我们的异常处理函数是否被运行了 if (success == 1) //Olly&Dbg Detected 这些基于异常的调试既是有插件存在的情况下也不能完全忽视很可能会给调试带来麻烦。有时候需要手动处理这些Anti这个时候需要一些Windows Seh异常链的知识。了解了SEH链这些东西也没什么神奇的了只要多注意这些引发异常的代码。预先在异常发生之前就设置好异常处理函数起始处的断点然后让程序跑起来触发异常即可。 |
|
[分享]Anti-Debugging-A Developer's View学习笔记
VII 检测代码被修改 自校验可以被用来检测我们的代码是否被修改。调试器在设置软件断点时会把设置断点处的指令的首个字节替换成int 3的操作码0xcc所以通过检测我们的代码是否被修改可以检测到软件断点. 自校验的方法就很多了Hash,Crc等。。这里需要注意的就是: 我们可以通过创建一个函数来完成校验。函数的输入参数 是 代码的起始地址(一般是一个函数指针)和范围一般通过需要校验的函数的下个函数的指针减去需要校验函数的指针得出. 一但我们有了一个有效的函数来计算目标代码区的CRC值 我们可以运行这个函数和预先硬编码的结果比较 或者我们的程序可以多次调用这个函数每次都与上次计算的结果比较。如果Crc校验结果变化了。很高的可能性是软件断点被设置了。一个需要注意的问题是你要确定你的VisualStudio项目设置中关闭了 增量链接 。 否则像例子中这样直接用函数指针可能不会正常工作(当增量链接使用jump thunks 时程序会增加一些跳转表来指示函数的位置,函数指针会指向跳转表而不是函数体) 在我们的例子中 CRCCCITT函数可以被其他的CRC函数或者哈希函数代替 void antidebug(int pass) { printf("Location of runmycode = %08x and antidebug = %08x\n", &runmycode, &antidebug); if (pass == 1) { original_crc = CRCCITT((unsigned char*)&antidebug, (DWORD)&runmycode - (DWORD)&antidebug, 0xffff, 0); } else { the_CRC = CRCCITT((unsigned char*)&antidebug, (DWORD) &runmycode - (DWORD) &antidebug, 0xffff, 0); } retrurn; } void runmycode() { if (the_crc != original_crc) { //Detected } else { //No debugger Detected } } |
|
[分享]Anti-Debugging-A Developer's View学习笔记
VI 基于时间的检测 在调试时一般都会让程序处于单步运行的模式。在这个模式下同样的代码的运行时间会增长很多。我们可以在特定的代码之间调用一些基于时间的函数。取两次的差值与一个合理的范围进行比较来判断程序是否在单步模式下运行从而判断是否被调试 A RDTSC x86系列cpu包括一个64位的时间戳计数器储存处理器从启动以来的时间戳计数(这个计数应该与处理器频率有关)。 RDTSC指令可以读取出这个计数。在C中我们可以调用__rdtsc()函数。 在例子中我们连续两次调用这个函数然后计算两次返回值的差值。将差值与0xff比较。从而判断程序是否单步运行。 i = __rdtsc(); j = __rdtsc(); if (j - i < 0xff) //No Debugger Detected ese //Debugger Detected B NtQUeryPerfoemanceCounter 现代处理器中还包含一些硬件性能计数器。这些计数器是一些储存cpu中的硬件相关的活动的计数。用QueryPerformanceCounter函数可以查询到这个寄存器中的值。看例子 QueryPerformanceCounter(&li); QueryPerformanceCounter(&li2); if ((li2.QuadPart - li.QuadPart) > 0xff) { //Detected } else { //No Detected } C GetTickCount() 我们还可以调用由kernel32.dll导出的API函数GetTickCount()函数来检测。这个函数的返回一个以毫秒为单位的计数。这个数字是从开机启动以来经过的时间。跟之前例子差别最大的地方在于由于单位是毫秒我们用来检测是否为单步操作结果的时间值变成0x10了 li = GetTickCOunt(); li2 = GetTickCOunt(); if ((li2 - li) > 0x10) //Detected else //No Detected D timeGetTime 这个函数跟GetTickCount非常相似。 li = timeGetTIme(); li2 = timeGetTime(); if ((li2 - li) > 0x10) //Detected else //No Detected |
|
[分享]Anti-Debugging-A Developer's View学习笔记
基于硬件和寄存器的检测 1.硬件断点 断点是一个告诉调试器在特定情况下停止进程运行标志。调试器可以用多种方法创建断点。 软件断点和硬件断点产生的结果是一样的。但是实现上有区别。软件断点是由调试器将指令操作码更改成断点操作码完成的。程序的原始操作码存放在一个表中当断点发生后调试器会将原始操作码写回原地址。这样程序可以继续运行。 硬件断点是由处理器的硬件自己实现的。这些硬件包括专门用来保存断点地址的寄存器。当总线上的地址与专用寄存器里的地址相同的时候,INT 1中断信号被激发。cpu会停止运行。x86系列cpu有8个调试寄存器Dr0~Dr7.Dr0~Dr3保存中断的地址。Dr7包含一些标志位用来启用或忽略Dr0~Dr3寄存器中的断点.Dr6是一个状态寄存器来允许调试器知道那个调试寄存器的断点被触发. 调用GetCurrentThreadContext可以读取调试寄存器的信息。然后我们比较调试寄存器和0x00可以得知当前处理器是否有硬件断点。 GetThreadContext(GetCurrentThread(),&ctx); if ((ctx.Dr0 != 0x00) || (ctx.Dr1 != 0x00) || (ctx.Dr2 != 0x00) || (ctx.Dr3 != 0x00) || (ctx.Dr6 != 0x00) || (ctx.Dr7 != 0x00)) { MessageBox(NULL, L"Detected!", NULL, MB_OK); } else { MessageBox(NULL, L"Not Detected!", NULL, MB_OK); } 个人感觉这个思路没问题。。不过在我在看雪下载的汉化原版Od里面。。我都没有检测到。。。 还有常见的Anti思路是把Dr0 Dr1 Dr2 Dr3 都设置成0x00破环已经设置好的断点 近来对于虚拟机的研究尤其是对VMware虚拟机的研究使得程序可以检测出运行在真实系统还是处于虚拟机。 虽然这些方法不是专门用来进行反调试的。但是由于大多数的逆向工作出于安全的考虑是在仿真环境下完成的。特别是恶意软件的调试与逆向。反调试代码可以被设计成只能在真实的本地系统下运行。或者至少帮助 确定一个是启发式的值代表逆向或者调试会话。 虚拟机必须模拟一些硬件资源来使操作系统的像预期的那样启动和工作。这些资源包括寄存器和一些特定的处理器部件。对于反调试有用的是这些虚拟机程序模拟的寄存器可以被没有特权等级的读取。Interrupt Descriptor Table Register(IDTR),全局描述附表寄存器(GDTR),和本地描述附表寄存器(LDTR)都显示出这样的特殊行为。 这些寄存器中的数据指向一些特殊的数据。换一种说法 指向特定数据的指针保存在这些寄存器中因为虚拟机必须模拟这些寄存器,这些指针必须指向不同的区域。基于我们从寄存器中得到的interrupt,global and local Descriptor tables 我们可以判断操作系统是否在虚拟机下运行 GDTR 和 IDTR在多核的处理器情况下用于检测是否为虚拟机是不准确的。因为多处理器系统中每个不同的核心有不同的值.LDTR寄存器 在所有的核心和处理器之间都是可以用来检测虚拟机存在与否的。 LDTR寄存器保存的值高位两字节在实体机系统中一直为0x00 unsigned char ldt_info[6]; int ldt_flag = 0; _asm { sldt ldt_info; } if ((ldt_info[0] != 00) && (ldt_info[1] != 0x00)) ldt_flag = 1; if (ldt_flag == 1) //VMware Detected else //No VMware C STR 寄存器 STR寄存器的原理类似于LDT寄存器,我们可以用汇编指令 str 来储存STR寄存器的内容到内存中如果第一字节的内容为0x00第二字节的内容等于0x40我们可以确定程序在Vmware下运行 unsigned char mem[4] = {0, 0, 0, 0}; _asm str mem; printf("\n[+] Test 4: STR\n"); printf("STR base: 0x%02x%02x%02x%02x\n", mem[0], mem[1], mem[2], mem[3]); if ((mem[0] == 0x00) && (mem[1] == 0x40)) //VMware Detected else //No Debugger Detected //由于我用的是VirtualBox虚拟机 而作者的这些都是根据Vm的虚拟机的检测方法。。我没有实验。。不过感觉思路很不错。。听起来像是真的。。 |
|
[分享]Anti-Debugging-A Developer's View学习笔记
OllyDbg 还有一个文件名bug,不能调试文件名以 %s%s 开头的文件。但是攻击者可以很容易的修改文件名。所以我们还需要在程序中检测文件名称是否被更改. 假设例子程序问件名为%s%s.exe GetModuleFileName(0, (LPWCH)&pathname, MAX_PATH); filename = wcsrchr(pathname, L'\\'); if (wcsncmp(filename, L"\\%s%s.exe", 9) == 0) { //没有发现调试器 } else { //文件名被修改 } 当然这个东西还是很容易被pach掉的。。。配合花指令代码混淆一类的东西使用应该能稍微增加一点实用性... 调试Api类似于IsDebuggerPresent(); 其实是在进程的PEB中读取标志检测。Api可能会被hook返回值被修改等。我们可以自己读取标志位。 NtQueryInformationProcess 的第二个参数为ProcessBasicInformation时函数会返回PROCESS_BASIC_INFORMATION structure构的指针. 结构如下 typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3; } PROCESS_BASIC_INFORMATION; 详细解释参见MSDN这样我们就可以得到PEB的指针了。。当然很多朋友喜欢mov eax,fs:[30h]的这种汇编方式。。这里只是提供另一个个方法。。其他的就是PEB结构的知识了。。 msdn里有一部分通用的 还有一些东西是未文档化的(也就是微软没有明确表名是这样)参见论坛里的精华贴或者是这个网站http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html PEB中还有很多有用的结构可以用来检测调试器 NtGlobalFlag 进程在被调试的情况下运行会与不被调试有所不同。尤其是在创建内存堆时。告知内核怎样创建堆的信息是被存放在PEB结构中的0x68偏移处的。当一个进程被调试的情况下运行 该信息会被设置成标志***_HEAP_ENABLE_TAIL_CHECK(0x10)、 ***_HEAP_ENABLE_FREE_CHECK(0x20)、***_HEAP_VALIDATE_PARAMETERS(0x40)。我们可以通过检测此数据是否为0x70(这三个标志的和)利用这一个特点来判断我们的进程是否被调试 例子: //很多东西在前面的例子中已经详细介绍了这里只列出主要部分 hmod = LoadLibrary(L"ntdll.dll"); _NtQueryInformationProcess = GetProcAddress(hmod, "NtQueryInformationProcess"); hnd = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCUrrentProcessId()); status = (_NtQueryInformationProcess)(hnd, ProcessBasicInformation, &pPIB, sizeof(PROCESS_BASIC_INFORMATION), &bytesWritten); value = (pPIB.PebBaseAddress); value = value + 0x68; printf("FLAG DWORD : %08x\n", *value); if (*value == 0x70) { //Detected } else { //No Detected } //接下来是一个Vista下的技术 由于我只有Xp系统这个还并未测试是否有错或者是否已被插件屏蔽 Vista TEB System DLL Pointer 这个反调试技术是Vista操作系统特有的,当一个进程没有被调试的时候主线程的TEB包含一个指向系统dll名字的Unicode字符串。这个指针位于TEB0xBFC偏移。一般这个Unicode字符串在0xC00偏移。 当进程在被调试的情况下运行。这个系统dll的名称会被替换为"HookSwitchHookEnabledEvent" 使用这个技术,我们应该在函数中首先检查程序是否在Vista系统上运行。如果进程在Vista系统上运行我们在用下面的代码来取得TIB void * getTib() { void *pTib; _asm { mov eax,fs:[18h] //fs:[18h]是TIB的地址 mov [pTib],eax } return pTib; } 得到了TIB的指针之后,读取出0xBFC偏移的内容。如果这个值是0x00000c00我们就可以在0xC00偏移出读出字符串比较他是否是HookSwitchHookEnableEvent wchar_t *hookStr = L"HookSwitchHookEnabledEvent"; strPtr = TIB + 0xBFC; delta = (int)(*strPtr) - (int)strPtr; if (delta == 0x4) { if (wcscmp(*strPtr, hookStr) == 0) { //Detected } else { //No } } else { //No Detect } PEB PRocessHeap Flag Debugger 在PEB中还有存有一个用来告知内核如何为堆分配内存附加结构的指针。这个标志也可以用来检测进程是否被调试。这个结构的一个指针,存放在PEB的 0x18偏移处。在这个结构的0x10偏移处会有一个特殊的标志告知内核是否有调试器存在。如果这个值非零说明调试器存在. __asm { MOV EAX,DWORD PTR FS:[18h] MOV EAX,DWORD PTR [EAX+30h] MOV EAX,DWORD PTR[EAX+18h] CMP DWORD PTR DS:[EAX+10h],0 JNE DebuggerDetecte } MessageBox(NULL, L"Not Detected!", NULL, MB_OK); break; DebuggerDetecte: MessageBox(NULL, L"Detected!", NULL, MB_OK); break; LDR_Module PEB的0xc偏移处是Ldr_data的指针在被调试的情况下Ldr_Module会有特殊的FEEEFEEE这个DWORD值存在我们只要在Ldr_Module中查找这个值即可。 例子如下: flag = 0; walk = ldr_module; __try { while (*ldr_module != 0xEEFEEEFE)//这个是因为Little-Endian的原因 { printf("Value at pointer : %08x\n", *ldr_module); walk = walk + 0x01;//walk 是一个字节 ldr_module = walk; } } __except (EXCEPTION_EXUTE_HANDLER) { flag = 1; //No Debugger Detected } if (flag == 0) //Debugger Detected 运行这段代码后有两种结果如果这个值被找到则不会触发异常。否则没有找到一直运行到触发内存访问异常。在异常处理中设置标志位则可以判断是否被调试 |
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值