a[0] = addr & 0xff 是什么意思?

what does a[0] = addr & 0xff?

我目前正在学习"the shellcoder's handbook"这本书,我对c的理解很深,但最近我遇到了一段我无法掌握的代码。

这是一段代码:

          char a[4];
          unsigned int addr = 0x0806d3b0;
          a[0] = addr & 0xff;
          a[1] = (addr & 0xff00) >> 8; 
          a[2] = (addr & 0xff0000) >> 16;
          a[3] = (addr) >> 24;

所以问题是这是做什么的,什么是 addr & 0xff(以及它下面的三行)以及是什么使 >> 8 成为它(我知道它将 8 除以 2)? Ps:如果您对我应该使用的标签有任何想法,请随时告诉我。

变量addr是32位数据,而数组a中的每个元素都是8位。代码所做的是将 addr 的 32 位复制到数组 a 中,一次一个字节。

让我们走这条线:

a[1] = (addr & 0xff00) >> 8; 

然后一步一步来

  1. addr & 0xff00 这样就得到了addr中的值的第8位到第15位,运算后的结果是0x0000d300.
  2. >> 8 这会将位向右移动,因此 0x0000d300 变为 0x000000d3.
  3. 将掩码的结果值赋值并转移到a[1]

显然,代码将各个字节与 addr 隔离开来,将它们存储在数组 a 中,以便对它们进行索引。第一行

a[0] = addr & 0xff;

通过使用0xff作为位掩码来屏蔽掉最低值的字节;后续行做同样的事情,但另外将结果移到最右边的位置。最后,最后一行

a[3] = (addr) >> 24;

不再需要屏蔽,因为所有不必要的信息都被转换丢弃了。

unsigned char a[4]; /* I think using unsigned char is better in this case */
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff; /* get the least significant byte 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* get the second least significant byte 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* get the second most significant byte 0x06 */
a[3] = (addr) >> 24; /* get the most significant byte 0x08 */

该代码有效地将 32 位地址存储在 4 个字符长的数组中。您可能知道,一个 char 有一个字节(8 位)。它首先复制地址的第一个字节,然后移位,复制第二个字节,然后移位,等等。你明白了要点。

它强制执行 endianness,并将整数以小端格式存储在 a 中。

参见维基百科上的 illustration

代码试图对数据输入强制执行字节顺序。具体来说,它试图对数据强制执行小端行为。解释如下:

a[0] = addr & 0xff; /* gets the LSB 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* gets the 2nd LSB 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* gets 2nd MSB 0x06 */
a[3] = (addr) >> 24; /* gets the MSB 0x08 */

所以基本上,代码屏蔽并分离出数据的每个字节,并以小端格式将其存储在数组 "a" 中。

另外,为什么不可视化位移结果..

    char a[4];
    unsigned int addr = 0x0806d3b0;
    a[0] = addr & 0xff;
    a[1] = (addr & 0xff00) >> 8; 
    a[2] = (addr & 0xff0000) >> 16;
    a[3] = (addr) >> 24;

    int i = 0;
    for( ; i < 4; i++ )
    {
        printf( "a[%d] = %02x\t", i, (unsigned char)a[i] );
    }
    printf("\n" );

输出:

a[0] = b0   a[1] = d3   a[2] = 06   a[3] = 08

我除了给出的多个答案外,代码还有一些缺陷需要修复才能使代码可移植。特别是 char 类型用于存储值是非常危险的,因为它的实现定义了符号。非常经典的 C 错误。如果代码是从一本书上摘下来的,那么你应该带着怀疑的态度去读那本书。

在我们这样做的同时,我们还可以整理代码,使其过于显式以避免未来潜在的维护错误,删除整数文字的一些隐式类型提升等。

#include <stdint.h>

uint8_t a[4];
uint32_t addr = 0x0806d3b0UL;
a[0] = addr & 0xFFu;
a[1] = (addr >>  8) & 0xFFu;
a[2] = (addr >> 16) & 0xFFu;
a[3] = (addr >> 24) & 0xFFu;

掩码 & 0xFFu 严格来说不是必需的,但它们可能会使您免受一些关于错误整数类型的误报编译器警告。或者,每个班次结果都可以转换为 uint8_t,这样也可以。