Skip to content

Commit fcd5a44

Browse files
committed
[[ Win x86_64 ]] Update _internal sign for 64 bit executables
This patch updates the `_internal sign windows` command for 64 bit executables. The patch also updates the deploy command to reuse: - `MCDeployWindowsPEHeaderOffset` which does some basic DOS header checking and extraction of the PE header offset. - `MCDeployWindowsArchitecture` which does some basic NT header checking and determiniation of the executable architecture.
1 parent 107e816 commit fcd5a44

File tree

3 files changed

+164
-83
lines changed

3 files changed

+164
-83
lines changed

engine/src/deploy.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,6 @@ enum MCDeployError
427427
kMCDeployErrorWindowsBadDocIcon,
428428
kMCDeployErrorWindowsBadManifest,
429429
kMCDeployErrorWindowsBadSecuritySection,
430-
kMCDeployErrorWindowsUnkownPlatform,
431430

432431
kMCDeployErrorLinuxNoHeader,
433432
kMCDeployErrorLinuxBadHeaderMagic,
@@ -538,4 +537,9 @@ bool MCDeployWritePayload(const MCDeployParameters& p_params, bool p_to_network,
538537

539538
////////////////////////////////////////////////////////////////////////////////
540539

540+
bool MCDeployWindowsPEHeaderOffset(MCDeployFileRef p_file, uint32_t &r_pe_offset);
541+
bool MCDeployWindowsArchitecture(MCDeployFileRef p_file, uint32_t p_pe_offset, MCDeployArchitecture &r_platform);
542+
543+
////////////////////////////////////////////////////////////////////////////////
544+
541545
#endif

engine/src/deploy_sign.cpp

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
10501058
bool 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

Comments
 (0)