authenticode.js improved encoding of the sections and header.

Ylian Saint-Hilaire committed Aug 11, 2022 at 01:53 UTC e0817fd3549ebddff9cee3fc5a0e61803ba331e5
1 file changed +59 -11
authenticode.js
+59 -11
@@ -1661,20 +1661,34 @@ function createAuthenticodeHandler(path) {
1661 var fullHeaderLen = obj.header.SectionHeadersPtr + (obj.header.coff.numberOfSections * 40);
1662 var fullHeader = readFileSlice(written, fullHeaderLen);
1663
1664 - // Compute the size of the resource segment
1665 - //const resSizes = { tables: 0, items: 0, names: 0, data: 0 };
1666 - //getResourceSectionSize(obj.resources, resSizes);
1664 + // Create the resource section and pad to next 512 byte boundry
1665 + var rsrcSection = generateResourceSection(obj.resources);
1666 + var rsrcSectionVirtualSize = rsrcSection.length;
1667 + var x = (rsrcSection.length % 512);
1668 + if (x != 0) { rsrcSection = Buffer.concat([rsrcSection, Buffer.alloc(512 - x)]); }
1669 + var rsrcSectionRawSize = rsrcSection.length;
1670
1671 // Calculate the location and original and new size of the resource segment
1672 var fileAlign = obj.header.peWindows.fileAlignment
1673 var resPtr = obj.header.sections['.rsrc'].rawAddr;
1674 var oldResSize = obj.header.sections['.rsrc'].rawSize;
1672 - var newResSize = obj.header.sections['.rsrc'].rawSize; // TODO: resSizes.data;
1675 + var newResSize = rsrcSection.length;
1676 var resDeltaSize = newResSize - oldResSize;
1677
1678 + // Compute the sizeOfInitializedData
1679 + var sizeOfInitializedData = 0;
1680 + for (var i in obj.header.sections) {
1681 + if (i != '.text') {
1682 + if (i == '.rsrc') {
1683 + sizeOfInitializedData += rsrcSectionRawSize;
1684 + } else {
1685 + sizeOfInitializedData += obj.header.sections[i].rawSize;
1686 + }
1687 + }
1688 + }
1689 +
1690 // Change PE optional header sizeOfInitializedData standard field
1676 - fullHeader.writeUInt32LE(obj.header.peStandard.sizeOfInitializedData + resDeltaSize, obj.header.peOptionalHeaderLocation + 8);
1677 - fullHeader.writeUInt32LE(obj.header.peWindows.sizeOfImage, obj.header.peOptionalHeaderLocation + 56); // TODO: resDeltaSize
1691 + fullHeader.writeUInt32LE(sizeOfInitializedData, obj.header.peOptionalHeaderLocation + 8);
1692
1693 // Update the checksum to zero
1694 fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 64);
@@ -1698,19 +1712,34 @@ function createAuthenticodeHandler(path) {
1712 if (obj.header.dataDirectories.clrRuntimeHeader.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.clrRuntimeHeader.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 208 + pePlusOffset); }
1713
1714 // Make changes to the segments table
1715 + var virtualAddress = 4096;
1716 for (var i in obj.header.sections) {
1717 const section = obj.header.sections[i];
1718 if (i == '.rsrc') {
1719 // Change the size of the resource section
1705 - fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 8); // virtualSize (TODO)
1706 - fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 16); // rawSize
1720 + fullHeader.writeUInt32LE(rsrcSectionVirtualSize, section.ptr + 8); // virtualSize
1721 + fullHeader.writeUInt32LE(rsrcSectionRawSize, section.ptr + 16); // rawSize
1722 +
1723 + // Set the virtual address of the section
1724 + fullHeader.writeUInt32LE(virtualAddress, section.ptr + 12); // Virtual address
1725 + var virtualAddressPadding = (rsrcSectionVirtualSize % 4096);
1726 + virtualAddress += rsrcSectionVirtualSize;
1727 + if (virtualAddressPadding != 0) { virtualAddress += (4096 - virtualAddressPadding); }
1728 } else {
1729 // Change the location of any other section if located after the resource section
1709 - if (section.virtualAddr > resPtr) { fullHeader.writeUInt32LE(section.virtualAddr + resDeltaSize, section.ptr + 12); }
1730 if (section.rawAddr > resPtr) { fullHeader.writeUInt32LE(section.rawAddr + resDeltaSize, section.ptr + 20); }
1731 +
1732 + // Set the virtual address of the section
1733 + fullHeader.writeUInt32LE(virtualAddress, section.ptr + 12); // Virtual address
1734 + var virtualAddressPadding = (section.virtualSize % 4096);
1735 + virtualAddress += section.virtualSize;
1736 + if (virtualAddressPadding != 0) { virtualAddress += (4096 - virtualAddressPadding); }
1737 }
1738 }
1739
1740 + // Write size of image. We put the next virtual address.
1741 + fullHeader.writeUInt32LE(virtualAddress, obj.header.peOptionalHeaderLocation + 56); // sizeOfImage
1742 +
1743 // Write the entire header to the destination file
1744 //console.log('Write header', fullHeader.length, written);
1745 fs.writeSync(output, fullHeader);
@@ -1726,7 +1755,6 @@ function createAuthenticodeHandler(path) {
1755 }
1756
1757 // Write the new resource section
1729 - var rsrcSection = generateResourceSection(obj.resources);
1758 fs.writeSync(output, rsrcSection);
1759 written += rsrcSection.length;
1760 //console.log('Write res', rsrcSection.length, written);
@@ -1989,7 +2017,7 @@ function start() {
2017 }
2018
2019 // Check that a valid command is passed in
1992 - if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'saveicons', 'header', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
2020 + if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'saveicons', 'header', 'sections', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
2021 console.log("Invalid command: " + process.argv[2]);
2022 console.log("Valid commands are: info, sign, unsign, createcert, timestamp");
2023 return;
@@ -2103,6 +2131,26 @@ function start() {
2131 if (command == 'header') { // Display the full executable header in JSON format
2132 if (exe == null) { console.log("Missing --exe [filename]"); return; }
2133 console.log(exe.header);
2134 + // Check that the header is valid
2135 + var ptr = 1024, sizeOfCode = 0, sizeOfInitializedData = 0;
2136 + for (var i in exe.header.sections) {
2137 + if (i == '.text') { sizeOfCode += exe.header.sections[i].rawSize; } else { sizeOfInitializedData += exe.header.sections[i].rawSize; }
2138 + if (exe.header.sections[i].rawAddr != ptr) { console.log('WARNING: ' + i + ' section should have a rawAddr or ' + ptr + ', but has ' + exe.header.sections[i].rawAddr + ' instead.'); }
2139 + ptr += exe.header.sections[i].rawSize;
2140 + }
2141 + if (exe.header.peStandard.sizeOfCode != sizeOfCode) { console.log('WARNING: Size of code is ' + exe.header.peStandard.sizeOfCode + ', should be ' + sizeOfCode + '.'); }
2142 + if (exe.header.peStandard.sizeOfInitializedData != sizeOfInitializedData) { console.log('WARNING: Size of initialized data is ' + exe.header.peStandard.sizeOfInitializedData + ', should be ' + sizeOfInitializedData + '.'); }
2143 + }
2144 + if (command == 'sections') { // Display sections in CSV format
2145 + if (exe == null) { console.log("Missing --exe [filename]"); return; }
2146 + var csvHeader = 'section';
2147 + for (var i in exe.header.sections['.text']) { csvHeader += ',' + i; }
2148 + console.log(csvHeader);
2149 + for (var i in exe.header.sections) {
2150 + var csvData = i;
2151 + for (var j in exe.header.sections[i]) { csvData += ',' + exe.header.sections[i][j]; }
2152 + console.log(csvData);
2153 + }
2154 }
2155 if (command == 'sign') { // Sign an executable
2156 if (typeof args.exe != 'string') { console.log("Missing --exe [filename]"); return; }