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:10g执行到断点,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 变量名 无疑是最快捷的方式。

posted @ 2017-08-23 14:58  jiangxueqiao  阅读(...)  评论(...编辑  收藏