Created
June 17, 2024 06:06
-
-
Save MichalStrehovsky/0d9e467b750df3d22ecb53d8a4ba7c72 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.Immutable; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Reflection.Metadata; | |
using System.Reflection.PortableExecutable; | |
using System.Text; | |
PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(machine: Machine.I386, imageCharacteristics: Characteristics.ExecutableImage); | |
ShimExeBuilder peBuilder = new ShimExeBuilder(peHeaderBuilder, "kernel32.dll", "ExitProcess"); | |
BlobBuilder o = new BlobBuilder(); | |
peBuilder.Serialize(o); | |
using var fs = File.Create("c:\\temp\\blah.exe"); | |
o.WriteContentTo(fs); | |
class ShimExeBuilder : PEBuilder | |
{ | |
private readonly byte[] _dllName; | |
private readonly byte[] _entrypointName; | |
private readonly PEDirectoriesBuilder _peDirectoriesBuilder; | |
private const string TextSectionName = ".text"; | |
private const string RelocationSectionName = ".reloc"; | |
private bool Is32Bit => Header.Machine is not Machine.Amd64 and not Machine.Arm64; | |
public ShimExeBuilder(PEHeaderBuilder header, string dllName, string entrypointName, | |
Func<IEnumerable<Blob>, BlobContentId> deterministicIdProvider = null) : base(header, deterministicIdProvider) | |
{ | |
_dllName = Encoding.UTF8.GetBytes(dllName); // TODO: this is probably not UTF-8 | |
_entrypointName = Encoding.UTF8.GetBytes(entrypointName); // TODO: this is probably not UTF-8 | |
_peDirectoriesBuilder = new PEDirectoriesBuilder(); | |
} | |
protected override ImmutableArray<Section> CreateSections() | |
{ | |
var textSection = new Section(TextSectionName, SectionCharacteristics.MemRead | SectionCharacteristics.MemExecute | SectionCharacteristics.ContainsCode); | |
if (Header.Machine != Machine.I386) | |
return [textSection]; | |
var relocSection = new Section(RelocationSectionName, SectionCharacteristics.MemRead | SectionCharacteristics.MemDiscardable | SectionCharacteristics.ContainsInitializedData); | |
return [textSection, relocSection]; | |
} | |
protected override BlobBuilder SerializeSection(string name, SectionLocation location) => | |
name switch | |
{ | |
TextSectionName => SerializeTextSection(location), | |
RelocationSectionName => SerializeRelocationSection(location), | |
_ => throw new UnreachableException(), | |
}; | |
private BlobBuilder SerializeTextSection(SectionLocation location) | |
{ | |
var builder = new BlobBuilder(); | |
// | |
// Write Import Address Table | |
// | |
int sizeOfImportAddressTable = Is32Bit ? 2 * sizeof(uint) : 2 * sizeof(ulong); | |
int importAddressTableRva = location.RelativeVirtualAddress + builder.Count; | |
{ | |
int ilRva = sizeOfImportAddressTable + 40; | |
int hintRva = ilRva + (Is32Bit ? 12 : 16); | |
if (Is32Bit) | |
{ | |
builder.WriteUInt32((uint)hintRva); // 4 | |
builder.WriteUInt32(0); // 8 | |
} | |
else | |
{ | |
builder.WriteUInt64((uint)hintRva); // 8 | |
builder.WriteUInt64(0); // 16 | |
} | |
} | |
_peDirectoriesBuilder.ImportAddressTable = new DirectoryEntry(importAddressTableRva, sizeOfImportAddressTable); | |
Debug.Assert(builder.Count - (importAddressTableRva - location.RelativeVirtualAddress) == sizeOfImportAddressTable); | |
// | |
// Write Import Table | |
// | |
int importTableRva = location.RelativeVirtualAddress + builder.Count; | |
{ | |
int ilRVA = importTableRva + 40; | |
int hintRva = ilRVA + (Is32Bit ? 12 : 16); | |
int nameRva = hintRva + _entrypointName.Length + 1 + 2; | |
// Import table | |
builder.WriteUInt32((uint)ilRVA); // 4 | |
builder.WriteUInt32(0); // 8 | |
builder.WriteUInt32(0); // 12 | |
builder.WriteUInt32((uint)nameRva); // 16 | |
builder.WriteUInt32((uint)importAddressTableRva); // 20 | |
builder.WriteBytes(0, 20); // 40 | |
// Import Lookup table | |
if (Is32Bit) | |
{ | |
builder.WriteUInt32((uint)hintRva); // 44 | |
builder.WriteUInt32(0); // 48 | |
builder.WriteUInt32(0); // 52 | |
} | |
else | |
{ | |
builder.WriteUInt64((uint)hintRva); // 48 | |
builder.WriteUInt64(0); // 56 | |
} | |
// Hint table | |
builder.WriteUInt16(0); // Hint 54|58 | |
builder.WriteBytes(_entrypointName); // 65|69 | |
builder.WriteByte(0); // 66|70 | |
} | |
_peDirectoriesBuilder.ImportTable = new DirectoryEntry(importTableRva, builder.Count - (importTableRva - location.RelativeVirtualAddress)); | |
// Write Name Table | |
builder.WriteBytes(_dllName); | |
builder.WriteByte(0); | |
builder.WriteUInt16(0); | |
// Generate indirect jump machine code | |
_peDirectoriesBuilder.AddressOfEntryPoint = location.RelativeVirtualAddress + builder.Count; | |
// entry point code, consisting of a jump indirect | |
if (Header.Machine == Machine.I386) | |
{ | |
builder.WriteByte(0xff); | |
builder.WriteByte(0x25); | |
builder.WriteUInt32((uint)importAddressTableRva + (uint)Header.ImageBase); | |
} | |
else if (Header.Machine == Machine.Amd64) | |
{ | |
builder.WriteByte(0xff); | |
builder.WriteByte(0x25); | |
builder.WriteInt32(importAddressTableRva - _peDirectoriesBuilder.AddressOfEntryPoint - 6); | |
} | |
else | |
{ | |
throw new NotImplementedException(); | |
} | |
return builder; | |
} | |
private BlobBuilder SerializeRelocationSection(SectionLocation location) | |
{ | |
var sectionBuilder = new BlobBuilder(); | |
// Page RVA | |
sectionBuilder.WriteUInt32((((uint)_peDirectoriesBuilder.AddressOfEntryPoint + 2) / 0x1000) * 0x1000); | |
// Block size | |
sectionBuilder.WriteUInt32(12u); | |
uint offsetWithinPage = ((uint)_peDirectoriesBuilder.AddressOfEntryPoint + 2) % 0x1000; | |
uint relocType = 3u; | |
ushort s = (ushort)((relocType << 12) | offsetWithinPage); | |
sectionBuilder.WriteUInt16(s); | |
sectionBuilder.WriteUInt16(0); // next chunk's RVA | |
_peDirectoriesBuilder.BaseRelocationTable = new DirectoryEntry(location.RelativeVirtualAddress, sectionBuilder.Count); | |
return sectionBuilder; | |
} | |
protected override PEDirectoriesBuilder GetDirectories() => _peDirectoriesBuilder; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment