windows下的用户态调试的底层与上层实现
操作系统:windows XP
调试器通过CreateProcess传入带有DEBUG_PROCESS和DEBUG_ONLY_THIS_PROCESS的dwCreationFlags创建被调试进程。这种情况下,进程创建的早期(执行NtCreateProcess或NtCreateProcessEx之前),调用DbgUiConnectToDbg()使调用线程和调试子系统建立连接。DbgUiConnectToDbg()内部调用ZwCreateDebugObject创建一个DEBUG_OBJECT内核对象,并将其句柄保存在当前线程环境块(TEB)的的DbgSsReserved[1]中。调试进程与被调试进程就是通过DEBUG_OBJECT进行交互的。之后该线程便被认作是调试工作线程。
调用NtCreateProcess或NtCreateProcessEx时,
NTSYSAPI
NTSTATUS
NTAPI
NtCreateProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL );
DbgSsReserved[1]中的句柄将传入,内核中进程创建函数PspCreateProcess会检查它是否为空,若不为空,则取得该句柄的对象指针(PDEBUG_OBJECT),并将其存储于新创建进程EPROCESS的DebugPort中。之后新创建进程便被认作是被调试进程。
之后PspCreateProcess调用MmCreatePeb创建进程环境块(PEB)时,MmCreatePeb会根据DebugPort是否为空,相应设置PEB中BeingDebugged字段。
另一种情况是调试器附加到被调试进程。通过调用 BOOL DebugActiveProcess(DWORD dwProcessId)完成。首先会调用DebugUiConnectToDbg使调用进程与调试子系统建立连接。之后调用ProcessIdToHandle取得句柄。之后调用NTDLL中的DbgUiDebugActiveProcess。该函数调用NtDebugActiveProcess并传入ProcessHandle和DebugObject的handle。NtDebugActiveProcess内部根据句柄取得要被调试进程的EPROCESS和用于调试的调试对象DEBUG_OBJECT,之后向调试对象发送杜撰的调试事件(在调试循环中再说),之后调用DbgkpSetProcessDebugObject将调试对象的指针存储到被调试进程EPROCESS的DebugPort中,并调用DbgkpMarkProcessPeb设置被调试进程PEB的BeingDebugged字段。当NtDebugActiveProcess成功返回后,DbgUiDebugActiveProcess调用DbgUiIssueRemoteBreakIn在被调试进程中创建远程中断线程(后述),使被调试进程中断到调试器中。
之后调试器和被调试程序便建立起了调试会话。
之后调试线程进入了调试循环。调试线程使用WaitForDebugEvent等待调试事件。
调试事件即被调试进程所发生的某些特殊动作。例如线程、进程的创建和销毁等,当然还有非常重要的异常事件。被调试进程通过调试子系统发送给调试器,此时,调试器的WaitForDebugEvent将结束等待,并相应处理调试事件。具体如下:
调试子系统内核部分设计了一系列的函数来采集调试事件,并将收集到的调试事件以DBGKM_APIMSG这样的结构存储在调试子系统的调试消息队列中。当被调试进程创建新的进程或线程时,将产生相应的调试事件。在进程管理器创建新线程时,最后将调用PspUserThreadStartup准备启动线程。在该函数中,会调用调试子系统内核函数DbgkCreateThread,DbgkCreateThread将检查DebugPort是否为空,若为空,则立即返回,若不为空,继续检查进程的用户态运行时间(UserTime)是否为0,从而判断是否为进程中第一个线程。若是,若是,则通过DbgkpSendApiMessage()向DebugPort发送DbgKmCreateProcessApi消息,若不是,则发送DbgKmCreateThreadApi消息。
NTSTATUS DbgkpSendApiMessage(IN OUT PDGBKM_APIMSG ApiMsg, IN PVOID Port, IN BOOLEAN SuspendProcess)
其中Port一般是DebugPort中的指向DEBUG_OBJECT的指针。SuspendProcess说明是否要挂起被调试进程。若SuspendProcess为真,则该函数调用DbgkpSuspendProcess挂起被调试进程。之后调用DbgkpQueueMessage,该函数根据参数决定是否需要等待,若需要,则通过等待函数,直到收到调试器回复后返回。若是发送杜撰消息,则无需等待直接返回。之后若挂起了调试进程,则调用DbgkpResumeProcess唤醒被调试进程。
DbgkpSuspendProcess调用KeFreezeAllThreads()冻结除被调试进程中产生调试事件的线程的所有线程。之后如上文,该线程调用DbgkpQueueMessage发送消息并在大部分情况下调用KeWaitForSingleObject等待调试器的返回。之后调试子系统会通知调试器读取调试事件。调试器调试线程WaitForDebugObject返回,调试线程处理调试事件。完成之后调用ContinueDebugEvent。之后调试子系统唤醒被调试进程在等待的那个线程。该线程醒来后,会调用KeThawAllThreads()唤醒被调试进程中的所有线程。
windows下的用户态调试的底层与上层实现的更多相关文章
- systemtap 用户态调试
#include <stdio.h> int main( void) { ; a=fun(,); printf("%d\n",a); } int fun(int a,i ...
- windows 下使用 mingw编译器 调试时 无法跟进源码
windows 下使用 mingw编译器 调试时 无法跟进源码 最近在公司使用QT 开发,官方在线下载的 安装的QT mingw 都是没有debug版本的 由于没有debug版本动态库 所以你调试的时 ...
- 总在用户态调试 C# 程序,终还是搭了一个内核态环境
一:背景 一直在用 WinDbg 调试用户态程序,并没有用它调试过 内核态,毕竟不是做驱动开发,也没有在分析 dump 中需要接触用内核态的需求,但未知的事情总觉得很酷,加上最近在看 <深入解析 ...
- [补] 如何在windows下用IDA优雅调试ELF
在windows下如何用IDA优雅调试ELF brief: 构建一个IDA-linux_server-docker镜像,优雅地IDA远程调试 使用传统虚拟机来运行一个linux程序就得跑一个完整的li ...
- 浅谈一下Windows下的用户权限
大学毕业后,选择做了苹果开发,一直是使用的Mac系统,所以对Windows的基本操作忘得几乎一干二净:因为做内网穿透的需要,要用到Windows下的权限问题,所以重新研究了一下Windows设置用户权 ...
- 如何在windows下用IDA优雅调试ELF
在windows下如何用IDA优雅调试ELF brief: 构建一个IDA-linux_server-docker镜像,优雅地IDA远程调试 使用传统虚拟机来运行一个linux程序就得跑一个完整的li ...
- windows下源码安装调试postgresql
环境:windows 10 postgresql版本:postgresql-9.6.5 使用工具:vs2017社区版 辅助工具:perl.diff.flex.bison 相关工具下载地址: perl下 ...
- Windows下搭建FFmpeg开发调试环境
背景 如果你是一个FFmpeg的使用者,那么绝大部分情况下只需要在你的程序中引用FFmpeg的libav*相关的头文件,然后在编译阶段链接相关的库即可. 但是如果你想调试FFmpeg内部相关的逻辑,或 ...
- 初探Windows用户态调试机制
我们在感叹Onlydbg强大与便利的同时,是否考虑过它实现的原理呢? 作为一个技术人员知其然必知其所以然,这才是我们追求的本心. 最近在学习张银奎老师的<软件调试>,获益良多.熟悉Wind ...
随机推荐
- linux内存管理--slab及其代码解析
Linux内核使用了源自于 Solaris 的一种方法,但是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存作为对象按照大小进行分配,被称为slab高速缓存. 内存管理的目标是提供一种方法,为实 ...
- 有关autoresizingMask属性遇到的一个小问题
前言:在讲述这个小问题之前,我们有必要先了解一下UIViewAutoresizing的有关属性概念和使用详解. 参考:自动布局之autoresizingMask使用详解(Storyboard& ...
- 菜单栏始终浮动在顶部 js
//菜单栏始终浮动在顶部var navH = $(".trade-tab-bot").offset().top;//获取要定位元素距离浏览器顶部的距离//滚动条事件$(window ...
- [深入React] 6.组件
组件是react的大杀器,超出其他框架几百里 react 组件和dom一样也是树状结构,只能由上而下传递变量(或者调用),不可以兄弟间或者更远的发生关系,为的就是简单,而且工作的很好. 每个组件有自己 ...
- HRBUST 1987 逃课的孩子
Sol:HASH + 二分 字符串处理,很基础的操作. 题意很明确就是找重复的次数统计下,范围比较大1≤n≤10000,1≤m≤10000. #include <cstdio> #inc ...
- 转义字符和ASCII
一.字符(char) 数字(int) 屏幕显示 '\n' 10 换行 '\0' ...
- ACtivity实现欢迎界面并添加动画切换效果
先看效果: 中间切换动画没来得及截图,凑合着看吧. 主要是java代码的实现: Welcom.java package kehr.activity.welcome; import android.ap ...
- C# winform xml的增删改查
代码如下: using System; using System.Collections.Generic; using System.IO; using System.Linq; using Syst ...
- c#中的面向对象基础知识总结
面向对象 1.面向过程----->面向对象 1. 面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作. 面向对象:意在写出一个通用的代码,屏蔽差异. 我们在代码中描述一个对象,通 ...
- ASP.NET常用编程代码(二)
1.绑定在DataList中的DropDownList private void dlistOrder_EditCommand(object source, System.Web.UI.WebCont ...