2

I'm trying to read the PE headers of a file to get some information. For .NET and C#, I'm using BitConverter to convert the Byte array obtained after having read the file to an integer equivalent. I wish to do the same with C++, but am not sure of the best approach. I'm using an unsigned char array as the Byte array equivalent.

The code is given below..

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

I'll be using the O/P to query the PE header for information. Need the equivalent of BitCOnverter here. and hopefully it will work.

UPDATE : Thanks for the replies. As suggested I'm trying to use the cast, to convert the character array into Int, to read the PE Header, but it's giving me an access violation Unhandled exception. This is the full code, the file is valid and is being read. I tried with debug, and Optimization disabled, but to no avail.

Kindly advise.

Thanks a lot.

4

3 回答 3

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 都支持未对齐的内存访问(当然,您会为此付出性能代价,但您可能不会注意到任何内容并且您会保存memcpys)。

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

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

编辑 2:根据您使用的更新代码,问题是 with *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现在可以正确计算偏移量(作为data与的总和PE_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 和机器字节序匹配)。

于 2014-10-08T11:02:07.183 回答
3

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

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));
于 2014-10-08T11:14:10.157 回答
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;
于 2014-10-08T12:02:39.697 回答