• 反调试技术二


    五、使用NtQueryInformationProcess函数

    NtQueryInformationProcess函数是一个未公开的API,它的第二个参数可以用来查询进程的调试端口。如果进程被调试,那么返回的端口值会是-1,否则就是其他的值。由于这个函数是一个未公开的函数,因此需要使用LoadLibraryGetProceAddress的方法获取调用地址,示例代码如下:

     

    // 声明一个函数指针。

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

    }

    未完待续

  • 相关阅读:
    SVN报错working copy is not uptodate
    AndroidStudio中获得的VersionCode一直为1和VersionName一直为1.0
    OkHttp
    MockWebServer使用指南(转载)
    Android的Toolbar(含溢出菜单设置[弹出菜单的使用])的使用PopMenu的样式
    8-13笔记-安卓兼容
    自定义Dialog
    安卓圆角Button XML文件
    递归方法扫面文件夹(JAVA控制台程序)
    8月12笔记-安卓文件扫描
  • 原文地址:https://www.cnblogs.com/killmyday/p/2064171.html
Copyright © 2020-2023  润新知