关于c#:C ++相当于BitConverter

C++ equivalent of BitConverter

我正试图读取一个文件的PE头来获取一些信息。对于.NETC#,我使用BitConverter将读取文件后获得的字节数组转换为等效整数。我希望对C++也这样做,但不确定最好的方法。我用一个unsignedchararray作为Byte array的等价物。

代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
uint16_t GetAppCompiledMachineType(string fileName)
{
    const int ptr_offset = 4096;            
    const int mac_offset = 4;
     char *data = new char[4096];
    fstream f;
    f.open(fileName, ios::in | ios::binary  );
    f.read(data, 4096);


    int32_t pe_addr= *reinterpret_cast<int32_t*>(data, ptr_offset);
    uint16_t machineUint = *reinterpret_cast<std::uint16_t*>(data, pe_addr + mac_offset);
    return machineUint;

 }
int _tmain(int argc, _TCHAR* argv[])
{

      string fileName ="<some_path>\\depends.exe";
      uint16_t tempInt = GetAppCompiledMachineType(fileName);
      cout<<tempInt;
      std::getchar();

    return 0;
}

我将使用O/P查询PE头以获取信息。这里需要相当于Bitconverter的东西。希望它能奏效。

更新:谢谢回复。正如建议的那样,我正在尝试使用强制转换,将character array转换为Int,以读取PE Header,但它给了我一个访问冲突未处理的异常。这是完整的代码,文件有效并且正在被读取。我尝试了调试,优化被禁用,但没有用。

请告知。

谢谢。


您有一个字节数组指针(char* data),然后只需将指针移动到所需的偏移量data + PE_POINTER_OFFSET,强制转换为指向整数(int*)(data + PE_POINTER_OFFSET)的指针,并将指针延迟到获取值:

1
2
int32_t head_addr = *reinterpret_cast<int32_t*>(data + PE_POINTER_OFFSET);
uint16_t machineUint = *reinterpret_cast<uint16_t*>(data + head_addr + macoffset);

编辑1:您正在尝试读取PE,因此我可以安全地假设您的环境是Windows。X86和X64都支持未对齐的内存访问(当然,您将为此付出性能方面的代价,但可能不会注意到任何内容,并且您将保存memcpy

Itanimum(如果您必须支持它)和(非常老的)arm可能是一个问题:第一个问题只使用__unaligned作为char数组,第二个问题(如果您不让编译器为您做这项工作)可以使用__packed

还要注意,这个假设(加上endianness)是有效的,因为您在Windows环境中处理PE文件,如果您必须编写可移植代码或读取其他代码,那么这不是正确的方法(简而言之,您必须处理单个字节并使用固定顺序复制它们)。

编辑2:根据您使用的更新代码,问题在于*reinterpret_cast(data, ptr_offset)中,请注意,您不使用带偏移量的指针求和,偏移量也无效(如果我没有错的话,应该是60)。你要做的是从地址4096的绝对位置读取,这将导致访问冲突。在代码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint16_t GetAppCompiledMachineType(string fileName)
{
    const int32_t PE_POINTER_OFFSET = 60;            
    const int32_t MACHINE_OFFSET = 4;

    char data[4096];

    fstream f;
    f.open(fileName, ios::in | ios::binary);
    f.read(data, sizeof(data));

    int32_t pe_header_offset = *reinterpret_cast<int32_t*>(
        data + PE_POINTER_OFFSET);

    // assert(pe_header_offset + MACHINE_OFFSET < sizeof(data));

    return *reinterpret_cast<std::uint16_t*>(
        data + pe_header_offset + MACHINE_OFFSET);
}

本规范仍远未达到生产质量要求,但注意以下几点变化:

  • 缓冲区data不是动态分配的,因此不需要释放内存(如果没有释放分配的内存,则当进程退出时,Windows将为您释放内存,但如果您多次调用该函数,则会消耗内存)。
  • 使用静态分配的数组,可以使用sizeof()来确定缓冲区大小(作为read()的输入)。
  • PE_POINTER_OFFSET现在具有正确的值(60而不是4096)。
  • data的偏移量现在计算正确(作为dataPE_POINTER_OFFSET的总和)。

所有这些都表明我们仍在使用缓冲方法,但在这里它是相当无用的,因为fstream将为我们管理它。让我们简化我们的代码(附带的副作用也使其更健壮,我们不假设PE头适合我们的4K缓冲区)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uint16_t GetAppCompiledMachineType(string fileName)
{
    const int32_t PE_POINTER_OFFSET = 60;            
    const int32_t MACHINE_OFFSET = 4;

    fstream f(fileName, ios::in | ios::binary);

    int32_t pe_header_offset:
    f.seekg(PE_POINTER_OFFSET); f >> pe_header_offset;

    uint16_t machineType;
    f.seekg(pe_header_offset + MACHINE_OFFSET); f >> machineType;

    return machineType;
}

现在,它不需要强制转换和转换(但仍然假设PE和机器端隙匹配)。


最好的方法是声明PE文件格式的结构。例子:

1
2
3
4
5
6
7
struct dos_header {
     char signature[2] ="MZ";
     boost::int16_t lastsize;
     ..
     boost::int16_t reserved2[10];
     boost::int32_t e_lfanew;
}

笔记:

  • 最好使用预期大小的跨平台整数(int32,而不是long)。
  • 小心结构对准(如果对准有问题,使用#pragma pack(8))。
  • 此结构在windows.h中声明,但对于跨平台开发,我建议您单独声明并可移植。
  • 在64位体系结构中,有些结构发生了变化。

当您拥有映射的结构时,可以将缓冲区转换为指向该结构的指针并访问成员。

Sample:

1
2
3
4
5
6
if (offset + sizeof(dos_header) > size_data) {
    // handle the error
    // exit
}
const dos_header* dh = static_cast<const dos_header*>(data + offset);
std::cout << dh->e_lfanew << std::endl;


重构以消除某些架构上的数据对齐问题:

1
2
3
4
5
6
7
template<class T>
T from_buffer(uint8_t* buffer, size_t offset)
{
  T t_buf = 0;
  memcpy(&t_buf, buffer + offset, sizeof(T));
  return t_buf;
}

1
2
int32_t head_addr = from_buffer<in32_t>(buffer, PE_POINTER_OFFSET);
uint16_t machineUint = from_buffer<uint16_t>(buffer, size_t(head_addr + macoffset));