郁金香外挂技术-郁金香灬老师

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

飞郁网络培训
 飞郁网络视频分享(每周更新)
郁金香终身VIP
管理员QQ150330575管理员接单必看QQ交流群(外挂,驱动)
查看: 357|回复: 0

常见 反调试手段 检测OD 调试器

[复制链接]
发表于 2018-12-26 22:55:10 | 显示全部楼层 |阅读模式
IsDebuggerPresent查询进程环境块(PEB)中的IsDebugged标志.

CheckRemoteDebuggerPresent
CheckRemoteDebuggerPresent同IsDebuggerPresent几乎一致。它不仅可以探测系统其他进程是否被调试,通过传递自身进程句柄还可以探测自身是否被调试。

BOOL CheckDebug()  
{  
    BOOL ret;  
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &ret);  
    return ret;  
}  

BOOL CheckDebug()
{
        int debugPort = 0;
        HMODULE hModule = LoadLibrary("Ntdll.dll");
        NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");
        NtQueryInformationProcess(GetCurrentProcess(), 0x7, &debugPort, sizeof(debugPort), NULL);
        return debugPort != 0;
}

BOOL CheckDebug()
{
        HANDLE hdebugObject = NULL;
        HMODULE hModule = LoadLibrary("Ntdll.dll");
        NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");
        NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &hdebugObject, sizeof(hdebugObject), NULL);
        return hdebugObject != NULL;
}

BOOL CheckDebug()
{
        BOOL bdebugFlag = TRUE;
        HMODULE hModule = LoadLibrary("Ntdll.dll");
        NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");
        NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &bdebugFlag, sizeof(bdebugFlag), NULL);
        return bdebugFlag != TRUE;
}

BOOL CheckDebug()
{
        DWORD errorValue = 12345;
        SetLastError(errorValue);
        OutputDebugString("Test for debugger!");
        if (GetLastError() == errorValue)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}

BOOL CheckDebug()
{
        char fib[1024] = {0};
        DeleteFiber(fib);
        return (GetLastError() != 0x57);
}


BOOL CheckDebug()
{
        char fib[1024] = {0};
        DeleteFiber(fib);
        return (GetLastError() != 0x57);
}


BOOL CheckDebug()
{
        int result = 0;
        __asm
        {
                mov eax, fs:[30h]
                mov al, BYTE PTR [eax + 2]
                mov result, al
        }
        return result != 0;
}

BOOL CheckDebug()
{
        int result = 0;
        DWORD dwVersion = GetVersion();
        DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
        //for xp
        if (dwWindowsMajorVersion == 5)
        {
                __asm
                {
                        mov eax, fs:[30h]
                        mov eax, [eax + 18h]
                        mov eax, [eax + 10h]
                        mov result, eax
                }
        }
        else
        {
                __asm
                {
                        mov eax, fs:[30h]
                        mov eax, [eax + 18h]
                        mov eax, [eax + 44h]
                        mov result, eax
                }
        }
        return result != 0;
}


BOOL CheckDebug()
{
        int result = 0;
        DWORD dwVersion = GetVersion();
        DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
        //for xp
        if (dwWindowsMajorVersion == 5)
        {
                __asm
                {
                        mov eax, fs:[30h]
                        mov eax, [eax + 18h]
                        mov eax, [eax + 0ch]
                        mov result, eax
                }
        }
        else
        {
                __asm
                {
                        mov eax, fs:[30h]
                        mov eax, [eax + 18h]
                        mov eax, [eax + 40h]
                        mov result, eax
                }
        }
        return result != 2;
}


BOOL CheckDebug()
{
        int result = 0;
        __asm
        {
                mov eax, fs:[30h]
                mov eax, [eax + 68h]
                and eax, 0x70
                mov result, eax
        }
        return result != 0;
}

BOOL CheckDebug()
{
        BOOL is_64;
        IsWow64Process(GetCurrentProcess(), &is_64);
        HKEY hkey = NULL;
    char key[] = "Debugger";
        char reg_dir_32bit[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug";
        char reg_dir_64bit[] = "SOFTWARE\\Wow6432Node\\Microsoft\\WindowsNT\\CurrentVersion\\AeDebug";
        DWORD ret = 0;
        if (is_64)
        {
                ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, reg_dir_64bit, &hkey);
        }
        else
        {
                ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, reg_dir_32bit, &hkey);
        }
        if (ret != ERROR_SUCCESS)
        {
                return FALSE;
        }
        char tmp[256];
    DWORD len = 256;
    DWORD type;
    ret = RegQueryValueExA(hkey, key, NULL, &type, (LPBYTE)tmp, &len);
        if (strstr(tmp, "OllyIce")!=NULL || strstr(tmp, "OllyDBG")!=NULL || strstr(tmp, "WinDbg")!=NULL || strstr(tmp, "x64dbg")!=NULL || strstr(tmp, "Immunity")!=NULL)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}


BOOL CheckDebug()
{
        if (FindWindowA("OLLYDBG", NULL)!=NULL || FindWindowA("WinDbgFrameClass", NULL)!=NULL || FindWindowA("QWidget", NULL)!=NULL)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}



BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)  
{  
        char cur_window[1024];
    GetWindowTextA(hwnd, cur_window, 1023);
        if (strstr(cur_window, "WinDbg")!=NULL || strstr(cur_window, "x64_dbg")!=NULL || strstr(cur_window, "OllyICE")!=NULL || strstr(cur_window, "OllyDBG")!=NULL || strstr(cur_window, "Immunity")!=NULL)
        {
                *((BOOL*)lParam) = TRUE;
        }
        return TRUE;
}

BOOL CheckDebug()
{
        BOOL ret = FALSE;
        EnumWindows(EnumWndProc, (LPARAM)&ret);
        return ret;
}

//软件断点检查
BOOL CheckDebug()
{
        DWORD ID;
        DWORD ret = 0;
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(pe32);
        HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if(hProcessSnap == INVALID_HANDLE_VALUE)
        {
                return FALSE;
        }
        BOOL bMore = Process32First(hProcessSnap, &pe32);
        while(bMore)
        {
                if (stricmp(pe32.szExeFile, "OllyDBG.EXE")==0 || stricmp(pe32.szExeFile, "OllyICE.exe")==0 || stricmp(pe32.szExeFile, "x64_dbg.exe")==0 || stricmp(pe32.szExeFile, "windbg.exe")==0 || stricmp(pe32.szExeFile, "ImmunityDebugger.exe")==0)
                {
                        return TRUE;
                }
                bMore = Process32Next(hProcessSnap, &pe32);
        }
        CloseHandle(hProcessSnap);
        return FALSE;
}


BOOL CheckDebug()
{
        PIMAGE_DOS_HEADER pDosHeader;
        PIMAGE_NT_HEADERS32 pNtHeaders;
        PIMAGE_SECTION_HEADER pSectionHeader;
        DWORD dwBaseImage = (DWORD)GetModuleHandle(NULL);
        pDosHeader = (PIMAGE_DOS_HEADER)dwBaseImage;
        pNtHeaders = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(pNtHeaders->Signature) + sizeof(IMAGE_FILE_HEADER) +
                                         (WORD)pNtHeaders->FileHeader.SizeOfOptionalHeader);
        DWORD dwAddr = pSectionHeader->VirtualAddress + dwBaseImage;
        DWORD dwCodeSize = pSectionHeader->SizeOfRawData;   
        BOOL Found = FALSE;
        __asm
        {
                cld            
                mov     edi,dwAddr
                mov     ecx,dwCodeSize
                mov     al,0CCH
                repne   scasb
                jnz     NotFound
                mov Found,1
NotFound:         
        }
        return Found;
}





//硬件断点 检测
BOOL CheckDebug()
{
        CONTEXT context;  
    HANDLE hThread = GetCurrentThread();  
    context.ContextFlags = CONTEXT_DEBUG_REGISTERS;  
    GetThreadContext(hThread, &context);  
    if (context.Dr0 != 0 || context.Dr1 != 0 || context.Dr2 != 0 || context.Dr3!=0)   
    {  
        return TRUE;  
    }  
    return FALSE;  
}


