mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-17 08:43:21 +00:00
Updated PE parser to read RVA and Resources.
This commit is contained in:
@@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
// Return information about this executable
|
// Return information about this executable
|
||||||
function parse(exePath)
|
function parse(exePath)
|
||||||
{
|
{
|
||||||
var retVal = {};
|
var retVal = {};
|
||||||
var fs = require('fs');
|
|
||||||
var fd = fs.openSync(exePath, 'rb');
|
var fd = fs.openSync(exePath, 'rb');
|
||||||
var bytesRead;
|
var bytesRead;
|
||||||
var dosHeader = Buffer.alloc(64);
|
var dosHeader = Buffer.alloc(64);
|
||||||
var ntHeader = Buffer.alloc(24);
|
var ntHeader = Buffer.alloc(24);
|
||||||
var optHeader;
|
var optHeader;
|
||||||
|
var z;
|
||||||
|
|
||||||
// Read the DOS header
|
// Read the DOS header
|
||||||
bytesRead = fs.readSync(fd, dosHeader, 0, 64, 0);
|
bytesRead = fs.readSync(fd, dosHeader, 0, 64, 0);
|
||||||
@@ -52,22 +54,50 @@ function parse(exePath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
retVal.optionalHeaderSize = ntHeader.readUInt16LE(20);
|
retVal.optionalHeaderSize = ntHeader.readUInt16LE(20);
|
||||||
retVal.optionalHeaderSizeAddress = dosHeader.readUInt32LE(60) + 20;
|
retVal.optionalHeaderSizeAddress = dosHeader.readUInt32LE(60) + 24;
|
||||||
|
retVal.sectionHeadersAddress = retVal.optionalHeaderSizeAddress + retVal.optionalHeaderSize;
|
||||||
|
|
||||||
// Read the optional header
|
// Read the optional header
|
||||||
optHeader = Buffer.alloc(ntHeader.readUInt16LE(20));
|
optHeader = Buffer.alloc(ntHeader.readUInt16LE(20));
|
||||||
bytesRead = fs.readSync(fd, optHeader, 0, optHeader.length, dosHeader.readUInt32LE(60) + 24);
|
bytesRead = fs.readSync(fd, optHeader, 0, optHeader.length, dosHeader.readUInt32LE(60) + 24);
|
||||||
var numRVA = undefined;
|
var numRVA = undefined;
|
||||||
|
var rvaStart = 0;
|
||||||
retVal.CheckSumPos = dosHeader.readUInt32LE(60) + 24 + 64;
|
retVal.CheckSumPos = dosHeader.readUInt32LE(60) + 24 + 64;
|
||||||
retVal.SizeOfCode = optHeader.readUInt32LE(4);
|
retVal.SizeOfCode = optHeader.readUInt32LE(4);
|
||||||
retVal.SizeOfInitializedData = optHeader.readUInt32LE(8);
|
retVal.SizeOfInitializedData = optHeader.readUInt32LE(8);
|
||||||
retVal.SizeOfUnInitializedData = optHeader.readUInt32LE(12);
|
retVal.SizeOfUnInitializedData = optHeader.readUInt32LE(12);
|
||||||
|
retVal.sections = {};
|
||||||
|
|
||||||
|
// read section headers
|
||||||
|
var sect = Buffer.alloc(40);
|
||||||
|
for (z = 0; z < 16; ++z)
|
||||||
|
{
|
||||||
|
fs.readSync(fd, sect, 0, sect.length, retVal.sectionHeadersAddress + (z * 40));
|
||||||
|
if (sect[0] != 46) { break; }
|
||||||
|
var s = {};
|
||||||
|
s.sectionName = sect.slice(0, 8).toString().trim('\0');
|
||||||
|
s.virtualSize = sect.readUInt32LE(8);
|
||||||
|
s.virtualAddr = sect.readUInt32LE(12);
|
||||||
|
s.rawSize = sect.readUInt32LE(16);
|
||||||
|
s.rawAddr = sect.readUInt32LE(20);
|
||||||
|
s.relocAddr = sect.readUInt32LE(24);
|
||||||
|
s.lineNumbers = sect.readUInt32LE(28);
|
||||||
|
s.relocNumber = sect.readUInt16LE(32);
|
||||||
|
s.lineNumbersNumber = sect.readUInt16LE(34);
|
||||||
|
s.characteristics = sect.readUInt32LE(36);
|
||||||
|
retVal.sections[s.sectionName] = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retVal.sections['.rsrc'] != null)
|
||||||
|
{
|
||||||
|
retVal.resources = readResourceTable(fd, retVal.sections['.rsrc'].rawAddr, 0); // Read all resources recursively
|
||||||
|
}
|
||||||
|
|
||||||
switch (optHeader.readUInt16LE(0).toString(16).toUpperCase())
|
switch (optHeader.readUInt16LE(0).toString(16).toUpperCase())
|
||||||
{
|
{
|
||||||
case '10B': // 32 bit binary
|
case '10B': // 32 bit binary
|
||||||
numRVA = optHeader.readUInt32LE(92);
|
numRVA = optHeader.readUInt32LE(92);
|
||||||
|
rvaStart = 96;
|
||||||
retVal.CertificateTableAddress = optHeader.readUInt32LE(128);
|
retVal.CertificateTableAddress = optHeader.readUInt32LE(128);
|
||||||
retVal.CertificateTableSize = optHeader.readUInt32LE(132);
|
retVal.CertificateTableSize = optHeader.readUInt32LE(132);
|
||||||
retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 132;
|
retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 132;
|
||||||
@@ -75,6 +105,7 @@ function parse(exePath)
|
|||||||
break;
|
break;
|
||||||
case '20B': // 64 bit binary
|
case '20B': // 64 bit binary
|
||||||
numRVA = optHeader.readUInt32LE(108);
|
numRVA = optHeader.readUInt32LE(108);
|
||||||
|
rvaStart = 112;
|
||||||
retVal.CertificateTableAddress = optHeader.readUInt32LE(144);
|
retVal.CertificateTableAddress = optHeader.readUInt32LE(144);
|
||||||
retVal.CertificateTableSize = optHeader.readUInt32LE(148);
|
retVal.CertificateTableSize = optHeader.readUInt32LE(148);
|
||||||
retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 148;
|
retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 148;
|
||||||
@@ -86,6 +117,12 @@ function parse(exePath)
|
|||||||
}
|
}
|
||||||
retVal.rvaCount = numRVA;
|
retVal.rvaCount = numRVA;
|
||||||
|
|
||||||
|
retVal.rva = [];
|
||||||
|
for (z = 0; z < retVal.rvaCount && z < 32; ++z)
|
||||||
|
{
|
||||||
|
retVal.rva.push({ virtualAddress: optHeader.readUInt32LE(rvaStart + (z * 8)), size: optHeader.readUInt32LE(rvaStart + 4 + (z * 8)) });
|
||||||
|
}
|
||||||
|
|
||||||
if (retVal.CertificateTableAddress)
|
if (retVal.CertificateTableAddress)
|
||||||
{
|
{
|
||||||
// Read the authenticode certificate, only one cert (only the first entry)
|
// Read the authenticode certificate, only one cert (only the first entry)
|
||||||
@@ -96,10 +133,207 @@ function parse(exePath)
|
|||||||
retVal.certificate = retVal.certificate.toString('base64');
|
retVal.certificate = retVal.certificate.toString('base64');
|
||||||
retVal.certificateDwLength = hdr.readUInt32LE(0);
|
retVal.certificateDwLength = hdr.readUInt32LE(0);
|
||||||
}
|
}
|
||||||
|
retVal.versionInfo = getVersionInfo(fd, retVal);
|
||||||
|
|
||||||
fs.closeSync(fd);
|
fs.closeSync(fd);
|
||||||
return (retVal);
|
return (retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read a unicode stting that starts with the string length as the first byte.
|
||||||
|
function readLenPrefixUnicodeString(fd, ptr)
|
||||||
|
{
|
||||||
|
var name = '';
|
||||||
|
var tmp = Buffer.alloc(1);
|
||||||
|
require('fs').readSync(fd, tmp, 0, 1, 0);
|
||||||
|
var nameLen = tmp[0];
|
||||||
|
|
||||||
|
var buf = Buffer.alloc(nameLen * 2);
|
||||||
|
require('fs').readSync(fd, buf, 0, buf.length, 1);
|
||||||
|
return (require('_GenericMarshal').CreateVariable(buf).Wide2UTF8);
|
||||||
|
}
|
||||||
|
// Read a resource item
|
||||||
|
// ptr: The pointer to the start of the resource section
|
||||||
|
// offset: The offset start of the resource item to read
|
||||||
|
function readResourceItem(fd, ptr, offset)
|
||||||
|
{
|
||||||
|
var buf = Buffer.alloc(16);
|
||||||
|
require('fs').readSync(fd, buf, 0, buf.length, ptr + offset);
|
||||||
|
var r = {};
|
||||||
|
r.offsetToData = buf.readUInt32LE(0);
|
||||||
|
r.size = buf.readUInt32LE(4);
|
||||||
|
r.codePage = buf.readUInt32LE(8);
|
||||||
|
r.reserved = buf.readUInt32LE(12);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
function readResourceTable(fd, ptr, offset)
|
||||||
|
{
|
||||||
|
var buf = Buffer.alloc(16);
|
||||||
|
fs.readSync(fd, buf, 0, buf.length, ptr + offset);
|
||||||
|
var r = {};
|
||||||
|
r.characteristics = buf.readUInt32LE(0);
|
||||||
|
r.timeDateStamp = buf.readUInt32LE(4);
|
||||||
|
r.majorVersion = buf.readUInt16LE(8);
|
||||||
|
r.minorVersion = buf.readUInt16LE(10);
|
||||||
|
var numberOfNamedEntries = buf.readUInt16LE(12);
|
||||||
|
var numberofIdEntries = buf.readUInt16LE(14);
|
||||||
|
r.entries = [];
|
||||||
|
var totalResources = numberOfNamedEntries + numberofIdEntries;
|
||||||
|
for (var i = 0; i < totalResources; i++)
|
||||||
|
{
|
||||||
|
buf = Buffer.alloc(8);
|
||||||
|
fs.readSync(fd, buf, 0, buf.length, ptr + offset + 16 + (i * 8));
|
||||||
|
var resource = {};
|
||||||
|
resource.name = buf.readUInt32LE(0);
|
||||||
|
var offsetToData = buf.readUInt32LE(4);
|
||||||
|
if ((resource.name & 0x80000000) != 0) { resource.name = readLenPrefixUnicodeString(fd, ptr + (resource.name - 0x80000000)); }
|
||||||
|
if ((offsetToData & 0x80000000) != 0) { resource.table = readResourceTable(fd, ptr, offsetToData - 0x80000000); } else { resource.item = readResourceItem(fd, ptr, offsetToData); }
|
||||||
|
r.entries.push(resource);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// Return the version info data block
|
||||||
|
function getVersionInfoData(fd, header)
|
||||||
|
{
|
||||||
|
var ptr = header.sections['.rsrc'].rawAddr;
|
||||||
|
for (var i = 0; i < header.resources.entries.length; i++)
|
||||||
|
{
|
||||||
|
if (header.resources.entries[i].name == 16)
|
||||||
|
{
|
||||||
|
const verInfo = header.resources.entries[i].table.entries[0].table.entries[0].item;
|
||||||
|
const actualPtr = (verInfo.offsetToData - header.sections['.rsrc'].virtualAddr) + ptr;
|
||||||
|
var buffer = Buffer.alloc(verInfo.size);
|
||||||
|
require('fs').readSync(fd, buffer, 0, buffer.length, actualPtr);
|
||||||
|
return (buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VS_FIXEDFILEINFO structure: https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
|
||||||
|
function readFixedFileInfoStruct(buf, ptr)
|
||||||
|
{
|
||||||
|
if (buf.length - ptr < 50) return null;
|
||||||
|
var r = {};
|
||||||
|
r.dwSignature = buf.readUInt32LE(ptr);
|
||||||
|
if (r.dwSignature != 0xFEEF04BD) return null;
|
||||||
|
r.dwStrucVersion = buf.readUInt32LE(ptr + 4);
|
||||||
|
r.dwFileVersionMS = buf.readUInt32LE(ptr + 8);
|
||||||
|
r.dwFileVersionLS = buf.readUInt32LE(ptr + 12);
|
||||||
|
r.dwProductVersionMS = buf.readUInt32LE(ptr + 16);
|
||||||
|
r.dwProductVersionLS = buf.readUInt32LE(ptr + 20);
|
||||||
|
r.dwFileFlagsMask = buf.readUInt32LE(ptr + 24);
|
||||||
|
r.dwFileFlags = buf.readUInt32LE(ptr + 28);
|
||||||
|
r.dwFileOS = buf.readUInt32LE(ptr + 32);
|
||||||
|
r.dwFileType = buf.readUInt32LE(ptr + 36);
|
||||||
|
r.dwFileSubtype = buf.readUInt32LE(ptr + 40);
|
||||||
|
r.dwFileDateMS = buf.readUInt32LE(ptr + 44);
|
||||||
|
r.dwFileDateLS = buf.readUInt32LE(ptr + 48);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim a string at the first null character
|
||||||
|
function stringUntilNull(str)
|
||||||
|
{
|
||||||
|
if (str == null) return null;
|
||||||
|
const i = str.indexOf('\0');
|
||||||
|
if (i >= 0) return str.substring(0, i);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringFileInfo structure: https://docs.microsoft.com/en-us/windows/win32/menurc/stringfileinfo
|
||||||
|
function readStringFilesStruct(buf, ptr, len)
|
||||||
|
{
|
||||||
|
var t = [], startPtr = ptr;
|
||||||
|
while (ptr < (startPtr + len))
|
||||||
|
{
|
||||||
|
const r = {};
|
||||||
|
r.wLength = buf.readUInt16LE(ptr);
|
||||||
|
if (r.wLength == 0) return t;
|
||||||
|
r.wValueLength = buf.readUInt16LE(ptr + 2);
|
||||||
|
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
|
||||||
|
r.szKey = stringUntilNull(require('_GenericMarshal').CreateVariable(buf.slice(ptr + 6, ptr + 6 + (r.wLength - 6))).Wide2UTF8); // String value
|
||||||
|
//console.log('readStringFileStruct', r.wLength, r.wValueLength, r.wType, r.szKey.toString());
|
||||||
|
if (r.szKey == 'StringFileInfo') { r.stringTable = readStringTableStruct(buf, ptr + 36 + r.wValueLength); }
|
||||||
|
if (r.szKey == 'VarFileInfo$') { r.varFileInfo = {}; } // TODO
|
||||||
|
t.push(r);
|
||||||
|
ptr += r.wLength;
|
||||||
|
ptr = padPointer(ptr);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringTable structure: https://docs.microsoft.com/en-us/windows/win32/menurc/stringtable
|
||||||
|
function readStringTableStruct(buf, ptr)
|
||||||
|
{
|
||||||
|
const r = {};
|
||||||
|
r.wLength = buf.readUInt16LE(ptr);
|
||||||
|
r.wValueLength = buf.readUInt16LE(ptr + 2);
|
||||||
|
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
|
||||||
|
r.szKey = require('_GenericMarshal').CreateVariable(buf.slice(ptr + 6, ptr + 6 + 16)).Wide2UTF8; // An 8-digit hexadecimal number stored as a Unicode string.
|
||||||
|
//console.log('readStringTableStruct', r.wLength, r.wValueLength, r.wType, r.szKey);
|
||||||
|
r.strings = readStringStructs(buf, ptr + 24 + r.wValueLength, r.wLength - 22);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String structure: https://docs.microsoft.com/en-us/windows/win32/menurc/string-str
|
||||||
|
function readStringStructs(buf, ptr, len)
|
||||||
|
{
|
||||||
|
var t = [], startPtr = ptr;
|
||||||
|
while (ptr < (startPtr + len))
|
||||||
|
{
|
||||||
|
const r = {};
|
||||||
|
r.wLength = buf.readUInt16LE(ptr);
|
||||||
|
if (r.wLength == 0) return t;
|
||||||
|
r.wValueLength = buf.readUInt16LE(ptr + 2);
|
||||||
|
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
|
||||||
|
|
||||||
|
|
||||||
|
//console.log('tmp', tmp.toString('hex'));
|
||||||
|
r.key = require('_GenericMarshal').CreateVariable(buf.slice(ptr + 6, ptr + 6 + (r.wLength - 6))).Wide2UTF8; // String value
|
||||||
|
//console.log('keyLen: ' + r.key.length, 'wValueLength: ' + r.wValueLength);
|
||||||
|
r.value = require('_GenericMarshal').CreateVariable(buf.slice(ptr + r.wLength - (r.wValueLength*2), ptr + r.wLength)).Wide2UTF8;
|
||||||
|
t.push(r);
|
||||||
|
ptr += r.wLength;
|
||||||
|
ptr = padPointer(ptr);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the next 4 byte aligned number
|
||||||
|
function padPointer(ptr) { return ptr + (ptr % 4); }
|
||||||
|
|
||||||
|
// VS_VERSIONINFO structure: https://docs.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
|
||||||
|
function readVersionInfo(buf, ptr)
|
||||||
|
{
|
||||||
|
const r = {};
|
||||||
|
if (buf.length < 2) return null;
|
||||||
|
r.wLength = buf.readUInt16LE(ptr);
|
||||||
|
if (buf.length < r.wLength) return null;
|
||||||
|
r.wValueLength = buf.readUInt16LE(ptr + 2);
|
||||||
|
r.wType = buf.readUInt16LE(ptr + 4);
|
||||||
|
|
||||||
|
r.szKey = require('_GenericMarshal').CreateVariable(buf.slice(ptr + 6, ptr + 36)).Wide2UTF8;
|
||||||
|
if (r.szKey != 'VS_VERSION_INFO') return null;
|
||||||
|
////console.log('getVersionInfo', r.wLength, r.wValueLength, r.wType, r.szKey.toString());
|
||||||
|
if (r.wValueLength == 52) { r.fixedFileInfo = readFixedFileInfoStruct(buf, ptr + 40); }
|
||||||
|
r.stringFiles = readStringFilesStruct(buf, ptr + 40 + r.wValueLength, r.wLength - 40 - r.wValueLength);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
function getVersionInfo(fd, header, resources)
|
||||||
|
{
|
||||||
|
var r = {};
|
||||||
|
var b = getVersionInfoData(fd, header, resources);
|
||||||
|
var info = readVersionInfo(b, 0);
|
||||||
|
if ((info == null) || (info.stringFiles == null)) return null;
|
||||||
|
var StringFileInfo = null;
|
||||||
|
for (var i in info.stringFiles) { if (info.stringFiles[i].szKey == 'StringFileInfo') { StringFileInfo = info.stringFiles[i]; } }
|
||||||
|
|
||||||
|
if ((StringFileInfo == null) || (StringFileInfo.stringTable == null) || (StringFileInfo.stringTable.strings == null)) return null;
|
||||||
|
const strings = StringFileInfo.stringTable.strings;
|
||||||
|
|
||||||
|
for (var i in strings) { r[strings[i].key] = strings[i].value; }
|
||||||
|
return r;
|
||||||
|
}
|
||||||
module.exports = parse;
|
module.exports = parse;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user