<逆向工程核心原理> 动态反调试技术总结

来源:互联网 发布:java获取tomcat版本号 编辑:程序博客网 时间:2024/05/16 10:25

1.异常-SEH

#include "stdio.h"#include "windows.h"#include "tchar.h"void AD_BreakPoint(){    printf("SEH : BreakPoint\n");    __asm {        // install SEH        push handler        push DWORD ptr fs:[0]        mov DWORD ptr fs:[0], esp        //INT 3 指令是CPU中断命令,在用户模式的调试器啥都不做(经过调试发现不会触发ntdll.dll中的KiUserExceptionDispatcher)。        // generating exception        int 3        // 1) debugging        //    go to terminating code        mov eax, 0xFFFFFFFF        jmp eax                 // process terminating!!!        // 2) not debugging        //    go to normal codehandler:        mov eax, dword ptr ss:[esp+0xc]        mov ebx, normal_code        mov dword ptr ds:[eax+0xb8], ebx        xor eax, eax        retnnormal_code:        //   remove SEH        pop dword ptr fs:[0]        add esp, 4    }    printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    AD_BreakPoint();    return 0;}

注意:OD的异常选项 忽略int 3 中断必须不选,strongOD插件选项最好不用,才能进入指令

 mov eax, 0xFFFFFFFF

jmp eax           

OD不能在int 3下断(F2),否则会循环进入异常地址 int 3.



2.异常-SetUnhandledExceptionFilter

#include "stdio.h"#include "windows.h"#include "tchar.h"LPVOID g_pOrgFilter = 0;LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS pExcept){    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)g_pOrgFilter);    // 8900    MOV DWORD PTR DS:[EAX], EAX    // FFE0    JMP EAX    pExcept->ContextRecord->Eip += 4;    return EXCEPTION_CONTINUE_EXECUTION;}void AD_SetUnhandledExceptionFilter(){    printf("SEH : SetUnhandledExceptionFilter()\n");//SetUnhandledExceptionFilter()来注册新的Top Level Exception Filter回调函数。//触发异常时,系统在前面没有处理异常的情况下,会调用Kernel32.dll中的//UnhandledExceptionFilter()函数。UnhandledExceptionFilter()会利用//ntdll.dll中的NtQueryInformationProcess()来判断是否被调试,//若判断在被调试,异常给调试器(调试器无法处理异常,进程终止)。//若判断未被调试,则调用Top Level Exception Filter回调函数。g_pOrgFilter = (LPVOID)SetUnhandledExceptionFilter(                                (LPTOP_LEVEL_EXCEPTION_FILTER)ExceptionFilter);    __asm {        xor eax, eax;        mov dword ptr [eax], eax        jmp eax                         }        printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    AD_SetUnhandledExceptionFilter();    return 0;}

3.Timing Check

#include "stdio.h"#include "windows.h"#include "tchar.h"void DynAD_RDTSC(){//Timing Check 技术通过计算运行时间的差异来反调试,反模拟。//1.Counter based method//RDTSC 汇编指令(RD==READ TSC == Time Stamp Counter)//x86 CPU中存在一个名为TSC(Time Stamp Counter,时间戳计数器)的64位寄存器。//CPU对每个Clock Cycle(时钟周期)计数,然后保存到TSC.RDTSC是一条汇编指令,//用来将TSC值读入EDX:EAX寄存器)。  //OD中的插件Olly Advanced->options  反调试2 Anti-RDSTC(基于驱动模式可以绕过),此功能有可能造成死机。  //kernel32!QueryPerformanceCounter()/ntdll!NtQueryPerformanceCounter()  //kernel32!GetTickCount()  //2.Time based method  //timeGetTime()  //_ftime()      DWORD dwDelta = 0;    printf("Timing Check (RDTSC method)");    __asm {pushad//0F31      rdtscrdtscpush edxpush eax//用于消耗时间的循环(实际代码相当复杂)xor eax, eaxmov ecx, 0x3e8_LOOP_START:inc eaxloop _LOOP_STARTrdtscpop esi// eaxpop edi// edx// check high order bitscmp edx, edija _DEBUGGER_FOUND// check low order bitssub eax, esi        mov dwDelta, eaxcmp eax, 0xffffffjb _DEBUGGER_NOT_FOUND        // debugger found -> crash!!!_DEBUGGER_FOUND:xor eax, eaxmov [eax], eax        // debugger not found_DEBUGGER_NOT_FOUND:popad}        printf(" : delta = %X (ticks)\n", dwDelta);    printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    DynAD_RDTSC();    return 0;}


4.陷阱标志

#include "stdio.h"#include "windows.h"#include "tchar.h"void DynAD_SingleStep(){   //陷阱标志指EFLAGS寄存器的第9个(Index8)比特位。   //TF值设置为1时,CPU将进入单步执行(Single Step)模式。单步直行模式中,CPU执行1条指令后即  //触发一个EXCEPTION_SINGLE_STEP(0x80000004)异常,异常地址为下一条指令。然后陷阱标志会  //自动清零。    printf("Trap Flag (Single Step)\n");    __asm {        // install SEH        push handler        push DWORD ptr fs:[0]        mov DWORD ptr fs:[0], esp                //因无法修改EFLAGS,故通过栈修改        pushfd        or dword ptr ss:[esp], 0x100        popfd                //执行完nop指令后,才触发EXCEPTION_SINGLE_STEP异常,OD在nop指令使用F7,F8,F9都可以。        //即KiUserExceptionDispatcher的异常地址指向 mov eax,0xFFFFFFFF        //1)若为正常运行,则运行前面注册过的SEH        //2)若为调试运行,则继续执行以下指令,不管使用F7,F8,F9都断在mov eax,0xFFFFFFFF        nop        // 1) debugging        //    go to terminating code        mov eax, 0xFFFFFFFF        jmp eax                 // process terminating!!!        // 2) not debugging        //    go to normal codehandler:        mov eax, dword ptr ss:[esp+0xc]        mov ebx, normal_code        mov dword ptr ds:[eax+0xb8], ebx        xor eax, eax        retnnormal_code:        //   remove SEH        pop dword ptr fs:[0]        add esp, 4    }    printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    DynAD_SingleStep();    return 0;}


#include "stdio.h"#include "windows.h"#include "tchar.h"void DynAD_INT2D(){//INT 2D 原为内核模式中用来触发异常的指令,也可以在用户模式下触发异常。但程序//调试运行时不会触发异常,只是忽略。//INT 2D指令会造成两个有趣的现象//1.在调试模式中执行INT2D指令后(F7,F8),下条指令的第一个字节将被忽略,后一个字节会被识别为新的指令继续执行。此特性,可用于代码混淆。//2.在调试模式中执行INT2D指令后(F7,F8),程序不会停在其下条指令开始的地方,而是一直运行,直到遇到断点(原有的代码字节顺序被打乱,OD的BUG)。    BOOL bDebugging = FALSE;    __asm {        // install SEH        push handler        push DWORD ptr fs:[0]        mov DWORD ptr fs:[0], esp                //可以在执行到此条指令时,修改EFlags寄存器TF=1,然后就能进入SEH处理函数        int 0x2d        nop        mov bDebugging, 1        jmp normal_codehandler:        mov eax, dword ptr ss:[esp+0xc]        mov dword ptr ds:[eax+0xb8], offset normal_code        mov bDebugging, 0        xor eax, eax        retnnormal_code:        //   remove SEH        pop dword ptr fs:[0]        add esp, 4    }    printf("Trap Flag (INT 2D)\n");    if( bDebugging )  printf("  => Debugging!!!\n\n");    else              printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    DynAD_INT2D();    return 0;}



5.0xCC探测

#include "stdio.h"#include "windows.h"#include "tchar.h"DWORD g_dwOrgChecksum = 0xF5934986;int _tmain(int argc, TCHAR* argv[]);void DynAD_Checksum(){    BOOL bDebugging = FALSE;    DWORD dwSize = 0;    printf("Checksum\n");        __asm {        mov ecx, offset _tmain        mov esi, offset DynAD_Checksum        sub ecx, esi            // ecx : loop count (buf size)        xor eax, eax            // eax : checksum        xor ebx, ebx_CALC_CHECKSUM:        movzx ebx, byte ptr ds:[esi]        add eax, ebx        rol eax, 1        inc esi        loop _CALC_CHECKSUM        cmp eax, g_dwOrgChecksum        je _NOT_DEBUGGING        mov bDebugging, 1_NOT_DEBUGGING:    }    if( bDebugging )  printf("  => Debugging!!!\n\n");    else              printf("  => Not debugging...\n\n");}int _tmain(int argc, TCHAR* argv[]){    DynAD_Checksum();    return 0;}


总结:

1. CC int3 指令(注意两者之间没有空格)。

2.CD 2D int 2d 指令

3.

pushfd

or [esp],100 

popfd

使EFlags寄存器TF==1.


此3种方法,都是利用了调试器特有的性质(1.忽略 2.一直运行,直到断点。 3.断在执行指令后的一条指令,都没有触发ntdll!KiUserExceptionDispatcher())。


0 0
原创粉丝点击