执行代码校验和检查
恶意代码可以计算代码段的校验并实现与扫描中断相同的目的。与扫描0xCC不同,这种检查仅执行恶意代码中机器码CRC或者MD5校验和检查。

BOOL CheckDebug()
{
        PIMAGE_DOS_HEADER pDosHeader;
        PIMAGE_NT_HEADERS32 pNtHeaders;
        PIMAGE_SECTION_HEADER pSectionHeader;
        DWORD dwBaseImage = (DWORD)GetModuleHandle(NULL);
        pDosHeader = (PIMAGE_DOS_HEADER)dwBaseImage;
        pNtHeaders = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(pNtHeaders->Signature) + sizeof(IMAGE_FILE_HEADER) +
                (WORD)pNtHeaders->FileHeader.SizeOfOptionalHeader);
        DWORD dwAddr = pSectionHeader->VirtualAddress + dwBaseImage;
        DWORD dwCodeSize = pSectionHeader->SizeOfRawData;   
        DWORD checksum = 0;
        __asm
        {
                cld
                mov     esi, dwAddr
                mov     ecx, dwCodeSize
                xor eax, eax
        checksum_loop :
                movzx    ebx, byte ptr[esi]
                add        eax, ebx
                rol eax, 1
                inc esi
                loop       checksum_loop
                mov checksum, eax
        }
        if (checksum != 0x46ea24)
        {
                return FALSE;
        }
        else
        {
                return TRUE;
        }
}

使用QueryPerformanceCounter和GetTickCount
同rdtsc指令一样,这两个Windows API函数也被用来执行一个反调试的时钟检测。使用这种方法的前提是处理器有高分辨率能力的计数器-寄存器,它能存储处理器活跃的时钟数。为了获取比较的时间差,调用两次QueryPerformanceCounter函数查询这个计数器。若两次调用之间花费的时间过于长,则可以认为正在使用调试器。GetTickCount函数返回最近系统重启时间与当前时间的相差毫秒数(由于时钟计数器的大小原因,计数器每49.7天就被重置一次)。

BOOL CheckDebug()
{
        DWORD time1 = GetTickCount();
        __asm
        {
                mov     ecx,10           
                mov     edx,6               
                mov     ecx,10
        }
        DWORD time2 = GetTickCount();
        if (time2-time1 > 0x1A)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}

判断父进程是否是explorer.exe
一般双击运行的进程的父进程都是explorer.exe,但是如果进程被调试父进程则是调试器进程。也就是说如果父进程不是explorer.exe则可以认为程序正在被调试

BOOL CheckDebug()
{
        LONG                      status;  
    DWORD                     dwParentPID = 0;  
    HANDLE                    hProcess;  
    PROCESS_BASIC_INFORMATION pbi;  
        int pid = getpid();
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);  
    if(!hProcess)  
        return -1;  
        PNTQUERYINFORMATIONPROCESS  NtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)GetProcAddress(GetModuleHandleA("ntdll"),"NtQueryInformationProcess");
        status = NtQueryInformationProcess(hProcess,SystemBasicInformation,(PVOID)&pbi,sizeof(PROCESS_BASIC_INFORMATION),NULL);
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(pe32);
        HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if(hProcessSnap == INVALID_HANDLE_VALUE)
        {
                return FALSE;
        }
        BOOL bMore = Process32First(hProcessSnap, &pe32);
        while(bMore)
        {
                if (pbi.InheritedFromUniqueProcessId == pe32.th32ProcessID)
                {
                        if (stricmp(pe32.szExeFile, "explorer.exe")==0)
                        {
                                CloseHandle(hProcessSnap);
                                return FALSE;
                        }
                        else
                        {
                                CloseHandle(hProcessSnap);
                                return TRUE;
                        }
                }
                bMore = Process32Next(hProcessSnap, &pe32);
        }
        CloseHandle(hProcessSnap);
}


