• 反调试技术常用API,用来对付检测od和自动退出程序


    在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术,首先我们来看看反调试技术。

    一、Windows API方法

    Win32提供了两个API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用来检测当前进程是否正在被调试,以IsDebuggerPresent函数为例,例子如下:
    BOOL ret = IsDebuggerPresent();
    printf("ret = %d ", ret);

    破解方法很简单,就是在系统里将这两个函数hook掉,让这两个函数一直返回false就可以了,网上有很多做hook API工作的工具,也有很多工具源代码是开放的,所以这里就不细谈了
    二、查询进程PEB的BeingDebugged标志位

    当进程被调试器所附加的时候,操作系统会自动设置这个标志位,因此在程序里定期查询这个标志位就可以了,例子如下:

    bool PebIsDebuggedApproach()8 R& ]/ {7 V6 T' z, K
    {
           char result = 0;  q" V3 N9 u5 @# q% d+ E$ J
           __asm
           {
                    // 进程的PEB地址放在fs这个寄存器位置上" C* e( ], k7 m
                  mov eax, fs:[30h]7 C/ M0 X; b- G. L' S+ x, S" V
                    // 查询BeingDebugged标志位6 J5 o, `6 U: a
                  mov al, BYTE PTR [eax + 2] & k5 `; M9 v( _6 [
                  mov result, al
           }
    ) n& r& d8 i5 c# t$ P8 s4 K
           return result != 0;, d; p" C' q, u8 I8
    }

    三、查询进程PEB的NtGlobal标志位 6 p6 C* _  T- V/ @' l. }
    2 g; ~6 k* y# S) R
    " ( O( p& i' H, f" T& K
    跟第二个方法一样,当进程被调试的时候,操作系统除了修改BeingDebugged这个标志位以外,还会修改其他几个地方,其中NtDll中一些控制堆(Heap)操作的函数的标志位就会被修改,因此也可以查询这个标志位,例子如下:

    bool PebNtGlobalFlagsApproach()  I  V7 g0 J& G9 P2 {2 j$ F# ]
    {
           int result = 0;
    __asm. ]& d" Z1 D$ h. m) d
           {" v+ s- m( Z$ c2 T* z8 f
                              // 进程的PEB
                  mov eax, fs:[30h]
                              // 控制堆操作函数的工作方式的标志位
                  mov eax, [eax + 68h]" c7 F2 s2 7 ^7 d7 O5 y; [
                              // 操作系统会加上这些标志位FLG_HEAP_ENABLE_TAIL_CHECK, " O7 I& t, {9 r: g. g) V  {9 J
                              // FLG_HEAP_ENABLE_FREE_CHECK and FLG_HEAP_VALIDATE_PARAMETERS,
                              // 它们的并集就是x705 W5 z6 _$ a( b  D" `8 u$ W
                              //' z6 B) L' P1 g! |
                              // 下面的代码相当于C/C++的5 W3 w5 s$ ?! Y3 u( ^+ N4 _* _
                              //     eax = eax & 0x70# C* f! ^$ B' y
                  and eax, 0x70
                  mov result, eax
           }; _7 j* E4 {% y) {# u
    ( `1 B: r, ?8 K# O6 f/ K3 T
           return result != 0;+ c5 L# A! R# A' ?
    }- T/ K/ B) X/ O
    6 k" i* H( ?# l4 O8 D2 y, V, V: L7 {


    四、查询进程堆的一些标志位" A) t* ^' n% E. ?. Z( F& L

    # x- t; ^1 U/ d7 ?+ O4 s2 P
    这个方法是第三个方法的变种,只要进程被调试,进程在堆上分配的内存,在分配的堆的头信息里,ForceFlags这个标志位会被修改,因此可以通过判断这个标志位的方式来反调试。因为进程可以有很多的堆,因此只要检查任意一个堆的头信息就可以了,所以这个方法貌似很强大,例子如下:
    0 P# v: B: N+ z' n* |) f
    bool HeapFlagsApproach()
    {
           int result = 0;
    & L9 V% Z  l) p) {, e9 Q
           __asm
           {' k) s' P6 p+ `& A, M9 T: f4 F5 G+ o
                          // 进程的PEB
                  mov eax, fs:[30h]' K6 Q" {4 [! |; s: T
                          // 进程的堆,我们随便访问了一个堆,下面是默认的堆
                  mov eax, [eax + 18h]
                              // 检查ForceFlag标志位,在没有被调试的情况下应该是4 z1 D  s$ I: ^% q  P5 _
                  mov eax, [eax + 10h]7 @7 s3 o8 Y* q5 Z  W
                  mov result, eax% H# F6 I4 z3 Y( h7 ?
           }
    . m4 Q7 Z3 Q; p3 B
           return result != 0;
    }8 w; p9 N& P6 k1 K: P7 L+ H
      S( R5 V0 X! c# r4 n' C2 ~0 L
    5 p" G# F! V3 W) @" |; P: V
    - G9 N+ ^4 j6 W0 u
    & B; E) _/ ]. ( B: r8 @
    五、使用NtQueryInformationProcess函数
    % d8 A0 |& v, T$ p- }9 J: l0 T

    NtQueryInformationProcess函数是一个未公开的API,它的第二个参数可以用来查询进程的调试端口。如果进程被调试,那么返回的端口值会是-1,否则就是其他的值。由于这个函数是一个未公开的函数,因此需要使用LoadLibrary和GetProceAddress的方法获取调用地址,示例代码如下:
    , o) s( p( Q+ [
      A0 j" n9 P2 i
    // 声明一个函数指针。7 r7 W/ w. T7 N! c
    typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)(
           HANDLE processHandle,
           PROCESSINFOCLASS processInformationClass,
           PVOID processInformation,
           ULONG processInformationLength,
           PULONG returnLength);7 z( z/ }+ I! u6 f4 ?, s: W
    ( B7 _7 ]2 W; K9 a. l* S% z7 a: z
    bool NtQueryInformationProcessApproach()# W5 g& c# g& Z6 Y: G8 g5 X
    {! k9 t/ F" e4 i4 _% T8 @
           int debugPort = 0;
           HMODULE hModule = LoadLibrary(TEXT("Ntdll.dll "));7 R, @8 S6 c  q- Y+ G
           NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");& n' @! [6 D8 n$ X3 f
           if ( NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)7, &debugPort, sizeof(debugPort), NULL) )9 ]/ @& h" y) n- o" F) o" E
                  printf("[ERROR NtQueryInformationProcessApproach] NtQueryInformationProcess failed ");4 O# M$ r9 [4 G' K6 x
           else
                  return debugPort == -1;
    8 z6 T$ A6 M8 f& u- U8 p
           return false;
    }
    , P; K$ f5 g5 v! n- {
    ! Y5 F; y9 a; E( s( t# o3 ]3 L- z: O

    六、NtSetInformationThread方法
    " W' Q4 a$ o3 y% R% |. C/ ]* l

    这个也是使用Windows的一个未公开函数的方法,你可以在当前线程里调用NtSetInformationThread,调用这个函数时,如果在第二个参数里指定0x11这个值(意思是ThreadHideFromDebugger),等于告诉操作系统,将所有附加的调试器统统取消掉。示例代码:1 J" g8 F, u# 3 A

    3 b2 h) Z4 P" ]) m- E" R
    // 声明一个函数指针。
    typedef NTSTATUS (*NtSetInformationThreadPtr)(HANDLE threadHandle,2 q7 n  ?% d. U5 S
           THREADINFOCLASS threadInformationClass,
           PVOID threadInformation,9 K) ]# i# c7 |2 }4 @
           ULONG threadInformationLength);6 F* o' |: S: {/ d5 f
    " {; s4 Q1 q" x: _) ( C! q$ v
    void NtSetInformationThreadApproach()1 x8 s( E- n0 q# }
    {
          HMODULE hModule = LoadLibrary(TEXT("ntdll.dll"));
          NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hModule, "NtSetInformationThread");5 b$ k4 e7 o, Z0 }
        
          NtSetInformationThread(GetCurrentThread(), (THREADINFOCLASS)0x11, 0, 0);: v; o' h2 p' W/ q) q% g* Z  V7 s
    }
    - u& ~) T9 o6 T  q$ s* e+ Y
    ( f3 V( |! s$ ~8 y0 R9 U
    6 u/ F" t( ~( e! S5 m" f
    七、触发异常的方法2 b' V& l& z# j" m
    : * L/ a( M) g

    这个技术的原理是,首先,进程使用SetUnhandledExceptionFilter函数注册一个未处理异常处理函数A,如果进程没有被调试的话,那么触发一个未处理异常,会导致操作系统将控制权交给先前注册的函数A;而如果进程被调试的话,那么这个未处理异常会被调试器捕捉,这样我们的函数A就没有机会运行了。
    8 c8 L# _+ a! _+ ?% v
    # y% j* t5 a: P- B0 ]# I$ N% y, k
    这里有一个技巧,就是触发未处理异常的时候,如果跳转回原来代码继续执行,而不是让操作系统关闭进程。方案是在函数A里修改eip的值,因为在函数A的参数_EXCEPTION_POINTERS里,会保存当时触发异常的指令地址,所以在函数A里根据这个指令地址修改寄存器eip的值就可以了,示例代码如下:/ |$ r: ]% X5 O
    " z6 t# 6 s2 o3 E* M; k
    ) H# x9 U/ t$ w5 H2 `
    // 进程要注册的未处理异常处理程序A/ {1 m! t/ S  r( A  b2 o7 z
    LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei)$ M. a( ~0 s# M! d7 m
    {' B/ V" Q: D5 J) r7 G8 [5 K1 ?
           SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)
                  pei->ContextRecord->Eax);
           // 修改寄存器eip的值
           pei->ContextRecord->Eip += 2;5 Y7 B9 A  {5 N- D1 B.
           // 告诉操作系统,继续执行进程剩余的指令(指令保存在eip里),而不是关闭进程+ 4 i- I( [/ F9 z% H1 `0 e/ n
           return EXCEPTION_CONTINUE_EXECUTION;7 [9 s6 K. R4 x7 i6 M  x3 o
    }

    bool UnhandledExceptionFilterApproach()
    {5 h% s9 O! v8 }- Q
           SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
           __asm
           {. j2 P# |9 H( b
                  // 将eax清零
                  xor eax, eax
                  // 触发一个除零异常
                  div eax9 q8 l3 f* v0 c4 Q
           }6 Q5 N7 g- J8 D2 h( z9 F  y1 u
    0 _2 j* U% D9 n" M
           return false;6 w7 ]! y1 r* p( r
    }  / z* ?( k" W


    八、调用DeleteFiber函数* o9 g' y6 r; y" o
    - h, g$ t3 F4 v

    如果给DeleteFiber函数传递一个无效的参数的话,DeleteFiber函数除了会抛出一个异常以外,还是将进程的LastError值设置为具体出错原因的代号。然而,如果进程正在被调试的话,这个LastError值会被修改,因此如果调试器绕过了第七步里讲的反调试技术的话,我们还可以通过验证LastError值是不是被修改过来检测调试器的存在,示例代码:


    bool DeleteFiberApproach()% _5 i% ?, d5 g
    {% Y0 y) Q+ b1 }7 o: Y& _2 O# e# o
           char fib[1024] = {0};
           // 会抛出一个异常并被调试器捕获
           DeleteFiber(fib);

           // 0x57的意思是ERROR_INVALID_PARAMETER
           return (GetLastError() != 0x57);& 9 m2 J, F" T" V
    }
    jpg改rar
  • 相关阅读:
    ch5 对链接应用样式
    ch4 圆角框
    ch4 背景图像基础
    ch8 CSS 3列(等高文本列)
    ch8 高度相等的列--CSS方法
    ch8 faux列
    java基础 (四)之集合
    java基础 (二)之HashMap,HashTable,ConcurrentHashMap区别
    java基础 (三)之ConcurrentHashMap(10)未完待续~~~
    java基础 (一)之HashMap(jdk1.7)
  • 原文地址:https://www.cnblogs.com/kuangke/p/6155375.html
Copyright © 2020-2023  润新知