64位寻址到16EB (1EB=1000TB)x64控制寻址范围16TB
ps: 退出指令 q
就好了。
vector: https://stackoverflow.com/questions/48938003/windbg-c-how-to-print-vector-contents
https://0cch.com/2014/08/16/dump-stle79a84vectorefbc8clistefbc8cmape79a84e4b889e4b8aawindbge8849ae69cac/
如果只想程序运行时的内存dump,直接从资源管理器;进程tab页上,选中程序右键,就有个产生dump选项。
debug工具一般都有debugger.chm这个文档可以参考
x64:
- 拖拽到windbg,lm查看模块状态
- 查看时间戳(lmt, 我自己喜欢
lmt sm
),找到对应pdb,设置pdb路径 - 重新加载pdb(.reload),运行分析命令(!analyze -v)
一般崩溃会有exception,就查找这个字符串,就可以找到对应执行函数。(~N s切换到N号线程,主线程是0号,后面要加个s
表示线程)
主线程没有就切换到其它线程查找(~*e kb
, 建议使用~* kb
这样会列出线程号和更多信息)
需要确认指令(需要实践):
托管代码,由托管器管理内存等,c++ clr工程就是。c#就是托管代码
找到hang住线程:
x32:
基本相同,但是当32位程序运行在64位系统的时候,产生的dump
- 先加载64位的扩展(.load wow64exts)
- 切换到32位模式(!sw)
- 然后才再像上面那样调试
源码条件语句
int类型的断点
|
|
官方只给了bp
的例子,感觉确实只有它能条件断
- 必须用
poi
指定变量(这里我有个变量as
,用这个指令才能指定它) - .echo 输出
gc
即Go from Conditional Breakpoint
,从断点处继续执行第二个
echo
会自动输出变量名字符类型的断点
123as /mu ${/v:mystr} @@( MyNest->Nestin.Buffer );.block { r $t0 = $spat( "${mystr}", "*11" ); }.if( @$t0 != 1 ) { gc } ;
- 使用:
bp mytest!mytest::ttfun "$$<d:\\commands.txt"
- 我试了很多遍,函数内部真心没找到指令,大多网上的和官网的都是指定函数参数,也就是说我要专门把监视字符放入一个函数的参数里面才行,不然完全没法断下来。
MyNest->Nestin.Buffer
是自己指定字符串,必须是字符指针,类就自己获取或指定到指针里面*11
匹配内容- 上面的是文件格式,不用文件记得把
"
变\"
地址
kb后产生的地址
依次是:ebp地址 返回地址 参数地址
参数地址默认是三个,但是无对应参数时显示的参数地址是随机的。只能根据函数参数来看地址
指令tips
bp 在.unload
会消失 bu则不会,bu当模块变化时会自动偏移,bp不会
bl 罗列断点
bc 清除断点(跟bl上面的编号)
p (f10)
t (f11)
lm sm //按文件名排序
lm vm {module name}//查看模块详细信息,ocx加载老是没有完整模块名,只有通过这个来查看
lmv 罗列模块详细信息
lmf 罗列模块image path(终于找到了,这个指令,上次那个加载错误路径的模块,下次就可以用这个指令来解决了)
!sym noisy 符合老是加载不上时,用这个开启模式;这样加载符号过程就会罗列详细信息
有一个数组我知道地址
dq 0x2721aae0 把数组展现出来
dt 2721aaf0 et_commonctrl::tag_ETControlData来查看第二个数组信息
dt this m_arrScenarioSheets 查看成员变量m_arrScenarioSheets (如果要详细信息加-r)
dt -v std::vector
符号tips
今天又测试了下,指定符号、源、exe后,lm居然没找到对应符号。。。
只好强制加载了。
后来发现居然要多个后缀才行:.reload /i simpledoc1.exe
这样才行。。。
lm会全部显示,太多了,如果只想显示能加载符号的模块的话用:lml
就可以了
栈破坏第一个例子
今天认真看了调试的第一个例子,做下记录,调试的是一个内存破坏的例子:
1、先用k
命令来显示异常前的函数执行堆栈(后面跟个数字就表示显示几行, k 3:显示最后三行)
我喜欢用
kpn
。因为p
会列举出函数的调用参数;n
会对他们排号,这样方便我用frame
来切换上下文作用域;(另外说下b
会列举参数地址,反正我也不咋习惯看,就不用)
2、找到我方代码函数后,用.frame <number>
的形式切换到函数的上下文里面。(这个 number 就是我上面说的n
所列出的排号)
为何要切换呢?因为我要找变量呀!! 默认的
X
找的是全局变量,我要找局部变量就只好切进函数里去。
X Mytest!g* //在Mytest模块中找 g 开头的变量
切进去,我直接 X g_ 就可以找到
3、 X指令或dv,找出变量。
我喜欢
X
,虽然都差不多。找的的变量会自动给出此变量的地址
4、dt转换
拿到变量地址后,就
dt CAppInfo 0x0047d768
它会把此地址转换成设置的类型,并变量的成员内存一一的列举出来。
简单来说这就是个c式的强制转换,只是为了方便研究成员变量的有效性
5、dd查看成员变量内存
如上面所说,成员变量被全部列出来了,就可以一个一个查看成员变量的内存
dd 0x72726f43
。这样就判断有效和无效!
断点
bp ConsoleApplication1!main+4c 函数第4行
bp ConsoleApplication1.cpp:4
t 运行单步
k命令
kb 显示的参数 从左到右分别为 ebp, ret ,arg1, arg2
内存溢出
r eip 查看下一指令地址
u eip 查看下一指令的汇编代码
约定不一致
|
|
自动错误
以前把程序放入windbg,指定pdb,然后运行。如果有空指针调用它会自动跳转到代码。
后来有几次测试,又不成功了。
今天我无意中发现,.frame <number>
到错误的函数后,它又能自动加载跳转到代码了。
堆错误-使用未初始化状态
此类报错,会在加载后,给出问题代码的,如果有pdb则会直接指出源代码。先看windbg显示的:
ConsoleApplication1!wmain+0x87:
00265107 c7000a000000 mov dword ptr [eax],0Ah ds:002b:baadf00d=????????
eax 的值是baadfood 填充模式时分配成功但没初始化。释放填充是feeefeee
堆错误-堆句柄不匹配
|
|
去找结构体,自己声明,还要自己定义(因为它定义了才会有符号,才能找到自己声明的那个结构体)
死锁情况
~*e kb
显示真进线程信息很有用 (~* s
切线程 切进程要加s
)
先看它们第一行信息,Unfrozen表示它们都在运行。
从堆栈看,俩个线程都在NtWaitForSingleObject
结束。找到执行函数RtlEnterCriticalSection
的参数
在分别!cs 00d9a138
!cs 00d9a150
(这个命令由于涉及到了内核对象,必须去服务器那边下符号)
发现这俩个临界区都锁住了,LOCKED LockCount = 0x1 WaiterWoken = No
;
而它们的OwningThread
则正好分别是彼此,说明是互相锁住了。
锁中产生异常
临界区的OwningThread
会被分配给一个找不到ID的地址(Windows异常模块接管),会导致接下来不会释放临界区。
且问题栈会有:DbgBreakPoint DbgUiRemoteBreakin
微软建议,封装个类,在析构函数进行解锁。(所以建议使用CCriticalSection而不是windows对象)
线程结束
当主线程结束工作者线程是,工作者线程正处于分配内存或释放内存的过程中。
在这些操作中,堆管理器通常会获得一个临界区。
当工作者线程被强行结束时,堆管理器将永远无法离开临界区。
意思就是说你terminal时正在开辟堆时(new),可能会导致堆管理器死锁。
DllMain的死锁问题
|
|
进入dllmain后创建线程。
当windows创建线程时,线程并不会自动从
kernel32!BaseThreadStart
开始执行;
而是首先由APC分发器分发一个APC到新线程,然后作为apc进行初始化完成后才会执行线程。
然而APC的分发有一个加载器锁来控制的,以此保护分发访问的冲突问题。
然而对于DllMain来说,它本身就是动态库的加载和创建也会出现新线程进程,它本身也会用到APC管理器
ps:apc分发和回收用的是同一个锁dllmain等待线程
dllmain等待子线程的创建和设置事件。
然而子线程等待dllmain的APC管理器锁解锁。
如此就造成了死锁的情况。
apc的死锁导致调试线程的注入发生中断,超过30秒后;调试线程会自动挂起进程中所有线程。
临界区的判断
|
|
dt CRITICAL_SECTION
和!cs
都行。
当处理一个尚未被初始化的临界区,看的值都是随机的
当被删除的临界区,看到的值是零
过度释放的临界区,会导致程序挂起,LockCount < -1 || RscursionCount < 0
64位
64位运行32位时,加载WOW(windows on windows)子系统模块
Mini dump代码直接实例初始化处调用RunCrashHandler
sobey的调试
以下是详细过程:
前面的都好说,dq L2 ,dq是四字方式,L2是2个四字,所以就是8字。刚好是一个指针的大小
0x48是函数内部的一个栈变量,至于为啥我也不知道,反正这是死命令,记住就ok了。