判断是否具有SeDebugPrivilege权限
默认情况下进程是没有SeDebugPrivilege权限的,但是当进程通过调试器启动时,由于调试器本身启动了SeDebugPrivilege权限,当调试进程被加载时SeDebugPrivilege也就被继承了。所以我们可以检测进程的SeDebugPrivilege权限来间接判断是否存在调试器,而对SeDebugPrivilege权限的判断可以用能否打开csrss.exe进程来判断。
BOOL CheckDebug()
{
        DWORD ID;
        DWORD ret = 0;
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(pe32);
        HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if(hProcessSnap == INVALID_HANDLE_VALUE)
        {
                return FALSE;
        }
        BOOL bMore = Process32First(hProcessSnap, &pe32);
        while(bMore)
        {
                if (strcmp(pe32.szExeFile, "csrss.exe")==0)
                {
                        ID = pe32.th32ProcessID;
                        break;
                }
                bMore = Process32Next(hProcessSnap, &pe32);
        }
        CloseHandle(hProcessSnap);
        if (OpenProcess(PROCESS_QUERY_INFORMATION, NULL, ID) != NULL)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}


使用TLS回调
Thread Local Storage(TLS),即线程本地存储,是Windows为解决一个进程中多个线程同时访问全局变量而提供的机制。TLS可以简单地由操作系统代为完成整个互斥过程,也可以由用户自己编写控制信号量的函数。当进程中的线程访问预先制定的内存空间时,操作系统会调用系统默认的或用户自定义的信号量函数,保证数据的完整性与正确性。下面是一个简单的TLS回调的例子,TLS_CALLBACK1函数在main函数执行前调用IsDebuggerPresent函数检查它是否正在被调试。



#include "stdafx.h"
#include <stdio.h>
#include <windows.h>

void NTAPI __stdcall TLS_CALLBACK1(PVOID DllHandle, DWORD dwReason, PVOID Reserved);

#ifdef _M_IX86
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")
#else
#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:_tls_callback")
#endif
EXTERN_C
#ifdef _M_X64
#pragma const_seg (".CRT$XLB")
const
#else
#pragma data_seg (".CRT$XLB")
#endif

PIMAGE_TLS_CALLBACK _tls_callback[] = { TLS_CALLBACK1,0};
#pragma data_seg ()
#pragma const_seg ()

#include <iostream>

void NTAPI __stdcall TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
        if (IsDebuggerPresent())
        {
                printf("TLS_CALLBACK: Debugger Detected!\n");
        }
        else
        {
                printf("TLS_CALLBACK: No Debugger Present!\n");
        }
}

int main(int argc, char* argv[])
{
        printf("233\n");
        return 0;
}



利用中断
因为调试器使用INT 3来设置软件断点,所以一种反调试技术就是在合法代码段中插入0xCC(INT 3)欺骗调试器,使其认为这些0xCC机器码是自己设置的断点。

BOOL CheckDebug()
{
      __try
      {
            __asm int 3
      }
      __except(1)
      {
            return FALSE;
      }
      return TRUE;
}



除了使用_try和_except以外还可以直接使用汇编代码安装SEH。在下面的代码中如果进程没有处于调试中,则正常终止;如果进程处于调试中,则跳转到非法地址0xFFFFFFFF处,无法继续调试
#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

        // generating exception
        int 3

        // 1) debugging
        //    go to terminating code
        mov eax, 0xFFFFFFFF
        jmp eax                 // process terminating!!!

        // 2) not debugging
        //    go to normal code
handler:
        mov eax, dword ptr ss:[esp+0xc]
        mov ebx, normal_code
        mov dword ptr ds:[eax+0xb8], ebx
        xor eax, eax
        retn

normal_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;
}



