I've an EXE which is code signed and saved on the server. When any user wants to download the EXE, I've to embed a user specific information into the EXE which I get to know when the download request comes from the user.
Constraints:
- I can't rebuild EXE.
- I can't resign EXE.
- Whatever I do, I have to make sure the integrity of content and signature remains intact.
I came to know that it is possible to store custom data in the signature field from this blog post signing. This blog mentioned three ways:
- Padding/stuffing
- Unauthenticated attributes of signature
- Additional certificate field of signature
The first technique mentioned over there is padding. In this technique we can leverage the unsed bytes in the last 8 bytes of the signature as signature always takes up space which is in multiles of 8.
So let's say if signature is of 60 bytes then 64 bytes will be allocated which is the nearest multiple of 8. Signature will use 60 bytes and we can use remaining 4 bytes to stuff our custom data.
I observed a couple of signed EXEs saw that we can indeed get 1 to 7 bytes:
- git-bash.exe - 2 byte
- cmtrace.exe - 3 bytes
- chrome.exe - 0 bytes
- iisexpress.exe - 0 bytes
- iexplore.exe - 2 bytes
But as you can see above in some cases it can be zero as well. The C# .NET Core code below tells me the starting position of signature and length of signature in any EXE:
public static byte[] ExtractPadding(string filePath, out long signatureStartLocation, out int signatureLengthInUse)
{
signatureLengthInUse = 0;
using (var file = new PortableExecutable(filePath))
{
var dosHeader = file.GetDosHeader();
var peHeader = file.GetPEHeader(dosHeader);
var signatureLocation = peHeader.DataDirectories[ImageDataDirectoryEntry.IMAGE_DIRECTORY_ENTRY_SECURITY];
signatureStartLocation = signatureLocation.VirtualAddress;
using (var signatureData = file.ReadDataDirectory(signatureLocation))
{
using (var reader = new BinaryReader(signatureData))
{
var winCertLength = reader.ReadUInt32();
var winCertRevision = reader.ReadUInt16();
var winCertType = reader.ReadUInt16();
if (winCertRevision != 0x200 && winCertRevision != 0x100)
{
return null;
}
if (winCertType != 0x0002)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
int read;
Span<byte> buffer = stackalloc byte[0x400];
while ((read = reader.Read(buffer)) > 0)
{
memoryStream.Write(buffer.Slice(0, read));
}
var winCertificate = memoryStream.ToArray();
var signer = new SignedCms();
signer.Decode(winCertificate);
var roundTrip = signer.Encode();
var sizeDifference = winCertificate.Length - roundTrip.Length;
var difference = new byte[sizeDifference];
signatureLengthInUse = roundTrip.Length;
Buffer.BlockCopy(winCertificate, roundTrip.Length, difference, 0, difference.Length);
return difference;
}
}
}
}
}
For my use case 7 bytes of space is very limiting as I need to pass data little more than that. Can someone help me in this if there is any other way or extension of the current approach which can help me write more data in signature field without invalidating the EXE or signature?
Say you find a piece of the file that can contain some random bytes and isn't affected by the signature, and you stuff your unique identifier in there... what's to stop any end-user from doing the exact same thing, essentially defeating your copy-protection? That's the whole point of code signing - to stop people from messing with a file in this exact manner.
If you "can't" (which I suspect is more like "don't want to") re-build and re-sign the file for each unique user, you can't embed your unique identifier into it. The fact that an arbitrary blog post from half a decade ago notes this technique doesn't mean that it works today, or that it will continue to work in the future, or that Microsoft won't flag your EXE as malicious. If you want to colour outside the lines, be prepared to be burned.