目录
大端顺序和小端顺序:
低字节和高字节:(左高右低)
在编程语言中,字符一般是占16位,8位为一字节,所以有高位字节和低位字节。
一个16进制数由1个字节组成,例如:A9。
高字节就是指 16 进制数的前 4 位(权重高的 4 位),如上例中的 A。
低字节就是指 16 进制数的后 4 位(权重低的 4 位),如上例中的 9。
以两个个16进制数为例:
0x12,其中1是高字节,2是低字节;0x1234,其中 1 是高高字节,2 是高字节,3 是低字节,4 是低低字节;
总结:
就是在 16 进制数中,越靠近左边的字节越高,越靠近右边的字节越低,位数从低位开始算,就是从右往左算。
小端顺序:
x86 处理器在内存中按小端(little-endian)顺序(低到高)存放和检索数据。最低有效字节存放在分配给该数据的第一个内存地址中,剩余字节存放在随后的连续内存位置中。考虑一个双字 12345678h。如果将其存放在偏移量为 0000 的位置,则 78h 存放在第一个字节,56h 存放在第二个字节,余下的字节存放地址偏移量为 0002 和 0003,如下图所示。
大端顺序:
其他有些计算机系统采用的是大端顺序(高到低)。 下图展示了 12345678h 从偏移量 0000 开始的大端顺序存放。
小端变量以最小拆分单位逆序摆放:
举个例子:
前面小端顺序 v1=0x12345678h 在内存的字节码中看起来是这样 78 56 34 12,这没有问题。如果这时有 v2=0x1234h,在内存的字节码中逆序反向后是 34 12。那么 v2 后面紧接着 v1 的话字节码应该就是 34 12 78 56 34 12 。再放一个 v3=0x12 的话,总的排序就是 12 34 12 78 56 34 12。
那么这说明什么呢?
一个变量,有大小,8 位、16 位、32 位、64 位,在变量内是按照大小类型为界先划分出一个区域,1字节、2字节、3字节、4字节这样。然后由于字节码里最小的大小类型为 1 字节,所以就按1字节来拆分来逆序摆放。特别的 8 位本身就是最小单位,所以其内部不会逆序,这也就是为什么 0x12 在内存中不会显示成 21。
但是在外面,变量与变量之间,是顺序排列的,互不干扰内部的逆序。
IDA 中热键 D 可改变大小类型:(程序输出时也是调整到对应大小才输出的,所以输出的都是正向)
在 IDA 中我们有 db、dw、dd 等 1 字节、2 字节、 4 字节这种大小类型。上面我们说了小端变量以最小拆分单位逆序摆放,关键是都是用字节码中 1 字节大小为单位拆分的。
v1=0x12345678h 中,我们改类型为 dw,就会显示 56 78 12 34,改类型为 dd,就会显示 12 34 56 78(hex 字节码视图中单位不会变,所以对该视图无效。)
用实验验证一下猜想:
那么我们扩展到 CTF 逆向中:
拿攻防世界逆向入门题之 logmein 为例,在逆向中遇到如 char v8[28]; strcpy(v8, ":\"AL_RT^L*.?+6/46"); 和如 __int64 v7; v7 = 0x65626D61726168LL; //'ebmarah'
现在结合上面的知识来分析:
先看 v8,首先 v8 是数组这个不用管,v8 大小类型是 8 位,是最小单位,所以字符串 ":\"AL_RT^L*.?+6/46" 会正向存放在 v8 中,用 v8 +i 或 v8[i] 都是正向取出。
再看 v7,大小类型是 64 位,也就是说在内存中它要按 1 字节拆分成 8 份,hex 码来看的话就是 68 61 72 61 6D 62 65 00。字符串来看就是 'h' 'a' 'r' 'a' 'm' 'b' 'e' '0'
实验验证:
最后附上我常规测试代码:
#include<string.h>
#include<stdio.h>
#include <stdint.h>
int main()
{
uint32_t v1;
strcpy((char *)&v1,"ABCD");
for(int a=3;a>=0;a--)
putchar((v1 >> (a * 8)) & 0xFF);
printf("\n0x%x",v1);
printf("\n%s",&v1);
printf("\n---------------------------------------------------\n");
uint32_t v11=0x41424344;
for(int a=3;a>=0;a--)
putchar((v11 >> (a * 8)) & 0xFF);
printf("\n0x%x",v11);
printf("\n%s",&v11);
printf("\n---------------------------------------------------\n");
uint32_t v10[17];
strcpy((char *)v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
for(int a=0;a<17;a++)
{
printf("0x%x\t",v10[a]);
for(int b=3;b>=0;b--)
printf("%c",(v10[a] >> (b * 8)) & 0xFF);
printf("\n");
}
printf("\n%s",v10);
printf("\n---------------------------------------------------\n");
uint16_t v2;
strcpy((char *)&v2,"AB");
for(int a=1;a>=0;a--)
putchar((v2 >> (a * 8)) & 0xFF);
printf("\n0x%x",v2);
printf("\n%s",&v2);
printf("\n---------------------------------------------------\n");
uint16_t v22=0x4142;
for(int a=1;a>=0;a--)
putchar((v22 >> (a * 8)) & 0xFF);
printf("\n0x%x",v2);
printf("\n%s",&v22);
printf("\n---------------------------------------------------\n");
uint16_t v20[34];
strcpy((char *)v20, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
for(int a=0;a<34;a++)
{
printf("0x%x\t",v20[a]);
for(int b=1;b>=0;b--)
printf("%c",(v20[a] >> (b * 8)) & 0xFF);
printf("\n");
}
printf("\n%s",v20);
printf("\n---------------------------------------------------\n");
uint8_t v3;
strcpy((char *)&v3,"A");
putchar(v3);
printf("\n0x%x",v3);
printf("\n%s",&v3);
printf("\n---------------------------------------------------\n");
uint8_t v33=0x41;
putchar(v33);
printf("\n0x%x",v33);
printf("\n%s",&v33);
printf("\n---------------------------------------------------\n");
uint8_t v30[68];
strcpy((char *)v30, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
for(int a=0;a<68;a++)
{
printf("%x\t",v30[a]);
putchar(v30[a]);
printf("\n");
}
printf("\n%s",v30);
printf("\n---------------------------------------------------\n");
return 0;
}