win32 调试器
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了win32 调试器相关的知识,希望对你有一定的参考价值。
概述
什么调试器?就是可以调试原程序的每行代码,在Win32
提供了相关API
给我们进行进行操作
1.CreateProcessA
打开指定的程序,并传入对应的flag
让其成为对应的调试器CreateProcessA
2.DebugActiveProcess
调试特定的已经打开的程序DebugActiveProcess
3.DebugActiveProcessStop
终止调试DebugActiveProcessStop
张银奎的《软件调试》是非常不错的一本书推荐
其大致原理如下:
我们大致说一下CreateProcessA
如何调试程序
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
对于CreateProcessA
想启用调试需要dwCreationFlags
传入调试标志DEBUG_ONLY_THIS_PROCESS
(Process Creation Flags)
在之后调用WaitForDebugEvent
等候调试事件
WaitForDebugEvent
调用ContinueDebugEvent
报告异常应该如何处理
ContinueDebugEvent
我们伪代码如下:
//打开一个进程,并让自己成为被打开程序的调试器
CreateProcessA(...DEBUG_ONLY_THIS_PROCESS...)
while(true)
//等候调试事件
WaitForDebugEvent(xx,xx)
//告诉这个异常如何处理,比如继续运行程序等
ContinueDebugEvent(zzz)
Demo 输出程序所有加载的dll
下面我们实现一个程序实现打印所有被调试进程加载dll
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib msvcrt.lib
includelib user32.lib
includelib kernel32.lib
; START 初始化变量结束 START
.data
g_hProcess dd 0
g_hThread dd 0
.data
g_szExePath db "calc.exe",0
g_szErrCreateProcess db "创建进程失败",0dh,0ah,0
g_szCREATE_PROCESS_DEBUG_EVENT db "CREATE_PROCESS_DEBUG_EVENT",0dh,0ah,0
g_szCREATE_THREAD_DEBUG_EVENT db "CREATE_THREAD_DEBUG_EVENT",0dh,0ah,0
g_szEXCEPTION_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT",0dh,0ah,0
g_szEXIT_PROCESS_DEBUG_EVENT db "EXIT_PROCESS_DEBUG_EVENT",0dh,0ah,0
g_szEXIT_THREAD_DEBUG_EVENT db "EXIT_THREAD_DEBUG_EVENT",0dh,0ah,0
g_szLOAD_DLL_DEBUG_EVENT db "LOAD_DLL_DEBUG_EVENT",0dh,0ah,0
g_szOUTPUT_DEBUG_STRING_EVENT db "OUTPUT_DEBUG_STRING_EVENT",0dh,0ah,0
g_szRIP_EVENT db "RIP_EVENT",0dh,0ah,0
g_szUNLOAD_DLL_DEBUG_EVENT db "UNLOAD_DLL_DEBUG_EVENT",0dh,0ah,0
g_szLoadDllFmt db "[event] Lod Dll Base:%08X ImageName:%s",0dh,0ah,0
g_dwLoadDllFmtLen dd $-offset g_szLoadDllFmt
g_dwAddr dd 01006121h
g_bt01dCode db 0
g_bIsSystemCC dd FALSE
g_szDebugFmt db "Excetion addr:%08X",0dh,0ah,0
g_szDebug2Fmt db "EXCEPTION_SINGLE_STEP addr:%08X",0dh,0ah,0
; END 初始化变量结束 END
.code
main proc
LOCAL @si:STARTUPINFO
LOCAL @pi:PROCESS_INFORMATION
LOCAL @de:DEBUG_EVENT
LOCAL @dwStatus:DWORD
;初始化一些变量
invoke RtlZeroMemory,addr @si,type @si
mov @si.cb,type @si
invoke RtlZeroMemory,addr @pi,type @pi
;开启一个程序,并传入调试标志
invoke CreateProcess,offset g_szExePath,NULL,NULL,NULL,FALSE,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
;判断是否启动成功
.if eax==FALSE
invoke crt_printf,offset g_szErrCreateProcess
ret
.endif
;存储局部变量
push @pi.hProcess
pop g_hProcess
;等候调试事件
.while TRUE
;开始等候调试事件
invoke RtlZeroMemory,addr @de,type @de
invoke WaitForDebugEvent,addr @de,INFINITE
;运行到此处证明有事件发生
invoke OpenThread,THREAD_ALL_ACCESS,FALSE,@de.dwThreadId
mov g_hThread,eax
;处理调试事件
mov @dwStatus,DBG_CONTINUE
;根据事件类型进行打印,我们这边打印LOAD_DLL_DEBUG_EVENT里面module名称
.if @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT
invoke crt_printf,offset g_szCREATE_PROCESS_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==CREATE_THREAD_DEBUG_EVENT
invoke crt_printf,offset g_szCREATE_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==EXCEPTION_DEBUG_EVENT
invoke crt_printf,offset g_szEXCEPTION_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==EXIT_PROCESS_DEBUG_EVENT
invoke crt_printf,offset g_szEXIT_PROCESS_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==EXIT_THREAD_DEBUG_EVENT
invoke crt_printf,offset g_szEXIT_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==LOAD_DLL_DEBUG_EVENT
;调用OnLoadDll打印所有dll
invoke OnLoadDll,addr @de
invoke crt_printf,offset g_szLOAD_DLL_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==OUTPUT_DEBUG_STRING_EVENT
invoke crt_printf,offset g_szOUTPUT_DEBUG_STRING_EVENT
.elseif @de.dwDebugEventCode ==RIP_EVENT
invoke crt_printf,offset g_szRIP_EVENT
.elseif @de.dwDebugEventCode ==UNLOAD_DLL_DEBUG_EVENT
invoke crt_printf,offset g_szUNLOAD_DLL_DEBUG_EVENT
.endif
;我们假设所有的事件都继续处理即可
invoke CloseHandle,g_hThread
invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
.endw
ret
main endp
START:
invoke main
xor eax,eax
invoke ExitProcess,eax
end START
我们最后看下OnLoadDll
函数
OnLoadDll proc uses esi pDe:ptr DEBUG_EVENT
LOCAL @szFileName[MAX_PATH*2 ]:CHAR
LOCAL @dwAddr:DWORD
LOCAL @dwBytesReaded:DWORD
LOCAL @szwLoadDllFmt[MAX_PATH ]:CHAR
mov esi,pDe
assume esi:ptr DEBUG_EVENT
;读取另一个进程的内存地址,得到指向module的名称的指针
invoke ReadProcessMemory,g_hProcess,[esi].u.LoadDll.lpImageName,addr @dwAddr,type @dwAddr,addr @dwBytesReaded
;判断是否成功
.if eax==FALSE || @dwAddr ==NULL
ret
.endif
;上一步得到指针后继续读取指针域得到结果
invoke ReadProcessMemory,g_hProcess,@dwAddr,addr @szFileName,MAX_PATH*2,addr @dwBytesReaded
;判断名称是否位unicode编码,如果是如unicode编码需要做一次转化后在打印
.if [esi].u.LoadDll.fUnicode ==TRUE
invoke crt_mbstowcs,addr @szwLoadDllFmt,offset g_szLoadDllFmt,g_dwLoadDllFmtLen
invoke crt_wprintf,addr @szwLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szFileName
.else
invoke crt_printf ,offset g_szLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szFileName
.endif
assume esi:nothing
ret
OnLoadDll endp
输出
Demo 单步调试
理解的知识点:
- 设置标志寄存器的TF位,会触发硬件提供的单步执行。即执行xxx后判断是否存在tf为1,如果为1那么会触发中断
- cc 即我们
int 3
会触发断点事件
我们结合上面两个技巧即可完成,所谓单步调试。
假设我们需要给一个程序01006121h
地址触发断点调试,每次路过这个位置时都会触发断点。
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib msvcrt.lib
includelib user32.lib
includelib kernel32.lib
.data
g_hProcess dd 0
g_hThread dd 0
.data
g_szExePath db "calc.exe",0
g_szErrCreateProcess db "创建进程失败",0dh,0ah,0
g_szCREATE_PROCESS_DEBUG_EVENT db "CREATE_PROCESS_DEBUG_EVENT",0dh,0ah,0
g_szCREATE_THREAD_DEBUG_EVENT db "CREATE_THREAD_DEBUG_EVENT",0dh,0ah,0
g_szEXCEPTION_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT",0dh,0ah,0
g_szEXIT_PROCESS_DEBUG_EVENT db "EXIT_PROCESS_DEBUG_EVENT",0dh,0ah,0
g_szEXIT_THREAD_DEBUG_EVENT db "EXIT_THREAD_DEBUG_EVENT",0dh,0ah,0
g_szLOAD_DLL_DEBUG_EVENT db "LOAD_DLL_DEBUG_EVENT",0dh,0ah,0
g_szOUTPUT_DEBUG_STRING_EVENT db "OUTPUT_DEBUG_STRING_EVENT",0dh,0ah,0
g_szRIP_EVENT db "RIP_EVENT",0dh,0ah,0
g_szUNLOAD_DLL_DEBUG_EVENT db "UNLOAD_DLL_DEBUG_EVENT",0dh,0ah,0
g_szLoadDllFmt db "[event] Lod Dll Base:%08X ImageName:%s",0dh,0ah,0
g_dwLoadDllFmtLen dd $-offset g_szLoadDllFmt
g_dwAddr dd 01006121h
g_bt01dCode db 0
g_bIsSystemCC dd FALSE
g_szDebugFmt db "Excetion addr:%08X",0dh,0ah,0
g_szDebug2Fmt db "EXCEPTION_SINGLE_STEP addr:%08X",0dh,0ah,0
.code
OnBreakPoint proc pER:ptr EXCEPTION_RECORD
LOCAL @dwStatus:DWORD
LOCAL @dwOldProtect:DWORD
LOCAL @btCodeCC:BYTE
LOCAL @dwBytesWrited:DWORD
LOCAL @dwBytesReaded:DWORD
LOCAL @ctx: CONTEXT
mov @dwStatus,DBG_CONTINUE
mov eax,@dwStatus
mov esi,pER
assume esi:ptr EXCEPTION_RECORD
;系统断点,忽略
.if g_bIsSystemCC==FALSE
mov g_bIsSystemCC,TRUE
mov eax,@dwStatus
ret
.endif
;判断是否是自己的断点
;判断是否是自己的断点
mov eax,g_dwAddr
.if [esi].ExceptionAddress == eax
;还原原指令
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOldProtect
invoke WriteProcessMemory,g_hProcess,g_dwAddr,offset g_bt01dCode,type g_bt01dCode,addr @dwBytesWrited
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,@dwOldProtect ,addr @dwOldProtect
;TF置位
invoke RtlZeroMemory,addr @ctx,type @ctx
mov @ctx.ContextFlags,CONTEXT_ALL
invoke GetThreadContext,g_hThread,addr @ctx
or @ctx.regFlag,100h
dec @ctx.regEip
invoke SetThreadContext,g_hThread,addr @ctx
mov eax,@dwStatus
ret
.endif
mov eax,@dwStatus
ret
ret
OnBreakPoint endp
OnSingleStep proc pER:ptr EXCEPTION_RECORD
LOCAL @dwOldProtect:DWORD
LOCAL @btCodeCC:BYTE
LOCAL @dwBytesWrited:DWORD
LOCAL @dwBytesReaded:DWORD
LOCAL @dwStatus:DWORD
;处理调试事件
mov @dwStatus,DBG_CONTINUE
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOldProtect
mov @btCodeCC,0cch
invoke ReadProcessMemory,g_hProcess,g_dwAddr,offset g_bt01dCode,type g_bt01dCode,addr @dwBytesReaded
invoke WriteProcessMemory,g_hProcess,g_dwAddr,addr @btCodeCC,type @btCodeCC,addr @dwBytesWrited
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,@dwOldProtect,addr @dwOldProtect
mov eax,@dwStatus
ret
OnSingleStep endp
OnException proc uses esi pDe:ptr DEBUG_EVENT
LOCAL @dwStatus:DWORD
LOCAL @dwOldProtect:DWORD
LOCAL @btCodeCC:BYTE
LOCAL @dwBytesWrited:DWORD
LOCAL @dwBytesReaded:DWORD
LOCAL @ctx: CONTEXT
;处理调试事件
mov @dwStatus,DBG_CONTINUE
mov esi,pDe
assume esi:ptr DEBUG_EVENT
lea esi,[esi].u.Exception
assume esi:ptr EXCEPTION_RECORD
.if [esi].ExceptionCode==EXCEPTION_BREAKPOINT
invoke crt_printf,offset g_szDebugFmt,[esi].ExceptionAddress
invoke OnBreakPoint,esi
ret
.elseif [esi].ExceptionCode==EXCEPTION_SINGLE_STEP
invoke crt_printf,offset g_szDebug2Fmt,[esi].ExceptionAddress
invoke OnSingleStep,esi
ret
.endif
assume esi:nothing
mov eax,@dwStatus
ret
OnException endp
OnCreateProcess proc uses esi pDe:ptr DEBUG_EVENT
LOCAL @dwOldProtect:DWORD
LOCAL @btCodeCC:BYTE
LOCAL @dwBytesWrited:DWORD
LOCAL @dwBytesReaded:DWORD
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOldProtect
mov @btCodeCC,0cch
invoke ReadProcessMemory,g_hProcess,g_dwAddr,offset g_bt01dCode,type g_bt01dCode,addr @dwBytesReaded
invoke WriteProcessMemory,g_hProcess,g_dwAddr,addr @btCodeCC,type @btCodeCC,addr @dwBytesWrited
invoke VirtualProtectEx,g_hProcess,g_dwAddr,1,@dwOldProtect,addr @dwOldProtect
ret
OnCreateProcess endp
OnLoadDll proc uses esi pDe:ptr DEBUG_EVENT
LOCAL @szFileName[MAX_PATH*2 ]:CHAR
LOCAL @dwAddr:DWORD
LOCAL @dwBytesReaded:DWORD
LOCAL @szwLoadDllFmt[MAX_PATH ]:CHAR
mov esi,pDe
assume esi:ptr DEBUG_EVENT
invoke ReadProcessMemory,g_hProcess,[esi].u.LoadDll.lpImageName,addr @dwAddr,type @dwAddr,addr @dwBytesReaded
.if eax==FALSE || @dwAddr ==NULL
ret
.endif
invoke ReadProcessMemory,g_hProcess,@dwAddr,addr @szFileName,MAX_PATH*2,addr @dwBytesReaded
.if [esi].u.LoadDll.fUnicode ==TRUE
invoke crt_mbstowcs,addr @szwLoadDllFmt,offset g_szLoadDllFmt,g_dwLoadDllFmtLen
invoke crt_wprintf,addr @szwLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szFileName
.else
invoke crt_printf ,offset g_szLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szFileName
.endif
assume esi:nothing
ret
OnLoadDll endp
main proc
LOCAL @si:STARTUPINFO
LOCAL @pi:PROCESS_INFORMATION
LOCAL @de:DEBUG_EVENT
LOCAL @dwStatus:DWORD
invoke RtlZeroMemory,addr @si,type @si
mov @si.cb,type @si
invoke RtlZeroMemory,addr @pi,type @pi
invoke CreateProcess,offset g_szExePath,NULL,NULL,NULL,FALSE,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
.if eax==FALSE
invoke crt_printf,offset g_szErrCreateProcess
ret
.endif
push @pi.hProcess
pop g_hProcess
.while TRUE
;等候调试事件
invoke RtlZeroMemory,addr @de,type @de
invoke WaitForDebugEvent,addr @de,INFINITE
;获取线程句柄
invoke OpenThread,THREAD_ALL_ACCESS,FALSE,@de.dwThreadId
mov g_hThread,eax
;处理调试事件
mov @dwStatus,DBG_CONTINUE
.if @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT
invoke OnCreateProcess,addr @de
invoke crt_printf,offset g_szCREATE_PROCESS_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==CREATE_THREAD_DEBUG_EVENT
invoke crt_printf,offset g_szCREATE_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==EXCEPTION_DEBUG_EVENT
invoke crt_printf,offset g_szEXCEPTION_DEBUG_EVENT
invoke OnException, addr @de
mov @dwStatus,eax
.elseif @de.dwDebugEventCode ==EXIT_PROCESS_DEBUG_EVENT
invoke crt_printf,offset g_szEXIT_PROCESS_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==EXIT_THREAD_DEBUG_EVENT
invoke crt_printf,offset g_szEXIT_THREAD_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==LOAD_DLL_DEBUG_EVENT
invoke OnLoadDll,addr @de
invoke crt_printf,offset g_szLOAD_DLL_DEBUG_EVENT
.elseif @de.dwDebugEventCode ==OUTPUT_DEBUG_STRING_EVENT
invoke crt_printf,offset g_szOUTPUT_DEBUG_STRING_EVENT
.elseif @de.dwDebugEventCode ==RIP_EVENT
invoke crt_printf,offset g_szRIP_EVENT
.elseif @de.dwDebugEventCode ==UNLOAD_DLL_DEBUG_EVENT
invoke crt_printf,offset g_szUNLOAD_DLL_DEBUG_EVENT
.endif
invoke CloseHandle,g_hThread
invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
.endw
ret
main endp
START:
invoke main
xor eax,eax
invoke ExitProcess,eax
end START
内存断点
硬件断点
以上是关于win32 调试器的主要内容,如果未能解决你的问题,请参考以下文章