I'm trying to sign an existing Portable Executable manually.
I'm following instructions found in this document:
The following code tries to get the part-to-be-hashed from the image:
// Variables
// full: vector<char> holding the image
// d: vector<char> where to store the data-to-be-hashed
// sections: vector of the sections, ensuring size > 0
// nt/pnt* : pointer inside full that points to the beginning of NT header
// Sort Sections
std::sort(sections.begin(), sections.end(), [](const section& s1, const section& s2) -> bool
{
if (s1.sec->PointerToRawData < s2.sec->PointerToRawData)
return true;
return false;
});
// Up to where?
size_t BytesUpToLastSection = ((char*)(sections[sections.size() - 1].sec) - full.data()) + sizeof(image_section_header);
d.resize(BytesUpToLastSection);
memcpy(d.data(), full.data(), BytesUpToLastSection);
// We remove the certificate table entry (8 bytes)
size_t offset = 0;
if (nt.Is32())
{
offset = offsetof(optional_header_32, DataDirectory[DIR_SECURITY]);
}
else
{
offset = offsetof(optional_header_64, DataDirectory[DIR_SECURITY]);
}
offset += sizeof(nt.FileHeader) + sizeof(nt.Signature);
offset += pnt - full.data();
d.erase(d.begin() + offset, d.begin() + offset + 8);
// We remove the checksum (4 bytes)
if (nt.Is32())
offset = offsetof(optional_header_32,CheckSum);
else
offset = offsetof(optional_header_64,CheckSum);
offset += sizeof(nt.FileHeader) + sizeof(nt.Signature);
offset += pnt - full.data();
d.erase(d.begin() + offset, d.begin() + offset + 4);
// Counter
size_t SUM_OF_BYTES_HASHED = 0;
if (nt.Is32())
SUM_OF_BYTES_HASHED = std::get<optional_header_32>(nt.OptionalHeader).SizeOfHeaders;
else
SUM_OF_BYTES_HASHED = std::get<optional_header_64>(nt.OptionalHeader).SizeOfHeaders;
for (auto& ss : sections)
{
if (ss.sectionData.sz == 0)
continue;
s = d.size();
d.resize(d.size() + ss.sectionData.sz);
memcpy(d.data() + s, ss.sectionData.p, ss.sectionData.sz);
SUM_OF_BYTES_HASHED += ss.sec->SizeOfRawData;
}
size_t FILE_SIZE = full.size();
if (FILE_SIZE > SUM_OF_BYTES_HASHED)
{
// Not entering here, test executable does not have extra data
}
There must be a problem somewhere. Signing this data and then updating the executable Certifcate Entry and appending the PCKS#7 signature results in an executable that is not recognized by Windows. Right click-> "Invalid Signature".
When comparing with the result of signtool.exe
, the signature is different. When I try to verify this signature with CryptVerifyDetachedMessageSignature
, there's an error 0x80091007 which means that the hash is incorrect.
Which means that I don't calculate correctly the "what to be signed" buffer. What do I miss?
I even hardcoded the removal of the entries:
d = full;
d.erase(d.begin() + 296, d.begin() + 296 + 8);
d.erase(d.begin() + 216, d.begin() + 216 + 4);
Thanks a lot.
I found the solution.
The signature inside the PE is not a typical PKCS#7, it contains also specific authenticated and unauthenticated attributes described in the document.
Once these are satisfied, the signature is OK. However I can't use CAdES because Windows will not accept any other authenticated attributed in the list, which is required for CAdES to be validated.
User contributions licensed under CC BY-SA 3.0