-
-
[原创]调试器编写-02一般断点(软件断点)
-
发表于: 2024-8-18 12:52 5176
-
断点的尊严
所有合格的断点都应该满足这3个要求
OD下断点实际是把指令的第一个字节改成了CC,当程序执行到CC的时候其实是抛了一个异常(EXCEPTION_BREAKPOINT),这个异常就会进入调试器里面因为处于调试状态,调试器拿到这个异常之后就会知道程序要断到这里,写了CC之后,这条指令正常的功能就被破坏了,但是这条指令正常功能中他还是应该执行的,那怎么让他走过去呢,那就是把这条指令恢复,下次再来只需要走过去之后再把它写回CC,这条指令执行完可通过 TF 标志位 来实现, (TF置1就会抛出异常(EXCEPTION_SINGLE_STEP)),
断步配合: 断点和单步配合实现断点下次再来
调试的时候 如果 断点下次没来 就检查断步配合,如果崩了说明没有恢复,如果程序跑飞了,断点不再来,说明单步没处理好
调试器对被调试进程拥有所有的权限(除了写)
第二个成员就是调试器判断异常第一个次来还是第二次
第一个成员,异常信息结构体
第一个参数: 线程句柄 ,第二个结构体指针(结构体类型要到 vs中去看, msdn没有)
以扫雷为例
https://bbs.pediy.com/thread-205590.htm
udis86官网 http://udis86.sourceforge.net/
扫雷的过程函数:
地址 机器码 助记符
01001BC9
55
push ebp
01001BCA
8BEC
mov ebp, esp
01001BCC
83EC
40
sub esp,
40
01001BCF
8B55
0C
mov edx, dword ptr [ebp
+
C]
/
/
在该行下断点
01001BD2
8B4D
14
mov ecx, dword ptr [ebp
+
14
]
01001BD5
53
push ebx
01001BD6
56
push esi
01001BD7
33DB
xor ebx, ebx
扫雷的过程函数:
地址 机器码 助记符
01001BC9
55
push ebp
01001BCA
8BEC
mov ebp, esp
01001BCC
83EC
40
sub esp,
40
01001BCF
8B55
0C
mov edx, dword ptr [ebp
+
C]
/
/
在该行下断点
01001BD2
8B4D
14
mov ecx, dword ptr [ebp
+
14
]
01001BD5
53
push ebx
01001BD6
56
push esi
01001BD7
33DB
xor ebx, ebx
.
586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
.data
g_szExe db
"winmine.exe"
,
0
;被调试的程序
g_hExe dd
0
;被调试的程序句柄
g_szEXCEPTION_DEBUG_EVENT db
"EXCEPTION_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szCREATE_THREAD_DEBUG_EVENT db
"CREATE_THREAD_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szCREATE_PROCESS_DEBUG_EVENT db
"CREATE_PROCESS_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szEXIT_THREAD_DEBUG_EVENT db
"EXIT_THREAD_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szEXIT_PROCESS_DEBUG_EVENT db
"EXIT_PROCESS_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szLOAD_DLL_DEBUG_EVENT db
"LOAD_DLL_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szUNLOAD_DLL_DEBUG_EVENT db
"UNLOAD_DLL_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szOUTPUT_DEBUG_STRING_EVENT db
"OUTPUT_DEBUG_STRING_EVENT"
,
0dh
,
0ah
,
0
g_szLoadDllFmt db
"%08X %s"
,
0dh
,
0ah
,
0
g_szwLoadDllFmt dw
'%'
,
'0'
,
'8'
,
'X'
,
' '
,
'%'
,
's'
,
0dh
,
0ah
,
0
g_szBpFmt db
"CC异常 %08X"
,
0dh
,
0ah
,
0
g_szSsFmt db
"单步异常 %08X"
,
0dh
,
0ah
,
0
g_btOldCode db
0
;记录没下断点之前的字符
g_dwBpAddr dd
01001BCFh
;下断点的地址
g_byteCC db
0CCh
; 短点符号 CC
.code
;处理异常信息
OnException proc uses esi pDE:ptr DEBUG_EVENT
LOCAL @dwOldProc:DWORD ;修改之前的内存属性
LOCAL @dwBytesOut:DWORD
LOCAL @hThread:HANDLE
LOCAL @ctx:CONTEXT
mov esi, pDE
assume esi:ptr DEBUG_EVENT
;判断是否断点异常
.
if
[esi].u.Exception.pExceptionRecord.ExceptionCode
=
=
EXCEPTION_BREAKPOINT
;判断是否是自己的CC 通过地址判断
mov eax, [esi].u.Exception.pExceptionRecord.ExceptionAddress
.
if
eax !
=
g_dwBpAddr ;该处地址是不是我们下断点地址
;不是自己的CC异常,不处理
mov eax, DBG_EXCEPTION_NOT_HANDLED
ret
.endif
;处理自己的CC异常
invoke crt_printf, offset g_szBpFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress
;修改内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, PAGE_EXECUTE_READWRITE, addr @dwOldProc
;恢复指令
invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut
;还原内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, @dwOldProc, addr @dwOldProc
;设置单步
;获取线程句柄
invoke OpenThread, THREAD_ALL_ACCESS, FALSE, [esi].dwThreadId
mov @hThread, eax
;设置结构体标志位,用来判断获取那些寄存器环境
; CONTEXT_FULL 表示获取控制,整数,段 CONTEXT_ALL 是所有的
mov @ctx.ContextFlags, CONTEXT_FULL
;获取寄存器环境
invoke GetThreadContext, @hThread, addr @ctx
;将TF标志位置
1
or
@ctx.regFlag,
100h
;返回当前代码地址 @ctx.regEip 执行完CC的地址
dec @ctx.regEip ;减
1
,cc是一个字节.
;设置寄存器环境
invoke SetThreadContext, @hThread, addr @ctx
;关闭线程句柄
invoke CloseHandle, @hThread
mov eax, DBG_CONTINUE
ret
.endif
;单步来了 (如果是单步异常)
.
if
[esi].u.Exception.pExceptionRecord.ExceptionCode
=
=
EXCEPTION_SINGLE_STEP
;处理自己的单步
invoke crt_printf, offset g_szSsFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress
;修改内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, PAGE_EXECUTE_READWRITE, addr @dwOldProc
;重设断点, 重新写入CC
invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut
;还原内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, @dwOldProc, addr @dwOldProc
;异常已处理,继续执行代码
mov eax, DBG_CONTINUE
ret
.endif
assume esi:nothing
mov eax, DBG_EXCEPTION_NOT_HANDLED
ret
OnException endp
OnCreateProcess proc
LOCAL @dwBytesOut:DWORD
LOCAL @dwOldProc:DWORD ;修改之前的内存属性
;修改内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, PAGE_EXECUTE_READWRITE, addr @dwOldProc
;保存原来的被下断点地址 指令到 g_btOldCode ,用于恢复
invoke ReadProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut
;在
01001BCF
(断点地址)写入CC
invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut
;还原内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, @dwOldProc, addr @dwOldProc
ret
OnCreateProcess endp
main proc
LOCAL @si:STARTUPINFO
LOCAL @pi:PROCESS_INFORMATION
LOCAL @de:DEBUG_EVENT
LOCAL @dwStatus:DWORD ;事件处理结果
invoke RtlZeroMemory, addr @si, size @si
invoke RtlZeroMemory, addr @pi, size @pi
invoke RtlZeroMemory, addr @de, size @de
mov @dwStatus, DBG_CONTINUE
;建立调试会话
invoke CreateProcess, NULL, offset g_szExe, NULL, NULL, FALSE, \
DEBUG_ONLY_THIS_PROCESS,\
NULL, NULL,\
addr @si,\
addr @pi
.
if
!eax
ret
.endif
mov eax, @pi.hProcess
mov g_hExe, eax
;循环接受调试事件
.
while
TRUE
invoke WaitForDebugEvent, addr @de, INFINITE
;处理调试事件
.
if
@de.dwDebugEventCode
=
=
EXCEPTION_DEBUG_EVENT
;invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT
invoke OnException, addr @de
mov @dwStatus, eax
.elseif @de.dwDebugEventCode
=
=
CREATE_THREAD_DEBUG_EVENT
invoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode
=
=
CREATE_PROCESS_DEBUG_EVENT
;invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENT
invoke OnCreateProcess
.elseif @de.dwDebugEventCode
=
=
EXIT_THREAD_DEBUG_EVENT
invoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode
=
=
EXIT_PROCESS_DEBUG_EVENT
invoke crt_printf, offset g_szEXIT_PROCESS_DEBUG_EVENT
.elseif @de.dwDebugEventCode
=
=
LOAD_DLL_DEBUG_EVENT
;invoke OnLoadDll, addr @de
.elseif @de.dwDebugEventCode
=
=
UNLOAD_DLL_DEBUG_EVENT
invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT
.elseif @de.dwDebugEventCode
=
=
OUTPUT_DEBUG_STRING_EVENT
invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT
.endif
;提交事件处理结果
invoke ContinueDebugEvent, @de.dwProcessId, @de.dwThreadId, @dwStatus
invoke RtlZeroMemory, addr @de, size @de
.endw
ret
main endp
start:
invoke main
end start
.
586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
.data
g_szExe db
"winmine.exe"
,
0
;被调试的程序
g_hExe dd
0
;被调试的程序句柄
g_szEXCEPTION_DEBUG_EVENT db
"EXCEPTION_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szCREATE_THREAD_DEBUG_EVENT db
"CREATE_THREAD_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szCREATE_PROCESS_DEBUG_EVENT db
"CREATE_PROCESS_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szEXIT_THREAD_DEBUG_EVENT db
"EXIT_THREAD_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szEXIT_PROCESS_DEBUG_EVENT db
"EXIT_PROCESS_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szLOAD_DLL_DEBUG_EVENT db
"LOAD_DLL_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szUNLOAD_DLL_DEBUG_EVENT db
"UNLOAD_DLL_DEBUG_EVENT"
,
0dh
,
0ah
,
0
g_szOUTPUT_DEBUG_STRING_EVENT db
"OUTPUT_DEBUG_STRING_EVENT"
,
0dh
,
0ah
,
0
g_szLoadDllFmt db
"%08X %s"
,
0dh
,
0ah
,
0
g_szwLoadDllFmt dw
'%'
,
'0'
,
'8'
,
'X'
,
' '
,
'%'
,
's'
,
0dh
,
0ah
,
0
g_szBpFmt db
"CC异常 %08X"
,
0dh
,
0ah
,
0
g_szSsFmt db
"单步异常 %08X"
,
0dh
,
0ah
,
0
g_btOldCode db
0
;记录没下断点之前的字符
g_dwBpAddr dd
01001BCFh
;下断点的地址
g_byteCC db
0CCh
; 短点符号 CC
.code
;处理异常信息
OnException proc uses esi pDE:ptr DEBUG_EVENT
LOCAL @dwOldProc:DWORD ;修改之前的内存属性
LOCAL @dwBytesOut:DWORD
LOCAL @hThread:HANDLE
LOCAL @ctx:CONTEXT
mov esi, pDE
assume esi:ptr DEBUG_EVENT
;判断是否断点异常
.
if
[esi].u.Exception.pExceptionRecord.ExceptionCode
=
=
EXCEPTION_BREAKPOINT
;判断是否是自己的CC 通过地址判断
mov eax, [esi].u.Exception.pExceptionRecord.ExceptionAddress
.
if
eax !
=
g_dwBpAddr ;该处地址是不是我们下断点地址
;不是自己的CC异常,不处理
mov eax, DBG_EXCEPTION_NOT_HANDLED
ret
.endif
;处理自己的CC异常
invoke crt_printf, offset g_szBpFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress
;修改内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, PAGE_EXECUTE_READWRITE, addr @dwOldProc
;恢复指令
invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut
;还原内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, @dwOldProc, addr @dwOldProc
;设置单步
;获取线程句柄
invoke OpenThread, THREAD_ALL_ACCESS, FALSE, [esi].dwThreadId
mov @hThread, eax
;设置结构体标志位,用来判断获取那些寄存器环境
; CONTEXT_FULL 表示获取控制,整数,段 CONTEXT_ALL 是所有的
mov @ctx.ContextFlags, CONTEXT_FULL
;获取寄存器环境
invoke GetThreadContext, @hThread, addr @ctx
;将TF标志位置
1
or
@ctx.regFlag,
100h
;返回当前代码地址 @ctx.regEip 执行完CC的地址
dec @ctx.regEip ;减
1
,cc是一个字节.
;设置寄存器环境
invoke SetThreadContext, @hThread, addr @ctx
;关闭线程句柄
invoke CloseHandle, @hThread
mov eax, DBG_CONTINUE
ret
.endif
;单步来了 (如果是单步异常)
.
if
[esi].u.Exception.pExceptionRecord.ExceptionCode
=
=
EXCEPTION_SINGLE_STEP
;处理自己的单步
invoke crt_printf, offset g_szSsFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress
;修改内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, PAGE_EXECUTE_READWRITE, addr @dwOldProc
;重设断点, 重新写入CC
invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut
;还原内存属性
invoke VirtualProtect, g_dwBpAddr,
1
, @dwOldProc, addr @dwOldProc
;异常已处理,继续执行代码
mov eax, DBG_CONTINUE
ret
.endif
assume esi:nothing
mov eax, DBG_EXCEPTION_NOT_HANDLED
ret
OnException endp
赞赏记录
参与人
雪币
留言
时间
一笑人间万事
为你点赞!
2024-11-3 01:58
鱼木
+2
你的分享对大家帮助很大,非常感谢!
2024-8-19 10:31
chinarenjf
+1
谢谢你的细致分析,受益匪浅!
2024-8-19 09:29
赞赏
他的文章
看原图
赞赏
雪币:
留言: