首页
社区
课程
招聘
Anti OllyDbg
发表于: 2009-7-12 15:46 13941

Anti OllyDbg

2009-7-12 15:46
13941

在论坛看到一老兄求助地址(http://bbs.pediy.com/showthread.php?t=92330),对其开始异常处理分析了一下。
该anti手段是利用OD的单步机制来实现的
所以anti的前提是:单步

在钱老师的鼓励之下逆向了OD的step
如有不妥 请勿见怪

测试请见附件中的demo
既然说是利用单步来的
请先走到开头处构造的异常语句处
然后 单步一下 最后Run起来就可以了


先上逆向分析过程的代码
首先用OD load 官方原版od 1.10 F9 run起来
被调试od载入附件中demo程序 事先走到
mov eax,eax   ;eax=0
这句来
然后回到调试od中 bp SetThreadContext 和 WaitForDebugEvent
为了方便 在调试od中忽略了内存访问异常
然后在被调试od中f8一下

0042EB1A  |> \53            |PUSH EBX                                              ; /pContext
0042EB1B  |.  8B45 EC       |MOV EAX,DWORD PTR SS:[EBP-14]                         ; |
0042EB1E  |.  8B50 0C       |MOV EDX,DWORD PTR DS:[EAX+C]                          ; |
0042EB21  |.  52            |PUSH EDX                                              ; |hThread
0042EB22  |.  E8 83060800   |CALL <JMP.&KERNEL32.SetThreadContext>                 ; \SetThreadContext

这段代码处于Go函数里面 该Go函数被调用于单步异常模块中
先不管它 F9 让它跑下那句异常代码先

00439616   > \6A 00         PUSH 0                                                 ; /Timeout = 0. ms
00439618   .  68 14574D00   PUSH OFFSET <OLLYDBG.DebugEvent.dwDebugEventCode>      ; |pDebugEvent = OFFSET <OLLYDBG.DebugEvent.dwDebugEventCode>
0043961D   .  E8 E85B0700   CALL <JMP.&KERNEL32.WaitForDebugEvent>                 ; \WaitForDebugEvent
00439622   .  85C0          TEST EAX,EAX
00439624   .  75 44         JNZ SHORT OLLYDBG.0043966A

;004D5714为全局的DEBUG_EVENT结构
004D5714 >   00000001    00000558    0000078C    C0000005 ;ExceptionCode
004D5724     00000000    00000000    00401011  ;ExceptionAddress

这时候就捕获了内存访问异常

跟着它的处理 来到
00439743   .  833D 14574D00>CMP DWORD PTR DS:[<DebugEvent.dwDebugEventCode>],1  ;判断是不是EXCEPTION_DEBUG_EVENT
0043974A   .  75 0E         JNZ SHORT OLLYDBG.0043975A
0043974C   .  8B0D 2C574D00 MOV ECX,DWORD PTR DS:[4D572C]                          ;  OLLYDBG.00401011        ;当前指令地址
00439752   .  3B0D 30814D00 CMP ECX,DWORD PTR DS:[4D8130]                          ;  OLLYDBG.00401013  ;理想中的下一条指令地址
00439758   . /74 0A         JE SHORT OLLYDBG.00439764
0043975A   > |C705 7C7C4D00>MOV DWORD PTR DS:[4D7C7C],1
00439764   > \33C0          XOR EAX,EAX
00439766   .  33D2          XOR EDX,EDX
00439768   .  A3 30814D00   MOV DWORD PTR DS:[4D8130],EAX
0043976D   .  8D4D AC       LEA ECX,DWORD PTR SS:[EBP-54]
00439770   .  C605 203A4E00>MOV BYTE PTR DS:[4E3A20],0
00439777   .  8915 543B4E00 MOV DWORD PTR DS:[4E3B54],EDX
0043977D   .  51            PUSH ECX                                             
0043977E   .  E8 4D54FFFF   CALL OLLYDBG.0042EBD0  ;执行一些前奏 比如此时是内存访问异常 这里会先判断下是否处于kernel32和是否选择了忽略在kernel32内存中的异常 和sprintf一些UI字符串"Access violation when %s [%08lX]%s"这些

然后就到了这里 因为要单步了
00439964   . /75 24         JNZ SHORT OLLYDBG.0043998A
00439966   . |833D CC574D00>CMP DWORD PTR DS:[4D57CC],8
0043996D   . |74 1B         JE SHORT OLLYDBG.0043998A
0043996F   . |6A 00         PUSH 0                                                 ; /Arg5 = 00000000
00439971   . |6A 00         PUSH 0                                                 ; |Arg4 = 00000000
00439973   . |6A 00         PUSH 0                                                 ; |Arg3 = 00000000
00439975   . |6A 00         PUSH 0                                                 ; |Arg2 = 00000000
00439977   . |A1 1C574D00   MOV EAX,DWORD PTR DS:[<DebugEvent.dwThreadId>]         ; |
0043997C   . |50            PUSH EAX                                               ; |Arg1 => 0000078C
0043997D   . |E8 92B0FFFF   CALL OLLYDBG._Go                                       ; \_Go

Go的原型:
int Go(ulong threadid,ulong tilladdr,int stepmode,int givechance,int backupregs);
关于参数三:
stepmode - stepping mode, one of the following:

STEP_SAME(0)        Same action as on previous call to Go
STEP_RUN(1)        Run program
STEP_OVER(2)        Step over (execute calls at once)
STEP_IN(3)        Step in (enter subroutines)
STEP_SKIP(4)        Skip sequence till specified address

OllyDbg Plugin API v1.10

由于上一步为单步
所以在函数实现内部
00434A80  |> \837D 10 00    CMP DWORD PTR SS:[EBP+10],0   ;判断是否为STEP_SAME
00434A84  |.  75 11         JNZ SHORT OLLYDBG.00434A97
00434A86  |.  8B56 7C       MOV EDX,DWORD PTR DS:[ESI+7C] ;取出上步操作
00434A89  |.  8955 10       MOV DWORD PTR SS:[EBP+10],EDX ;STEP_OVER(2)

所以此时变成了单步了

00434C88  |.  837D 10 02    CMP DWORD PTR SS:[EBP+10],2
00434C8C  |.  0F94C0        SETE AL
00434C8F  |.  83E0 01       AND EAX,1
00434C92  |.  83FF 70       CMP EDI,70
00434C95  |.  8945 F8       MOV DWORD PTR SS:[EBP-8],EAX
00434C98  |.  0F85 41010000 JNZ OLLYDBG.00434DDF   ;判断是否为单步

最后来到此处:
0043534F  |.  6A 00         PUSH 0                                                 ; /Arg4 = 00000000
00435351  |.  A3 583B4E00   MOV DWORD PTR DS:[4E3B58],EAX                          ; |
00435356  |.  8B55 18       MOV EDX,DWORD PTR SS:[EBP+18]                          ; |
00435359  |.  C705 543B4E00>MOV DWORD PTR DS:[4E3B54],1                            ; |
00435363  |.  52            PUSH EDX                                               ; |Arg3
00435364  |.  8B4D 14       MOV ECX,DWORD PTR SS:[EBP+14]                          ; |
00435367  |.  51            PUSH ECX                                               ; |Arg2
00435368  |.  8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]                           ; |
0043536B  |.  50            PUSH EAX                                               ; 线程ID
0043536C  |.  E8 BFC0FFFF   CALL OLLYDBG.00431430                                  ; \OLLYDBG.00431430    ;跟进

00431498  |.  E8 3FD3FFFF   CALL <OLLYDBG.SetContext>                              ; \OLLYDBG.0042E7DC    ; 该函数遍历被调试进程的各个线程 然后设置其CONTEXT

0042E8CA  |.  F680 F4020000>|TEST BYTE PTR DS:[EAX+2F4],1
0042E8D1  |.  74 11         |JE SHORT OLLYDBG.0042E8E4
0042E8D3  |.  818B C0000000>|OR DWORD PTR DS:[EBX+C0],100

eax是一个t_thread结构
typedef struct t_thread {              // Information about active threads
  ulong          threadid;             // Thread identifier
  ulong          dummy;                // Always 1
  ulong          type;                 // Service information, TY_xxx
  HANDLE         thread;               // Thread handle
  ulong          datablock;            // Per-thread data block
  ulong          entry;                // Thread entry point
  ulong          stacktop;             // Working variable of Listmemory()
  ulong          stackbottom;          // Working variable of Listmemory()
  CONTEXT        context;              // Actual context of the thread
  t_reg          reg;                  // Actual contents of registers
  int            regvalid;             // Whether reg is valid
  t_reg          oldreg;               // Previous contents of registers
  int            oldregvalid;          // Whether oldreg is valid
  int            suspendcount;         // Suspension count (may be negative)
  long           usertime;             // Time in user mode, 1/10th ms, or -1
  long           systime;              // Time in system mode, 1/10th ms, or -1
  ulong          reserved[16];         // Reserved for future compatibility
} t_thread;

typedef struct t_reg {                 // Excerpt from context
  int            modified;             // Some regs modified, update context
  int            modifiedbyuser;       // Among modified, some modified by user
  int            singlestep;           // Type of single step, SS_xxx
  ulong          r[8];                 // EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
  ulong          ip;                   // Instruction pointer (EIP)
  ulong          flags;                // Flags
  int            top;                  // Index of top-of-stack
  long double    f[8];                 // Float registers, f[top] - top of stack
  char           tag[8];               // Float tags (0x3 - empty register)
  ulong          fst;                  // FPU status word
  ulong          fcw;                  // FPU control word
  ulong          s[6];                 // Segment registers ES,CS,SS,DS,FS,GS
  ulong          base[6];              // Segment bases
  ulong          limit[6];             // Segment limits
  char           big[6];               // Default size (0-16, 1-32 bit)
  ulong          dr6;                  // Debug register DR6
  ulong          threadid;             // ID of thread that owns registers
  ulong          lasterror;            // Last thread error or 0xFFFFFFFF
  int            ssevalid;             // Whether SSE registers valid
  int            ssemodified;          // Whether SSE registers modified
  char           ssereg[8][16];        // SSE registers
  ulong          mxcsr;                // SSE control and status register
  int            selected;             // Reports selected register to plugin
  ulong          drlin[4];             // Debug registers DR0..DR3
  ulong          dr7;                  // Debug register DR7
} t_reg;

最后也就是开头处的
0042EB1A  |> \53            |PUSH EBX                                              ; /pContext
0042EB1B  |.  8B45 EC       |MOV EAX,DWORD PTR SS:[EBP-14]                         ; |
0042EB1E  |.  8B50 0C       |MOV EDX,DWORD PTR DS:[EAX+C]                          ; |
0042EB21  |.  52            |PUSH EDX                                              ; |hThread
0042EB22  |.  E8 83060800   |CALL <JMP.&KERNEL32.SetThreadContext>                 ; \SetThreadContext

偏移2F4处就是singlestep字段 将此线程CONTEXT.EFlags |= 100h 了
走到系统处理异常领空之后 虽然这时的TF被清位了
但此时的CONTEXT结构被作为异常处理回调函数的参数了
当回调函数返回值为ExceptionContinueExection 也即0的时候
系统就会把线程环境设置为CONTEXT里的结构并继续执行
导致异常处理函数返回值 又增加了一个中断异常 这个也是该anti所使用的机制

由于此anti是利用了调试器的单步机制
所以避免方法就是:
F9过去 也就只是避免单步执行异常语句

菜鸟玩逆向 难免啰嗦了点
感谢钱老师的启发

      by科锐三期学员


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (17)
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
2
另附demo源码

.386
.model flat,stdcall
option casemap:none

include         user32.inc
includelib        user32.lib

.code
start:
        xor         eax,eax
        assume fs:nothing
        push        _SEH_PROC
        push         fs:[eax]
        mov         fs:[0],esp
        mov         [eax],eax
       
        jmp         _NOT_FOUND
        jmp                _FOUND
_NOT_FOUND:
        push        0
        call         @f
        db 'Demo',0
@@:
        call        @f
        db 'Not Found!',0
@@:       
        push         0
        call         MessageBox
        add         esp,8
        ret
       
_FOUND:
        push        0
        call         @f
        db 'Demo',0
@@:
        call        @f
        db 'Found OllyDbg!',0
@@:       
        push         0
        call         MessageBox
        add         esp,8
        ret               

_SEH_PROC:
        mov         edx,[esp+4]
        mov         edx,DWORD ptr ds:[edx+0Ch]
        mov         word ptr ds:[edx],0EBh
        xor         eax,eax
        ret
       
end start
2009-7-12 15:48
0
雪    币: 370
活跃值: (52)
能力值: ( LV13,RANK:350 )
在线值:
发帖
回帖
粉丝
3
好文章 谢谢分享  慢慢学习
2009-7-12 23:38
0
雪    币: 46
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持个!!!
2009-7-13 00:02
0
雪    币: 264
活跃值: (11)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
5
受教 保存下这个异常anti处理了

并且猛然发现自己原来写程序时居然一直没注意堆栈平衡..
2009-7-13 02:06
0
雪    币: 546
活跃值: (1657)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
6
好贴,学习。
2009-7-13 06:44
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
7
勾上这个,就不被anti了
上传的附件:
  • 2.jpg (24.34kb,574次下载)
2009-7-13 16:57
0
雪    币: 53
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
支持我现在正在学习反汇编呢很有帮助谢谢
2009-7-13 18:33
0
雪    币: 62
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
9
这位小哥好生厉害
2009-7-13 19:55
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
10
[QUOTE=海风月影;654833]勾上这个,就不被anti了
[/QUOTE]

不行阿,是版本的问题么
上传的附件:
2009-7-13 20:00
0
雪    币: 2067
活跃值: (82)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
11
跟插件无关
OD 的 Single-step break 不要打勾就好了

换句话说就是 多出来的单步例外 不会传给Target了, 就检不出来了
2009-7-13 22:18
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
12
不会传给被调试进程?
是OD不去捕获吧

难道是我OD的毛病
上传的附件:
2009-7-13 22:37
0
雪    币: 2067
活跃值: (82)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
13
你将同时忽略以下指定.... 的勾勾去掉吧
你可能内有 80000004

(Single-step break 也不要打勾)
2009-7-13 22:55
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
14
我到今天才注意到那个里也包含了异常
果然是这样 谢谢大牛提醒
我再看下OD咋处理的
2009-7-13 23:09
0
雪    币: 2067
活跃值: (82)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
15
你再研究研究吧, 你好像看得很细. 整体来讲这不算是anti.
利用例外做SMC很常见, 你的设定+追码方式刚好误触二次.

你开头讲的:  "所以anti的前提是:单步"  ..... 然后在被调试od中F8一下
 后面讲的: 由于此anti是利用了调试器的单步机制
         所以避免方法就是: F9过去 也就只是避免单步执行异常语句
听来似乎没错. 但.... (除非我误会你的意思)

会有此Anti的现象并不是因为你按了 F7 的单步. (更别提F8不是靠单步来的)
而是因为你停在 mov [eax], eax 这条的关系
你停在这条,不管按 F7/F8/F9 或在任何地方(例如MessageBoxA)按 F4 都是一样的效果.

因为 OD 在交控制权时会先 TF=1 执行一条指令  \  回来OD  \  TF=0 Run
所以不管你按 F4/F7/F8/F9, 只要你是停在 [eax],eax 这行都是一样的效果.

所以你说 " 避免方法就是:F9过去 "  <- 应声明: 需在 mov [eax], eax 之前就按

----------------------
只有当停在 9C pushfd 及 9D popfd 不采用 TF=1 的方式.
9C ' 9D 优先采用硬断点,不够用时采CC软断. 来达到先执行一条指令的目的.
2009-7-14 07:46
0
雪    币: 1074
活跃值: (160)
能力值: ( LV13,RANK:760 )
在线值:
发帖
回帖
粉丝
16
听sessiondiy大侠的,没错!我信赖!
2009-7-14 07:54
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
17
的确是 是F9跳过那个语句 没有说明白
关于那个pushfd和popfd 下次再研究看看
至于anti,也算吧,至少当初逆向那壳的时候把我给整死循环了
郁闷了我几分钟
听君一席话 省我十本书
2009-7-14 10:30
0
雪    币: 99
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
顶,果然够强
2009-7-16 10:49
0
游客
登录 | 注册 方可回帖
返回
//