Created
January 27, 2016 19:08
-
-
Save djhaskin987/d1860a7d98193913bcfa to your computer and use it in GitHub Desktop.
Get version from EXE file on linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Distributed under the CC-wiki license. | |
* user contributions licensed under cc by-sa 3.0 with attribution required: https://creativecommons.org/licenses/by-sa/3.0/ | |
* Originally taken from the answer by @rodrigo, found here: http://stackoverflow.com/a/12486703/850326 | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <memory.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <sys/stat.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) | |
{ | |
//buf is a IMAGE_DOS_HEADER | |
if (READ_WORD(buf) != 0x5A4D) //MZ signature | |
return NULL; | |
//pe is a IMAGE_NT_HEADERS32 | |
const char *pe = buf + READ_DWORD(buf + 0x3C); | |
if (READ_WORD(pe) != 0x4550) //PE signature | |
return NULL; | |
//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; | |
//optHeader is a IMAGE_OPTIONAL_HEADER32 | |
const char *optHeader = coff + 20; | |
if (READ_WORD(optHeader) != 0x10b) //Optional header magic (32 bits) | |
return NULL; | |
//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; | |
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; | |
DWORD vaSec = READ_DWORD(sec + 12); | |
const char *raw = buf + READ_DWORD(sec + 20); | |
const char *resSec = raw + (vaRes - vaSec); | |
WORD numNamed = READ_WORD(resSec + 12); | |
WORD numId = READ_WORD(resSec + 14); | |
int j; | |
for (j = 0; j < numNamed + numId; ++j) | |
{ | |
//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; | |
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); | |
const char *verPtr = raw + (verVa - vaSec); | |
return verPtr; | |
} | |
return NULL; | |
} | |
return NULL; | |
} | |
int PrintVersion(const char *version, int offs) | |
{ | |
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); | |
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); | |
} | |
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); | |
} | |
int main(int argc, char **argv) | |
{ | |
struct stat st; | |
if (stat(argv[1], &st) < 0) | |
{ | |
perror(argv[1]); | |
return 1; | |
} | |
char *buf = malloc(st.st_size); | |
FILE *f = fopen(argv[1], "r"); | |
if (!f) | |
{ | |
perror(argv[1]); | |
return 2; | |
} | |
fread(buf, 1, st.st_size, f); | |
fclose(f); | |
const char *version = FindVersion(buf); | |
if (!version) | |
printf("No version\n"); | |
else | |
PrintVersion(version, 0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment