[异常分发]二:WaitForDebugEvnet哪里获取Debug_Evnet信息

调试异常事件结构体:

typedef struct _DEBUG_EVENT {       //出自MSDN
  DWORD dwDebugEventCode;       //异常代码
  DWORD dwProcessId;         //PID
  DWORD dwThreadId; 
  union { 
      EXCEPTION_DEBUG_INFO Exception;              //调试异常结构体信息
      CREATE_THREAD_DEBUG_INFO CreateThread;       //创建线程结构体信息
      CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; //创建进程结构体信息
      EXIT_THREAD_DEBUG_INFO ExitThread;           //退出线程
      EXIT_PROCESS_DEBUG_INFO ExitProcess;         //退出进程
      LOAD_DLL_DEBUG_INFO LoadDll;                 //载入DLL
      UNLOAD_DLL_DEBUG_INFO UnloadDll;        //卸载DLL          
      OUTPUT_DEBUG_STRING_INFO DebugString; 
      RIP_INFO RipInfo; 
  } u; 
} DEBUG_EVENT, *LPDEBUG_EVENT;

 主要函数的调用过程:WaitForDebugEvent   DbgUiWaitStateChange  NtWaitForDebugEvent
DbgUiWaitStateChange 传入要获取异常事件的结构体地址、等待时间及调试对象等信息调用NtWaitForDebugEvent 进入R0, 调用代码如下:

.text:7C975E0A     push [ebp+pDebug_Event]        ;要传出调试异常信息的结构体
.text:7C975E0D     mov eax, large fs:_TEB.NtTib.Self      ; 获取TEB
.text:7C975E13     push [ebp+seconds]
.text:7C975E16     push 1
.text:7C975E18     push [eax+(_TEB.DbgSsReserved+4)]   ; _teb.DbgSsReserved[1]保存Debug_Object
.text:7C975E1E     call _NtWaitForDebugEvent@16        ; 获取Debug_Evnet结构体信息

 NtWaitForDebugEvent主要完成获取R0下的Debug_Event结构体信息并转换成用户下的结构
1)获取R0下的Debug_Event结构体信息
   关键过程,通过KeWaitForSingleObject返回值判断来判断要进行遍历异常事件链表的操作,在循环中判断调试对象的标志不是结束调试进程,则进行遍历Debug_Object.EventList中的Debug_Event链表信息。
遍历过程:取出Debug_Evnt一成员判断是否结束,不结束判断调试对象标志是否为 DEBUG_EVENT_READ|DEBUG_EVENT_INACTIVE,不是则用取出的成员与EventList其他不同节点的PID作比较,有 相等的(说明不是唯一)则修改Debug_Event的状态信息及局部标志值, 没有相同的就转换成用户模式下的结构体, 也就是 WaitForDebugEvent的参数Debug_Event结构体。
;根据KeWaitForSingleObject的返回值, 决定是否需要做遍历动作。如果返回值小于0,或者为STATUS_TIMEOUT或为STATUS_ALERTED或者是STATUS_USER_APC则结束循环遍历过程。

PAGE:005905E0     call _KeWaitForSingleObject@20      ; KeWaitForSingleObject(x,x,x,x,x)
PAGE:005905E5     mov ebx, eax
PAGE:005905E7     cmp ebx, esi                        ; if(返回值EAX >= 0) 就继续循环
PAGE:005905E9     jl  END_WHILE_LOOP                  ; 函数返回值如果是<0,则退出循环结构
PAGE:005905EF     jmp short BEGIN_WHILE_LOOP
WHILE_NEXT_LOOP:                       
PAGE:005905F1     xor esi, esi                        ; ESI清零
BEGIN_WHILE_LOOP:                                      
PAGE:005905F3     cmp ebx, STATUS_TIMEOUT
PAGE:005905F9     jz  END_WHILE_LOOP             ; {  执行循环体,否则跳出循环 }                                
PAGE:005905FF     cmp ebx, STATUS_ALERTED
PAGE:00590605     jz  END_WHILE_LOOP
PAGE:0059060B     cmp ebx, STATUS_USER_APC       ;这里也是判断函数返回值,成立就结束循环
PAGE:00590611     jz  END_WHILE_LOOP
PAGE:00590617     mov [ebp+isGotEvent_LV], 0          ; 局部标志变量清零
PAGE:0059061B     lea ecx, [edi+_DEBUG_OBJECT.Mutex]                                       
PAGE:0059061E     call @ExAcquireFastMutex@4          ; 请求互斥对象
PAGE:00590623                                    ; 1 ==> 直到调试对象被删除
PAGE:00590623                                    ; 2 ==> 结束关闭所有调试进程
PAGE:00590623     test byte ptr [edi+_DEBUG_OBJECT.Flags], 1 ; if(DebugObject->Flags == 1)  
PAGE:00590627     jnz short RealseFastMutex           ;

 ;取出调试事件list的头节点,Debug_Evnt首成员为ListEntry保存的前后节点指针

代码:
;GE:00590629     lea eax, [edi+_DEBUG_OBJECT.EventList] ; eax = &pDebug_Object->EventList
PAGE:0059062C     mov ecx, [eax]   ; 取下一个List_Entry到ecx ( ecx = pDebug_Ob->EventList.Flink )
PAGE:0059062E     jmp short IS_LIST1_IS_END
LIST1_NOT_END: 
PAGE:00590630     mov esi, ecx         ; ;esi = debug_object.EventList.Flink
;DEBUG_EVENT_READ|DEBUG_EVENT_INACTIVE  1|4 = 5
PAGE:00590632      test byte ptr [ecx+Debug_event.Flags], 5 ; 
PAGE:00590636     jnz short  EQU_EventRead_OR_EventInactive ; 取下一个DebugEvent
 PAGE:00590638     mov [ebp+isGotEvent_LV], 1          ; 局部标志值 = 1
PAGE:0059063C     jmp short IS_LIST2_IS_END
LIST2_IS_NOT_END:        
PAGE:0059063E     mov edx, [esi+Debug_event.ClientId.UniqueProcess]
PAGE:00590641     cmp edx, [eax+Debug_event.ClientId.UniqueProcess]
PAGE:00590644     jz  short UniquePRocess_EQU   ; 如果进程相等,跳去做点活,也就结束的List2的遍历
IS_LIST2_IS_END:           
PAGE:00590646     mov eax, [eax+Debug_event.EventList.Flink]
PAGE:00590648     cmp eax, ecx        ; if(pDebug_ob->Event.Flink != pDebug_ob->Event.Flink )
PAGE:0059064A     jnz short LIST2_IS_NOT_END          ; 这是Entry2的下一个循环判断
PAGE:0059064C     jmp short LOOP2_IS_END
PAGE:0059064E
 UniquePRocess_EQU:               
PAGE:0059064E     or  [esi+Debug_event.Flags], DEBUG_EVENT_INACTIVE ; 
PAGE:00590652     and [esi+Debug_event.BackoutThread], 0 ;  清零
PAGE:00590656     mov [ebp+isGotEvent_LV], 0          ; 标志设置为0用来判断是否唯一进程
LOOP2_IS_END:             
PAGE:0059065A    cmp [ebp+isGotEvent_LV], 0 ; GotEvent = 1 ==> 遍历List2时没有相等的UniqueProcess
PAGE:0059065A                            ; GotEvent = 0 ==> 有UniqueProcess相等的情况
PAGE:0059065E     jnz short GOT_EVENT_NOT_EQU_0
PAGE:00590660
 EQU_EventRead_OR_EventInactive:        
PAGE:00590660     mov ecx, [ecx+Debug_event.EventList.Flink] ; 取下一个DebugEvent结构体地址
PAGE:00590662     lea eax, [edi+_DEBUG_OBJECT.EventList] ; 取EventList的头地址
PAGE:00590665
IS_LIST1_IS_END:                        
PAGE:00590665    cmp ecx, eax     ; 判断 pDebug_Ob-Event.Flink != &Debug_Ob->Event 即是否遍历完
PAGE:00590667     jnz short LIST1_NOT_END             ; List没有结束,则继续
PAGE:00590669     cmp [ebp+isGotEvent_LV], 0      ; 判断局部标志是否为0 
PAGE:0059066D     jz  short GOT_EVENT_EQU_0   ; GotEvent = 1 ==> 遍历List2时没有相等的UniqueProcess
PAGE:0059066D                                   ; GotEvent = 0 ==> 有UniqueProcess相等的情况

主要操作就是遍历Debug_Object里EventList保存的异常事件链表, 查找UniqueProcess是否唯一,参考如下图示。

GOT_EVENT_NOT_EQU_0:        
PAGE:0059066F     mov ebx, [esi+Debug_event.Process]
PAGE:00590672     mov [ebp+Process_LV], ebx
PAGE:00590675     mov ecx, [esi+Debug_event.Thread]; 将Debug_event中进程和线程保存到局部变量中
PAGE:00590678     mov [ebp+Thread_LV], ecx
PAGE:0059067B     call @ObfReferenceObject@4          ; ECX传参,增加线程对像的引用计数
PAGE:00590680     mov ecx, ebx                        ; Object
PAGE:00590682     call @ObfReferenceObject@4          ; 增加进程的引用计数
PAGE:00590687     push esi                            ; DebugEvent
PAGE:00590688     lea eax, [ebp+tWaitStateChange_LV]
PAGE:0059068E     push eax                            ; WaitStateChange
;DbgkpConvertKernelToUserStateChange将R0的转成R3的Debug_Event结构体信息
PAGE:0059068F     call _DbgkpConvertKernelToUserStateChange@8 ; 
PAGE:00590694     or  [esi+Debug_event.Flags], DEBUG_EVENT_READ

 2):DbgkpConvertKernelToUserStateChange
将R0下和R3下的Debug_event调试异常结构体成员不同需要转换成用户需要的格式。根据异常类型对不同的异常类型的结构体信息作信息填充,结构体赋值给传出参数。
对应操作,如下图简单标示

根据ApiNumber对应的异常类型作相应的赋值处理。

PAGE:00590443     mov edx, [ebp+DebugEvent]
PAGE:00590446     mov ecx, [edx+Debug_event.ClientId.UniqueProcess]
PAGE:00590449     mov eax, [ebp+WaitStateChange]

 ; 将R0的AppClientId保存到R3下的结构中
PAGE:0059044C     mov [eax+_DBGUI_WAIT_STATE_CHANGE.AppClientId.UniqueProcess], ecx 
PAGE:0059044F     mov ecx, [edx+Debug_event.ClientId.UniqueThread]
 ; 转存线程信息
PAGE:00590452     mov [eax+_DBGUI_WAIT_STATE_CHANGE.AppClientId.UniqueThread], ecx 
PAGE:00590455     mov ecx, [edx+Debug_event.ApiMsg.ApiNumber] ;根据 Msg类型作处理
PAGE:00590458     sub ecx, 0
PAGE:0059045B     push esi
PAGE:0059045C     push edi
PAGE:0059045D     jz  short DbgKmExceptionApi_0       ; 异常信息结构体
PAGE:0059045F     dec ecx
PAGE:00590460     jz  short DbgKmCreateThreadApi_1    ; 创建线程
PAGE:00590462     dec ecx
PAGE:00590463     jz  short DbgKmCreateProcessApi_2   ; 创建进程
PAGE:00590465     dec ecx
PAGE:00590466     jz  short DbgKmExitThreadApi_3      ; 退出线程
PAGE:00590468     dec ecx
PAGE:00590469     jz  short DbgKmExitProcessApi_4     ; 退出进程
PAGE:0059046B     dec ecx
PAGE:0059046C     jz  short DbgKmLoadDllApi_5         ; 载入DLL
PAGE:0059046E     dec ecx
PAGE:0059046F     jnz DEFAULT_7                       ; 为0则是第6个为UnLoadDll
PAGE:00590475     mov [eax+_DBGUI_WAIT_STATE_CHANGE.NewState], 0Ah ; DbgUnloadDllStateChange
PAGE:0059047B     jmp short DbgKmUnloadDllApi_6       ; 卸载DLL
PAGE:0059047D ; ---------------------------------------------------------------------------

  ;根据异常信息类型,对相应的消息结构体作赋值处理,设置新的状态值,保存ApiMsg的信息等操作,下面是只列出LoadDll操作,其他消息类型操作类似。

代码:
DbgKmLoadDllApi_5:  
PAGE:0059047D     add edx, 58h                        ; 偏移到ApiMsg的共同体
PAGE:00590480     push 5
;9 = DbgLoadDllStateChange
PAGE:00590482 mov [eax+_DBGUI_WAIT_STATE_CHANGE.NewState], 9 ; 
PAGE:00590488     lea edi, [eax+_DBGUI_WAIT_STATE_CHANGE.StateInfo]
PAGE:0059048B     pop ecx
PAGE:0059048C     mov esi, edx           ; esi = &Debug_Event.ApiMsg
PAGE:0059048E     rep movsd        ; Debug_Wait_State_Change.StateInfo = Debug_Event.ApiMsg
PAGE:00590490     and [edx+_DBGKM_UNLOAD_DLL.BaseAddress], 0
PAGE:00590493     jmp short DEFAULT_7

那么调试对象Debug_Object中的调试事件信息Debug_Event又是什么时候添加进的呢?

备注:
    关于调试对象的创建等相关信息,请参考其人的分析

 

转载于:https://www.cnblogs.com/DreamOfGalaxy/articles/4491978.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值