显示指定地址的 16 个字节的内容(十六进制)

Display contents (in hex) of 16 bytes at the specified address

我正在尝试显示特定地址的内容,为该地址指定一个 char*。到目前为止,我曾尝试使用以下实现来做到这一点

int mem_display(char *arguments) {
    int address = *arguments; 
    int* contents_pointer = (int*)address;
    int contents = *contents_pointer;
    printf("Address %p: contents %16x\n", contents_pointer, contents);              
}

但我一直收到 "Segmentation Fault (Core Dumped)" 错误。我试图制作一个虚拟指针来测试

char foo = 6;  
char *bar = &foo;

但错误仍然存​​在

我发现很难解释问题出在哪里,因为您的代码中几乎每一行都是错误的。

我会这样做:

void mem_display(const void *address) {
    const unsigned char *p = address;
    for (size_t i = 0; i < 16; i++) {
        printf("%02hhx", p[i]);
    }
    putchar('\n');
}

需要遍历地址的内容,逐一打印,直到16。示例代码:

#include <stdio.h>

void mem_display(unsigned char *arguments) {
    printf("Address %p: ", arguments);
    int i =0;
    unsigned char* byte_array = arguments;
    while (i < 16)
    {
        printf("%02hhx", byte_array[i]);
        i++;
    }            
}

int main(void) {
    unsigned char foo = 6;  
    unsigned char *bar = &foo;
    mem_display(bar);

    return 0;
}

输出:

Address 0x7ffe5b86a777: 0677A7865BFE7F000060054000000000

如果您已经有一个指向要打印其内容的地址的指针,您可以将其直接提供给 printf,如下所示:

void print_16_bytes_at(const char *arguments)
{
   printf("Address %p: contents", (const void *)arguments);
   for (int i = 0; i < 16; i++)
       printf(" %02x", (unsigned int)(unsigned char)arguments[i]);
   putchar('\n');
}

如果 arguments 不是指向要打印其内容的内存的指针,那么我不明白它实际上是什么 而我需要你最好解释一下。

以上示例代码注意事项:

  1. 要在不引发未定义行为的情况下使用 %p,您必须明确地将指针转换为 [const] void *,除非它已经是 [const] void *。因为 printf 采用可变数量的参数,编译器不知道其参数的预期类型,所以它不会为您插入此转换,就像采用 [=15= 的普通函数一样] 争论。
  2. 双重转换 (unsigned int)(unsigned char) 强制从 arguments[i] 读取的 char 被零扩展而不是符号扩展到 unsigned int 的宽度。如果您不这样做,从 0x80 开始的值可能会打印出一堆前导 F(例如 ffffff80),这可能不是您想要的。
  3. 如果在提供的地址处没有 16 字节的可读内存,这仍然会出现段错误。在你的例子中

    char foo = 6;
    print_16_bytes_at(&foo);
    

    foo的地址只能保证有一个字节的可读内存。 (它可能可以在任何您可以轻松获得的计算机上运行,​​但根据 C 标准的字母,它仍然允许崩溃。)

原始代码存在一些问题。首先,它两次间接传递传入的指针。它将 arguments 视为指向要打印的内容的指针。因为 arguments 是指向 char 的指针,所以这肯定是错误的。 (它最终会从一个非常小的地址读取,这在大多数架构上肯定是分段违规或其他崩溃。)

其次,除非您知道 arguments 指针已正确对齐,否则通过该指针加载 int 可能会由于未对齐的访问而崩溃。 (这很可能会显示为分段违规。)您可能无法假设此例程的正确对齐。

第三,如果您需要打印 16 个字节,那么 int 将(通常)只打印 4 或 8 个字节。使用标准打印例程连接所有片段比逐字节循环更棘手。 (有关此类循环的示例,请参见上面的答案。)

我认为您要么过于复杂,要么没有充分描述实际输入是什么。是指向指针的指针吗?

无论如何,要用一些指向内存的指针以简单的方式完成它,例如你可以这样做(C-like C++,抱歉,在网上匆忙在线完成cpp.sh):

#include <iostream>

const unsigned char fakeData[] = { 1, 13, 0, 255 };

void mem_display(
    std::FILE* file,
    const unsigned char* memoryPtr,
    const size_t size)
{
    fprintf(file, "Address %p:", (const void*)memoryPtr);
    for (size_t i = 0; i < size; ++i) fprintf(file, " %02hhx", memoryPtr[i]);
    fprintf(file, "\n");
}


int main()
{
    mem_display(stdout, fakeData, 4);
}

输出:

Address 0x4008e6: 01 0d 00 ff

要打印 16 个字节,只需更改大小参数。

我想不出有 16 个字节的常见类型,所以我不确定你为什么要将它打印为单个数字,通常像上面那样的单字节输出用于调试器(直到用户请求不同尺寸的单位)。

很长一段时间以来,我使用以下函数来打印内存区域的内容:

/*************************************************************/
/**
  * Function to dump memory area
  * \param address     start address
  * \param len         length of memory area
  */
/*************************************************************/
void dump( void* address, int len ) noexcept{
    int i;
  printf("dump from 0x%lX (%d bytes)\n",(long)address, len);
  printf("=============================================");
  for(i=0; i<len; i++){
    if(i%16==0){printf("\n");}
     printf("%2X ",(*((char*)address+i))&0xFF);
  }
  printf("\n=============================================\n");
}

对于 C 程序删除 noexcept 关键字。