图0: 下方是一个简化过的代码
做一个软件调试器最基本的是,首先要调试一个进程那么就要有一个进程
拿x96dbg来讲调试一个进程有两种方式,第一种通过附加(如图1),通过附加可以对已经创建的进程进行调试,第二种通过打开(如图2)创建一个进程对它进行调试,图0里的代码也有体现,创建一个进程CreateProcess函数,另外一种DebugActiveProcess函数,需要提供任务管理器里的进程id。如图3
打开或附近进程成功的话:
就有一个类似Windows消息循环,如图4,就是首先给它传递一个结构体(DEBUG_EVENT),然后通过WaitForDebugEvent(等待调试时间的函数)只要有调试事件我们就能收到,然后通过消息里的dbgevent.dwDebugEventCode就能够处理了,它会有模块的加载,下个断点给我们回馈等,DEBUG_EVENT里的内容很丰富,也就是调试器与被调试进程通过 图4 的代码给衔接起来了,它们是通过Windows消息机制来传递的。
以上就是一个调试器最核心的地方,知道这些东西之后,再分析CreateProcess是怎样达成一个调试的状态的。
CreateProcess里有一个dwCreationFlags 进程创建标志位,它的取值很多,需要去看微软提供的文档,跟调试器有关了有两个,DEBUG_PROCESS、DEVUG_ONLY_THIS_PROCESS
DEBUG_PROCESS说明:
得到调试进程的权限并且可以调试它的子进程
DEVUG_ONLY_THIS_PROCESS:
只能调试当前进程
然后 CreateProcess 创建被调试进程的底层函数:
DbgUiConnectToDbg:
调试器进程与调试器子系统建立链接,意思是调试它是通过一套复杂的系统来完成的,涉及到消息的传递、管理等,早期操作系统是通过smss.exe 或 crss.exe去完成的,它怎样建立链接的,它是先创建一个DEBUG_OBJECT类型的内核对象(在Windows2000或Windows2000以前的时候不是),然后把内核对象保存到线程环境里TEB结构里有一个DbgSsReserved[1](Windows2000会保存到0和1两个位置),这个对象是保存在调试进程里的,不是被调试进程里,这时被调试进程还没被创建
NtCreateProcess() 或 NtCreateProcessEx()
创建进程,首先内核里有一个进程管理器,需要把 DbgSsReserved[1] 传递给内核进程管理器,然后调用内核里的PsCreateProcess函数把 DbgSsReserved 这个字段传递给EPROCESS结构里的DebugPort字段,然后创建进程的函数(PsCreateProcess)调用MmCreatePeb创建进程PEB(3环进程结构或者说是用户层的进程结构这里有peb结构怎样去找,以及通过peb结构找模块链的例子)数据,MmCreatePeb函数会根据DebugProt的值,它的值不被调试的时候一定是0,被调试的时候一定是有内容的,如果为0,PEB结构下的BeingDebugged字段的值等于0,DebugProt的值有内容BeingDebugged字段的值就为1
EPROCESS结构说明:https://note.youdao.com/s/Qp1hET5X
特征:
进程一但被调试,它的两大特征是DebugPort字段在内核下一定是有内容的,在应用态下PEB里的BeingDebugged是由内容的,它的依赖就是着一系列路径上用到的函数
接下俩再说对现有进程做调试的:
首先第一步还是跟调试子系统建立链接,接下来因为给它传的是一个进程id,进程id要转换成内核对象句柄,所以这时就会调用OpenProcess,然后OpenProcess再调用底层的NtOpenProcess来得到句柄,得到句柄以后然后设置它进程的调试状态,这时就利用DbgUiDebugActiveProcess函数来得到它的PPROCESS结构,再根据DbgkpSetProcessDebugObject函数将DebugProt建立链接,然后再通过DbgkpMarkProcessPeb来设置调试进程的BeingDebuggged
特征:
两种方法归根到底,都是设置DebugProt、BeingDebuggged这俩字段
结论:
一个进程被调试,内核状态下EPROCESS结构的DebugProt一定不为0
用户态模式下PEB结构的BeingDebuged一定不为0
图1:
图2:
图3:
图4: