需要用到的函数 :
CreateProcess , WaitForDebugEvent,ContinueDebugEvent ,WriteProcessMemory,GetThreadContext,SetThreadContext.
ReadProcessMemory 一般都要用, 这里没用到
WriteProcessMemory : 用来增加int 3 断点(0cch), 以及恢复旧数据的
GetThreadContext: 用来获取被调试进程的线程所保存的寄存器, 这些寄存器用来恢复线程继续执行的
SetThreadContext: 用来来设置EFLAGS 寄存器中的tf位 (context.EFlags |= 0x100;) , tf置1即 int 1 , 单步中断用的 , 以及设置eip, 控制线程执行代码流程
int 3 引起异常的地址指向下一条指令, int 1还是原地址(具体来说就是Exception.ExceptionAddr == context,Eip )
下面的第一个代码仅仅用tf位 ,来制作一个调试器,虽然没啥*用
第二个代码, 给其他进程打补丁
(代码用asm写的, call 或者 invoke 相当于函数调用,mov 就是赋值 ,addr 就是取地址, local 定义一个局部变量, proc 一个过程)
先让自己成为调试器
1.CreateProcess 标志位 DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS
2.DebugActiveProcess
2个方式任选一种
被调试进程产生异常(throw , RaiseException),或int 3(断点 0cch)或 tf = 1(int 1 , 单步中断) ,在内部都将封装成一个DEBUG_EVENT 然后根据debugport 派发到调试进程,
用 NtQueryInformationProcess 可以查询到调式进程
派发过程由内核转用户 , 反正很长, 具体看od.
调试进程调用 WaitForDebugEvent 和 ContinueDebugEvent 循环处理
重点关注事件 EXCEPTION_DEBUG_EVENT .
无论是断点还是单步中断 , 还是其他空指针 栈溢出全部走这个流程
伪代码:
//伪代码
while(1)
{
WaitForDebugEvent(&event,-1);
switch(event.dwDebugEventCode) // if ...
case CREATE_PROCESS_DEBUG_EVENT: CreateProcess后的第一个事件
...
case EXIT_PROCESS_DEBUG_EVENT: break; 被调试进程结束
...
case EXCEPTION_DEBUG_EVENT: 唯一需要重点关注的
ContinueDebugEvent();
}
如果要设置断点, 就需要在被调试进程的某个地址 WriteProcessMemory 一个字节0CCh, 就是那个烫
被覆盖的那个字节要保存起来, 之后恢复, 否则这行指令就无效了 , 恢复之后, 需要把线程的context.Eip - 1, 让恢复的指令重新执行;
用tf标志位来写一个记录eip的调试器:
用 GetThreadContext 获取被调试线程的 context.EFlags 寄存器, 然后把tf位置1 context.EFlags |= 0x100,再SetThreadContext 设置回去.
此时当被调试线程运行的时候 由于tf =1 ,cpu 产生单步中断(int 1), 系统将被调试进程暂停,产生一个DEBUG_EVENT 分派给调试器,
调试器通过WaitForDebugEvent 获取此事件, 然后通过EXCEPTION_SINGLE_STEP 来处理此单步中断
注意:
1.当调用中断前, 硬件都将自动把tf = 0,以免不断的 int 1; 因此如果