本来这篇文章是没有前言的,只不过是文章已经快结尾了,突然断电,发现CSDN上没有自动保存的功能,一大篇字都白码了,所以在第二次起笔的时候吐槽下。之前明明是自动保存,FCK
其实,让人犯晕的主要是
dd出来的数据地址标识的是这一行数据的最小地址,而不是数据第一个字节的地址。
基于对小端字节序的真实使用情况的疑问,LZ写了程序定义了一个4字节整数变量a=0x12345678,
想看下每个字节的保存地址。
#include
#include "stdafx.h"
int main()
{
unsigned int a = 0x12345678;
unsigned int addr = (unsigned int)&a;
printf("hex: 0x%x in addr: 0x%x\r\n", a, &a);
for (int i = 0; i < 4; ++i)
{
printf("hex: 0x%x in addr: 0x%x\r\n", *(char *)(addr +i), (addr+i));
}
return 0;
}
64bit windows 7 - visual studio x86编译器
主机序使用小端字节序,所以内存中的数据都是低地址存低位,在低地址看到0x78是符合预期的。
还原windbg调试的真实场景,因为我们会随意观察地址数据,寻找有意义的key。但是在这之前具体哪个地址才是数据正确的起始地址,数据结构,数据类型是怎样的完全不知道。当然,在运行时,寄存器会告诉你。
这里,我们用地址偏移一个字节的前后状态作为对比来观察内存。
用dd查看二进制数据
>dd &a
0x0048FE40 12345678 cccccccc c2c474a4 0048fe60
0x0048FE50 00171fae 00000001 002868f8 00286ec8
0x0048FE60 0048feb8 00171e10 c2c47450 00000000
0x0048FE70 00000000 7efde000 00000000 ffffffff
地址后移1个字节,用dd查看二进制数据
>dd 0x0048FE41
0x0048FE41 cc123456 a4cccccc 60c2c474 ae0048fe
0x0048FE51 0100171f f8000000 c8002868 b800286e
0x0048FE61 100048fe 5000171e 00c2c474 00000000
0x0048FE71 00000000 007efde0 ff000000 ffffffff
神奇的事情出现了,地址只是偏移了一个字节,显示的数据就变成了cc[123456 a4]cc,鲁莽的LZ有时就会认为这个数据是0x123456a4。
用db查看二进制数据
>db &a
0x0048FE40 78 56 34 12 cc cc cc cc a4 74 c4 c2 60 fe 48 00
地址后移1个字节,用db查看二进制数据
>db 0x0048FE41
0x0048FE41 56 34 12 cc cc cc cc a4 74 c4 c2 60 fe 48 00 ae
可以看到用db观察数据,地址偏移不会影响显示的序列,0x12后面还是0xcc, 不像dd,改变编译后,56后面不再是0x78,也不是0xcc(0x78后面的字节,以为少了0x78就会轮到0xcc)。
重新梳理
地址变化: 0x0048FE40-> 0x0048FE41
数据变化:低地址少了一个字节
dd变化:低地址变为0x0048FE41,保存的是0x56, 高地址变为0x0048FE44,保存的是0xcc, 下一个4字节同理,高地址保存的0x4a拿到高位
因为dd是按4字节分割的,所以地址右移一个字节,对导致很大的变化。当时这种移位是无意义的,因为变量a保存的是0x12345678,不是0xcc123456,在观察时需要找到正确的数据起始位置,数据类型,数据大小去提取正确的数据,才能不会被地址移位导致的数据显示出现大幅度波动的问题而烦心。这才是正确的使用dd的姿势。
上面代码的可执行文件中0x12345678的保存顺序是大端还是小端?
二进制文件的存储由编译器决定,编译器与执行器统一按照主机序保存并读取执行。