C++ equivalent of BitConverter
我正试图读取一个文件的PE头来获取一些信息。对于
代码如下所示。
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的东西。希望它能奏效。
更新:谢谢回复。正如建议的那样,我正在尝试使用强制转换,将
请告知。
谢谢。
您有一个字节数组指针(
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都支持未对齐的内存访问(当然,您将为此付出性能方面的代价,但可能不会注意到任何内容,并且您将保存
Itanimum(如果您必须支持它)和(非常老的)arm可能是一个问题:第一个问题只使用
还要注意,这个假设(加上endianness)是有效的,因为您在Windows环境中处理PE文件,如果您必须编写可移植代码或读取其他代码,那么这不是正确的方法(简而言之,您必须处理单个字节并使用固定顺序复制它们)。
编辑2:根据您使用的更新代码,问题在于
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 的偏移量现在计算正确(作为data 和PE_POINTER_OFFSET 的总和)。
所有这些都表明我们仍在使用缓冲方法,但在这里它是相当无用的,因为
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)); |