C ++相当于BitConverter

时间:2014-10-08 10:57:14

标签: c# c++ arrays c++11 bitconverter

我试图读取文件的PE头以获取一些信息。对于.NETC#,我使用BitConverter将读取文件后获得的字节数组转换为等效的整数。我希望对C++做同样的事情,但我不确定最好的做法。我使用unsigned char array作为等效的Byte array

代码如下:

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,但它会给我访问权限违规未处理的例外。这是完整的代码,文件有效并正在读取。我尝试使用调试,并禁用优化,但无济于事。

请告知。

非常感谢。

3 个答案:

答案 0 :(得分:3)

你有一个字节数组指针(char* data),然后简单地将指针移动到你需要的偏移量data + PE_POINTER_OFFSET,转换为指向整数(int*)(data + PE_POINTER_OFFSET)的指针并将指针推迟到值:

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

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

编辑2 :根据更新的代码,您使用的问题是*reinterpret_cast<int32_t*>(data, ptr_offset),请注意,您不要将指针与偏移量相加,并且偏移量也是无效(应该是60 - 如果我没有错)。你在那里做的是从绝对位置读取地址4096并且它会导致访问冲突。在代码中:

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缓冲区。)

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和机器字节序匹配)。

答案 1 :(得分:2)

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

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;
}

...

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));

答案 2 :(得分:2)

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

struct dos_header {
     char signature[2] = "MZ";
     boost::int16_t lastsize;
     ..
     boost::int16_t reserved2[10];
     boost::int32_t e_lfanew;
}

注意:

  • 最好使用预期大小的跨平台整数(int32_t而不是long)。
  • 注意结构对齐(如果对齐有问题,请使用#pragma pack(8)。)
  • 此结构在windows.h中声明,但对于跨平台开发,我建议您单独申报并随身携带。
  • 在64位架构中,一些结构发生了变化。

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

样品:

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;