authenticode.js new computes the new checksum on sign/unsign.

Ylian Saint-Hilaire committed Jun 8, 2022 at 00:27 UTC f5f4305841a5cde548f25b0e2aadd524bbc2d28c
1 file changed +90 -11
authenticode.js
+90 -11
@@ -244,9 +244,6 @@ function createAuthenticodeHandler(path) {
244 obj.header.siglen = obj.header.dataDirectories.certificateTable.size
245 obj.header.signed = ((obj.header.sigpos != 0) && (obj.header.siglen != 0));
246
247 - // Compute the checkSum value for this file
248 - obj.header.peWindows.checkSumActual = getChecksum(readFileSlice(0, obj.filesize));
249 -
247 // The section headers are located after the optional PE header
248 obj.header.SectionHeadersPtr = obj.header.peOptionalHeaderLocation + obj.header.coff.sizeOfOptionalHeader;
249
@@ -272,6 +269,9 @@ function createAuthenticodeHandler(path) {
269 obj.header.sections[sectionName] = section;
270 }
271
272 + // Compute the checkSum value for this file
273 + obj.header.peWindows.checkSumActual = runChecksum();
274 +
275 // If there is a .rsrc section, read the resource information and locations
276 if (obj.header.sections['.rsrc'] != null) {
277 obj.resources = readResourceTable(obj.header.sections['.rsrc'].rawAddr, 0); // Read all resources recursively
@@ -742,18 +742,85 @@ function createAuthenticodeHandler(path) {
742 while (ptr < end) { const buf = readFileSlice(ptr, Math.min(65536, end - ptr)); hash.update(buf); ptr += buf.length; }
743 }
744
745 + // Checksum the file loading 64k chunks
746 + function runChecksum() {
747 + var ptr = 0, c = createChecksum();
748 + while (ptr < obj.filesize) { const buf = readFileSlice(ptr, Math.min(65536, obj.filesize - ptr)); c.update(buf); ptr += buf.length; }
749 + return c.digest();
750 + }
751 +
752 + // Checksum the open file loading 64k chunks
753 + function runChecksumOnFile(fd, filesize) {
754 + var ptr = 0, c = createChecksum(), buf = Buffer.alloc(65536);
755 + while (ptr < filesize) { var len = fs.readSync(fd, buf, 0, Math.min(65536, filesize - ptr), ptr); c.update(buf, len); ptr += len; }
756 + return c.digest();
757 + }
758 +
759 + // Steaming checksum methods
760 + // TODO: Works only with files padded to 4 byte.
761 + function createChecksum() {
762 + const obj = { checksum: 0, length: 0 };
763 + obj.update = function (data, len) {
764 + if (!len) { len = data.length; }
765 + for (var i = 0; i < (len / 4) ; i++) {
766 + if (((obj.length / 4) + i) == 54) continue; // Skip PE checksum location
767 + const dword = data.readUInt32LE(i * 4);
768 + var checksumlo = (obj.checksum > 4294967296) ? (obj.checksum - 4294967296) : obj.checksum;
769 + var checksumhi = (obj.checksum > 4294967296) ? 1 : 0;
770 + obj.checksum = checksumlo + dword + checksumhi;
771 + if (obj.checksum > 4294967296) {
772 + checksumlo = (obj.checksum > 4294967296) ? (obj.checksum - 4294967296) : obj.checksum;
773 + checksumhi = (obj.checksum > 4294967296) ? 1 : 0;
774 + obj.checksum = checksumlo + checksumhi;
775 + }
776 + }
777 + obj.length += len;
778 + }
779 + obj.digest = function () {
780 + obj.checksum = (obj.checksum & 0xffff) + (obj.checksum >>> 16);
781 + obj.checksum = (obj.checksum) + (obj.checksum >>> 16);
782 + obj.checksum = obj.checksum & 0xffff;
783 + obj.checksum += obj.length;
784 + return obj.checksum;
785 + }
786 + return obj;
787 + }
788 +
789 + // Simple checksum method that works on a complete file at once
790 + // TODO: Works only with files padded to 4 byte.
791 + function updateChecksum(data) {
792 + var checksum = 0;
793 + for (var i = 0; i < (data.length / 4) ; i++) {
794 + if (i == 54) continue; // Skip PE checksum location
795 + var dword = data.readUInt32LE(i * 4);
796 + var checksumlo = (checksum > 4294967296) ? (checksum - 4294967296) : checksum;
797 + var checksumhi = (checksum > 4294967296) ? 1 : 0;
798 + checksum = checksumlo + dword + checksumhi;
799 + if (checksum > 4294967296) {
800 + checksumlo = (checksum > 4294967296) ? (checksum - 4294967296) : checksum;
801 + checksumhi = (checksum > 4294967296) ? 1 : 0;
802 + checksum = checksumlo + checksumhi;
803 + }
804 + }
805 + checksum = (checksum & 0xffff) + (checksum >>> 16);
806 + checksum = (checksum) + (checksum >>> 16);
807 + checksum = checksum & 0xffff;
808 + checksum += data.length;
809 + return checksum;
810 + }
811 +
812 // Compute the PE checksum of an entire file
813 function getChecksum(data) {
747 - var checksum = 0, top = Math.pow(2, 32);
814 + var checksum = 0;
815 for (var i = 0; i < (data.length / 4) ; i++) {
816 if (i == 54) continue; // Skip PE checksum location
817 var dword = data.readUInt32LE(i * 4);
751 - var checksumlo = (checksum > top) ? (checksum - top) : checksum;
752 - var checksumhi = (checksum > top) ? 1 : 0;
818 + var checksumlo = (checksum > 4294967296) ? (checksum - 4294967296) : checksum;
819 + var checksumhi = (checksum > 4294967296) ? 1 : 0;
820 checksum = checksumlo + dword + checksumhi;
754 - if (checksum > top) {
755 - checksumlo = (checksum > top) ? (checksum - top) : checksum;
756 - checksumhi = (checksum > top) ? 1 : 0;
821 + if (checksum > 4294967296) {
822 + checksumlo = (checksum > 4294967296) ? (checksum - 4294967296) : checksum;
823 + checksumhi = (checksum > 4294967296) ? 1 : 0;
824 checksum = checksumlo + checksumhi;
825 }
826 }
@@ -816,7 +883,7 @@ function createAuthenticodeHandler(path) {
883
884 // Open the output file
885 var output = null;
819 - try { output = fs.openSync(args.out, 'w'); } catch (ex) { }
886 + try { output = fs.openSync(args.out, 'w+'); } catch (ex) { }
887 if (output == null) return false;
888 var tmp, written = 0;
889 var executableSize = obj.header.sigpos ? obj.header.sigpos : this.filesize;
@@ -853,6 +920,12 @@ function createAuthenticodeHandler(path) {
920 fs.writeSync(output, win);
921 fs.writeSync(output, p7signature);
922 if (padding > 0) { fs.writeSync(output, Buffer.alloc(padding, 0)); }
923 + written += p7signature.length + padding + 8;
924 +
925 + // Compute the checksum and write it in the PE header at position (54 * 4)
926 + var tmp = Buffer.alloc(4);
927 + tmp.writeUInt32LE(runChecksumOnFile(output, written));
928 + fs.writeSync(output, tmp, 0, 4, 54 * 4);
929
930 // Close the file
931 fs.closeSync(output);
@@ -862,7 +935,7 @@ function createAuthenticodeHandler(path) {
935 // Save an executable without the signature
936 obj.unsign = function (args) {
937 // Open the file
865 - var output = fs.openSync(args.out, 'w');
938 + var output = fs.openSync(args.out, 'w+');
939 var written = 0, totalWrite = obj.header.sigpos;
940
941 // Compute pre-header length and copy that to the new file
@@ -881,6 +954,12 @@ function createAuthenticodeHandler(path) {
954 fs.writeSync(output, tmp);
955 written += tmp.length;
956 }
957 +
958 + // Compute the checksum and write it in the PE header at position (54 * 4)
959 + var tmp = Buffer.alloc(4);
960 + tmp.writeUInt32LE(runChecksumOnFile(output, written));
961 + fs.writeSync(output, tmp, 0, 4, 54 * 4);
962 +
963 fs.closeSync(output);
964 }
965