Skip to content

Instantly share code, notes, and snippets.

@liath
Last active November 30, 2020 20:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liath/c148ce9f72a64457150e16f2a880e7c4 to your computer and use it in GitHub Desktop.
Save liath/c148ce9f72a64457150e16f2a880e7c4 to your computer and use it in GitHub Desktop.
Extracts FileVersion and other fun fields as seen in the Properties dialog for dll and exe files
const fs = require('fs');
const file = fs.readFileSync(process.argv[2]);
let at = file.readUInt32LE(0x3c);
if (file.slice(at, at + 0x4).toString('utf-8') !== 'PE\x00\x00') {
// bail if not PE header
console.error('Did not see PE magic constant');
process.exit(1);
}
const header = {
sections: file.readUInt16LE(at + 0x6),
sizeOfOptionalHeader: file.readUInt16LE(at + 0x14),
resourcesVAddr: 0,
resources: 0,
};
// console.log({header});
for (let i = 0; i < header.sections; i += 1) {
const offset = at + 0x18 + header.sizeOfOptionalHeader + (i * 0x28);
const vaddr = file.readUInt32LE(offset + 0xc);
const addr = file.readUInt32LE(offset + 0x14);
const name = file.toString('utf-8', offset, offset + 8).split('\0').shift();
if (name === '.rsrc') {
header.resourcesVAddr = vaddr;
header.resources = addr;
break;
}
}
if (!header.resources) {
console.error('Did not find resource table offset');
process.exit(1);
}
const parseResources = (offset) => {
const namedEntriesCount = file.readUInt16LE(offset + 0xc);
const idEntriesCount = file.readUInt16LE(offset + 0xe);
const entries = [];
for (let i = 0; i < namedEntriesCount + idEntriesCount; i += 1) {
const cur = offset + 0x10 + (i * 0x8);
const id = file.readUInt32LE(cur);
const data = file.readUInt32LE(cur + 0x4);
// true if high bit is set
const isDir = !!(data >>> 31);
// clear high bit
const target = data & 0x7fffffff;
entries.push({
id,
isDir,
target,
});
}
return entries;
};
const versionDataEntry = {
offset: parseResources(
parseResources(
parseResources(header.resources)
.find((x) => x.id === 16 && x.isDir).target + header.resources,
)
.find((x) => x.isDir).target + header.resources,
)
.find((x) => !x.isDir).target + header.resources,
};
versionDataEntry.rvaTarget = file.readUInt32LE(versionDataEntry.offset);
versionDataEntry.sizeTarget = file.readUInt32LE(versionDataEntry.offset + 0x4);
// set position to start of version data adjusted for virtual address
at = versionDataEntry.rvaTarget - header.resourcesVAddr + header.resources;
const readUTF16String = (offset) => {
let count = 0;
let i = 0;
while (count < 2) {
count += file[offset + i] === 0 ? 1 : -1;
i += 1;
}
return file.toString('utf-16le', offset, offset + i - 1);
};
const dwordAlign = (offset, base) => ((offset + base + 3) & 0xfffffffc) - (base & 0xfffffffc);
const utf16len = (x) => 2 * (x.length + 1);
if (readUTF16String(at + 0x6) !== 'VS_VERSION_INFO') {
console.error('Failed to find version struct');
process.exit(1);
}
const parseStringFileInfo = (offset) => {
const entrySize = file.readUInt16LE(offset + 0x2);
const name = readUTF16String(offset + 0x6);
const valueOffset = dwordAlign(offset + 0x6 + utf16len(name), at);
return {
totalSize: file.readUInt16LE(offset),
name,
value: entrySize ? readUTF16String(valueOffset) : null,
};
};
let curStringFileInfo = dwordAlign(0x5a + at, at);
while (curStringFileInfo < at + versionDataEntry.sizeTarget) {
const stringFileInfo = parseStringFileInfo(curStringFileInfo);
if (stringFileInfo.name === 'StringFileInfo') {
let curStringTable = dwordAlign(curStringFileInfo + 0x6 + utf16len(stringFileInfo.name), at);
while (curStringTable < curStringFileInfo + stringFileInfo.totalSize) {
const stringTableInfo = parseStringFileInfo(curStringTable);
let curStringTableEntry = dwordAlign(curStringTable + 0x6 + utf16len(stringTableInfo.name), at);
while (curStringTableEntry < curStringTable + stringTableInfo.totalSize) {
const stringEntryInfo = parseStringFileInfo(curStringTableEntry);
console.log(`${stringEntryInfo.name}: ${stringEntryInfo.value}`);
curStringTableEntry = dwordAlign(curStringTableEntry + stringEntryInfo.totalSize, at);
}
curStringTable = dwordAlign(curStringTable + stringTableInfo.totalSize, at);
}
}
curStringFileInfo = dwordAlign(curStringFileInfo + stringFileInfo.totalSize, at);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment