将自己在工作中的记录的一些windbg的命令记录下来,方便查阅,此页面会不断更新:
内核调试时OutputDebugString函数输出内容windbg无法看到不修改注册表的解决办法:ed nt!Kd_DEFAULT_Mask 8
参考自:https://blog.csdn.net/xbgprogrammer/article/details/43021689
windbg加载符号相关:
加载符号时开启嘈杂模式:!sym noisy
加载符号时开启静默模式:!sym quiet
CTRL+S设置符号表路径:F:\VsCode\TestHelloWorld\x64\Debug;SRV*F:\virtual_Symbol\win10_21H2_x64* http://msdl.microsoft.com/download/symbols
一般F:\VsCode\TestHelloWorld\x64\Debug放的是调试程序对应的pdb,如果在此目录下或者F:\virtual_Symbol\win10_21H2_x64下都找不到符号文件,那么就到微软符号服务器下载,可以设置多个路径在最前面,通过;分割。
加载用户空间符号:.reload /user
重新加载: .reload /f xxx.exe/dll/sys
强制加载某个不匹配的PDB文件: .reload /i xxx.exe/dll/sys
查找模块内函数:
例如查看MessageBoxW: x User32!*MessageBoxW*
查看模块:
查看当前加载所有模块:lm
查看某个模块详细信息: lm vm XXX 不加后缀
查看用户态回调函数表:
用户态下使用命令 !peb
dt nt!_PEB xxx 偏移大概0x58的位置 就是KernelCallbackTable,
也可以使用命令 dt nt!_PEB KernelCallbackTable XXX 含义为XXX为_PEB结构体,查询结构体中的KernelCallbackTable变量
找到之后使用名 dps XXX 或者 dqs、dds根据64位、32位不同
双机调试,使用VirtualKD-Redux软件可以很方便的进行双机调试
双机调试下调试用户态程序:最好可以让用户态程序运行时暂时中断下来如:getchar()、MessageBox、system("pause")等等。
在宿主机上的windbg中使用菜单Debug->Break或者键盘CTRL+Pause,即可使被调试机器中断下来:
使用命令 !process 0 0 xxx.exe 可以查看特定进程对应的_EPROCESS地址,例如:
kd> !process 0 0 cmd.exe
PROCESS ffffe5048cbae340
SessionId: 2 Cid: 11b0 Peb: 65471e6000 ParentCid: 1618
DirBase: 3a560002 ObjectTable: ffffbe09a24b2080 HandleCount: 73.
Image: cmd.exe
会话ID为2,进程ID为0x11b0,peb地址为65471e6000,父进程id为1618,DirBase不太懂,好像比较复杂,暂时放下,ObjectTable为句柄表地址,HandleCount为句柄数量。
要调试此回话需要使用如下几条命令:
.process /i xxx xx为EPROCESS的地址
g
.reload /user
如果没有加载主程序的pdb .reload /f XXX.exe/dll/sys 为应用程序模块
如果还是没有加载主程序的pdb .reload /i xxx.exe/dll/sys,如果还是没有加载,检查.sympath是否包含pdb路径
双机调试快速调试用户程序:在应用程序中源码中加入DebugBreak()或者int 3断点,程序运行后,宿主机调试器会自动中断下来:
使用命令: .reload /user 加载用户态符号
在查看栈回溯:k
此时已经在用户空间了,可以进行调试。
查看进程token
一些结构体 dt nt!_EPROCESS
偏移0x4b8 为 TOKEN, 结构体为 _EX_FAST_REF
+0x000 Object :
+0x000 RefCnt :
+0x000 Value
如果找到pid为4的system进程,获取Object 的值,然后写入到想提权的进程Object属性中,即可提权权限到sytem级别
使用命令 dt nt!_TOKEN object的值,
偏移0x40为Privileges,结构体为 _SEP_TOKEN_PRIVILEGES
[+0x000] Present : 0x800000000000 [Type: unsigned __int64]
[+0x008] Enabled : 0x40800000000000 [Type: unsigned __int64]
[+0x010] EnabledByDefault : 0x0 [Type: unsigned __int64]
其中Present代表着是否可以开启某项权限,Enabled代表是否开启了某项权限,EnabledByDefault 为是否默认开启了某项权限,
要提权时可以修改token为system的token,也可以换一种方式:开启SeDebugPrivilege权限,默认情况下只有过了UAC的high权限及权限之上的程序才可以打开此权限,如果存在内核漏洞
设置Present及Enabled为0x100000即可,因为此漏洞利用方式需要利用进程注入技术,需要打开winlogon.exe,并且写入数据,而OpenProcess打开system进程时会去判断当前进程是否开启了SeDebugPrivilege权限,如果开启了此权限,即可打开system进程,至于为什么是0x100000,可以在windbg下输入!token命令:
kd> !token
Thread is not impersonating. Using process token...
_EPROCESS 0xffffe50489885080, _TOKEN 0x0000000000000000
.........
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes - Enabled Default
在cmd下输入命令 whoami /priv 也可以看到与上面相似的结果
其实这些权限每个占一位,而SeDebugPrivilege 占的是第二十位,1<<20二进制为100000000000000000000,十六进制为0x100000,之后OpenProcess打开高权限进程时即可通过验证,其实设置为-1即0xFFFFFFFFFFFFFFFF也可以,不过这样设置的话,在cmd下使用命令 whoami /priv会出现问题,可能是因为有很多标志不存在,但是还是设置为1了。
本地内核态调试:
可以使用SysinternalsSuite套件的工具之一livekd,不需要在电脑上配置参数,对于本地查看内核态结构体比较方便,启动一个管理员CMD,使用如下命令:
livekd.exe -k "D:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" 或者kd.exe,使用kd.exe启动的是控制台,使用不太方便,
在win10下需要打开测试模式再进行本地内核调试,打开方式如下:
使用windbg.exe比较方便,可以使用windbg下的内核命令查看结构体,应该是调试器与livekdd.sys进行交互,有一些局限:
无法修改内核数据,这个比较正常,而且获取到的信息不是实时的,例如我当前启动了三个cmd.exe,使用命令!process 0 0 cmd.exe只能查到三个,如果再启动一个cmd.exe,然后使用再次输入此命令:还是只能查到三个cmd.exe,除非重新启动livekd.exe程序。
windbg编辑内存
ed 地址 数据 编辑四字节数据
eb 地址 数据 编辑1字节数据
ea 地址 数据 编辑ASCII字符串数据 不以0结尾
eza 地址 数据 编辑ASCII字符串数据 以0结尾
eu 地址 数据 编辑unicode字符串数据 不以0结尾
ezu 地址 数据 编辑unicode字符串数据 以0结尾
查看进程的入口点代码
u $exentry
调试服务进程
在服务主进程中启动一个线程调用如下代码,
while (!IsDebuggerPresent())
Sleep(1);
DebugBreak();
这样挂到调试器后会自动断下来,这种方式进程会占用较多的cpu,可以修改sleep的时间。
远程调试服务进程,使用客户端服务器模式,如果已经设置Windbg为即时调试器,修改注册表\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug
子项Debugger的值为 "D:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe" -p %ld -e %ld -QY -c ".server tcp:port=2000"
服务启动后,如果出现无法处理的异常,会自动启动即时调试器,但是服务处于session 0,所以刚才启动的这个windbg界面不可见,在启动windbg时命令行参数中,让windbg作为服务端,使用Process Explorer查看
可以看到正在监听2000端口,在本机启动一个windbg,点击左上角File->Connect to Remote Debugger Session 打开对话框,输入tcp:port=2000,server=127.0.0.1,点击Ok按钮,后即可连接到作为服务端的windbg,进行调试。
在调用栈中 查看函数传入参数类型 kPL
查询函数传入参数类型 字节数 x /D ntdll!*
查询进程中的锁 使用!locks命令 !cs -l命令
对于双误错误来说,硬件会使用新的进程来处理错误,硬件会修改cr3为新进程的cr3,之后会执行蓝屏操作,此时操作系统还没有切换新的进程,对于处理这类问题,查看是从哪个任务切换过来的,使用命令.tss 段选择子 即可切换回双误的线程。
kvnL可以查看栈回溯中更详细的信息。
.srcpath D:\source 后面跟路径可以设置源文件的路径。
!pte address 命令可以查看某个页面是否 合法。
在每次页错误异常时,CPU会把访问的地址写到CR2寄存器中。
使用bp /p EPROCESS nt!ZwCreateFile 可以观察特定进程调用ZwCreateFile函数的行为。
32位下 调试时监视文件操作:
bp KERNELBASE!CreateFileW+0x5 "dU /c 50 poi(@ebp+8);gc" /c 后面数字 是一行多少个字符。
延迟加载符号命令
bm modulename!function
查看每个线程的ID和Last Error值
~*e ?@$tid;!gle
加载用于解析托管语义的SOS扩展命令模块 .reload sos mscorwks mscorwks是模块名,然后执行!clrstack 观察托管方法的调用经过。
使用SOS的!threads 观察线程的异常,使用!do(含义为dumpobject) +异常对象的地址 可以显示异常对象的详情。
也可以使用SOS的!printexception命令打印出当前线程的异常信息,可以更简便地得到与上面!do命令结果同样的错误描述信息。
分析转储文件时,通过.excr元命令可以访问异常数据块,读取异常数据流,回到异常现场。
.cxr 命令
使用ildasm工具可以 查看c#模块的代码
执行!pcr命令来观察处理器控制区,查看CPU当时的状态。在内核调试时 使用 dg @fs命令查看当前CPU的PCR,结构体为dt _KPCR
!drvobj 驱动名称 可以查看驱动的更多信息,找到设备对象后,可以使用!devstack 设备对象地址 查看设备栈信息。
在windbg中使用命令 ~*e .ttime 可以查看各个线程的创建时间,内核运行时间,用户态运行时间。
使用.ttime 可以查看当前线程创建时间、运行时间。
显示线程id和线程运行时间
~*e ?@$tid;.ttime 切换到某个线程 ~~[tid]s
windbg中使用循环命令寻找链表
recx=poi(ecx);r ecx;z(ecx!=0)
一次执行到下一条分支语句 windbg的tb命令
一次执行到下一个函数调用 windbg的tc命令
一下子返回到上一级函数 windbg的gu命令和gdb的finish命令。
访问指定的设备空间(I/O)时断下来,IO访问断点,windbg的ba i
使用windbg的 r 命令显示通用寄存器,
使用 rdmsr和wrmsr命令来访问MSR寄存器
使用 ib/iw/id命令来读取IO端口
使用 ob/ow/od命令来写IO端口
使用 !apic 命令来显示CPU内部的中断控制器寄存器
使用 !idt 命令来观察中断描述符表
使用 !pci 命令来观察PCI总线上的设备。
windbg中查看结构体如CONTEXT,dt _CONTEXT 加下划线,这是因为按照C语言的规范,编译器产生符号时会在源程序的名字前加一个下划线。
!idt -a 可以观察idt表
查看SSDT,之后再做记录,暂时不太熟练,还不会
KeServiceDescriptorTable
KeServiceDescriptorTableShadow
软件工程师至少要对寄存器(通用寄存器、MSR寄存器)、IO端口、中断、物理内存、PCI总线等概念有比较深刻的理解,否则就始终根基不牢。