C библиотека для чтения EXE-версии из Linux?
есть ли библиотека, которую я могу использовать в Linux, которая вернет свойства EXE-файла Windows, которые перечислены на вкладке версии проводника? Это такие поля, как название продукта, версия продукта, описание и т. д.
для моего проекта, EXE-файл можно только читать из памяти, а не из файла. Я хотел бы избежать записи EXE-файла на диск.
5 ответов
версия файла находится в VS_FIXEDFILEINFO
struct, но вы должны найти его в исполняемый данных. Есть два способа сделать то, что вы хотите:
- найдите подпись VERSION_INFO в файле и прочитайте
VS_FIXEDFILEINFO
структура напрямую. - найти парсят дереве ресурсов, найти
RT_VERSION
ресурс, проанализируйте его и извлекитеVS_FIXEDFILEINFO
данные.
первый легче, но восприимчив к поиску подпись случайно оказалась не в том месте. Кроме того, другие данные, которые вы просите (название продукта, описание и т. д.) не находятся в этой структуре, поэтому я попытаюсь объяснить, как получить данные сложным способом.
формат PE немного запутан, поэтому я вставляю код по частям, с комментариями и с минимальной проверкой ошибок. Я напишу простую функцию, которая сбрасывает данные в стандартный вывод. Написание его как правильной функции оставлено как упражнение для читателя :)
обратите внимание, что я буду использовать смещения в буфере вместо непосредственного сопоставления структур, чтобы избежать проблем переносимости, связанных с выравниванием или заполнением полей структуры. Во всяком случае, я аннотировал тип используемых структур (см. include file winnt.H для деталей).
сначала несколько полезных заявлений, они должны быть самоочевидны:
typedef uint32_t DWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
#define READ_BYTE(p) (((unsigned char*)(p))[0])
#define READ_WORD(p) ((((unsigned char*)(p))[0]) | ((((unsigned char*)(p))[1]) << 8))
#define READ_DWORD(p) ((((unsigned char*)(p))[0]) | ((((unsigned char*)(p))[1]) << 8) | \
((((unsigned char*)(p))[2]) << 16) | ((((unsigned char*)(p))[3]) << 24))
#define PAD(x) (((x) + 3) & 0xFFFFFFFC)
затем функция, которая находит ресурс версии в исполняемом образе (без размера проверяет.)
const char *FindVersion(const char *buf)
{
первой структурой в EXE является заголовок MZ (для совместимости С MS-DOS).
//buf is a IMAGE_DOS_HEADER
if (READ_WORD(buf) != 0x5A4D) //MZ signature
return NULL;
единственное поле, интересное в заголовке MZ, - это смещение заголовка PE. Заголовок PE-это настоящая вещь.
//pe is a IMAGE_NT_HEADERS32
const char *pe = buf + READ_DWORD(buf + 0x3C);
if (READ_WORD(pe) != 0x4550) //PE signature
return NULL;
на самом деле, заголовок PE довольно скучный, мы хотим заголовок COFF, который имеет все символические данные.
//coff is a IMAGE_FILE_HEADER
const char *coff = pe + 4;
нам просто нужны следующие поля из этого.
WORD numSections = READ_WORD(coff + 2);
WORD optHeaderSize = READ_WORD(coff + 16);
if (numSections == 0 || optHeaderSize == 0)
return NULL;
в необязательный заголовок на самом деле является обязательным в EXE, и это сразу после COFF. Магия отличается для 32 и 64 бит Windows. Я предполагаю 32 бита отсюда.
//optHeader is a IMAGE_OPTIONAL_HEADER32
const char *optHeader = coff + 20;
if (READ_WORD(optHeader) != 0x10b) //Optional header magic (32 bits)
return NULL;
вот интересная часть: мы хотим найти раздел ресурсов. Он состоит из двух частей: 1. данные раздела 2. раздел метаданные.
расположение данных находится в таблице в конце необязательного заголовка, и каждый раздел имеет хорошо известный индекс в этой таблице. Раздел ресурса в индекс 2, поэтому мы получаем виртуальный адрес (VA) раздела ресурсов с помощью:
//dataDir is an array of IMAGE_DATA_DIRECTORY
const char *dataDir = optHeader + 96;
DWORD vaRes = READ_DWORD(dataDir + 8*2);
//secTable is an array of IMAGE_SECTION_HEADER
const char *secTable = optHeader + optHeaderSize;
чтобы получить метаданные раздела, нам нужно повторить таблицу разделов, ища раздел с именем .rsrc
.
int i;
for (i = 0; i < numSections; ++i)
{
//sec is a IMAGE_SECTION_HEADER*
const char *sec = secTable + 40*i;
char secName[9];
memcpy(secName, sec, 8);
secName[8] = 0;
if (strcmp(secName, ".rsrc") != 0)
continue;
структура раздела имеет два соответствующих члена: VA раздела и смещение раздела в файл (также размер раздела, но я не проверяю его!):
DWORD vaSec = READ_DWORD(sec + 12);
const char *raw = buf + READ_DWORD(sec + 20);
теперь смещение в файле, которая соответствует vaRes
Ва мы получили раньше легко.
const char *resSec = raw + (vaRes - vaSec);
это указатель на данные ресурса. Все отдельные ресурсы устанавливаются в виде дерева, с 3 уровнями: 1) тип ресурса, 2) идентификатор ресурса, 3) язык ресурса. Для версии мы получим самый первый из правильного типа.
во-первых, у нас есть каталог ресурсов (для типа ресурса), мы получаем количество записей в каталоге, как именованных, так и неназванных и повторите:
WORD numNamed = READ_WORD(resSec + 12);
WORD numId = READ_WORD(resSec + 14);
int j;
for (j = 0; j < numNamed + numId; ++j)
{
для каждой записи ресурса мы получаем тип ресурса и отбрасываем его, если он не является константой RT_VERSION (16).
//resSec is a IMAGE_RESOURCE_DIRECTORY followed by an array
// of IMAGE_RESOURCE_DIRECTORY_ENTRY
const char *res = resSec + 16 + 8 * j;
DWORD name = READ_DWORD(res);
if (name != 16) //RT_VERSION
continue;
если это RT_VERSION, мы переходим к следующему каталогу ресурсов в дереве:
DWORD offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) == 0) //is a dir resource?
return NULL;
//verDir is another IMAGE_RESOURCE_DIRECTORY and
// IMAGE_RESOURCE_DIRECTORY_ENTRY array
const char *verDir = resSec + (offs & 0x7FFFFFFF);
и перейти на следующий уровень каталога, мы не заботимся о ИД. из этого:
numNamed = READ_WORD(verDir + 12);
numId = READ_WORD(verDir + 14);
if (numNamed == 0 && numId == 0)
return NULL;
res = verDir + 16;
offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) == 0) //is a dir resource?
return NULL;
третий уровень имеет язык ресурса. Нам тоже все равно, так что хватай первый. один:
//and yet another IMAGE_RESOURCE_DIRECTORY, etc.
verDir = resSec + (offs & 0x7FFFFFFF);
numNamed = READ_WORD(verDir + 12);
numId = READ_WORD(verDir + 14);
if (numNamed == 0 && numId == 0)
return NULL;
res = verDir + 16;
offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) != 0) //is a dir resource?
return NULL;
verDir = resSec + offs;
и мы добираемся до реального ресурса, ну, на самом деле структура, которая содержит местоположение и размер реального ресурса, но нас не волнует размер.
DWORD verVa = READ_DWORD(verDir);
это VA версии resouce,который легко преобразуется в указатель.
const char *verPtr = raw + (verVa - vaSec);
return verPtr;
и Done! Если не найдено return NULL
.
}
return NULL;
}
return NULL;
}
теперь, когда ресурс версии найден, мы должны проанализировать его. На самом деле это дерево (что еще) пар "значение name." Некоторые ценности хорошо известны, и это то, что вы ищете, просто сделайте некоторый тест, и вы узнаете, какие из них.
Примечание: все строки хранятся в UNICODE (UTF-16), но мой пример кода выполняет немое преобразование в ASCII. Кроме того, нет проверок на переполнение.
функция принимает указатель на ресурс версии и смещение в эту память (0 для стартеров) и возвращает количество байтов проанализированный.
int PrintVersion(const char *version, int offs)
{
прежде всего смещение должно быть кратным 4.
offs = PAD(offs);
затем мы получаем свойства узла дерева версий.
WORD len = READ_WORD(version + offs);
offs += 2;
WORD valLen = READ_WORD(version + offs);
offs += 2;
WORD type = READ_WORD(version + offs);
offs += 2;
имя узла-строка с нулевым завершением в Юникоде.
char info[200];
int i;
for (i=0; i < 200; ++i)
{
WORD c = READ_WORD(version + offs);
offs += 2;
info[i] = c;
if (!c)
break;
}
больше прокладки, если необходимо:
offs = PAD(offs);
если type
не 0, тогда это данные Строковой версии.
if (type != 0) //TEXT
{
char value[200];
for (i=0; i < valLen; ++i)
{
WORD c = READ_WORD(version + offs);
offs += 2;
value[i] = c;
}
value[i] = 0;
printf("info <%s>: <%s>\n", info, value);
}
еще, если имя VS_VERSION_INFO
, то это VS_FIXEDFILEINFO
структура. Иначе это двоичные данные.
else
{
if (strcmp(info, "VS_VERSION_INFO") == 0)
{
я просто печатаю версию файла и продукта, но вы можете легко найти другие поля этой структуры. Остерегайтесь смешанный прямой порядок.
//fixed is a VS_FIXEDFILEINFO
const char *fixed = version + offs;
WORD fileA = READ_WORD(fixed + 10);
WORD fileB = READ_WORD(fixed + 8);
WORD fileC = READ_WORD(fixed + 14);
WORD fileD = READ_WORD(fixed + 12);
WORD prodA = READ_WORD(fixed + 18);
WORD prodB = READ_WORD(fixed + 16);
WORD prodC = READ_WORD(fixed + 22);
WORD prodD = READ_WORD(fixed + 20);
printf("\tFile: %d.%d.%d.%d\n", fileA, fileB, fileC, fileD);
printf("\tProd: %d.%d.%d.%d\n", prodA, prodB, prodC, prodD);
}
offs += valLen;
}
теперь выполните рекурсивный вызов для печати полного дерева.
while (offs < len)
offs = PrintVersion(version, offs);
и еще несколько прокладок перед возвращением.
return PAD(offs);
}
наконец, в качестве бонуса, a
Я знаю pev
Это инструмент на Ubuntu, который позволяет вам видеть эту информацию, а также много другой информации заголовка PE. Я также знаю, что это написано в с. возможно, вы захотите взглянуть на него. Немного от ее раздел История в документации:
ПЭВ родился в 2010 году от простой потребности: программа, чтобы узнать версия (версия файла) файла PE32, который может быть запущен в Linux. Этот номер версии хранится в ресурсах (.rsrc) раздел но время, когда мы решили просто искать строку в целом двоичный, без какой-либо оптимизации.
позже мы решили проанализировать файл PE32 до достижения .rsrc раздел и получить поле версия файла. Для этого мы понял, что мы должны разобрать весь файл, и мы подумали, если бы мы могли распечатайте также все поля и значения...
до версии 0.40, pev была уникальной программой для разбора заголовков PE и разделы (теперь readpe несет ответственность за это). В версии 0.50 мы ориентирован на анализ вредоносных программ и разбит на различные программы за библиотекой под названием либпе. В настоящее время все программы pev используют libpe.
установить winelib http://www.winehq.org/docs/winelib-guide/index Это порт MS Windows API для других систем, включая linux.
затем используйте MS Windows API. Как GetFileVersionInfo
http://msdn.microsoft.com/en-us/library/windows/desktop/ms647003 (v=против 85).aspx
или любые другие функции.
Я никогда этого не делал, но я бы начал с этих выводов.
Что касается exe-файла в ограничении памяти, вы можете скопировать его в ОЗУ диск?
вот патч для кода для поддержки PE32+. Протестировано на некоторых файлах и, похоже, работает.
//optHeader is a IMAGE_OPTIONAL_HEADER32
const char *optHeader = coff + 20;
WORD magic = READ_WORD(optHeader);
if (magic != 0x10b && magic != 0x20b)
return NULL;
//dataDir is an array of IMAGE_DATA_DIRECTORY
const char *dataDir = optHeader + (magic==0x10b ? 96: 112);
DWORD vaRes = READ_DWORD(dataDir + 8*2);
вот пример на языке Tcl для анализа через a .EXE-файл для получения информации о версии.
чтение информации о версии из исполняемых файлов Win32.
этой веб-страница описывает .заголовок exe-формате. Я не уверен в дате этой информации или же она относится к более поздним версиям Windows или нет. Однако это отправная точка.