Win32调试API第一部分,第1张

Win32调试API第一部分,第2张

理论:
Win32有一些面向程序员的API,提供相当于调试器的功能。它们被称为Win32调试API(或原语)。借助这些API,我们可以:

加载一个程序或者绑定到正在运行的程序进行调试
获取被调试程序的底层信息,如进程ID、入口地址、映像基址等。
当与调试相关的事件发生时得到通知,如进程/线程的开始/结束、DLL的加载/释放等。
修改被调试的进程或线程[我们可以用这些API写一个简单的调试器。因为这个题目太大,我把它分成几个部分,这个教程是它的第一部分。在本教程中,我将解释一些基本概念和Win32调试API的一般框架。
使用Win32调试API的步骤如下:

创建进程或绑定到正在运行的进程。这是使用Win32调试API的第一步。由于我们的程序将扮演调试器的角色,所以我们正在寻找一个用于调试的程序。被调试者称为被调试者。可以通过以下两种方式获取debuggee:
通过CreateProcess创建一个debuggee进程。若要创建被调试进程,必须指定DEBUG_PROCESS标志。这个标志告诉Windows我们想要调试这个进程。当debuggee中发生与调试相关的重要事件(调试事件)时,Windows会向我们的程序发送通知。debuggee会立即挂断,等待我们的程序准备好。如果debuggee还创建了一个子进程,Windows也会向我们的程序发送通知,以调试每个子进程中的事件。该功能通常是不必要的。我们可以通过指定DEBUG_ONLY_THIS_PROCESS和DEBUG_PROCESS的组合标志来禁止它。
我们还可以使用DebugActiveProcess标志绑定到正在运行的进程。[/ Br/]等待调试事件。在获得一个debuggee进程后,debuggee的主线程被挂起,这将持续到我们的程序调用WaitForDebugEvent。这个函数类似于其他WaitForXXX函数,比如它阻塞调用线程,直到等待事件发生。对于这个函数,它等待Windows发送的调试事件。下面是它的定义:
WaitForDebugEvent ProtolpDebugEvent:dword,dwmilliseconds: dword

Lpdebugevent是debug _ event的地址。此结构将填充有关调试对象中发生的调试事件的信息。

DwMilliseconds该函数以毫秒为单位等待调试事件。如果在此期间没有调试事件发生,WaitForDebugEvent将返回调用方。另一方面,如果此参数被指定为一个无限常数,函数将一直等待,直到调试事件发生。

现在让我们看看DEBUG_EVENT结构。

DEBUG _ EVENT STRUCT
dwDebugEventCode DD?
dwProcessId dd?
dwThreadId dd?
u DEBUG struct
DEBUG _ EVENT结束

该值指定等待发生的调试事件的类型。因为有许多类型的事件发生,我们的程序应该检查这个值,知道要发生的事件类型并做出响应。该值的可能值如下:

值含义
创建create _ process _ debug _ event流程。当被调试进程刚刚被创建(尚未运行)或者我们的程序刚刚被捆绑到一个正在运行的具有DebugActiveProcess的进程时,就会发生该事件。这是我们的程序应该得到的第一个事件。
EXIT_PROCESS_DEBUG_EVENT进程退出。
CREATE_THEAD_DEBUG_EVENT当在deuggee进程中创建新线程或者我们的程序第一次绑定到正在运行的进程时,会发生该事件。需要注意的是,在创建debugge的主线程时,不会收到这个通知。
该事件在exit _ thread _ debug _ event debuggee中的线程退出时发生。debugee的主线程在退出时不会收到此通知。我们可以认为debuggee的主线程就是Debugge process的代名词。因此,当我们的程序看到CREATE_PROCESS_DEBUG_EVENT标志时,对于主线程来说,就是CREATE_THREAD_DEBUG_EVENT标志。
Load _ DLL _ debug _ event debugger加载DLL。当PE加载程序第一次断开与dll的链接时,我们将收到该事件。(调用CreateProcess加载调试器时)调试器调用LoadLibrary时也会发生。
UNLOAD_DLL_DEBUG_EVENT从调试器中卸载DLL时发生该事件。
EXCEPTION_DEBUG_EVENT调试器中发生异常时发生的事件。注意:此事件仅在调试器开始第一条指令之前发生一次。该异常实际上是一个调试中断(int 3h)。如果要恢复调试器事件,请使用DBG _继续标志调用ContinueDebugEvent函数。不要使用DBG _异常_未处理标志,否则被调试程序将拒绝在NT下运行(它在Win98下运行良好)。
OUTPUT _ DEBUG _ STRING _ EVENT当debuggee调用DebugOutputString函数向我们的程序发送消息字符串时,会发生此事件。
RIP_EVENT系统调试错误

调试事件的进程和线程Id。我们可以使用这些值作为我们感兴趣的进程或线程的标识符。请记住,如果我们使用CreateProcess来加载debuggee,我们仍然可以在PROCESS_INFO结构中获取debuggee的进程和线程。我们可以使用这些值来区分调试事件是发生在被调试对象中还是其子进程中(当没有指定DEBUG_ONLY_THIS_PROCESS标志时)。

u是包含有关调试事件的更多信息的联合。根据上面dwDebugEventCode的不同,可以是以下结构:

解释dwDebugEventCode u
Create _ PROCESS _ DEBUG _ event是一个名为CreateProcessInfo的CREATE_PROCESS_DEBUG_INFO结构
Exit _ PROCESS _ DEBUG _ INFO结构名为Exit PROCESS
Create _ thread _ DEBUG _ event Create _ thread _ DEBUG _ INFO结构名为CreateThread
Exit _ thread _ DEBUG _ event Exit _ thread结构名为ExitThread
load _ dll _ DEBUG _ EVENT load _ dll _ DEBUG _ INFO结构名为load dll
Unload _ dll _ DEBUG _ EVENT Unload _ dll _ DEBUG _ INFO结构名为Unload dll
Exception _ EVENT Exception _ DEBUG _ INFO结构名为Exception
OUTPUT _ DEBUG _ STRING _ DEBUG _ STRING _ INFO结构名为DEBUG STRING
RIP _ EVENT RIP _ INFO结构名为RipInfo
在本教程中我不会深入讨论这些结构的所有细节。 这里详细说一下CREATE_PROCESS_DEBUG_INFO结构。
假设我们的程序调用WaitForDebugEvent函数并返回它,我们要做的第一件事就是检查dwDebugEventCode中的值,看看debuggee进程中发生了什么样的调试事件。例如,如果dwDebugEventCode的值为CREATE_PROCESS_DEBUG_EVENT,则U的成员可以视为CreateProcessInfo,由u.CreateProcessInfo访问。

响应程序中的调试事件。当WaitForDebugEvent返回时,这意味着调试事件或超时发生在被调试程序进程中。因此,我们的程序应该检查dwDebugEventCode以做出适当的响应。这有点像处理Windows消息:用户选择并忽略消息。
继续运行被调试程序。当调试事件发生时,Windows挂起被调试程序,因此,当我们结束调试事件时,我们必须让被调试程序继续运行。调用ContinueBugEvent函数来完成此过程。
ContinueBugEvent原始DW ProcessID: dword,dwthreadID: dword,dwContinueStatus: dword

这个函数恢复由于调试事件而挂起的线程。
dwProcessId和dwThreadId是要恢复的线程的进程Id和线程Id。通常,这两个值是从DEBUG_EVENT结构的dwProcessId和dwThreadId成员中获取的。
dwContinueStatus显示如何继续报告调试事件的线程。有两个可能的值:DBG _继续和DBG _异常_未处理。对于大多数调试事件,这两个值是相同的:恢复线程。异常为EXCEPTION_DEBUG_EVENT,表示被调试程序的线程中发生了异常。如果指定了DBG_CONTINUE,线程将忽略自己的异常处理部分,继续执行。在这种情况下,我们的程序必须在用DBG_CONTINUE恢复线程之前检查并处理异常,否则异常会无休止地发生.....如果我们指定DBG _异常_未处理值,也就是告诉Windows我们的程序不处理异常:Windows将使用调试器默认的异常处理功能来处理异常。
简而言之,如果我们的程序不考虑异常,并且调试事件指向调试器进程中的异常,那么您应该使用DBG _继续标志调用ContinueDebugEvent函数。否则,我们的程序必须用DBG _异常_非_处理来调用ContinueDebugEvent。但DBG_CONTINUE标志必须在以下情况下使用:ExceptionCode成员中值为EXCEPTION_BREAKPOINT的第一个EXCEPTION_DEBUG_EVENT事件。。当debuggee开始执行它的第一条指令时,我们的函数将收到一个异常调试事件。它实际上是一个调试中断(int 3h)。如果我们用DBG _异常_未_处理来调用ContinueDebugEvent来响应调试事件,Windows NT将拒绝执行被调试程序(因为它没有异常处理)。所以在这种情况下,我们应该使用DBG_CONTINUE标志来告诉Windows我们希望

继续上述步骤循环,直到被调试程序退出。我们的程序必须处于无限循环中,就像消息循环一样,直到调试程序结束。循环大致如下:
。当true
调用WaitForDebugEvent,Addr DebugEvent,INFINITE
。休息。if DEBUG EVENT . dwdebugeventcode = = EXIT _ PROCESS _ DEBUG _ EVENT

invoke continued bug EVENT,DebugEvent.dwProcessId,DebugEvent.dwThreadId,DBG _ EXCEPTION _ NOT _ HANDLED
。endw


也就是说,当我们开始调试程序时,我们的程序直到结束才能脱离debuggee。

让我们再总结一下这些步骤:

创建一个进程或将我们的程序绑定到一个正在运行的进程。
等待调试事件
响应调试事件。
继续执行调试器。
继续这个无限循环,直到调试器进程完成
示例:
此示例调试win32程序,并显示进程句柄。

. 386
。模型平面,stdcall
选项casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 \ include \ kernel 32 . Inc
include \ masm 32 \ include \ com DLG 32 . Inc
include \ masm 32 \ include \ user 32 . Inc
include lib \ masm 32 \ lib \ kernel 32 . lib
include lib \ masm 32 \ lib \ com DLG 3data
AppName db "Win32调试示例no.1 ",0
of n open filename
filter string db "可执行文件",0," *。exe ",0
db "所有文件",0," *。* ",0,0
ExitProc db "被调试程序退出",0
NewThread db "一个新线程被创建",0
EndThread db "一个线程被销毁",0
ProcessInfo db "文件句柄:%lx ",0dh,0Ah
db "进程句柄:%lx ",0Dh,0Ah
db "线程句柄:%lx ",0Dh,0Ah
db "映像库:%lx ",0Dh数据?
缓冲db 512 dup(?)
startinfo startup info
pi PROCESS _ INFORMATION
db EVENT DEBUG _ EVENT
。code
start:
mov of n . l structsize,sizeof of of n
mov of n . lpstrfilter,offset FilterString
mov of n . lpstrfile,offset buffer
mov of n . nmax file,512
mov ofn。标志,OFN _文件必须存在或OFN _路径必须存在或OFN _长名字或OFN _资源管理器或OFN _隐藏只读
调用GetOpenFileName,ADDR ofn
。if eax = = TRUE
invoke getstartup info,addr startinfo
invoke CreateProcess,addr buffer,NULL,NULL,NULL,FALSE,DEBUG _ PROCESS+DEBUG _ ONLY _ THIS _ PROCESS,NULL,NULL,addr startinfo,addr pi
。当TRUE
调用WaitForDebugEvent,addr DBEvent,INFINITE
。if db EVENT . dwdebugeventcode = = EXIT _ PROCESS _ DEBUG _ EVENT
invoke MessageBox,0,addr ExitProc,addr AppName,MB_OK+MB_ICONINFORMATION
。break
。else if db EVENT . dwdebugeventcode = = CREATE _ PROCESS _ DEBUG _ EVENT
invoke wsprintf,addr buffer,addr ProcessInfo,db EVENT . u . createprocessinfo . hfile,db EVENT . u . createprocessinfo . h PROCESS,db EVENT . u . createprocessinfo . hthread,db EVENT . u . createprocessinfo . lpbaseoimage,db EVENT . u . createprocessinfo . lpstartaddress
invoke MessageBox,0,addr buffer,addr apprelse if db EVENT . dwdebugeventcode = = EXCEPTION _ DEBUG _ EVENT
。if db event . u . EXCEPTION . pexception record . EXCEPTION code = = EXCEPTION _ BREAKPOINT
invoke continued bug event,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE
。继续
。endif
。else if db EVENT . dwdebugeventcode = = CREATE _ THREAD _ DEBUG _ EVENT
invoke MessageBox,0,addr NewThread,addr AppName,MB_OK+MB_ICONINFORMATION
。else if db EVENT . dwdebugeventcode = = EXIT _ THREAD _ DEBUG _ EVENT
invoke MessageBox,0,addr EndThread,addr AppName,MB_OK+MB_ICONINFORMATION
。endif
invoke ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,DBG _异常_未处理
。endw
invoke CloseHandle,pi . h process
invoke close handle,pi.hThread
。endif
invoke ExitProcess,0
end start

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » Win32调试API第一部分

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情