Я пытаюсь прочитать PE заголовки файла, чтобы получить некоторую информацию. За .NET
а также C#
, Я использую 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
, но это дает мне нарушение доступа необработанное исключение. Это полный код, файл действителен и читается. Я попытался с отладкой, и оптимизация отключена, но безрезультатно.
Добрый совет.
Большое спасибо.
У вас есть указатель байтового массива (char* data
) затем просто переместите указатель на нужное смещение data + PE_POINTER_OFFSET
приведение к указателю на целое число (int*)(data + PE_POINTER_OFFSET)
и deference указатель, чтобы получить значение:
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
,
Также обратите внимание, что эти предположения (плюс порядковый номер) действительны, потому что вы работаете с PE-файлами в среде Windows, если вам приходилось писать переносимый код или читать что-то еще, тогда это неправильный способ сделать это (короче говоря, у вас есть адресовать отдельные байты и копировать их, используя фиксированный порядок).
РЕДАКТИРОВАТЬ 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
теперь рассчитывается правильно (как сумма 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 и машинный порядок совпадают).
рефакторинг для устранения проблем с выравниванием данных на некоторых архитектурах:
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));
Наилучшим подходом было бы объявить структуру формата файла PE. Пример:
struct dos_header {
char signature[2] = "MZ";
boost::int16_t lastsize;
..
boost::int16_t reserved2[10];
boost::int32_t e_lfanew;
}
Заметки:
#pragma pack(8)
если есть проблемы с выравниванием).windows.h
но для кроссплатформенной разработки рекомендую объявить отдельно и переносимо.Когда у вас есть сопоставленные структуры, вы можете привести буфер в качестве указателя на структуру и получить доступ к членам.
Образец:
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;