Created
September 4, 2014 14:35
-
-
Save krupitskas/458207b2a15e1686e1bd to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#include <stdio.h> | |
#include <stdint.h> | |
#include <cstring> | |
#include <cstdlib> | |
#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); //e_lfanew pointer | |
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); //Raw Size | |
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 = (char*) 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