Skip to content

Instantly share code, notes, and snippets.

@gsuberland
Created August 28, 2021 04:02
Show Gist options
  • Save gsuberland/1ec41a34f1c904fec69f91d875dd184d to your computer and use it in GitHub Desktop.
Save gsuberland/1ec41a34f1c904fec69f91d875dd184d to your computer and use it in GitHub Desktop.
Linear Executable (LE) EXE header dumper for mixed 16/32-bit VXDs
// 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