windbg内存查看(d*)
d*命令
d{a|b|c|d|D|f|p|q|u|w|W} Address [/c ColumuWidth] [l Length]
Address:查看address地址处的内存。
ColumnWidth:Windbg每行显示的多少个数据单位。默认为16进制数字,十进制需加前缀0n
Length:总共显示Address
地址后的多少个数据单位。
如:
db /c 0n32 06beee78 l 0n128
表示 显示06beee78之后的128个byte,每行显示32个byte,
dw /c 0n32 06beee78 l 0n128
表示 显示06beee78之后的128个word,每行显示32个word。
db: 按照Byte(单字节)来显示。如果为ASCII字符就显示ASCII字符。会在第八个和第九个16进制间插入分隔符-
;右边显示每个16进制数据对应的ASCII字符,ASCII不支持的、或者不能显示的字符用.
点号代替。
如:
地址002afa48指向一个char s[51]
的字符串:
红框中的-表示第八个和第九个16进制数据之前的分隔符。
蓝框中的表示ASCII字符。
地址002afa84指向长整型变量 long a = 0x12345678;
为什么没有按照12345678的顺序显示,而是显示的78563412了? 这个涉及到了数据存储的大小端问题,不清楚的话可以先看1.2节。
dw:按照Word(双字节)来显示。
现在显示的是5678 1234
dd: 按照Double-Word(4个字节)来显示。
dq :按照8个字节来显示。
du: 按照Unicode字符格式来显示。
df :按照单浮点(float,4字节)来显示。
dD :按照双浮点(double,8字节)来显示。
dt:不仅可以显示内存的值,还能显示变量的值、类型、结构等信息。
dv:显示局部变量、全局变量等。
大小端
什么是大小端?
Big-Endian和Little-Endian的定义如下:
Little-Endian 数据的低位字节位存放在内存的低地址端,高位字节存放在内存的高地址端。
Big-Endian 数据的高位字节位存放在内存的低地址端,低位字节存放在内存的高地址端。
比如1.1节中提到的数字0x12345678,它总共占4个字节(1个字节8位,2个16进制占8位,所以1个字节最大表示0xFF)。这个数据在大小端模式下,在内存中的存储布局为:
大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
因为windows是小端模式,所以,db 002afa84
以字节为单位显示002afa84地址处的数据(0x12345678)会显示为:
更详细的介绍可以参考:http://blog.csdn.net/ce123_zhouwei/article/details/6971544
程序判断大小端
bool IsLittleEndian() {
int a = 0x1234;
char c = *(char *)&a;
if (c == 0x34) {
return true;
}
return false;
}
STL(std::string)显示
文件stl_show.cpp:
void test1(long l, char* pMsg, std::string str, std::vector<int> vInts) {
printf("%ld, %s, %s, %d", l, pMsg, str.c_str(), vInts.size()); // 第10行
}
int main()
{
long l = 0x12345678;
char szMsg[100] = { "I'm ok" };
std::string strInfo = "hello 123";
std::vector<int> vInts;
vInts.push_back(1);
vInts.push_back(2);
vInts.push_back(3);
test1(l, szMsg, strInfo, vInts);
return 0;
}
test1函数第3个参数为std::string,在第10行printf函数下断点bp stl_show.cpp:10
,g
执行到断点,kvn查看堆栈信息:
0:000> kbn
# ChildEBP RetAddr Args to Child
00 002af970 0018899e 12345678 002afb10 0033e1d8 stl_show!test1+0x1e [c:\users\jeffery\desktop\stl_show\stl_show\stl_show.cpp @ 10]
01 002afb94 00189b4e 00000001 0033db98 0033cbd0 stl_show!main+0x14e [c:\users\jeffery\desktop\stl_show\stl_show\stl_show.cpp @ 26]
02 002afba8 001899b0 9bea5bac 00000000 00000000 stl_show!invoke_main+0x1e [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 64]
03 002afc00 0018984d 002afc10 00189b68 002afc1c stl_show!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253]
04 002afc08 00189b68 002afc1c 76de336a 7efde000 stl_show!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 296]
05 002afc10 76de336a 7efde000 002afc5c 77659902 stl_show!mainCRTStartup+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
06 002afc1c 77659902 7efde000 776bd5e0 00000000 kernel32!BaseThreadInitThunk+0xe
07 002afc5c 776598d5 001810f5 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
08 002afc74 00000000 001810f5 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b
栈帧#0显示第三个参数值为0033e1d8,因为现在加载了pdb,我直接使用dt -b 0033e1d8
是可以显示std::string的结构和值的,但windbg却提示我:
Symbol not found at address 0033e1d8.
无论0033e1d8地址存储的是什么数据,但windbg识别不了这是std::string结构,我猜想这个值可能是string结构中某个成员的值,下面我来证明我的猜想。
因为函数test1前2个参数各占4个字节,所以第三个参数地址应该是EBP+0x8+0x4+0x4=EBP+0x10=002af970+0x10=002af980
,
查看该地址的内容 dd 002af980
刚好和栈帧#0显示第三个参数值为0033e1d8一样。
所以windbg针对std::string类型的参数提示多做了一步,这一步刚好也是多余的。
现在需要分析std::string结构,在pdb文件的情况直接使用dt -b str
来查看str变量的结构,如图:
可以看到,字符串缓冲区的地址等于参数地址+0x004偏移即0x002af980+0x004,使用db 0x002af980+0x004
查看字符串内容:
综上所述,对于std::string变量,输出其存储的字符串的方法为:
db 变量地址+0x4
,这种方法无论是否有pdb符号文件都适用。
在有pdb的情况,使用 !stl 变量名
无疑是最快捷的方式。