Created
August 28, 2021 04:02
-
-
Save gsuberland/1ec41a34f1c904fec69f91d875dd184d to your computer and use it in GitHub Desktop.
Linear Executable (LE) EXE header dumper for mixed 16/32-bit VXDs
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
// dump LE EXE headers for mixed 16/32-bit VXDs | |
// ref: https://faydoc.tripod.com/formats/exe-LE.htm | |
// ref: https://github.com/open-watcom/open-watcom-v2/blob/master/bld/watcom/h/exeflat.h | |
// ref: http://www.textfiles.com/programming/FORMATS/lxexe.txt (this is for LX, not LE, but layout is roughly the same) | |
enum Endianness : byte | |
{ | |
LittleEndian = 0, | |
BigEndian = 1 | |
} | |
enum CPUType : UInt16 | |
{ | |
Intel_80286 = 1, | |
Intel_80386 = 2, | |
Intel_80486 = 3, | |
Intel_80586 = 4, | |
Intel_i860 = 0x20, | |
Intel_N11 = 0x21, | |
MIPS_R2000_R3000 = 0x40, | |
MIPS_R6000 = 0x41, | |
MIPS_R4000 = 0x42 | |
} | |
enum TargetOS : UInt16 | |
{ | |
OS2 = 0x1, | |
Windows = 0x2, | |
DOS_4x = 0x3, | |
Windows386 = 0x4 | |
} | |
[Flags] | |
enum ModuleTypeFlags : UInt32 | |
{ | |
PerProcessLibraryInitialisation = 0x4, | |
InternalFixupsApplied = 0x10, | |
ExternalFixupsApplied = 0x20, | |
ModuleNotLoadable = 0x2000, | |
PerProcessLibraryTermination = 0x40000000, | |
} | |
enum ModuleType : UInt32 | |
{ | |
ProgramModule = 0, | |
LibraryModule = 0x8000, | |
ProtectedMemoryLibraryModule = 0x18000, | |
PhysicalDeviceDriverModule = 0x20000, | |
VirtualDeviceDriverModule = 0x28000, | |
} | |
enum ModuleWindowing : UInt32 | |
{ | |
Unknown = 0, | |
IncompatibleWithPMWindowing = 1, | |
CompatibleWithPMWindowing = 2, | |
UsesPMWindowingAPI = 3, | |
} | |
static string FlagsToString(Enum value) | |
{ | |
var sb = new StringBuilder(); | |
Type enumType = value.GetType(); | |
bool first = true; | |
foreach (var enumValue in enumType.GetEnumValues()) | |
{ | |
if (value.HasFlag((Enum)enumValue)) | |
{ | |
if (!first) | |
{ | |
sb.Append(", "); | |
} | |
first = false; | |
sb.Append(Enum.GetName(enumType, (Enum)enumValue)); | |
} | |
} | |
if (sb.Length == 0) | |
return "( none )"; | |
return sb.ToString(); | |
} | |
Regex camelCaseRegex = new Regex(@"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"); | |
string SeparateCamelCase(string camelCase) | |
{ | |
return string.Join(" ", camelCaseRegex.Split(camelCase)); | |
} | |
delegate void FieldHandlerDelegate(object value); | |
[AttributeUsage(AttributeTargets.Field)] | |
class FieldHandlerAttribute : Attribute | |
{ | |
public FieldHandlerDelegate Handler { get; } | |
public FieldHandlerAttribute(string delegateName) | |
{ | |
var type = typeof(UserQuery); | |
var targetMethod = type.GetMethod(delegateName, BindingFlags.Static | BindingFlags.Public); | |
Handler = (FieldHandlerDelegate)Delegate.CreateDelegate(typeof(FieldHandlerDelegate), targetMethod); | |
} | |
} | |
public static void ModuleTypeFlagsHandler(object value) | |
{ | |
UInt32 moduleTypeFlagsRaw = (UInt32)value; | |
ModuleTypeFlags moduleTypeFlags = (ModuleTypeFlags)(moduleTypeFlagsRaw); | |
ModuleType moduleType = (ModuleType)(moduleTypeFlagsRaw & 0x00038000); | |
ModuleWindowing moduleWindowing = (ModuleWindowing)((moduleTypeFlagsRaw & 0x700) >> 8); | |
Console.WriteLine("Module Type Flags: " + FlagsToString(moduleTypeFlags)); | |
Console.WriteLine("Module Type: " + moduleType); | |
Console.WriteLine("Module Windowing: " + moduleWindowing); | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
class HeaderInformationBlock | |
{ | |
public UInt16 SignatureWord; | |
public Endianness ByteOrder; | |
public Endianness WordOrder; | |
public UInt32 ExecutableFormatLevel; | |
public CPUType CPUType; | |
public TargetOS TargetOperatingSystem; | |
public UInt32 ModuleVersion; | |
[FieldHandler("ModuleTypeFlagsHandler")] | |
public UInt32 ModuleTypeFlags; | |
public UInt32 NumberOfMemoryPages; | |
public UInt32 InitialObjectCSNumber; | |
public UInt32 InitialEIP; | |
public UInt32 InitialSSObjectNumber; | |
public UInt32 InitialESP; | |
public UInt32 MemoryPageSize; | |
public UInt32 BytesOnLastPage; | |
public UInt32 FixupSectionSize; | |
public UInt32 FixupSectionChecksum; | |
public UInt32 LoaderSectionSize; | |
public UInt32 LoaderSectionChecksum; | |
public UInt32 ObjectTableOffset; | |
public UInt32 ObjectTableEntries; | |
public UInt32 ObjectPageMapOffset; | |
public UInt32 ObjectIterateDataMapOffset; | |
public UInt32 ResourceTableOffset; | |
public UInt32 ResourceTableEntries; | |
public UInt32 ResidentNamesTableOffset; | |
public UInt32 EntryTableOffset; | |
public UInt32 ModuleDirectivesTableOffset; | |
public UInt32 ModuleDirectivesTableEntries; | |
public UInt32 FixupPageTableOffset; | |
public UInt32 FixupRecordTableOffset; | |
public UInt32 ImportedModulesNameTableOffset; | |
public UInt32 ImportedModulesCount; | |
public UInt32 ImportedProcedureNameTableOffset; | |
public UInt32 PerPageChecksumTableOffset; | |
public UInt32 DataPagesOffsetFromTopOfFile; | |
public UInt32 PreloadPagesCount; | |
public UInt32 NonResidentNamesTableOffsetFromTopOfFile; | |
public UInt32 NonResidentNamesTableLength; | |
public UInt32 NonResidentNamesTableChecksum; | |
public UInt32 AutomaticDataObject; | |
public UInt32 DebugInformationOffset; | |
public UInt32 DebugInformationLength; | |
public UInt32 PreloadInstancePagesNumber; | |
public UInt32 DemandInstancePagesNumber; | |
public UInt32 HeapSize; | |
public UInt32 StackSize; | |
public UInt32 Reserved1; | |
public UInt32 Reserved2; | |
public UInt32 WindowsVXDVersionInfoResourceOffset; | |
public UInt32 WindowsVXDVersionInfoResourceLength; | |
public UInt16 WindowsVXDDeviceID; | |
public UInt16 WindowsDDKVersion; | |
} | |
void Main() | |
{ | |
using (var fs = new FileStream(@"N:\Research\Vtdapi.w95", FileMode.Open, FileAccess.Read)) | |
using (var br = new BinaryReader(fs)) | |
{ | |
string mz = Encoding.ASCII.GetString(br.ReadBytes(2)); | |
if (mz != "MZ") | |
{ | |
Console.WriteLine("Missing MZ magic number. Editing"); | |
return; | |
} | |
fs.Seek(0x3C, SeekOrigin.Begin); | |
UInt32 e_lfanew = br.ReadUInt32(); | |
Console.WriteLine($"e_lfanew: {e_lfanew:x8}"); | |
if (e_lfanew > fs.Length) | |
{ | |
Console.WriteLine("e_lfanew exceeds file size. Exiting."); | |
return; | |
} | |
fs.Seek(e_lfanew, SeekOrigin.Begin); | |
string magic = Encoding.ASCII.GetString(br.ReadBytes(2)); | |
Console.WriteLine($"Magic: {magic}"); | |
if (magic != "LE") | |
{ | |
Console.WriteLine("Not an LE EXE. Exiting."); | |
return; | |
} | |
fs.Seek(e_lfanew, SeekOrigin.Begin); | |
Console.WriteLine(); | |
var header = new HeaderInformationBlock(); | |
foreach (var fieldInfo in typeof(HeaderInformationBlock).GetFields(BindingFlags.Public | BindingFlags.Instance)) | |
{ | |
var fieldType = fieldInfo.FieldType; | |
var targetType = fieldType; | |
if (fieldType.IsEnum) | |
{ | |
targetType = Enum.GetUnderlyingType(fieldType); | |
} | |
switch (targetType.Name.ToLower()) | |
{ | |
case "uint16": | |
fieldInfo.SetValue(header, br.ReadUInt16()); | |
break; | |
case "uint32": | |
fieldInfo.SetValue(header, br.ReadUInt32()); | |
break; | |
case "byte": | |
fieldInfo.SetValue(header, br.ReadByte()); | |
break; | |
default: | |
throw new NotSupportedException($"Type not supported: {targetType.Name}"); | |
} | |
} | |
foreach (var fieldInfo in typeof(HeaderInformationBlock).GetFields(BindingFlags.Public | BindingFlags.Instance)) | |
{ | |
var fieldType = fieldInfo.FieldType; | |
var fieldName = SeparateCamelCase(fieldInfo.Name); | |
var value = fieldInfo.GetValue(header); | |
var fieldHandlerAttrib = (FieldHandlerAttribute)fieldInfo.GetCustomAttribute(typeof(FieldHandlerAttribute)); | |
if (fieldHandlerAttrib != null) | |
{ | |
fieldHandlerAttrib.Handler(value); | |
} | |
else | |
{ | |
if (fieldType.IsEnum) | |
{ | |
if (fieldType.GetCustomAttribute(typeof(FlagsAttribute)) != null) | |
{ | |
Console.WriteLine($"{fieldName}: {FlagsToString((Enum)value)}"); | |
} | |
else | |
{ | |
Console.WriteLine($"{fieldName}: {value}"); | |
} | |
} | |
else | |
{ | |
switch (fieldType.Name.ToLower()) | |
{ | |
case "uint32": | |
Console.WriteLine($"{fieldName}: {value:x8}"); | |
break; | |
case "uint16": | |
Console.WriteLine($"{fieldName}: {value:x4}"); | |
break; | |
case "byte": | |
Console.WriteLine($"{fieldName}: {value:x2}"); | |
break; | |
default: | |
throw new NotSupportedException($"Type not supported: {fieldType.Name}"); | |
} | |
} | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment