这是分析报告的第三部分,请多多指教。  

Ollydbg(以下均简称为OD)中的硬件断点的主要原理是:将要下断点的内存地址放入调试寄存器对应的选项中,由调试寄存器将其断下,并报异常给OD,等待调试人员操作。

硬件断点的长度有3种情况:1个字节(1)、2个字节(2)、4个字节(4)
硬件标识有8种情况:未知(0)、执行断点(1)、访问断点(2)、写入断点(3)、未知(4)、临时断点(5)、未知(6)、未知(7)
临时断点主要用于单步跳过
OD在以下结构体中存放了以下硬件断点的信息:
第一个4字节:硬件断点的首地址
第二个4字节:硬件断点的长度
第三个4字节:硬件断点的标识
下面三个4字节:未知
004D8D70  7A 06 40 00 01 00 00 00 02 00 00 00 00 00 00 00  z @. ... .......
004D8D80  00 00 00 00 00 00 00 00 00 00 00 00              ............

通过分析,硬件断点表的设置、断点表的添加以及断点表的删除都是由不同的函数来完成的,一共有三个函数。
这里硬件断点表的添加和删除要特别说明一下,函数中会在添加断点表时检查调试线程是否是运行状态,如果是运行状态就会进入一个判断函数,并查看是否需要断下。写完断点表后,断点的处理就不需要由这两个函数来完成了。这里先概括的介绍其流程,下面再详细分析代码:

添加硬件断点表(Sethardwarebreakpoint):
该函数有三个参数:断点首地址、断点长度、断点标识(访问、写入、执行断点等等)
1、判断是何种类型的断点,若是可执行断点的话,下断的内存长度只能是1,而且会强制设为1;
2、若为断点类型不为4(这个还没有逆向出来)的话,要判断下断的内存长度是否是4的倍数,如果不是则退出;
3、判断下断的内存长度是否为1、2、4,如果都不符合,则退出;
4、读取硬件断点表数据,用于下面的比较;
5、循环判断当前断点 是否已经存在在硬件断点表中,判断的方法如下:
  a)  判断断点类型是否相同,不同则判断表中的下一个元素;
  b)  断点地址是否相同,相同则继续,不同则判断原先的断点是否命中在当前断点地址之中,若在其中,则修改原先的断点地址和长度,若不在其范围内,则判断下一个元素;
  c)  断点长度是否相同,相同则继续,不同则如同b的处理;
  d)  判断4个硬件调试寄存器是否已经用完,若用完了,则弹出硬件断点对话框,传入参数为1,要求用户必须删掉一个断点,若不删除,则退出;
  e)  若为类型为5、6、7的话,直接退出;
  f)  若有空余的寄存器元素,则把当前的断点信息赋值;
6、检查线程是否运行,线程信息结构体是否存在,若没有运行则跳过下面的处理,直接退出;
7、暂停所有活动线程;
8、遍历所有线程,查询硬件断点表,EIP的地址是否等于断点表中的地址,判断方法如下:
  a)  设置调试寄存器属性为CONTEXT_DEBUG_REGISTERS,并得到线程环境,若得到线程环境失败则查询下一个线程;
  b)  根据硬件断点表中的数据修改线程环境结构体数据;
  c)  遍历整个硬件断点表,判断表中是否有值,若无则继续检查下一个;
  d)  根据硬件断点的不同做相应的处理,主要是设置调试寄存器的dr7;
  e)  遍历完硬件断点表后,使用SetThreadContext函数将线程环境设置回去;
  f)  继续遍历线程
9、重启线程并退出函数

00451CE3   .  6A 03         push    3             ; BreakPoint_Flag
00451CE5   .  6A 01         push    1             ; BreakPoint_Len
00451CE7   .  8B4D B0       mov     ecx, dword ptr [ebp-50]       ; |
00451CEA   .  51            push    ecx                        ; | BreakPoint_Addr
00451CEB   .  E8 A069FBFF   call    _Sethardwarebreakpoint    ; \_Sethardwarebreakpoint
该函数主要有三个参数:断点首地址、断点长度、断点标识(访问、写入、执行断点)

00408690 >/$  55            push    ebp
00408691  |.  8BEC          mov     ebp, esp
00408693  |.  81C4 24FDFFFF add     esp, -2DC
00408699  |.  53            push    ebx
0040869A  |.  56            push    esi
0040869B  |.  57            push    edi
0040869C  |.  8B75 10       mov     esi, dword ptr [ebp+10]    ;断点标识
0040869F  |.  8B7D 08       mov     edi, dword ptr [ebp+8]    ;断点首地址
004086A2  |.  833D 5C374D00>cmp     dword ptr [4D375C], 0
004086A9  |.  75 08         jnz     short 004086B3
004086AB  |.  83C8 FF       or      eax, FFFFFFFF
004086AE  |.  E9 2F030000   jmp     004089E2

一个switch…case循环体,用来判断要设置什么类型的硬件断点:
004086B3  |>  83FE 01       cmp     esi, 1                     ;比较是否是执行断点
004086B6  |.  74 0F         je      short 004086C7        ;跳转到处理函数
004086B8  |.  83FE 05       cmp     esi, 5
004086BB  |.  74 0A         je      short 004086C7
004086BD  |.  83FE 06       cmp     esi, 6
004086C0  |.  74 05         je      short 004086C7
004086C2  |.  83FE 07       cmp     esi, 7
004086C5  |.  75 09         jnz     short 004086D0      ;如果都不是,跳到下面处理
处理执行断点:
004086C7  |>  C745 0C 01000>mov     dword ptr [ebp+C], 1         ;设置断点长度为1
004086CE  |.  EB 21         jmp     short 004086F1        ;跳转到长度处理
比较该断点标识是不是4
004086D0  |>  83FE 04       cmp     esi, 4
004086D3  |.  75 08         jnz     short 004086DD
004086D5  |.  81E7 FFFF0000 and     edi, 0FFFF                   ;  Case 4 
004086DB  |.  EB 14         jmp     short 004086F1        ;跳转到长度处理
比较该断点标识是不是0
004086DD  |>  85F6         test    esi, esi
004086DF  |.  74 10         je      short 004086F1          ;跳转到长度处理
断点标识为1、2、3时检查断点的长度及地址是否按内存对齐:
004086E1  |.  8B55 0C       mov     edx, dword ptr [ebp+C]       ;  Default 
004086E4  |.  4A            dec     edx
004086E5  |.  85FA          test    edx, edi
004086E7  |.  74 08         je      short 004086F1          ;跳转到长度处理
004086E9  |.  83C8 FF       or      eax, FFFFFFFF        ;内存不对齐则返回错误
004086EC  |.  E9 F1020000   jmp     004089E2
断点长度检查处理:
004086F1  |>  837D 0C 01    cmp     dword ptr [ebp+C], 1         ;  Case 0 
004086F5  |.  74 14         je      short 0040870B      ;长度为一跳转
004086F7  |.  837D 0C 02    cmp     dword ptr [ebp+C], 2
004086FB  |.  74 0E         je      short 0040870B      ;长度为二跳转
004086FD  |.  837D 0C 04    cmp     dword ptr [ebp+C], 4
00408701  |.  74 08         je      short 0040870B      ;长度为四跳转
00408703  |.  83C8 FF       or      eax, FFFFFFFF    ;长度不为1 / 2 / 4时,返回错误
00408706  |.  E9 D7020000   jmp     004089E2
查询断点表,要下的断点是否在表中,如果是则直接返回:
0040870B  |>  B8 708D4D00   mov     eax, 004D8D70     ;004D8070是断点表的首地址
00408710  |.  33D2          xor     edx, edx
00408712  |.  8955 F8       mov     dword ptr [ebp-8], edx
00408715  |.  33DB          xor     ebx, ebx
00408717  |>  8B50 08       /mov     edx, dword ptr [eax+8]
0040871A  |.  85D2          |test    edx, edx      ;查询表是否有记录
0040871C  |.  74 3E         |je      short 0040875C  ;没有则跳转到下一个记录结构体
0040871E  |.  3BF2          |cmp     esi, edx      ;比较断点类型是否相同
00408720  |.  75 3A         |jnz     short 0040875C  ;不同则跳转到下一个记录结构体
如果要下的断点在断点表某个断点的范围内,则直接返回:
00408722  |.  3B38          |cmp     edi, dword ptr [eax] ;比较断点首地址与断点表地址
00408724  |.  72 15         |jb      short 0040873B     ;断点首地址小则跳转
00408726  |.  8B08          |mov     ecx, dword ptr [eax]
00408728  |.  8B55 0C       |mov     edx, dword ptr [ebp+C]
0040872B  |.  0348 04       |add     ecx, dword ptr [eax+4]
0040872E  |.  03D7          |add     edx, edi
00408730  |.  3BCA          |cmp     ecx, edx    ;比较断点尾地址与断点表尾地址
00408732  |.  72 07         |jb      short 0040873B   ;断点尾地址大则跳转
00408734  |.  33C0          |xor     eax, eax
00408736  |.  E9 A7020000   |jmp     004089E2    ;跳转到结束处
如果要下的断点在断点表中存在,则直接返回:
0040873B  |>  3B38          |cmp     edi, dword ptr [eax] ;比较断点首地址与断点表地址
0040873D  |.  77 1D         |ja      short 0040875C     ;断点首地址大则跳转
0040873F  |.  8B08          |mov     ecx, dword ptr [eax]
00408741  |.  8B55 0C       |mov     edx, dword ptr [ebp+C]
00408744  |.  0348 04       |add     ecx, dword ptr [eax+4]
00408747  |.  03D7          |add     edx, edi
00408749  |.  3BCA          |cmp     ecx, edx      ;比较断点尾地址与断点表尾地址
0040874B  |.  77 0F         |ja      short 0040875C     ;断点尾地址小则跳转
0040874D  |.  8938          |mov     dword ptr [eax], edi
0040874F  |.  8B4D 0C       |mov     ecx, dword ptr [ebp+C]
00408752  |.  8948 04       |mov     dword ptr [eax+4], ecx
00408755  |.  C745 F8 01000>|mov     dword ptr [ebp-8], 1
跳转到下一个结构体中:
0040875C  |>  43            |inc     ebx
0040875D  |.  83C0 1C       |add     eax, 1C
00408760  |.  83FB 04       |cmp     ebx, 4
00408763  |.^ 7C B2         \jl      short 00408717    如果断点表没访问完,继续比较
查询断点是否包含在断点表中,包含则跳转到标识检查:
00408765  |.  837D F8 00    cmp     dword ptr [ebp-8], 0   
00408769  |.  0F85 91000000 jnz     00408800       
通过检查断点表中的标识,判断断点表是否已满:
0040876F  |.  33DB          xor     ebx, ebx
00408771  |.  B8 788D4D00   mov     eax, 004D8D78
00408776  |>  8338 00       /cmp     dword ptr [eax], 0
00408779  |.  74 09         |je      short 00408784  ;如果有空位,跳转到下面处理
0040877B  |.  43            |inc     ebx
0040877C  |.  83C0 1C       |add     eax, 1C
0040877F  |.  83FB 04       |cmp     ebx, 4
00408782  |.^ 7C F2         \jl      short 00408776    ;若断点表没访问完,继续比较
如果断点表未满,则跳转到设置断点表代码中去:
00408784  |>  83FB 04       cmp     ebx, 4
00408787  |.  7C 40         jl      short 004087C9
如果断点表已满,比较断点标识,如果小于5则跳转到处理函数:
00408789  |.  83FE 05       cmp     esi, 5      ;标识为5,返回-1
0040878C  |.  74 0A         je      short 00408798
0040878E  |.  83FE 06       cmp     esi, 6      ;标识为6,返回-1
00408791  |.  74 05         je      short 00408798
00408793  |.  83FE 07       cmp     esi, 7      ;标识为7,返回-1
00408796  |.  75 08         jnz     short 004087A0
00408798  |>  83C8 FF       or      eax, FFFFFFFF
0040879B  |.  E9 42020000   jmp     004089E2    ;跳转到函数结束处
断点表已满处理函数(弹出对话框选择在断点表中删除一个断点来放置现在的断点):
004087A0  |>  6A 01         push    1                            ; /Arg1 = 00000001
004087A2  |.  E8 AD070000   call    _Hardbreakpoints             ; \_Hardbreakpoints
004087A7  |.  59            pop     ecx
004087A8  |.  85C0          test    eax, eax      ;查看是否有删除断点表中的值
004087AA  |.  74 08         je      short 004087B4    ;如果有删除则跳转到下面检查空位
004087AC  |.  83C8 FF       or      eax, FFFFFFFF
004087AF  |.  E9 2E020000   jmp     004089E2      ;如果选择取消,则返回-1
这里检查断点表中是否有空位,没有则报错,并返回-1:
004087B4  |>  33DB          xor     ebx, ebx
004087B6  |.  B8 788D4D00   mov     eax, 004D8D78
004087BB  |>  8338 00       /cmp     dword ptr [eax], 0    ;比较断点表的标识变量
004087BE  |.  74 09         |je      short 004087C9    ;如果为0则跳转
004087C0  |.  43            |inc     ebx          ;比较下一个表中的值
004087C1  |.  83C0 1C       |add     eax, 1C
004087C4  |.  83FB 04       |cmp     ebx, 4
004087C7  |.^ 7C F2         \jl      short 004087BB    ;如果没有比较完,继续比较
004087C9  |>  83FB 04       cmp     ebx, 4    ;查看断点表中有多少个值
004087CC  |.  7C 13         jl      short 004087E1    ;如果断点表不满则跳转到下面
004087CE  |.  68 040D4B00   push    004B0D04; /当前没有空闲的位置进行新的硬件中断。
004087D3  |.  E8 44B80400   call    _Error                       ; \_Error
004087D8  |.  59            pop     ecx
004087D9  |.  83C8 FF       or      eax, FFFFFFFF
004087DC  |.  E9 01020000   jmp     004089E2    ;如果断点表满则返回-1,并报错
如果断点表有空位,则将断点的信息填入表中:
004087E1  |>  8BC3          mov     eax, ebx
004087E3  |.  C1E0 03       shl     eax, 3
004087E6  |.  2BC3          sub     eax, ebx
004087E8  |.  893C85 708D4D>mov     dword ptr [eax*4+4D8D70], edi  ;断点表第一个变量
004087EF  |.  8B55 0C       mov     edx, dword ptr [ebp+C]
004087F2  |.  891485 748D4D>mov     dword ptr [eax*4+4D8D74], edx  ;断点表第二个变量
004087F9  |.  893485 788D4D>mov     dword ptr [eax*4+4D8D78], esi  ;断点表第三个变量
检查标识,比较断点的类型,正确则跳转到结束处,错误则返回-1:
00408800  |>  833D 5C5A4D00>cmp     dword ptr [4D5A5C], 3    ;检查调试程序是否运行中
00408807  |.  0F85 D3010000 jnz     004089E0        ;若不是运行中则直接结束
0040880D  |.  83FE 05       cmp     esi, 5      ;是否是临时断点
00408810  |.  0F84 CA010000 je      004089E0
00408816  |.  83FE 06       cmp     esi, 6      ;…
00408819  |.  0F84 C1010000 je      004089E0
0040881F  |.  83FE 07       cmp     esi, 7      ;…
00408822  |.  0F84 B8010000 je      004089E0
00408828  |.  8B0D B07D4D00 mov     ecx, dword ptr [4D7DB0]
0040882E  |.  894D F4       mov     dword ptr [ebp-C], ecx
00408831  |.  837D F4 00    cmp     dword ptr [ebp-C], 0    ;…
00408835  |.  75 13         jnz     short 0040884A
00408837  |.  68 390D4B00   push    004B0D39   ; /内部错误:不知道如何设置硬件断点。
0040883C  |.  E8 DBB70400   call    _Error                       ; \_Error
00408841  |.  59            pop     ecx
00408842  |.  83C8 FF       or      eax, FFFFFFFF
00408845  |.  E9 98010000   jmp     004089E2    ;返回-1

一个大的循环体,设置一个线程的调试寄存器,下硬件断点(这里面的断点判断是用于调试程序运行中下硬件断点用的,与硬件断点表删除时代码一样,一起在下面分析):
……
……
恢复现场,返回设置成功:
004089E0  |>  33C0          xor     eax, eax    ;返回0
004089E2  |>  5F            pop     edi
004089E3  |.  5E            pop     esi
004089E4  |.  5B            pop     ebx
004089E5  |.  8BE5          mov     esp, ebp
004089E7  |.  5D            pop     ebp
004089E8  \.  C3            retn


删除硬件断点表(Deletehardwarebreakpoint):
该函数只有一个参数:硬件断点表中的序号
1、查看线程是否加载、断点表ID是否超出范围,出现错误则返回-1并退出;
2、将硬件断点表要删除的项置零;
3、后面的流程与添加硬件断点表流程的6~9步一样;

004089EC >/$  55            push    ebp
004089ED  |.  8BEC          mov     ebp, esp
004089EF  |.  81C4 28FDFFFF  add     esp, -2D8
查看线程是否加载、断点表ID是否超出范围,出现错误则返回-1并退出:
004089F5  |.  833D 5C374D00>cmp     dword ptr [4D375C], 0
004089FC  |.  53            push    ebx
004089FD  |.  56            push    esi
004089FE  |.  57            push    edi
004089FF  |.  8B45 08       mov     eax, dword ptr [ebp+8]
00408A02  |.  74 09         je      short 00408A0D
00408A04  |.  85C0          test    eax, eax      ;检查断点表序号是否小于0
00408A06  |.  7C 05         jl      short 00408A0D
00408A08  |.  83F8 04       cmp     eax, 4      ;检查断点表序号是否大于4
00408A0B  |.  7C 08         jl      short 00408A15
00408A0D  |>  83C8 FF       or      eax, FFFFFFFF    ;返回-1
00408A10  |.  E9 E9010000   jmp     00408BFE      ;跳转到结束处
将硬件断点表要删除的项置零:
00408A15  |>  8BD0          mov     edx, eax
00408A17  |.  33C9          xor     ecx, ecx
00408A19  |.  C1E2 03       shl     edx, 3
00408A1C  |.  2BD0          sub     edx, eax
00408A1E  |.  33C0          xor     eax, eax
00408A20  |.  890C95 708D4D>mov     dword ptr [edx*4+4D8D70], ecx  ;断点首地址置零
00408A27  |.  33C9          xor     ecx, ecx
00408A29  |.  890C95 748D4D>mov     dword ptr [edx*4+4D8D74], ecx  ;断点长度置零
00408A30  |.  890495 788D4D>mov     dword ptr [edx*4+4D8D78], eax  ;断点标识置零
检查线程是否出现异常,如果出现异常则报错,并返回-1:
00408A37  |.  833D 5C5A4D00>cmp     dword ptr [4D5A5C], 3  ;检查进程是否运行中
00408A3E  |.  0F85 B8010000 jnz     00408BFC        ;如果不运行则跳转到结束处
00408A44  |.  8B15 B07D4D00 mov     edx, dword ptr [4D7DB0]
00408A4A  |.  8955 F8       mov     dword ptr [ebp-8], edx
00408A4D  |.  837D F8 00    cmp     dword ptr [ebp-8], 0  ;查看线程信息是否存在
00408A51  |.  75 13         jnz     short 00408A66
00408A53  |.  68 740D4B00   push    004B0D74         ; /内部错误:不能删除硬件断点。
00408A58  |.  E8 BFB50400   call    _Error                                 ; \_Error
00408A5D  |.  59            pop     ecx
00408A5E  |.  83C8 FF       or      eax, FFFFFFFF    ;返回-1
00408A61  |.  E9 98010000   jmp     00408BFE      ;跳转到结束处
得到断点线程句柄:
00408A66  |>  33DB          xor     ebx, ebx
00408A68  |.  8B45 F8       mov     eax, dword ptr [ebp-8]
00408A6B  |.  8D70 0C       lea     esi, dword ptr [eax+C]  ;esi = hThread
00408A6E  |.  EB 0F         jmp     short 00408A7F
暂停多线程:
00408A70  |>  8B06          /mov     eax, dword ptr [esi]
00408A72  |.  50            |push    eax                          ; /hThread
00408A73  |.  E8 4A670A00   |call    <jmp.&KERNEL32.SuspendThread>  ; \SuspendThread
00408A78  |.  43            |inc     ebx        ;增加线程计数
00408A79  |.  81C6 6C060000 |add     esi, 66C      ;跳转到下个线程句柄
00408A7F  |>  3B1D 987D4D00  cmp     ebx, dword ptr [4D7D98]  ;是否遍历完多线程
00408A85  |.^ 7C E9         \jl      short 00408A70        ;如无则继续暂停进程
将第一个线程句柄给一个局部变量,初始化循环体:
00408A87  |.  33DB          xor     ebx, ebx
00408A89  |.  8B45 F8       mov     eax, dword ptr [ebp-8]
00408A8C  |.  8D50 0C       lea     edx, dword ptr [eax+C]
00408A8F  |.  8955 F4       mov     dword ptr [ebp-C], edx
00408A92  |.  E9 38010000   jmp     00408BCF    ;跳转到循环体条件判断处

一个大的循环体,设置一个线程的调试寄存器,下硬件断点(这里面的断点判断是用于调试程序运行中删除硬件断点用的,与下硬件断点时代码一样,在这里一起分析):
首先设置调试寄存器属性为CONTEXT_DEBUG_REGISTERS,并得到线程环境:
00408A97  |>  C785 28FDFFFF>/mov     dword ptr [ebp-2D8], 10010
00408AA1  |.  8D8D 28FDFFFF |lea     ecx, dword ptr [ebp-2D8]
00408AA7  |.  51            |push    ecx                         ; /pContext
00408AA8  |.  8B45 F4       |mov     eax, dword ptr [ebp-C]         ; |
00408AAB  |.  8B10          |mov     edx, dword ptr [eax]          ; |
00408AAD  |.  52            |push    edx                        ; |hThread
00408AAE  |.  E8 0D660A00   |call <jmp.&KERNEL32.GetThreadContext>;  \GetThreadContext
00408AB3  |.  85C0          |test    eax, eax
00408AB5  |.  0F84 0C010000 |je      00408BC7     ;如果没有得到线程环境,跳转到判断处
修改线程环境,将硬件断点表中的数值填入:
00408ABB  |.  8B0D 708D4D00 |mov     ecx, dword ptr [4D8D70]
00408AC1  |.  A1 8C8D4D00   |mov     eax, dword ptr [4D8D8C]
00408AC6  |.  898D 2CFDFFFF |mov     dword ptr [ebp-2D4], ecx  ; Context.Dr0
00408ACC  |.  8985 30FDFFFF |mov     dword ptr [ebp-2D0], eax  ; Context.Dr1
00408AD2  |.  8B15 A88D4D00 |mov     edx, dword ptr [4D8DA8]
00408AD8  |.  8B0D C48D4D00 |mov     ecx, dword ptr [4D8DC4]
00408ADE  |.  B8 788D4D00   |mov     eax, 004D8D78
00408AE3  |.  8995 34FDFFFF |mov     dword ptr [ebp-2CC], edx  ; Context.Dr2
00408AE9  |.  33D2          |xor     edx, edx
00408AEB  |.  898D 38FDFFFF |mov     dword ptr [ebp-2C8], ecx  ; Context.Dr2
00408AF1  |.  BE 00040000   |mov     esi, 400
这里是一个while循环,遍历整个断点表,判断断点表中是否有值:
00408AF6  |>  8338 00       |/cmp     dword ptr [eax], 0  ;标识是否为0
00408AF9  |.  0F84 A3000000 ||je      00408BA2      ;为0则跳转到下一个断点记录
这里是对各类硬件断点标识处理的switch循环体:
00408AFF  |.  8BCA          ||mov     ecx, edx
00408B01  |.  03C9          ||add     ecx, ecx
00408B03  |.  BF 01000000   ||mov     edi, 1
00408B08  |.  D3E7          ||shl     edi, cl
00408B0A  |.  0BF7          ||or      esi, edi
00408B0C  |.  8B08          ||mov     ecx, dword ptr [eax]
00408B0E  |.  83F9 07       ||cmp     ecx, 7                    ;  Switch (cases 1..7)
00408B11  |.  77 62         ||ja      short 00408B75  ;超出断点标识范围则
00408B13  |.  FF248D 1A8B40>||jmp     dword ptr [ecx*4+408B1A]
这是switch跳转表,对应0~7的硬件断点标识的处理,主要是设置Context.Dr7:
00408B1A  |.  758B4000      ||dd      Ollydbg.00408B75   ;  分支表 被用于 00408B13
00408B1E  |.  3A8B4000      ||dd      Ollydbg.00408B3A
00408B22  |.  488B4000      ||dd      Ollydbg.00408B48
00408B26  |.  578B4000      ||dd      Ollydbg.00408B57
00408B2A  |.  668B4000      ||dd      Ollydbg.00408B66
00408B2E  |.  3A8B4000      ||dd      Ollydbg.00408B3A
00408B32  |.  3A8B4000      ||dd      Ollydbg.00408B3A
00408B36  |.  3A8B4000      ||dd      Ollydbg.00408B3A
标识为1(执行断点),5,6,7时,设置断点长度为1,跳转到
00408B3A  |>  33C9          ||xor     ecx, ecx       ;  Cases 1,5,6,7 of switch 00408B0E
00408B3C  |.  894D FC       ||mov     dword ptr [ebp-4], ecx
00408B3F  |.  C740 FC 01000>||mov     dword ptr [eax-4], 1
00408B46  |.  EB 32         ||jmp     short 00408B7A
当标识为2(访问断点)时,设置单步断点:
00408B48  |>  C745 FC 03000>||mov     dword ptr [ebp-4], 3  ;  Case 2 of switch 00408B0E
00408B4F  |.  81CE 00010000 ||or      esi, 100
00408B55  |.  EB 23         ||jmp     short 00408B7A
当标识为3(写入断点)时,设置单步断点:
00408B57  |>  C745 FC 01000>||mov     dword ptr [ebp-4], 1  ;  Case 3 of switch 00408B0E
00408B5E  |.  81CE 00010000 ||or      esi, 100
00408B64  |.  EB 14         ||jmp     short 00408B7A
当标识为4时,设置单步断点:
00408B66  |>  C745 FC 02000>||mov     dword ptr [ebp-4], 2  ;  Case 4 of switch 00408B0E
00408B6D  |.  81CE 00010000 ||or      esi, 100
00408B73  |.  EB 05         ||jmp     short 00408B7A
这是默认处理处:
00408B75  |>  33C9          ||xor     ecx, ecx       ;  Default case of switch 00408B0E
00408B77  |.  894D FC       ||mov     dword ptr [ebp-4], ecx
检查硬件断点的长度,跳转到对应的处理函数:
00408B7A  |>  8B48 FC       ||mov     ecx, dword ptr [eax-4]
00408B7D  |.  83E9 02       ||sub     ecx, 2
00408B80  |.  74 07         ||je      short 00408B89  ;长度为2时跳转
00408B82  |.  83E9 02       ||sub     ecx, 2
00408B85  |.  74 08         ||je      short 00408B8F  ;长度为4时跳转
00408B87  |.  EB 0A         ||jmp     short 00408B93
下面是根据断点长度设置Context.Dr7,先将值放在esi寄存器中:
00408B89  |>  834D FC 04    ||or      dword ptr [ebp-4], 4  ;
00408B8D  |.  EB 04         ||jmp     short 00408B93
00408B8F  |>  834D FC 0C    ||or      dword ptr [ebp-4], 0C ;
00408B93  |>  8BCA          ||mov     ecx, edx
00408B95  |.  C1E1 02       ||shl     ecx, 2
00408B98  |.  83C1 10       ||add     ecx, 10
00408B9B  |.  8B7D FC       ||mov     edi, dword ptr [ebp-4]
00408B9E  |.  D3E7          ||shl     edi, cl
00408BA0  |.  0BF7          ||or      esi, edi
遍历断点表的判断和自加部分:
00408BA2  |>  42            ||inc     edx
00408BA3  |.  83C0 1C       ||add     eax, 1C
00408BA6  |.  83FA 04       ||cmp     edx, 4
00408BA9  |.^ 0F8C 47FFFFFF |\jl      00408AF6    ;如果没有比较完,继续遍历
遍历完断点表完毕后,
00408BAF  |.  89B5 40FDFFFF |mov     dword ptr [ebp-2C0], esi
00408BB5  |.  8D85 28FDFFFF |lea     eax, dword ptr [ebp-2D8]
00408BBB  |.  50            |push    eax                        ; /pContext
00408BBC  |.  8B55 F4       |mov     edx, dword ptr [ebp-C]        ; |
00408BBF  |.  8B0A          |mov     ecx, dword ptr [edx]         ; |
00408BC1  |.  51            |push    ecx                        ; |hThread
00408BC2  |.  E8 E3650A00   |call <jmp.&KERNEL32.SetThreadContext>  ; \SetThreadContext
00408BC7  |>  43            |inc     ebx
00408BC8  |.  8145 F4 6C060>|add     dword ptr [ebp-C], 66C
判断是否遍历所有线程,如果没有则继续遍历:
00408BCF  |>  3B1D 987D4D00  cmp     ebx, dword ptr [4D7D98]
00408BD5  |.^ 0F8C BCFEFFFF \jl      00408A97
初始化硬件断点的线程数以及得到第一个线程句柄:
00408BDB  |.  33DB          xor     ebx, ebx
00408BDD  |.  8B45 F8       mov     eax, dword ptr [ebp-8]
00408BE0  |.  8D70 0C       lea     esi, dword ptr [eax+C]    ;esi = hThread
00408BE3  |.  EB 0F         jmp     short 00408BF4  ;开始设置线程断点

设置完硬件断点后激活多线程:
00408BE5  |>  8B06          /mov     eax, dword ptr [esi]
00408BE7  |.  50            |push    eax                            ; /hThread
00408BE8  |.  E8 87650A00   |call    <jmp.&KERNEL32.ResumeThread>    ; \ResumeThread
00408BED  |.  43            |inc     ebx        ;增加线程计数
00408BEE  |.  81C6 6C060000 |add     esi, 66C      ;得到下个线程句柄
00408BF4  |>  3B1D 987D4D00  cmp     ebx, dword ptr [4D7D98]  ;是否遍历完多进程
00408BFA  |.^ 7C E9         \jl      short 00408BE5        ;如无则继续激活进程
恢复现场,返回成功:
00408BFC  |>  33C0          xor     eax, eax    ;返回0
00408BFE  |>  5F            pop     edi
00408BFF  |.  5E            pop     esi
00408C00  |.  5B            pop     ebx
00408C01  |.  8BE5          mov     esp, ebp
00408C03  |.  5D            pop     ebp
00408C04  \.  C3            retn


硬件断点的设置(SetHardbpoint):
该函数只有一个参数:标识是否覆盖线程信息结构体中oldreg成员
1、  检查当前线程结构体是否为NULL,若是则直接退出;
2、  遍历所有线程,设置每个线程的信息结构体数据,设置方法如下:
a)  根据传入的标识判断是否覆盖线程信息结构体中的oldreg成员;
b)  查看线程信息结构体中寄存器值是否改变,若发生改变,则将寄存器中的值赋值到线程信息结构体中的线程环境成员结构体中;
c)  检查寄存器是否设置单步标识,若有设置则修改线程环境成员结构体中;
d)  检查浮点寄存器,根据寄存器对线程环境成员结构体做相应设置;
e)  检查寄存器的段基址,若有效则设置段寄存器值到线程环境成员结构体中;
f)  最后根据线程环境结构体中的值设置dr7;
g)  继续遍历下个线程;
3、  查看设置是否成功,若失败则报错,并返回-1退出
4、  否则就根据设置的返回值设置该函数的返回值,最后退出

这个函数在按F9运行时每走一句汇编代码都会调用一次,即是调试状态时下硬件断点的处理函数:
首先检查线程是否存在,如果不存在直接返回:
0042E7DC  /$  55            push    ebp
0042E7DD  |.  8BEC          mov     ebp, esp
0042E7DF  |.  83C4 E4       add     esp, -1C
0042E7E2  |.  33C0          xor     eax, eax
0042E7E4  |.  8B15 B07D4D00 mov     edx, dword ptr [4D7DB0]
0042E7EA  |.  53            push    ebx
0042E7EB  |.  56            push    esi
0042E7EC  |.  57            push    edi
0042E7ED  |.  8945 F4       mov     dword ptr [ebp-C], eax
0042E7F0  |.  8955 EC       mov     dword ptr [ebp-14], edx
0042E7F3  |.  837D EC 00    cmp     dword ptr [ebp-14], 0  ;线程信息指针是否为NULL
0042E7F7  |.  75 07         jnz     short 0042E800    ;不是就跳到下面执行
0042E7F9  |.  33C0          xor     eax, eax
0042E7FB  |.  E9 6E030000   jmp     0042EB6E      ;跳转到结束处

这里用来遍历所有加载的线程:
0042E800  |>  33D2          xor     edx, edx
0042E802  |.  8955 FC       mov     dword ptr [ebp-4], edx
0042E805  |.  E9 32030000   jmp     0042EB3C      ;跳转到检查线程数量处
这里是设置线程环境中的八个一般寄存器、八个浮点寄存器和六个断寄存器:
0042E80A  |>  8B4D EC       /mov     ecx, dword ptr [ebp-14]  ;得到reg信息有效性标识
0042E80D  |.  83B9 82040000>|cmp     dword ptr [ecx+482], 0
0042E814  |.  0F84 18030000 |je      0042EB32        ;如果为0则设置下个进程
0042E81A  |.  837D 08 00    |cmp     dword ptr [ebp+8], 0
0042E81E  |.  74 28         |je      short 0042E848
先将结构体中的线程寄存器信息保存到结构体中旧线程寄存器信息中:
0042E820  |.  8B45 EC       |mov     eax, dword ptr [ebp-14]
0042E823  |.  8B55 EC       |mov     edx, dword ptr [ebp-14]
0042E826  |.  8DB0 EC020000 |lea     esi, dword ptr [eax+2EC]
0042E82C  |.  8DBA 86040000 |lea     edi, dword ptr [edx+486]
0042E832  |.  B9 65000000   |mov     ecx, 65
0042E837  |.  F3:A5         |rep     movs dword ptr es:[edi], dword ptr [e>
0042E839  |.  66:A5         |movs    word ptr es:[edi], word ptr [esi]
0042E83B  |.  8B45 EC       |mov     eax, dword ptr [ebp-14]
0042E83E  |.  C780 1C060000>|mov     dword ptr [eax+61C], 1
然后查看寄存器值是否修改,如果没有修改,则到下面处理,否则就将当前线程环境赋值给线程结构体中的线程上下文环境信息中:
0042E848  |>  8B55 EC       |mov     edx, dword ptr [ebp-14]
0042E84B  |.  83BA EC020000>|cmp     dword ptr [edx+2EC], 0    ;标识寄存器是否修改
0042E852  |.  0F84 7E010000 |je      0042E9D6    ;跳转到处理未修改寄存器情况的函数
下面开始赋值八个一般寄存器:
0042E858  |.  8B5D EC       |mov     ebx, dword ptr [ebp-14]
0042E85B  |.  83C3 20       |add     ebx, 20
0042E85E  |.  C703 1F000100 |mov     dword ptr [ebx], 1001F
0042E864  |.  8B55 EC       |mov     edx, dword ptr [ebp-14]
0042E867  |.  81C2 EC020000 |add     edx, 2EC
0042E86D  |.  8B42 0C       |mov     eax, dword ptr [edx+C]
0042E870  |.  8983 B0000000 |mov     dword ptr [ebx+B0], eax    ;context.eax = reg.eax
0042E876  |.  8B4A 10       |mov     ecx, dword ptr [edx+10]
0042E879  |.  898B AC000000 |mov     dword ptr [ebx+AC], ecx    ;context.ecx = reg.ecx
0042E87F  |.  8B42 14       |mov     eax, dword ptr [edx+14]
0042E882  |.  8983 A8000000 |mov     dword ptr [ebx+A8], eax    ;context.edx = reg.edx
0042E888  |.  8B4A 18       |mov     ecx, dword ptr [edx+18]
0042E88B  |.  898B A4000000 |mov     dword ptr [ebx+A4], ecx    ;context.ebx = reg.ebx
0042E891  |.  8B42 1C       |mov     eax, dword ptr [edx+1C]
0042E894  |.  8983 C4000000 |mov     dword ptr [ebx+C4], eax    ;context.esp = reg.esp
0042E89A  |.  8B4A 20       |mov     ecx, dword ptr [edx+20]
0042E89D  |.  898B B4000000 |mov     dword ptr [ebx+B4], ecx    ;context.ebp = reg.ebp
0042E8A3  |.  8B42 24       |mov     eax, dword ptr [edx+24]
0042E8A6  |.  8983 A0000000 |mov     dword ptr [ebx+A0], eax  ;context.esi = reg.esi
0042E8AC  |.  8B4A 28       |mov     ecx, dword ptr [edx+28]
0042E8AF  |.  898B 9C000000 |mov     dword ptr [ebx+9C], ecx    ;context.edi = reg.edi
0042E8B5  |.  8B42 2C       |mov     eax, dword ptr [edx+2C]
0042E8B8  |.  8983 B8000000 |mov     dword ptr [ebx+B8], eax    ;context.eip = reg.eip
0042E8BE  |.  8B4A 30       |mov     ecx, dword ptr [edx+30]
0042E8C1  |.  898B C0000000 |mov     dword ptr [ebx+C0], ecx    ;context.eflags = reg.flags
检查是否是单步运行,如果是则设置单步断点,然后处理浮点寄存器:
0042E8C7  |.  8B45 EC       |mov     eax, dword ptr [ebp-14]
0042E8CA  |.  F680 F4020000>|test    byte ptr [eax+2F4], 1  ;查看是否是单步运行
0042E8D1  |.  74 11         |je      short 0042E8E4  ;不是单步则跳过设置单步
0042E8D3  |.  818B C0000000>|or      dword ptr [ebx+C0], 100    ;设置单步运行
0042E8DD  |.  83A2 E6000000>|and     dword ptr [edx+E6], FFFFFFF0  ;设置SSE寄存器
下面部分设置浮点寄存器:
0042E8E4  |>  8B42 34       |mov     eax, dword ptr [edx+34]
0042E8E7  |.  8B4B 20       |mov     ecx, dword ptr [ebx+20]  ;得到浮点标识
0042E8EA  |.  83E0 07       |and     eax, 7
0042E8ED  |.  81E1 FFC7FFFF |and     ecx, FFFFC7FF
0042E8F3  |.  C1E0 0B       |shl     eax, 0B
0042E8F6  |.  0BC8          |or      ecx, eax
0042E8F8  |.  33C0          |xor     eax, eax
0042E8FA  |.  894B 20       |mov     dword ptr [ebx+20], ecx    ;重新设置浮点标识
0042E8FD  |.  8D4B 38       |lea     ecx, dword ptr [ebx+38]
0042E900  |.  894D E4       |mov     dword ptr [ebp-1C], ecx
0042E903  |.  8D8A 88000000 |lea     ecx, dword ptr [edx+88]
0042E909  |.  894D E8       |mov     dword ptr [ebp-18], ecx
这里开始设置八个浮点寄存器,首先检查浮点寄存器的栈顶是否访问到系统内存:
0042E90C  |>  8B4A 34       |/mov     ecx, dword ptr [edx+34]
0042E90F  |.  03C8          ||add     ecx, eax
0042E911  |.  81E1 07000080 ||and     ecx, 80000007
0042E917  |.  79 05         ||jns     short 0042E91E
0042E919  |.  49            ||dec     ecx
0042E91A  |.  83C9 F8       ||or      ecx, FFFFFFF8
0042E91D  |.  41            ||inc     ecx
这部分开始设置浮点寄存器相关信息:
0042E91E  |>  894D F8       ||mov     dword ptr [ebp-8], ecx
0042E921  |.  8BC8          ||mov     ecx, eax
0042E923  |.  03C9          ||add     ecx, ecx
0042E925  |.  BE 03000000   ||mov     esi, 3
0042E92A  |.  D3E6          ||shl     esi, cl
0042E92C  |.  F7D6          ||not     esi
0042E92E  |.  2373 24       ||and     esi, dword ptr [ebx+24]
0042E931  |.  8BC8          ||mov     ecx, eax
0042E933  |.  03C9          ||add     ecx, ecx
0042E935  |.  8B7D E8       ||mov     edi, dword ptr [ebp-18]
0042E938  |.  0FB63F        ||movzx   edi, byte ptr [edi]
0042E93B  |.  83E7 03       ||and     edi, 3
0042E93E  |.  D3E7          ||shl     edi, cl
0042E940  |.  0BF7          ||or      esi, edi
0042E942  |.  8973 24       ||mov dword ptr [ebx+24], esi;设置CONTEXT.FloatSave.TagWord
0042E945  |.  8B4D F8       ||mov     ecx, dword ptr [ebp-8]
0042E948  |.  8D0C89        ||lea     ecx, dword ptr [ecx+ecx*4]
0042E94B  |.  8B75 E4       ||mov     esi, dword ptr [ebp-1C]
0042E94E  |.  8B7C4A 38     ||mov     edi, dword ptr [edx+ecx*2+38]
0042E952  |.  893E          ||mov     dword ptr [esi], edi  ; context.st0 = reg_st0
0042E954  |.  8B7C4A 3C     ||mov     edi, dword ptr [edx+ecx*2+3C]
0042E958  |.  897E 04       ||mov     dword ptr [esi+4], edi  ; context.st1 = reg_st1
0042E95B  |.  66:8B7C4A 40  ||mov     di, word ptr [edx+ecx*2+40]
0042E960  |.  66:897E 08    ||mov     word ptr [esi+8], di  ; context.st2 = reg_st2
0042E964  |.  40            ||inc     eax
0042E965  |.  8345 E4 0A    ||add     dword ptr [ebp-1C], 0A  ;设置下个浮点寄存器
0042E969  |.  FF45 E8       ||inc     dword ptr [ebp-18]
0042E96C  |.  83F8 08       ||cmp     eax, 8
0042E96F  |.^ 7C 9B         |\jl      short 0042E90C    ;继续设置浮点寄存器信息
下面设置段寄存器信息:
0042E971  |.  8B82 90000000 |mov     eax, dword ptr [edx+90]
0042E977  |.  8943 20       |mov     dword ptr [ebx+20], eax  ; StatusWord = s_base_fs
0042E97A  |.  8B8A 94000000 |mov     ecx, dword ptr [edx+94]
0042E980  |.  894B 1C       |mov     dword ptr [ebx+1C], ecx   ;ControlWord = s_base_gs
0042E983  |.  8B82 98000000 |mov     eax, dword ptr [edx+98]
0042E989  |.  8983 94000000 |mov     dword ptr [ebx+94], eax    ; SegEs = limit_es
0042E98F  |.  8B8A 9C000000 |mov     ecx, dword ptr [edx+9C]
0042E995  |.  898B BC000000 |mov     dword ptr [ebx+BC], ecx    ; SegCs = limit_cs
0042E99B  |.  8B82 A0000000 |mov     eax, dword ptr [edx+A0]
0042E9A1  |.  8983 C8000000 |mov     dword ptr [ebx+C8], eax    ; SegSs = limit_ss
0042E9A7  |.  8B8A A4000000 |mov     ecx, dword ptr [edx+A4]
0042E9AD  |.  898B 98000000 |mov     dword ptr [ebx+98], ecx    ; SegDs = limit_ds
0042E9B3  |.  8B82 A8000000 |mov     eax, dword ptr [edx+A8]
0042E9B9  |.  8983 90000000 |mov     dword ptr [ebx+90], eax    ; SegFs = limit_fs
0042E9BF  |.  8B8A AC000000 |mov     ecx, dword ptr [edx+AC]
0042E9C5  |.  898B 8C000000 |mov     dword ptr [ebx+8C], ecx    ;SegGs = limit_gs
0042E9CB  |.  8B92 E6000000 |mov     edx, dword ptr [edx+E6]
0042E9D1  |.  8953 14       |mov     dword ptr [ebx+14], edx  ;set SSE reg
0042E9D4  |.  EB 3F         |jmp     short 0042EA15
比较是否是单步,如果不是则继续比较下个线程的硬件断点:
0042E9D6  |>  8B45 EC       |mov     eax, dword ptr [ebp-14]
0042E9D9  |.  F680 F4020000>|test    byte ptr [eax+2F4], 1
0042E9E0  |.  0F84 4C010000 |je      0042EB32    ;跳转到上面继续比较
设置成单步执行:
0042E9E6  |.  8B5D EC       |mov     ebx, dword ptr [ebp-14]
0042E9E9  |.  83C3 20       |add     ebx, 20
0042E9EC  |.  C703 11000100 |mov     dword ptr [ebx], 10011
0042E9F2  |.  8B55 EC       |mov     edx, dword ptr [ebp-14]
0042E9F5  |.  81C2 EC020000 |add     edx, 2EC
0042E9FB  |.  8B42 2C       |mov     eax, dword ptr [edx+2C]
0042E9FE  |.  8983 B8000000 |mov     dword ptr [ebx+B8], eax
0042EA04  |.  818B C0000000>|or      dword ptr [ebx+C0], 100    ;设置单步标识
0042EA0E  |.  83A2 E6000000>|and     dword ptr [edx+E6], FFFFFFF0

设置完寄存器后对应其标识处理context.dr7并设置线程环境,这部分与硬件断点表删除中的代码一样,可以参考从00408AF6到00408BCB部分的代码解释,就不再重复分析了:
0042EA51  |>  833A 00       |/cmp     dword ptr [edx], 0
0042EA54  |.  0F84 B0000000 ||je      0042EB0A
0042EA5A  |.  F605 10574D00>||test    byte ptr [4D5710], 1
0042EA61  |.  0F85 A3000000 ||jnz     0042EB0A
……
……
0042EB17  |.  8973 18       |mov     dword ptr [ebx+18], esi
0042EB1A  |>  53            |push    ebx                       ; /pContext
0042EB1B  |.  8B45 EC       |mov     eax, dword ptr [ebp-14]       ; |
0042EB1E  |.  8B50 0C       |mov     edx, dword ptr [eax+C]        ; |
0042EB21  |.  52            |push    edx                        ; |hThread
0042EB22  |.  E8 83060800   |call <jmp.&KERNEL32.SetThreadContext>  ; \SetThreadContext
0042EB27  |.  85C0          |test    eax, eax
0042EB29  |.  75 07         |jnz     short 0042EB32
0042EB2B  |.  C745 F4 FFFFF>|mov     dword ptr [ebp-C], -1
0042EB32  |>  FF45 FC       |inc     dword ptr [ebp-4]
0042EB35  |.  8145 EC 6C060>|add     dword ptr [ebp-14], 66C
这里检查设置过的线程数量,如果没有全部设置,继续跳转到设置函数:
0042EB3C  |>  8B4D FC        mov     ecx, dword ptr [ebp-4]
0042EB3F  |.  3B0D 987D4D00 |cmp     ecx, dword ptr [4D7D98]
0042EB45  |.^ 0F8C BFFCFFFF \jl      0042E80A    ;跳转到设置CONTEXT函数体中
查看返回值是否为0,不是则弹出报错窗口:
0042EB4B  |.  837D F4 00    cmp     dword ptr [ebp-C], 0
0042EB4F  |.  74 1A         je      short 0042EB6B
0042EB51  |.  68 195F4B00   push    004B5F19             ; /无法写回被修改的寄存器
0042EB56  |.  6A 00         push    0                    ; |Arg1 = 00000000
0042EB58  |.  E8 D32A0000   call    _Message              ; \_Message
0042EB5D  |.  83C4 08       add     esp, 8
0042EB60  |.  68 195F4B00   push    004B5F19           ;  无法写回被修改的寄存器
0042EB65  |.  E8 B2540200   call    _Error
0042EB6A  |.  59            pop     ecx
设置返回值:
0042EB6B  |>  8B45 F4       mov     eax, dword ptr [ebp-C]
恢复现场:
0042EB6E  |>  5F            pop     edi
0042EB6F  |.  5E            pop     esi
0042EB70  |.  5B            pop     ebx
0042EB71  |.  8BE5          mov     esp, ebp
0042EB73  |.  5D            pop     ebp
0042EB74  \.  C3            retn

                              武汉科锐学员: angelqkm
                                2008-5-23