NT 2D原为内核模式中用来触发断点异常的指令,也可以在用户模式下触发异常。但程序调试运行时不会触发异常,只是忽略。INT 2D指令在ollydbg中有两个有趣的特性。在调试模式中执行INT 2D指令,下一条指令的第一个字节将被忽略。使用StepInto(F7)或者StepOver(F8)命令跟踪INT 2D指令,程序不会停在下一条指令开始的地方,而是一直运行,就像RUN(F9)一样。在下面的代码中,程序调试运行时,执行INT 2D之后不会运行SEH,而是跳过NOP,把bDebugging标志设置为1,跳转到normal_code;程序正常运行时,执行INT 2D之后触发SEH,在异常处理器中设置EIP并把bDebugging标志设置为0。


BOOL CheckDebug()
{
    BOOL bDebugging = FALSE;

    __asm {
        // install SEH
        push handler
        push DWORD ptr fs:[0]
        mov DWORD ptr fs:[0], esp

        int 0x2d

        nop
        mov bDebugging, 1
        jmp normal_code

handler:
        mov eax, dword ptr ss:[esp+0xc]
        mov dword ptr ds:[eax+0xb8], offset normal_code
        mov bDebugging, 0
        xor eax, eax
        retn

normal_code:
        //   remove SEH
        pop dword ptr fs:[0]
        add esp, 4
    }

    printf("Trap Flag (INT 2D)\n");
    if( bDebugging )  return 1;
    else              return 0;
}


片内仿真器(ICE)断点指令ICEBP(操作码0xF1)是Intel未公开的指令之一。由于使用ICE难以在任意位置设置断点,因此ICEBP指令被设计用来降低使用ICE设置断点的难度。运行ICEBP指令将会产生一个单步异常,如果通过单步调试跟踪程序,调试器会认为这是单步调试产生的异常,从而不执行先前设置的异常处理例程。利用这一点,恶意代码使用异常处理例程作为它的正常执行流程。为了防止这种反调试技术,执行ICEBP指令时不要使用单步



BOOL CheckDebug()
{
      __try
      {
            __asm __emit 0xF1
      }
      __except(1)
      {
            return FALSE;
      }
      return TRUE;
}


EFLAGS寄存器的第八个比特位是陷阱标志位。如果设置了,就会产生一个单步异常。
BOOL CheckDebug()
{
      __try
      {
            __asm
            {
                  pushfd     
                  or word ptr[esp], 0x100            
                  popfd
                  nop        
            }
      }
      __except(1)
      {
            return FALSE;
      }
      return TRUE;
}


RaiseException
RaiseException函数产生的若干不同类型的异常可以被调试器捕获
BOOL TestExceptionCode(DWORD dwCode)
{
      __try
      {
            RaiseException(dwCode, 0, 0, 0);
      }
      __except(1)
      {
            return FALSE;
      }
      return TRUE;
}

BOOL CheckDebug()
{
      return TestExceptionCode(DBG_RIPEXCEPTION);   
}


SetUnhandledExceptionFilter
进程中发生异常时若SEH未处理或注册的SEH不存在,会调用UnhandledExceptionFilter,它会运行系统最后的异常处理器。UnhandledExceptionFilter内部调用了前面提到过的NtQueryInformationProcess以判断是否正在调试进程。若进程正常运行,则运行最后的异常处理器;若进程处于调试,则将异常派送给调试器。SetUnhandledExceptionFilter函数可以修改系统最后的异常处理器。下面的代码先触发异常,然后在新注册的最后的异常处理器内部判断进程正常运行还是调试运行。进程正常运行时pExcept->ContextRecord->Eip+=4;将发生异常的代码地址加4使得其能够继续运行;进程调试运行时产生无效的内存访问异常,从而无法继续调试。

#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");

    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;
}


OutputDebugString漏洞
恶意代码常尝试利用OllyDbg1.1的格式化字符串漏洞,为OutputDebugString函数提供一个%s字符串的参数,让OllyDbg崩溃。因此,需要注意程序中可疑的OutputDebugString调用,例如OutputDebugString("%s%s%s%s%s%s%s%s%s")。如果执行了这个调用,OllyDbg将会崩溃。






郁金香外挂教程,学习中...
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|郁金香外挂技术-郁金香灬老师 ( 苏ICP备10059359号 )

GMT+8, 2019-1-21 04:53 , Processed in 0.045753 second(s), 18 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表