windbg的使用

    xiaoxiao2023-10-21  159

    目录

    调试可执行程序

    1.注册表设置 要调试的exe的 debugger 为 x86的windbg

    2.打开windbg,需要设置这三个路径或者环境变量

    3.设置好了以后,关闭windbg。

    分析dump文件


    《格蠹汇编》

    Windbg是微软开发的免费源码级调试工具。Windbg可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。

    windbg是一个调试器,那么就可以挂上正在运行的程序进行调试。

    使用的版本 

    以下的windbg相关的命令可以在文档中找到:


    0 看丢失的博客的内容

    1.启动例子提示找不到ntdll的symbol(已经解决了,需要让windbg能上msdn下载符号表,需要fq)

    .sympath 和 !sym noisy 符号文件路径相关

    >.sympath+ 一个文件路径

    加一个符号路径,扩大,然后执行.reload,刷新模块信息。

     

    >.srcpath  一个文件路径

    设置源文件的路径

     

     

     !heap 和 !address 无法执行,有符号错误 (已经解决了)

    !heap 查看堆数量

    !address 查看用户态空间的所有区域

    2.查看内存内容

    1)s -u 10000 L8000000 "当年在交大"

    s:查看内存命令

    -u:Unicode字符

    10000 L8000000 符号范围

    2) dU 001b5942 L1000

    使用dU查看地址的内容 。可以用来看比如栈回溯中,函数的某个参数如果是字符串的话,dU 某地址。

    The d* commands display the contents of memory in the given range.

    U 是Unicode字符

    L1000 长度(小写的l1000也可以)

    3) db 001b5942-8

    db

    Byte values and ASCII characters.

    Each display line shows the address of the first byte in the line, followed by up to 16 hexadecimal byte values. The byte values are immediately followed by the corresponding ASCII values. The eighth and ninth hexadecimal values are separated by a hyphen (-). All nonprintable characters, such as carriage returns and line feeds, are displayed as periods (.).

    The default count is 128 bytes.

    默认显示128个字节

    显示字节内容

    4)eb  001b5942-8 ff fe

    The e* commands enter into memory the values that you specify.

    eb

    Byte values. 后面的是字节值

    ff fe 是小端序 utf16 的标识符  https://blog.csdn.net/u012138730/article/details/84032003

    5).writemem c:\gedu\blog.txt 001b5942-8 L1458

    The .writemem command writes a section of memory to a file.

    .writemem FileName Range

    5.使用内存编辑命令在栈上输入中文字符查看其二进制值

    1)在栈中进行编辑,先用栈顶寄存器的值找到栈顶的位置。esp https://blog.csdn.net/u012138730/article/details/90748419

    2)前面用过eb 写的是字节,ew写的word。输入中文。同理dw看的就是看的是word,2个字节

    ew

    Word values (2 bytes).


    1.内核级别的调试的案例

    https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0xc000021a--winlogin-fatal-error

    案例1:一台电脑打开就蓝屏了,还没有走到登录界面那里

    大概流程:

    在一台电脑上启动windbg调试器(我们称这台为主机),然后用一个串口电缆链接这个和蓝屏的电脑系统(我们称这台为目标机器)。

    启动那台会蓝屏的电脑,按F8,选择debugging mode 方式启动。

    当检测到bug check的时候,在有调试器的情况下,bug check会触发系统中断到调试器;没有调试器的情况下,就会蓝屏和重启。

     

    主机启用windbg内核调试:

    估计应该是选COM?串口电缆链接的话,待验证

     

    目标机器开启内核调试模式:

    (如果不能进入目标机器了,就用wer机制修改)

    (以管理员身份执行cmd,直接执行bcdedit可以看到当前的启动加载项,应该能看到current)

    bcdedit /copy {current} /d "Kernel Debug"——" Kernel Debug "是新启动入口的名字。(这个启动入口会在重新启动以后显示出来,试过了)

    C:\WINDOWS\system32>bcdedit /copy {current} /d "Kernel Debug" 已将该项成功复制到 {795bcaaf-1f5b-11eb-a1fe-d2cde0ec311b}。 

     bcdedit /debug {795bcaaf-1f5b-11eb-a1fe-d2cde0ec311b} on——对新启动入口启用内核调试。

    设置目标机与主机之间的通信参数:比如使用串行口1号,波特率115200

     bcdedit /set {某GUID} debugtype serial

     bcdedit /set {某GUID} debugport 1

     bcdedit /set {某GUID} baudrate 115200

    感觉是在注册表上创建了一个东西:

     

    步骤:(一个串口电缆链接)

    1)目标机器启用内核调试以后,在选择入口的界面上停留

    2)主机启动windbg调试。(处于等待状态的windbg会反复向外发送复位包)

    3)目标机器选择启动内核调试的入口进入。(内核调试引擎收到复位包,进行回复复位包,windbg收到版本信息,并打印出来,就是连接成功了)

     

     

    知识:windows系统启动过程:

    1)内核创建的第一个用户态进程:启动SMSS.exe(SessionManager管理器)

    2)SMSS.exe 先检查注册表的一个选项 如下,进行逐一删除或重命名(延迟删除 https://blog.csdn.net/mizac32/article/details/9994017)

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\PendingFileRenameOperations 和PendingFileRenameOperations2

    3)SMSS.exe 再启动 CSRSS.exe 和 WinLogon.exe。这两个如果启动失败的话,会触发bug check。

    其中CSRSS.exe : Client Server Runtime Subsystem——这个进程启动完成就说明完成Windows子系统的初始化,也就是内核初始化,执行体初始化等,大部分的驱动程序的加载工作

    和 WinLogon.exe :是负责 创建登录桌面 和 系统服务管理进程 Services.exe(这个系统服务进程后面会讲到)

     

    如果说WinLogon.exe 在系统启动过程中被删除导致一直蓝屏重启,可能是:

    1)WinLogon.exe 被加入到了上述的 PendingFileRenameOperations当中去了 

    2)某个驱动程序(比如杀毒软件的驱动程序)根据读取一个配置文件,在启动WinLogon.exe之前把配置文件中的应用(比如有 WinLogon.exe)给删除了。(这时候普通的进程没机会启动,只有驱动程序才有)

     

    案例2:电脑登录系统了以后没有开始菜单(即资源管理器没有运行)

     

    资源管理器:

    即explorer.exe,是操作系统的外壳,在登录了到系统以后,理论上会自动启动这个进程。

    (如果平时在用的过程中,资源管理器突然退出了看不见最下面的那些进程图标,使用快捷键打开cmd,键入explorer.exe,重新启动就好了。)

     

    当输入用户名密码以后(WinLogon.exe 是负责登录的系统进程):

    1.WinLogon将用户名密码发送给LSASS(一个负责安全认证的系统进程。)(LSASS 验证通过,返回访问令牌对象)

    2.用户名密码验证通过以后,WinLogon会启动 UserInit表键下的程序,默认是userinit.exe。

    (UserInit键下面可以注册多个程序,应该会依次执行,如果执行成功了就不走下一个了,如果每一个程序都没法正常启动,那么系统就会强制log off登出。)

    3.UserInit表键下的程序会执行登录和初始化操作,userinit.exe启动shell表键中的程序,是explorer.exe,即资源管理器。

    问题出现的原因:

    就是 UserInit表键下的值被修改了成了其他值,而不是应该是的:

    C:\Windows\system32\userinit.exe, 

     

    ERD光盘启动方式

    windows启动模式:

    普通模式:

    安全模式:选择safe mode 

    调试模式:选择debugging mode 

    winER模式:是简化的windows,选择Repair your computer。也叫WER机制。1)可以用来拷贝出目标系统的dump文件,2)可以给目标系统设置增加新入口,新入口开启内核调试模式。3)对目标系统设置启用JIT

     

    问题3:弹出AE提示电源服务PowerService或者Pnp(Plug and Play)服务意外终止了,需要重启 

    错误发生在用户态,但是仍然可以通过内核方式设置断点。或者等待发生未处理异常的时候中断到内核调试器。

    后续会用三种方法分析这个问题。


    2.JIT调试——应用程序错误对话框

    应用程序错误对话框,简称AE(ApplicationError)。当应用程序出现了严重的错误(比如非法访问内存,触发cpu的保护模式异常),而应用程序自己又没有处理时,会被系统的未处理异常函数接管——弹出AE,终止应用程序等操作。

    (但是有的应用程序的一些错误 不能唤起JIT。估计还是要有特定错误路线走向才能唤起JIT。后续看看)

     

    知识点:及时调试器(JIT)的设置方法:

    方法一:设置AeDebug项,Debugger设置为windbg,Auto要设置为1。这样应用程序崩溃的时候就会自动打开windbg了。 

    计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug

    ~~

     

    方法二:使用windbg调试器自己的注册功能

    管理员模式启动cmd,然后输入 windbg.exe(所在路径) -I (就会在注册表中生成了 如上图)

     

    方法三:如何在winER模式下,启用JIT

    在winER下,cmd启动register,然后加载目标系统的注册表文件的software子健(目标系统的盘符:Windows\System32\config 下的),加载以后然后设置同方法一。

    实验:

    设置完jit之后,打开例子中的程序,崩溃以后会直接弹出windbg了,断到崩溃的地方。同时源代码也会读取出来,因为路径设置好了。

    -p

    -e 

    -g

    崩溃的时候执行的汇编指令:(ACSVio是模块,main是函数)

    (00401076就是eip的值,cs:ip 就是当前执行的指令的位置)

    (不懂,为什么002B:760034c0 会等于 0f9425ff)

    对应的源代码: 重点就在*,*ProcAdd指向的内存地址是一个函数所在的地址,当然是只读的区域,不可以更改的,所以执行这句话就会崩溃。

    可以看到ProcAdd的地址应该是 002B:760034c0 (ds:ecx), 根据源代码中看这个应该是Kernel32.dll模块中的一个地址:求证一下

    1.k (应该默认就等于kn  n:number?)——看当前线程当时的栈

    也可以在windbg打开相应的窗口看。

    一般也叫栈回溯。

    2. .frame /c 栈帧号——切换到某一个栈帧的现场 

    图略

    3.kv 1——显示包含参数的栈帧信息 (kv 显示包含参数的栈回溯)

    图略

    4.dt ——display type

    1.看函数原型

    2.将该地址上的东西翻译成此类型: dt 类型 地址

     

    5.lm——看模块。   lm vm 模块名

     


    问题:提示电源服务PowerService或者Pnp(Plug and Play)服务意外终止了,需要重启 (第5,第6,第7章)

    下面会采用三个方法来解析这个问题。分析dump文件,使用内核调试,使用JIT调试。

    知识点:

    1)SvcHost.exe:

    很多重要的服务,比如日志服务,PnP服务,电源服务,DCOM等都host在SvcHost.exe中。

    (什么叫做服务?)

    而SvcHost.exe这个进程是运行在不可见的session 0中的。

     

    2)基于Cookie的溢出检查。GS机制:

    GS机制是在可能发生溢出的函数所使用的栈帧起始处(EBP-4)存放一个整数,在函数返回值检查这个Cookie是否完整。(ebp的介绍 可以看另外一篇文章的函数调用)

    如果返回的时候检查这个Cookie不完整那就说明是溢出了。

    部署和检查cookie的代码,都是编译器在编译时加入的。(如何用windbg命令反汇编出一个模块U的函数U呢 后面有讲到  uf lsm!CUtils::StartServiceW )

    当触发了GS机制的时候,会调用__report_gsfailure函数(在 gs_report.c中搜此函数),函数内容

    1)调用UnhandledExceptionFilter

    2)结束进程TerminateProcess

     

    如果没有GS机制,当函数的返回地址被破坏也能继续运行,而如果函数是会返回到一个精心设计的地方,那么就会开始执行恶意代码了。

     

    UnhandledExceptionFilter函数 的对栈溢出异常(0xC0000409)的处理:

    在没有内核调试的情况下,没有挂着调试器的情况下,UnhandledExceptionFilter 会调用BaseReportFault,进而调用WerpReportFault函数,也就是WER(windows error report)的错误报告函数。(估计其他异常也会这么走)

    在有调试器下,会执行了INT 3指令了,出发断点异常0x80000003(只有栈溢出异常会走特殊处理吧)。接下去执行的话,也是跟上面一样的逻辑,也就说会调用到WerpReportFault。

     

    windows本地调试中的软件断点功能,是依赖于cpu的断点指令(对于x86,即int 3)。cpu执行到断点指令的时候,会中断下来,并以异常的方式报告给操作系统,再由操作系统分发给调试器(简称 产生断点异常 )。

     

    这是cpu提供的的调试支持。此外cpu还以下直接提供:

    标志寄存器

    调试寄存器

    调试异常

    任务状态段

    分支记录机制

    性能监视

    JTAG支持:

     

    此外,cpu还间接支持

    异常机制

    保护模式

    心而能监视

     

    WER系统服务 如何收到错误 以及 如何处理:

    前面说到UnhandledExceptionFilter 会调用到WerpReportFault, WerpReportFault会进一步调用WerpReportFaultInternal。

    WerpReportFaultInternal内部创建一个事件对象来同步。

    (什么是事件对象?)

    然后通过SendMessageToWERService函数 ,向WER系统服务报告错误:

           WER系统服务 收到报告后,创建错误报告进程WerpFault.exe(那个AE就是werpfault.exe?)

           WerpFault.exe启动后,为发起调用的出错的进程,产生转储文件dump文件。

     

    3)修改 系统服务 的启动类型为禁止 

    打开服务控制面板services.msc,修改启动类型

    如果打不开桌面了,就用WER模式启动,打开注册表,加载目标系统的system注册表(同之前说的software):

     然后选择修改Start键值,2为4(2是自动,3是手动启动,4是禁止)3是手动启动,也包括那种被其他服务启动的模式。不是说用户主动去双击进程启动。

    Select中的Current为1,就是指示着,CurrentControlSet就是指向ControlSet001的。

     

     

    4)让Power服务所在的会话0露面

    所有的系统服务运行在会话0

    (不是和普通程序运行在一个会话中,应该是会话1)。但是在任务管理器里面还是可以看到那个服务进程的。

    会话0有自己的桌面和窗口系统,默认不可见。让其可见需要做:

    1)打开services.msc 修改登录属性。

    或者 修改注册表:上上个图中 Power中看到有个Type建值 为0x20 (代表这个服务与其他服务共享一个宿主进程.ps如果想要独占一个,就0x10),把0x20改成0x120(即与0x100与一下)。

    2)找到 Interactive Services Detection服务,启动之。(但是我的win10找不到这个)负责检测有没有服务的交互窗口的。

     

    做了这两步以后,当某个服务在会话0有界面显示的时候,就会弹出来一个窗口,问要不要进入会话0,选择要就行了。

     

    5)修改 系统服务 启动时候的超时时间

    现在是单位5000ms Control下面的一个键值。看样子是对所有的服务都生效的。

    6)如何在启动一个进程的时候就挂上调试器

    1.注册表设置 要调试的exe的 debugger 为 x86的windbg

    打开注册表,在[运行] (win + R)中输入 regedit,找到下面这个路径:

    比如想要调试test.exe, 就新建一个test.exe 文件夹,并新建 一个 字符串值的键,名称(Key)为 Debugger,值(Value)为 windbg所在的路径

     

    2.打开windbg,需要设置这三个路径或者环境变量

    但是这个设置win10都是单次有效,退出就失效了。再次打开不会保留上次的设置。

    所以可以用环境变量进行设置

    Path名分别是 _NT_SYMBOL_PATH (map文件所在目录) 、 _NT_SOURCE_PATH  (源文件所在目录)、 _NT_EXECUTABLE_IMAGE_PATH (exe文件所在目录)

    https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/general-environment-variables

    当你设置了这个以后,每次打开都会是这个了

    如果有修改的就每次打开了以后再设置一下上上图的具体要改的路径,然后选中reload。

     

    3.设置好了以后,关闭windbg。

    打开test.exe 就会自动打开windbg了

     

     

     

    方法一:通过拿到dump文件,分析是哪里的问题(第5章)

    最后发现是栈溢出了。(待重新添加)

     

    方法二:使用内核调试查找问题的原因(第6章)

    连接上以后目标系统监测到有调试器的存在,并主动触发断点异常。目标系统主动中断到调试器。

    (int 3 就是cpu的断点指令。cpu运行到断点指令,中断出来,报告操作系统,操作系统查找调试器接管)

    ps:如果7588cebd 是断点指令的地址,从这个地址可以看到这个是在用户态空间的(如果都是小于2G(0x80000000),那么都是用户空间的)。

    后面执行k指令,可以看一些返回地址和栈帧基地址,是不是都是在用户态空间的地址。

     

    因为是内核调试,所以调试器能看到的是系统中运行的所有的进程。先确定这是哪个进程断进来的,使用如下命令查看:

    >!process (好像是内核模式才能用的指令)

    会依次列出该进程的参数 

    0)PROCESS:进程地址

    1)会话ID:0表示系统服务使用,没有界面显示

    2)进程ID:ClientID 即 CId

    3)DirBase:进程的页目录表的基地址,物理地址

    4)ObjectTable:内核对象表的线性地址

    5)HandleCount:句柄数

    6)Image:进程的可执行映像文件名称

    通过上面的指令,看输出的Image,正好是SvcHost.exe,说明是某个服务的宿主进程中断进来的。

     

    因为是内核调试,所以要先加载该进程的用户态模块

    >.reload /user

    >.symfilepath c:\symbols  ===>设置符号路径,并保证主机端可以访问微软的符号服务器。

    然后使用kn看栈回溯:就是下面画红线的,电源服务线程导致的。同第5章分析的一样。

     

    过程中用到的一些命令:

    1)?? ——计算表达式

    自己测试一下

    >?? (unsigned int)(-1)

     

    2)u——看接下去的汇编指令,推算出函数的伪代码

    比如上面例子中断在UnhandledExceptionFilter 中,那么执行u,

    >u

     

    3)!error——windbg 内核模式下看错误码的描述

    !error 错误码  ===> 比如  !error cC000409

     

    4)dt 类型 地址——看地址上的值,翻译成可读模式

     

    方法三:使用JIT调试查找问题的原因(第7章)

    当尝试对目标机器使用JIT调试以后,启动系统后黑屏,无法看到调试器,为了解决这个问题,所以先启动了内核调试。而且是花了大短时间解决这个问题。这也是三个方法中最曲折的,但是作为学习的目的,作者还是一步一步带着我们解决了。

    节外生枝——目标系统启用JIT后,启动进入系统后黑屏了,先要解决这个

    所以要先启用内核调试,解决这个问题。

    这里等到黑屏出现的时候再启动主机端的调试器。

     

    思路:

    看上文中有关——windows系统启动过程——的描述,先看看是不是该有的进行都起了。

     

    >!process 0 0 ——首先使用命令看看 目标系统的所有进程 

    (!process 好像只有内核模式才能看 反正我本地生成的dump是执行不了)

    上面的命令会列出所有的进程。

    我们不仅看到了该有的进程都有了,而且还看到了

    在所有进程中找到windbg.exe的相关描述。记住其进程地址—— PROCESS:进程地址

     

    >!process 上面的进程地址——先windbg.exe是不是正常的

    上面的命令会列举这个进程的详细信息。

    有两个线程。

         一个是UI线程,在等待消息输入

         一个是调试器的工作线程,在执行调试器的工作循环(EnglineLoop)

    windbg.exe 已经启动完毕,说明已经中断到了某个线程,但是黑屏状态。

    是不是正是因为windbg中断了,导致某个线程阻塞了,从而导致让windows的启动过程受阻了。

    看看启动的关键进程 winlogon.exe 进程的相关信息

     

    >.PROCESS /i winlogon的进程地址——切换到这个winlogon.exe看用户信息

    根据提示执行一个g命令

    然后用之前说过 执行 .reload /user 加载该进程的用户态模块

     

    >!process winlogon的进程地址——看winlogon.exe的详细信息

    会列出线程情况。第一个为主线程。

    所有线程都在等待状态

    除了主线程(列出的第一个线程),其他都处于没有需要执行的空闲状态。

    WaitForSingleObject 等待的是一个事件对象—— 938024f8 下面看其详细信息

     

    >!object 938024f8

    这个是终端服务已经准备就绪的一个同步事件 

    再看WaitForSingleObject 的上一帧是 WaitForLsmStart函数。

    大概是说Winlogon.exe在等待lsm.exe的一个事件。

    (Lsm.exe 我本地是找不到) 

    书中例子是说lsm.exe已经启动了,为什么winlogon.exe还在等待lsm启动呢?

    然后切换到lsm.exe进程看详细情况,看进程情况

     

    >!process lsm的进程地址——看lsm.exe的详细信息

    看看其主线程

    执行了Start和StartServiceW方法用来启动某个服务以后,开始SleepEx 

     

    留意上面的第7行的 WaitStartTickCount 和 Ticks 

    接下去切到该进程的主线程看看

     

    >!thread 9383a030(lsm的主线程地址)——显示包含参数信息的栈回溯

    找到StartServiceW栈帧的第一参数值:

    dU 第一个参数的地址——看启动的是什么服务。显示出来是 RpcSs

     

    找到SleepEx栈帧的第一参数值:

    ?? 一个参数值——看要sleep多久,显示3600000(ms),即1个小时。

     

    上图的Ticks等待的时间,打算一直等待够1个小时,看看接下去执行什么 

    >bp 00bf114d ——“下”(动词)一个断点在这个返回地址上

    00bf114d 是 SleepEx函数 的返回地址。(虽然感觉有点小奇怪)

     

    >uf lsm!CUtils::StartServiceW ——看函数的反汇编的指令是uf。

    上面一步下了断点以后,等着被执行到。

    然后使用p进行单步调试。

    在这个StartServiceW这个函数里面继续执行,但是当调用到CloseServiceHandle这个API进去以后就没返回了。

    主动点击break,将目标中断。看看此时的堆栈信息。

    切换到lsm进程,看主线程的栈回溯,发现确实是断在了CloseServiceHandle函数中了(倒数第8行)。

    CloseServiceHandle 后面是  RPC调用,然后转化成本地过程调用 ALPC。

    (ALPC是windows系统中一种常用的通信机制,是LPC的改进版本。经常用作内核与用户态的通信,或者不同系统之间的通信)

    这个线程正在等待针对 9322a230 消息的答复。看这个消息的具体结构内容,命令为

    >!aplp /m 9322a230

    (为什么可以这么看呢)

    得到如下图:

    QueuePort:这个消息所发送到ALPC的端口号是8c3f1a88 —— !aplc /p 8c3f1a88 可以看这个端口的详细情况

    QueuePortOwnerProcess:这个端口的宿主进程是 services.exe (服务管理器进程,之前有提到过)

    ServerThread:服务线程是84b74538

     

    所以切换到 services.exe 进程,然后看线程 !thread 84b74538 观察栈回溯,发现这个线程也陷入了等待,且已经等待了很久了(看 WaitStartTickCount 和 Ticks  )

     

    梳理一下流程,总结黑屏的原因就是:

    1)电源服务进程的挂起过程

    电源服务进程启动,主线程从umpo!UmpoMain开始执行,会调用RegisterServiceCtrlHandleExW 向服务管理器(services.exe)注册服务控制函数。

    然后调用自己的umpo!UmpoAlpcInit函数创建一个新的线程(),线程的入口为umpo!UmpoAlpcListenForProMessages,用于监听由全局变量mpo!UmpoAlpcPoPort所标识的ALPC端口。

    主线程继续调用UmpoGroupPolicyInit函数,调用UmpoNotifyKernelAllPowerPolicyChanged函数通知电源策略变化,但是函数中发生了栈溢出,当崩溃后,由于注册了JIT调试器,系统自动启动JIT附加到这个进程,使得电源服务进程挂起。

     

    2)

    服务管理器进程,在得到电源服务进程启动得消息后,进程中得scext模块企图通过ALPC消息注册电源事件回调,(意思是 服务管理器进程进程给电源服务进程发消息了,等待他得回复吗,通过alpc端口?),但是由于电源服务进程挂起,注册过程受阻,导致服务管理器进程de服务线程被阻塞。(估计就是一直在等待回复de状态)

     

    3)

    LSM进程,启动rpcss服务时与服务管理器进行通信,但是由于服务管理器进程de服务线程被阻塞,导致这个进程也在等待状态也被阻塞。

     

    4)

    Winlogon进程的主线程(系统启动的关键路径),会等待一个termsevreadyevent事件(应该是由 LSM进程 发出的?),但是一直没有收到,所以电脑就一直黑屏了。

     

    大概就是Winlogon进程等待着 LSM进程, LSM进程等待着服务管理器进程,服务管理器进程等待着电源服务进程,但是电源服务进程被挂起,所以就一直等待着了就是黑屏了。

    所以最后执行.kill 9381e870 和 g 命令,将windbg进程杀掉,黑屏就重见天日了。

     

    【服务是后台进程的意思吗?】

    所以解决黑屏的方法是延迟 电源服务的启动,修改为禁止启动。等待进入了桌面以后,再去手动启动,使之崩溃。

     

    进入会话0界面用windbg查看电源服务进程

    根据前面知识点4)中 设置操作可以进入会话0的操作。

     

    但是当对电源服务进行了上述交互式的设置以后,没有弹出要不要进去会话0,猜测原因跟 之前 黑屏一样,电源服务的栈溢出以后的某些逻辑阻止了 Interactive Services Detection 这个进程的工作。

     

    所以在电源服务启动(宿主进程svchost.exe)的时候就启动调试器,让早一点检测到,方法如文中最后所说(如何在启动一个进程的时候就挂上调试器)。

    操作以后便出现了正常弹出窗口,进入会话0,看到windbg的界面,马上点击g命令,让进行运行,然后就断到了方法一(第五章)中所示的dump一样的堆栈信息,具体分析方法看方法一(第五章)。

     

    除了进入会话0查看,也可以在会话1远程会话0中的JIT调试器

    之前猜测Interactive Services Detection 弹出交互框这个进程受阻了,导致弹出不了进入会话0的对话框。这里可以尝试另一种方法“进入”,使用另一个windbg远程会话0的windbg。

    例子中当jit在会话0中被启动的时候,是在系统的其他服务都启动后的。网络和命名管道应该都是已经可以用的,所以尝试修改JIT设置中的AeDebug的JIT的设置路径为:

    windbg的exe -p %ld -e %ld -QY -c ".server tcp:port=2000" -logo 日志路径

    不能有-g开关:如果有的话,会跳过电源服务进程的断点异常了,所以不加。

    .server tcp:port=2000:让windbg创建调试服务,监听tcp网络接口连接。

    同时去掉上文中 在电源服务启动(宿主进程svchost.exe)的时候就启动调试器 所作的设置。

    然后启动电源服务进程,然后在任务管理器里面看到了这个windbg进程起来以后,在启动一个windbg实例,并且操作菜单使用远程会话,设置参数:

    点击ok以后,就能正在连接会话0的JIT调试器。

    接下来就可以输入命令开始调试了,

    ~*k列出所有的线程

    ~<n>s切换到那个线程

    接下来的分析就一样了。

     


    问题:拖动一个脚本到PPT的时候,导致了PowerPoint的挂死

    知识点

    1)使用adplus或者windbg保存现场,方便以后随时调试

    https://www.cnblogs.com/grayguo/p/8064783.html

    Adplus -pn powerpnt.exe -pn wincmd32.exe -hang -o c:\test

     给涉及到的两个进程,产生dmp,日志,和报告文件。

    (我自己保存一个别的好像没有生成dmp文件)

    使用windbg:

    https://www.cnblogs.com/johnice/archive/2013/01/17/2864205.html

     

    2)使用kPL命令看函数原型和参数

    如果有模块的符号表,就可以使用kPL命令了。如果没有就不能用。

    (具体命令是啥呢?)

     

    3)怎么看懂栈回溯

    栈回溯——根据保存在栈上的信息,可以生成一个线程的函数调用过程,从当前的执行点反向追溯到父函数。(查看另外一篇文章的 函数调用过程可以更清楚的理解原理。)

    对于几乎所有的WindowsGUI程序,编号为0的初始线程就是UI线程。

    界面没有响应,其实就是负责消息处理的界面更新的UI线程阻塞了。

    使用~0s切换到0号线程:0表示0号线程:

    eip寄存器是cpu下一次要执行的指令地址(eip保存的地址上的值现在是c3(机器码),对应就是ret(汇编语言)。)。看到下一条要执行返回ret。但是还没执行到,一直停着了。

    可以看到模块是ntdll! 就是内核模块的,意思就是进入到了内核模块的FastSystemCallRet函数执行中了,而且至今尚未返回。

     

    使用kn 100看100个深度的栈回溯:

    od帧 那个 POWERPNT+地址,是微软的符号服务器没有包含Office程序的符号文件(pdb),所以显示不出来的符号名。

    OLE是对象链接与嵌入,03帧的CDdeObject 动态数据交换相关,跟拖动脚本到ppt这个过程有关。

    第1帧是发起一个系统服务调用,NtUserMessagesCall

    第0帧以快速系统调用方式进入了内核态了。 

     

    4)使用windbg附加到进程

    1)如果挂的进程是64位的,就用64位的windbg。(挂错了就提示 NTSTATUS 0xC00000BB)

    2)windbg需要用管理员模式启动,否则会提示拒绝访问

     

    查问题步骤

    1)使用windbg挂到挂死的ppt进程上,看栈回溯,也就是上图。

    2)查看最近几个函数的原型(09帧的OleCreateFromFileEx),参数值。

    使用MSDN中的文档查看OleCreateFromFileEx,其函数的第二个参数就是文件名(EBP+C),验证一下是否是拖进去的那个文件的路径,执行完下面这条命令了以后,确实是拖进去那个文件路径。

    >dU poi(0013a988+c) 

    3)所以就是ppt在接受这个文件的时候,意外挂死了。在接收的过程中最后调用了内核服务NtUserMessageCall,同样看看这个函数的函数原型,然后分析一下传入的参数。使用以下命令看前两个栈帧的有关参数:

    >kb 2 

     第一个参数是要发送消息的目标窗口句柄,0xffffffff是特殊值,表示用来广播消息的(HWND_BROADCAST)

    第二个参数是发送消息,0x3e0表示的消息类型后面再看看,先对这个数值有印象。

     

    4)再开启一个wdb实例,进入本地内核调试会话(设置内核调试的方法前面的内容中提到过)

    打开windbg,File>Kernel Debug>Local

    然后执行如下命令找到PowerPoint进程的进程地址

    >!process 0 0 powerpnt.exe

    然后执行如下命令看PowerPoint进程的线程信息

    >!PROCESS PowerPoint进程的进程地址 2

     然后执行如下命令看UI线程的栈回溯

    >!THREAD PowerPoint的第一个线程地址UI线程地址

    显示如下:

    一般来说SendMesseage的第一个参数都是窗口句柄(看上图,好几个都是bc74ed08 ),试着看看其值是多少

    >dd bc74ed08 l1

    其值是 bc74ed08 地址的值为 001506C4。

    我们验证看这个值是否是窗口句柄值,可以使用spy++工具来查看,也可以通过skywing 编写的sdbgext模块中的hwnd扩展命令来查看:

    如上所示,确实是窗口句柄。

    根据其ProcessID在任务管理器里面找到这个进程。

    到此为止,导致PPT挂死的原因应该就是,这个进程的这个线程没有回复消息给PPT,导致PPT阻塞了。

     

    5)再开启一个wdb实例,附加到上述找到的那个进程,查看原因为什么不恢复消息

    >~*————显示所有的线程,看到上述的那个线程值(000016d8)的线程号是什么

    >~线程号s——切换过去

    >kn 100查看栈回溯

     

    然后执行lm,列出进程的所有模块,找个某个服务来自何方。然后再系统服务管理器中停止这个服务以后,【这里没看懂 68页】

     

    找到之前挂PowerPoint进程的windbg,然其detach(Debug>detach),PowerPoint然后恢复了。

     

    所以问题的原因就是 ppt发送了一个广播消息,但是有个第三方的服务没有回复消息。

     

    许多使用OLE技术的程序都有这个问题

    很多挂死的问题都是因为双方或者多方协作除了问题。

    之前的3e0的消息就是WM_DDE_INITIATE消息,要求系统中每一个窗口都相互谦让。

     

    但是这个问题在Win7中没有发生挂死了。

    关键在于之前图中的 CPackagerMoniker::BindToObject方法的 06号栈帧

    再win7中BindToObject没有在再调用DdeBindToObject,原因是:

    BindToObject 的内部实现是 调用 CoCreateInstance函数。

    CoCreateInstance的第一个参数,要创建的对象的组件的ID是 {一个GID值},

    到注册表里面去查找这个组件,CoCreateInstance就会返回成功,

    于是BindToObject就会调用这个组件对象来执行而不是调用DdeBindToObject了(继续调用DdeBindToObject就会出现挂死问题了)。

    (xp也是这个,但是xp的系统里面找不到这个组件的注册信息,BindToObject 就会调用DdeBindToObject了)

    而这个组件是在packager.dll中的,如下搜组件的GID

    Packager.dll就是Windwos Server 2003引入的模块,被称为Object Packager 2,即第二代的对象打包器。

    (所以在win7系统下,如果将注册表该项目的 packager.dll改名,或者直接在调试器中修改调用CoCreateInstance函数的返回值,就可以重现这个崩溃了。)


    问题:经典阅读器的经典死锁(第9章)

    问题:邮件中打开pdf附件以后,pdf阅读器无响应了。鼠标点击pdf阅读器窗口无反应。

    (windows子系统发现pdf阅读器长时间不处理窗口消息,在标题条打上不响应的标签。)

    (Windows处理窗口消息函数是 GetMessage PeekMessage 等)

     

    知识点

    0.导致程序不响应的原因

    1)粘滞在内核态:ppt的例子,因为UI线程调用SendMessageAPI广播DDE消息进入内核态一去不回。

    2)死锁:这个例子

    3)死循环:后面讲

    1.windbg的.dump命令

    .dump /mfh c:\dumps\pdf\acrobat.dmp

    m——代表minidump。但不一定是真的mini。只不过windows下的用户态转储文件一遍都是使用minidumpwritedump这个api。

    f——代表full。打印完整的用户态转储。转储文件中包含用户态空间的所有内存数据。会比较大的。

    h——代表包含句柄信息。对于调试程序挂死非常有价值。

    2. 线程前的符号含义 (9.2图重新拍过)

    . 表示 调试会话中的当前线程

    #表示 中断到调试器,产生转储时的当前线程、

    、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、多个命令的组合:看线程命令+栈回溯命令

    ~* kv1 看每个线程的栈回溯,只看第一个栈帧 (k 看栈回溯  v 有参数  1 栈回溯深度为1)

    ~ns 切换到n号线程。

    ~n k 看n号线程的栈回溯。

     

    3.系统服务在ntdll.dll中的桩函数(stub)

    NtWait  ZwWait ,这些要么以nt开始,要么以zw开始。表示已经调用系统服务而进入到了内核态了。

    他们调用的事等待函数。那么他们在等谁,要等多久。

     

    比如 NtWaitForSingleObject API签名:(通过kv 就可以看到参数了,就是下面这三个参数具体是什么值)

    第一个参数:等待的对象

    第三个参数:等待的时间

    可以看到好几个都是0,就是NULL。这意味着线程在无线等待第一个参数指定的内核对象。

     

    4.线程在等待的原因

    1)等待内核对象。比如NtWaitForSingleObject ,等待内核对象改变(比如临界区)

    2)是线程池的工作函数(看栈回溯的上面几层可以看出来),在等待事情做。

     

    5.用户态代码

     

     

    6.看寄存器的值 dd @ebx l1

    相当于看 dword ptr [ebx] 的值 

    dd:https://blog.csdn.net/u012138730/article/details/90748419

    看 dword ptr [ebx+28h] ——dd @ebx+28h l1

     

    7.锁,windbg看有哪些属于锁定状态的锁 !locks

    等待往往和锁有关。因为要进入的区域被加锁了,所以要等待。

    通过上述得出的信息:

    1)关键区的地址 都位于 CoolType模块中。

    2)有3个处于锁定状态,即有3个线程进入了这三个关键区。OwningThread就是拥有这个关键区的线程号。观察图,其实是两个线程号,后面两个属于同一个线程号25f4.

    3)总有541个关键区,关键区的概念。(ps:上面的图没有下面的图说的LockSemaphore的字段的记录)

    (ps:用!cs -l 命令弥补没有LockSemaphore的字段的记录)

     

    ps:但是我本地输入!locks 出现错误,以后再看吧  https://stackoverflow.com/questions/2876506/why-isnt-locks-working-for-me

     

    8.死锁

    2460拥有的那个关键区的对应的事件(LockSemaphore) 是 2e0。

    (ID是 25f4  线程号6,等待的对象正是 2e0。图9.2)

    25f4 拥有的第一个关键区的对应的事件(LockSemaphore) 是 2DC。

    (ID是 2460 线程号3,等待的对象正是 2DC。图9.2)

    25f4  想进入2460所拥有的关键区,2460想进入25f4所拥有的关键区。

     

    如何避免这样的死锁:如果多个线程同时需要获取多个锁,那么获取的顺序一定要一致。下面是错误的示意图,不同的顺序获取,导致了死锁。


    转储分析:蓝屏崩溃产生的系统转储文件(第10章)

    知识点:

    1)转储分析、转储文件、系统转储文件

     

    2)windbg自动分析命令 !analyze -v 

    可以给你分析是什么类型的错误,错误码是什么,断在那句话了,哪个模块等等可以自己挨句话看。

     

    比如该例子的蓝屏分析

    停止码。0x7f:内核态错误,超出了系统所能处理的范围。

    最后四行:第一行,类型 8 表示双误异常(double fault)

     

    比如更加常见的异常分析:自己看吧

     

    3)双误异常以及什么时候会发生双误异常

    cpu报告了一个比较严重的异常以后,异常处理过程还没有结束,又发生了第二个比较严重的异常。

    (注意是比较严重。比如连续两次页错误异常,连续两次通用保护错误(General Fault)异常。但是断点异常,调试异常等有益的异常不算是严重异常)

    IA-32cpu中 是一种异常,向量号位8,属于终止类异常,一旦发生,不可恢复运行,不可处理异常。

    (关于上面说的 各种异常,向量号 在 汇编语言 这篇文章中有详细描述)

     

    4) 页目录基地址和CR3为何会不一致

    CR3寄存器就是用来存放当前进程的页目录基地址的。

    执行>!process ————可以看到当前进程 DirBase 就是页目录基地址的值 0ff2f000 。

    而 执行 >r cr3  ——确实是0003000  (r就是看寄存器 )

    为什么会不一样呢,这是双误异常发生后的典型症状。

    当发生双误异常以后,cpu会动用硬件的线程切换机制(应该是一般不会动用硬件切换),把CR3寄存器切换到系统进程所使用的页目录基地址(这里是00030000),在新的线程中开始执行双误代码,让系统崩溃和保存转储信息。

    但此时操作系统没有来得及更新当前进程。所以产生了,操作系统没有更新当前进程,但是CR3已经是新的了。

    即cpu眼中的进程是新的,操作系统眼中的是旧的。

     

    5) 为何process能看到当前进程的信息,这些信息如何保存的

    在CPU的控制区,FS寄存器所指向的段中保存着每个CPU的当前线程结构(KTHREAD):在线程结构中,有这个线程所属进程的进程结构,即EPROCESS。

    PsGetCurrentProcessId函数就是利用类似的方法得到的:

    >u nt!PsGetCurrentProcessId

    下面是例子中的输入的:[不过不明白为什么是+150h]:因为我通过结果推算出来是150h的。(通过例子给的ImBuggy.exe的进程地址是PROCESS 85ad72c0,然后对比看87de6140是的内存值,正好第150h的位置处是85ad72c0)

    可能是因为 fs:[00000124h]是当前线程结构的地址。第214h是线程id的数据,而第150h处是所属的进程的地址。具体就要看,线程结构了。

    6) 每次发生页错误的时候,CPU会把访问的地址写到CR2中。

    看寄存器的值就用命令r

     

    7)>ln 地址——查找地址所对应的符号

     

    8)内核态栈

    大多数的Windows线程都是有两个栈。一个是内核态的,一个是用户态的。用户态的栈默认大小是1MB,通常不会溢出。

    而内核态的栈通常是十几千字节,到几十千字节之间。基于不同的处理器的Windows系统默认的内核态栈大小:

    1)x86 cpu系统中,内核态栈初始大小是12K

    2)x64cpu(英特尔64和AMD64)中,是24K

    3)安腾处理器中,是36K

     

    10)看栈回溯中栈帧基地址跨越很大,说明显示是两个不同的栈的了

    因为windbg去寻找tss信息找到另外一个栈,所以能将两个不同的栈连接起来了

    还可以从返回地址看出这是用户态还是内核态的地址空间((如果都是小于2G(0x80000000),那么都是用户空间的))

     

    1.把dump拖到windbg中

     

    2.>!process 0 0 列出系统中的所有的进程,看看系统启动过程进行到哪一步了

    看到只有SMSS和CSRSS,WinLogon进程还没有出现(看上文的Windows启动过程)

     

    3.>!analyze -v 看看还有没有其他信息

    1)分析出来是一个双误异常,也就是现在处于双误线程的执行中。

    2)在转去执行双误异常之前断在push ebp,函数开头,然后就cpu就转去执行双误异常的KiTrap08例程了。

    3)当前进程CSRSS的页目录基地址和CR3寄存器的内容不同。正常情况下应该是一样的,不一致说明cpu已经切换了CR3寄存器的内容,但是操作系统还没有切换当前进程。

     

    4.>kvn——信息后面有 FPO:TSS 28:0 得到保存在TSS段的宝贵信息

    (FPO:TSS 28:0) 是所谓的任务门。(任务门,是指的是,登记在IDT表中的一种特殊入口。IDT表:中断向量表吗?)

    CPU可以根据这个任务门,所指向的任务状态段(TSS,这里指向的任务状态段是 28 ),来切换到指定的任务(线程)

    比如发生双误的时候,cpu找到登记在IDT表中的8号(双误异常是8号)表项的任务门,来切换到处理双误异常的新线程。

     

    通过

    >!pcr ——显示处理器控制域信息(Processor Control Region),也就是KPCR结构信息,每个CPU对应一个KPCR结构,可以在命令中指定要显示的CPU序号,不指定,就显示当前CPU的PCR信息。

    >dt _RTSS TSS的地址——显示当前线程的TSS的内容

    下图是例子中的内容

    TSS(任务状态段)中有字段:

    CR3:cr3寄存器的内容,案例中的是0x30000,我这个是最后亲自动手的例子中的。

    Backlink:previous task link field。指的是cpu所执行的前一个任务,这里是0x28

    也就是说在双误线程之前,前一个线程的TSS的段选择子是0x28.

     

    5.>.tss 28 返回到到一个前一个线程查看现场

    回到上一个段选择子的现场。可以看到当时的寄存器的数值。

    可以看到,当前要执行的指令是 push ebp(一般来说eip指向的都是是下一条要执行的指令,但是这里通过后面分析,应该是已经执行过这条指令,然后又重新指向这条指令的)

    当cpu执行到错误类异常的时候,比如页错误,那么他会把程序指针重新指向这条导致异常的指令开始处(页错误就是这么设计的,之前文章讲到过,缺页时候的处理)。

    而这个例子,反汇编 pusb ebp的前一条指令,不太可能会出现异常,因为是前一条计算指令,对esp进行减法的指令,没有对内存进行读写。而push ebp这条指令,对栈空间进行push,很有可能导致异常,比如要存的地址,栈无效了)

    通过,esp寄存器的值(f655cfb48),计算当时要push的内存地址,也就是esp-4=f655cfb4,存入ebp的值(f655d7dc)。手动写一下如下,果然是行不通呢。

    >ed f655cfb4 f655d7dc

    那么,看看这个地址空间上的数据能不能读取吧。如下:

    >dd f655cfb4 

    问号的可能性:1)可能是这段内存没有包含在转储文件中,2)也有可能是那段内存根本就是没有分配,不存在。怎么判断内存是不是没有分配对应的物理内存呢,就是通过!pte命令

    >!pte f655cfb4 ——页表项没有指向物理内存,所以此地址就是就是发生了要写的内存无效而产生的页错误异常。

    >!pte f655d000——页表项指向PFN=100e1的物理内存页

     

    6.>!thread  看当前的线程,验证上面说的要访问的已经超出了范围

    确实是的,可以看出

    1)f6560000-f655d000=00003000=12K 栈空间大小,而esp-4=f655cfb8,都已经超出了f655d000, 72个字节了

    2)当前进程是csrss.exe

    那为什么esp会指向无效的地址呢,是当调用到此函数的时候,函数使用了FPO(帧指针省略,《软件调试》第22章)优化。没有建立栈帧基地址ebp,直接开始调整栈指针,为当前函数分配栈帧空间,而栈空间又溢出了,导致的。

    可以用r cr2验证是不是正好是访问那个无效地址的时候导致的页错误,cr2确实是=f655cfb8

     

    7.由于内核驱动程序的栈空间很小导致了栈溢出,查找具体原因

    因为函数开头是一个对esp的减408的操作(这个是看了之前push ebp 之前的那条指令,就是对esp减408),所以把esp还原回去。然后就可以看当时的栈空间了

    >dd f655cfb8+408

    返回地址是 aebc6eac(之前文章说过函数调用过程中栈的存放,一般都是从右到左压入栈,最后是函数返回地址。嗯,应该是这样,待确认)

    通过ln命令,看这个地址对应的符号是什么,得到函数名。

    >ln aebc6eac  ——是xxx文件的大概多少行

    作者在源代码中找到这个函数调用,在函数开头是局部变量的定义。而栈就是分配局部变量的主要空间,除了适合分配在寄存器上的少数变量外,大多数局部变量是分配在栈上的。

    所以之前的对esp减408的操作就是因为函数开头定义的局部变量。

    而这里的局部变量导致的栈溢出,是由于栈空间不够了。由于这个是驱动程序的代码。

    因为驱动程序是运行在内核态的,大小通常只有几千到几十千字节之间。(例子中就是只有12K)

    那为什么会栈空间用完了呢?继续使用k看栈回溯,看看是怎么把这12K空间用完的:

    除了前两个,后面有的是系统的函数,有的是显卡驱动程序的函数。

    大概是WIndows子系统在初始化视频显示(InitVideo,倒数第10行),Windows子系统的用户态模块调用内核态驱动,内核态驱动调用显卡驱动,显卡驱动在访问注册表的时候,触发了有问题驱动的注册表挂钩函数,而导致了溢出。

     


    标准文件流有关的陷阱:混乱的数据从何而来(第11章)

    知识点

    1.后台程序

    后台程序具体的定义是啥不清楚。一般接触到的:

    linux下:daemon程序

    windows下:有系统服务 SystemService

     

    2.linux下基于Sysfs技术的虚拟文件

    Sysfs:设备驱动程序文件系统,是一种使用内存,作为存储媒介的,特殊文件系统。是linux下,用户态模块和内核态模块相互通信的常用技术。

    利用Sysfs,应用程序(用户态)便可以用读写文件的方式,与驱动程序交换数据(内核态)。

    应用程序,可以从驱动程序那里读取来自硬件设备的数据,读取驱动程序的状态,配置驱动程序。

     

    驱动程序创建虚拟文件的内核函数如下:

    (每个驱动程序可以创建多个虚拟文件。虚拟文件系统,会将这些文件都映射到sys/设备名/vfile)

    sysfs_create_file()

    sysfs_create_bin_file()

    sysfs_create_group()

     

    输入参数是:struct device_attribute 结构体:

     

    3.open,write,write函数

    一些c标准库函数

    printk:驱动开发中常用的打印函数

    dmsg | grep " "

    4.文件描述符

    在之前的文章中说到过文件描述符。

    默认的文件描述符 0 1 2分别是标准输入。后续再打开文件的话,就从3开始。

    如果把0 1 2这三个文件描述符都关掉的话,再次打开新的文件,就会从0开始分配了。


    为什么安装补丁会失败(第12章)

    知识点

    1.事件查看器 EventViewer

    针对某个应用程序的错误,可以看看事件记录里面的详细信息,有没有报告什么错误代码以及提供什么信息。

    MsiInstaller:应该就是msi的安装程序吧。

    2.对Windows Installer 启用日志记录

    启用以后,生成的日志在系统的临时目录下,MSI日志文件以msiXXX.log结尾。

    3.切换到临时目录下

    cmd中,输入 cd %temp%

    或者“运行”的那个程序,输入 %temp%

    其实就是 c:\users\用户名\AppData\Local\Temp

    4.如何设置断点

    1.比如要看在调用了CreateFileW函数的时候,创建了哪些文件:

    >bp KERNELBASE!CreateFileW+0x5 "dU /c 50 poi(@ebp+8);gc"

    bp 位置:设置断点的位置,不知道为啥要+0x5,其他数字应该也行吧

    "dU /c 50 poi(@ebp+8);gc":断点执行到那了以后的输入的命令

              dU /c 50 poi(@ebp+8)  :打印 ebp+8 处的内存内容,Unicode字符显示 ——poi 和 @ 之前都有用过——也就是CreateFileW的第一个参数,是一个字符串。

              gc:继续执行的意思吧?

    >bp KERNELBASE!CreateFileW+0x5 "dU /c 50 poi(@ebp+8);gu;r eax;gc"

              gu:不知道啥意思,猜测是不是这个函数执行完?

              r eax:看eax的值,看函数的返回值,为了看函数的返回值有没有异常。

    >bp KERNELBASE!CreateFileW+0x5 "dU /c 50 poi(@ebp+8);gu;r eax;.if (@eax<0){.echo hit} .else{gc}"

              .if (@eax<0){.echo hit} .else{gc}:看eax的值,看函数的返回值,如果小于0,.echo hit,并停止运行了。否则继续运行。——中断了以后就是返回值小于0。可以查看k 看不正常的时候的栈信息了

     

    2.比如要看在调用了OpenFileMappingW函数的时候,创建了哪些文件:

    >bp KERNELBASE!OpenFileMappingW+0x5 "kv10;dU /c 30 poi(@ebp+10)"

    bp 位置:设置断点的位置

    "kv10;dU /c 30 poi(@ebp+10)":断点执行到那了以后的输入的命令

              dU /c 30 poi(@ebp+10)  :打印 ebp+10 处的内存内容,Unicode字符显示 ——也就是OpenFileMappingW的第三个参数就是字符串类型的,是文件映射对象名称——@ebp+4 返回地址 @ebp+8第一个参数 @ebp+c第二个参数  @ebp+10第三个参数

              这里没有gc了,就会断在那里了,需要手动按g让继续执行了,因为这里需要观察一下,有没有异常,有异常的话就分析一下

     

     

    4. 一些调试命令

    .restart —— 重新启动

    .restart /f——让目标复位,啥意思?

    单步跟踪命令——p

    手动中断,快捷键Ctrl+Break

     

    5.查看字符串地址的来源

    用ln 和 !address  没怎么看懂其实。

     

    6.补丁安装的原理

    1)解压到临时目录 

    Setup.exe:补丁安装程序。SetupEngine.dll:通用引擎模块。ParameterInfo.xml: 补丁包的参数信息文件。 

            最上面两行补丁的详细信息:核心补丁文件名,大小,补丁的guid

             ApplicableIf 节:判断补丁的适用性。 MsiPatch描述补丁,TargetProduct描述的是修补的目标(可以看看这些目标,在注册表中是否存在)。

    MSP文件:补丁内容和执行补丁动作。资源文件

    2)分析补丁的适用性,确定补丁应用顺序 

    3)调用MsiExec,解析和执行MSP文件中补丁逻辑:在这一步中才会进行执行文件替换,注册表更新等修补动作,才会产生MSI日志文件。

     

     

    7.C:\Windows\Installer文件夹

    有msi文件,msp文件,以及一些子目录

    这些文件是用来卸载和修复已经安装的软件的。

    是以前安装软件是安装程序故意留下的,可以想象这些为MSI本地包。

    注册表里面用来记录他们的路径的表键叫 LocalPackage。

     


    windbg的命令汇总

    0)调试命令

    使用f10 进行单步调式,也就是p

    使用pc,进入到下一个函数中

    1)模块相关—— lm 查看模块,以及模块所在的地址。

     

    2)线程相关 —— ~ 查看线程,~*查看所有线程

    ~ns 切换到线程号为n的线程。

    ~nk 看n号线程的栈回溯。

    3)进程相关 —— !process 0 0—!process 进程地址 — !process /i 进程地址——.process 当前进程,比!process简单很多

    (!process 只有内核模式才能用)

    !process 0 0 ——看所有的进程

    !process /i 地址 ——切换到某个进程

    !process 地址 ——看某个进程,如果是当前进程就 !process就行咯

    !process  ——看当前进程

    !process cid ——看cid=4的进程

     

    4)一些参数的含义

    280是进程号,2a4是线程号。(再使用 ~ 查看线程,~*查看所有线程)

    比如umpo! 指的是umpo模块(可以使用 lmvm umpo 看这个模块的信息)

    比如0a帧的最后一列,是执行到函数UmpoAlpcSendPowerMessage的第0x88字节,即进入了__report_gsfailure函数了(看上一个帧09的最后)。

    而0a帧的第一列,就是函数UmpoAlpcSendPowerMessage的栈帧基址,即EBP为009afb30。

    而0a帧的第二列,是返回地址,即UmpoAlpcSendPowerMessage这个函数被调用的地方,输入命令:dd 009afb30(后面可以用l+数字长度 来指定显示的长度,不加默认就是128),显示出来的依次是旧的EBP值(感觉显示出来的旧的EBP值不对啊),这个函数的返回地址值,这个函数的输入参数的值(看 https://blog.csdn.net/u012138730/article/details/90273751)

     

    3:kd > 前面的3是3号cpu的意思。 目标系统有多个cpu,当前是3号。

     

    5)k看栈回溯

    观察下图:

    其实就是n是帧号显示,默认就有了。v是参数显示。v比b更多。最后的数字是栈回溯的深度。

    还有个kPL 是显示函数原型和参数值

     


    分析dump文件

     

    好像打开windbgx64 和 windbgx86都没事

    直接把dump文件拖进来就可以了

    当然也要设置上面说的三个路径

    在窗口中输入 :.ecxr  回车

    view 中 打开堆栈窗口,看执行到哪里了

    下次再查崩溃可以好好看下下面的文章先

    https://blog.csdn.net/iwilldoitx/article/details/81048500

    https://blog.csdn.net/xumaojun/article/details/78566935

    http://www.cppblog.com/ay19880703/archive/2011/10/30/159364.html

    https://www.cnblogs.com/pugang/archive/2012/06/24/2559697.html

    最新回复(0)