@@ -627,51 +627,45 @@ static bool MCDeployBuildSpcIndirectDataContent(BIO *p_hash, SpcIndirectDataCont
627627 return t_success;
628628}
629629
630+ constexpr uint32_t kSecurityEntryOffset32 = 152 ;
631+ constexpr uint32_t kSecurityEntryOffset64 = 168 ;
632+
630633// This method checks to see if the input file is a valid Windows EXE (well, as
631634// valid as we need it to be). It then returns the offset to the PE header if
632635// successful. Additionally, we return the offset of the certificate entry, or
633636// the length of the file (if no current cert).
634- static bool MCDeploySignCheckWindowsExecutable (MCDeployFileRef p_input, uint32_t & r_pe_offset, uint32_t & r_cert_offset)
637+ static bool MCDeploySignCheckWindowsExecutable (MCDeployFileRef p_input, uint32_t & r_pe_offset, uint32_t & r_cert_offset, MCDeployArchitecture &r_architecture )
635638{
636639 // First get the length of the input file
637640 uint32_t t_length;
638641 if (!MCDeployFileMeasure (p_input, t_length))
639642 return false ;
640643
641- // Now check the first two bytes - these should be MZ
642- char t_buffer[4 ];
643- if (!MCDeployFileReadAt (p_input, t_buffer, 2 , 0 ) ||
644- !MCMemoryEqual (t_buffer, " MZ" , 2 ))
645- return MCDeployThrow (kMCDeployErrorWindowsBadDOSSignature );
646-
647- // Now read in the offset to the pe header - this resides at
648- // byte offset 60 (member e_lfanew in IMAGE_DOS_HEADER).
649644 uint32_t t_offset;
650- if (t_length < 64 ||
651- !MCDeployFileReadAt (p_input, &t_offset, 4 , 60 ))
652- return MCDeployThrow (kMCDeployErrorWindowsBadDOSHeader );
653-
654- // Swap from non-network to host byte order
655- MCDeployByteSwap32 (false , t_offset);
656-
657- // Check the NT header is big enough - here 160 is the minimum size of the
658- // NT header we need. This is:
659- // 4 byte signature
660- // 20 byte file header
661- // 28 byte standard header
662- // 68 byte optional header
663- // 40 byte data directory (i.e. up to and including SECURITY entry).
664- if (t_length < t_offset + 160 )
665- return MCDeployThrow (kMCDeployErrorWindowsNoNTHeader );
666-
667- // Now make sure the NT Signature is correct and read in the existing cert
668- // fields. Note that the offset here is that of the 5th data directory entry
669- // (4 + 20 + 28 + 64 + 5 * 8).
645+ if (!MCDeployWindowsPEHeaderOffset (p_input, t_offset))
646+ return false ;
647+
648+ MCDeployArchitecture t_arch;
649+ if (!MCDeployWindowsArchitecture (p_input, t_offset, t_arch))
650+ return false ;
651+
652+ uint32_t t_security_offset = t_offset;
653+ if (t_arch == kMCDeployArchitecture_I386 )
654+ {
655+ t_security_offset += kSecurityEntryOffset32 ;
656+ }
657+ else
658+ {
659+ t_security_offset += kSecurityEntryOffset64 ;
660+ }
661+
662+ // Now read in the existing cert fields. Note that the offset here is
663+ // that of the 5th data directory entry
670664 uint32_t t_cert_section[2 ];
671- if (!MCDeployFileReadAt (p_input, t_buffer, 4 , t_offset) ||
672- !MCMemoryEqual (t_buffer, " PE\0\0 " , 4 ) ||
673- !MCDeployFileReadAt (p_input, t_cert_section, 2 * sizeof (uint32_t ), t_offset + 152 ))
665+ if (!MCDeployFileReadAt (p_input, t_cert_section, 2 * sizeof (uint32_t ), t_security_offset))
666+ {
674667 return MCDeployThrow (kMCDeployErrorWindowsBadNTSignature );
668+ }
675669
676670 MCDeployByteSwap32 (false , t_cert_section[0 ]);
677671 MCDeployByteSwap32 (false , t_cert_section[1 ]);
@@ -687,6 +681,7 @@ static bool MCDeploySignCheckWindowsExecutable(MCDeployFileRef p_input, uint32_t
687681 r_cert_offset = t_length;
688682
689683 r_pe_offset = t_offset;
684+ r_architecture = t_arch;
690685
691686 return true ;
692687}
@@ -720,7 +715,7 @@ static bool MCDeploySignCopyFileAt(BIO *p_output, MCDeployFileRef p_input, uint3
720715
721716// This method reconstructs the output executable from the input executable
722717// while computing the hash of the critical parts of the file.
723- static bool MCDeploySignHashWindowsExecutable (MCDeployFileRef p_input, BIO *p_output, uint32_t p_pe_offset, uint32_t p_cert_offset, BIO*& r_hash)
718+ static bool MCDeploySignHashWindowsExecutable (MCDeployFileRef p_input, BIO *p_output, uint32_t p_pe_offset, uint32_t p_cert_offset, MCDeployArchitecture p_architecture, BIO*& r_hash)
724719{
725720 bool t_success;
726721 t_success = true ;
@@ -742,8 +737,9 @@ static bool MCDeploySignHashWindowsExecutable(MCDeployFileRef p_input, BIO *p_ou
742737 // the input executable as we go.
743738
744739 // The first part of the output file is everything up to the start of the
745- // 'CheckSum' field of the IMAGE_OPTIONAL_HEADER32 structure. This is at
746- // offset 88 in said structure. This part is part of the hash.
740+ // 'CheckSum' field of the IMAGE_OPTIONAL_HEADER structure. This is at
741+ // offset 88 in both IMAGE_OPTIONAL_HEADER32 and IMAGE_OPTIONAL_HEADER64.
742+ // This part is part of the hash.
747743 if (t_success)
748744 t_success = MCDeploySignCopyFileAt (t_hash, p_input, 0 , p_pe_offset + 88 );
749745
@@ -757,20 +753,30 @@ static bool MCDeploySignHashWindowsExecutable(MCDeployFileRef p_input, BIO *p_ou
757753 t_success = MCDeployThrowOpenSSL (kMCDeployErrorBadWrite );
758754 }
759755
756+ uint32_t t_security_offset = p_pe_offset;
757+ if (p_architecture == kMCDeployArchitecture_I386 )
758+ {
759+ t_security_offset += kSecurityEntryOffset32 ;
760+ }
761+ else
762+ {
763+ t_security_offset += kSecurityEntryOffset64 ;
764+ }
765+
760766 // Next is the section of the header after the CheckSum field and up to
761- // the 'Security' data directory entry. This entry is at offset 152.
767+ // the 'Security' data directory entry.
762768 if (t_success)
763- t_success = MCDeploySignCopyFileAt (t_hash, p_input, p_pe_offset + 92 , 60 );
769+ t_success = MCDeploySignCopyFileAt (t_hash, p_input, p_pe_offset + 92 , t_security_offset - (p_pe_offset + 92 ) );
764770
765771 // Now write out the (current) value of the Security data directory entry,
766772 // but not into the hash.
767773 if (t_success)
768- t_success = MCDeploySignCopyFileAt (p_output, p_input, p_pe_offset + 152 , 8 );
774+ t_success = MCDeploySignCopyFileAt (p_output, p_input, t_security_offset , 8 );
769775
770776 // After the Security data directory, everything up to the cert offset is
771777 // hashed.
772778 if (t_success)
773- t_success = MCDeploySignCopyFileAt (t_hash, p_input, p_pe_offset + 160 , p_cert_offset - (p_pe_offset + 160 ));
779+ t_success = MCDeploySignCopyFileAt (t_hash, p_input, t_security_offset + 8 , p_cert_offset - (t_security_offset + 8 ));
774780
775781 // Finally we round the output up to the nearest 8 bytes.
776782 if (t_success && (p_cert_offset % 8 != 0 ))
@@ -1047,6 +1053,8 @@ static bool MCDeploySignWindowsAddTimeStamp(const MCDeploySignParameters& p_para
10471053 return t_success;
10481054}
10491055
1056+ static bool s_objects_created = false ;
1057+
10501058bool MCDeploySignWindows (const MCDeploySignParameters& p_params)
10511059{
10521060 bool t_success;
@@ -1094,13 +1102,14 @@ bool MCDeploySignWindows(const MCDeploySignParameters& p_params)
10941102 // Next we check the input file, and compute the hash, writing out the new
10951103 // version of the executable as we go.
10961104 uint32_t t_pe_offset, t_cert_offset;
1105+ MCDeployArchitecture t_arch;
10971106 if (t_success)
1098- t_success = MCDeploySignCheckWindowsExecutable (t_input, t_pe_offset, t_cert_offset);
1107+ t_success = MCDeploySignCheckWindowsExecutable (t_input, t_pe_offset, t_cert_offset, t_arch );
10991108
11001109 BIO *t_hash;
11011110 t_hash = nil;
11021111 if (t_success)
1103- t_success = MCDeploySignHashWindowsExecutable (t_input, t_output, t_pe_offset, t_cert_offset, t_hash);
1112+ t_success = MCDeploySignHashWindowsExecutable (t_input, t_output, t_pe_offset, t_cert_offset, t_arch, t_hash);
11041113
11051114 // Next we create a PKCS#7 object ready for filling with the stuff we need for
11061115 // Authenticode.
@@ -1145,12 +1154,18 @@ bool MCDeploySignWindows(const MCDeploySignParameters& p_params)
11451154
11461155 // The various ASN.1 structures we are going to use require a number of ObjectIDs.
11471156 // We register them all here, to save having to check the return value of OBJ_txt2obj.
1148- if (t_success)
1157+ if (t_success && !s_objects_created)
1158+ {
11491159 if (!OBJ_create (SPC_INDIRECT_DATA_OBJID, SPC_INDIRECT_DATA_OBJID, SPC_INDIRECT_DATA_OBJID) ||
11501160 !OBJ_create (SPC_PE_IMAGE_DATA_OBJID, SPC_PE_IMAGE_DATA_OBJID, SPC_PE_IMAGE_DATA_OBJID) ||
11511161 !OBJ_create (SPC_STATEMENT_TYPE_OBJID, SPC_STATEMENT_TYPE_OBJID, SPC_STATEMENT_TYPE_OBJID) ||
11521162 !OBJ_create (SPC_SP_OPUS_INFO_OBJID, SPC_SP_OPUS_INFO_OBJID, SPC_SP_OPUS_INFO_OBJID))
1163+ {
11531164 t_success = MCDeployThrowOpenSSL (kMCDeployErrorBadSignature );
1165+ }
1166+
1167+ s_objects_created = true ;
1168+ }
11541169
11551170 // Authenticode signatures require a single SignerInfo structure to be present.
11561171 // To create this we add a signature for the certificate we just located.
@@ -1305,6 +1320,16 @@ bool MCDeploySignWindows(const MCDeploySignParameters& p_params)
13051320 // ... And update the SECURITY table entry in the PE header appropraitely.
13061321 if (t_success)
13071322 {
1323+ uint32_t t_security_offset = t_pe_offset;
1324+ if (t_arch == kMCDeployArchitecture_I386 )
1325+ {
1326+ t_security_offset += kSecurityEntryOffset32 ;
1327+ }
1328+ else
1329+ {
1330+ t_security_offset += kSecurityEntryOffset64 ;
1331+ }
1332+
13081333 uint32_t t_entry[2 ];
13091334
13101335 // First entry is the offset of the cert, making sure it is padded to 8
@@ -1315,7 +1340,7 @@ bool MCDeploySignWindows(const MCDeploySignParameters& p_params)
13151340 t_entry[1 ] = 8 + t_signature_size + (t_signature_size % 8 != 0 ? (8 - (t_signature_size % 8 )) : 0 );
13161341
13171342 MCDeployByteSwapRecord (false , " ll" , t_entry, sizeof (t_entry));
1318- if (BIO_seek (t_output, t_pe_offset + 152 ) == -1 ||
1343+ if (BIO_seek (t_output, t_security_offset ) == -1 ||
13191344 BIO_write (t_output, t_entry, sizeof (t_entry)) != sizeof (t_entry))
13201345 t_success = MCDeployThrowOpenSSL (kMCDeployErrorBadWrite );
13211346 }
0 commit comments