Windows 10の実行ファイルはイメージファイルの一種であり、普通はPEファイルフォーマットを持ちます。PEファイルフォーマットではファイルの先頭にMS-DOSと共通のMZファイルヘッダー(IMAGE_DOS_HEADER
構造体)、続けてMS-DOS Real Modeスタブプログラムを持ち、さらにIMAGE_DOS_HEADET.e_lfanew
の指す位置にPEヘッダーを持ちます。
Windowsディレクトリのイメージファイル(実行ファイルやDLL等)のe_lfanew
を確認したところ、その範囲は思ったよりも広範でした。以下にサンプルコードを示します。
using System; using System.IO; using System.Linq; using System.Runtime.InteropServices; class Program { static void Main() { var paths = Directory.GetFiles(Environment.SystemDirectory); var infos = paths .Select(path => (FileName: Path.GetFileName(path), Header: MZFileInfo.TryGet(path, out var info) ? info : null)) .Where(filenameInfoPair => filenameInfoPair.Header?.IsValid ?? false) .ToArray(); // e_lfanewsの値の確認用 var lfanews = infos.Select(info => info.Header.Raw.e_lfanew).Distinct().OrderBy(x => x); var lfanewsStr = string.Join(", ", lfanews); // "128, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248, 256, 264, 272, 280, 288, 296, 304, 10872" var lookupByLfanew = infos.ToLookup( info => info.Header.Raw.e_lfanew, info => info); } } internal sealed class MZFileInfo { [StructLayout(LayoutKind.Sequential)] public struct IMAGE_DOS_HEADER { public ushort e_magic; public ushort e_cblp; public ushort e_cp; public ushort e_crlc; public ushort e_cparhdr; public ushort e_minalloc; public ushort e_maxalloc; public ushort e_ss; public ushort e_sp; public ushort e_csum; public ushort e_ip; public ushort e_cs; public ushort e_lfarlc; public ushort e_ovno; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public ushort[] e_res; public ushort e_oemid; public ushort e_oeminfo; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public ushort[] e_res2; public int e_lfanew; } private const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // "MZ" public ushort ImageDOSSignature = IMAGE_DOS_SIGNATURE; private IMAGE_DOS_HEADER dosHeader; public static bool TryGet(string filename, out MZFileInfo info) { try { info = new MZFileInfo(filename); return true; } catch { info = default(MZFileInfo); return false; } } public static bool TryGet(Stream stream, out MZFileInfo info) { try { info = new MZFileInfo(stream); return true; } catch { info = default(MZFileInfo); return false; } } public MZFileInfo() { } public MZFileInfo(string filename) { using var file = File.OpenRead(filename); Init(file); } public MZFileInfo(Stream stream) { Init(stream); } private void Init(Stream stream) { var buffer = new byte[Marshal.SizeOf<IMAGE_DOS_HEADER>()]; stream.Read(buffer); using (var pointer = new SafeManagedObjectPointer(buffer)) { dosHeader = Marshal.PtrToStructure<IMAGE_DOS_HEADER>(pointer); } } public ref readonly IMAGE_DOS_HEADER Raw => ref dosHeader; public bool IsValid => dosHeader.e_magic == ImageDOSSignature; } internal sealed class SafeManagedObjectPointer : IDisposable { private GCHandle handle; public IntPtr Value { get; private set; } public SafeManagedObjectPointer() { } public SafeManagedObjectPointer(object obj) { handle = GCHandle.Alloc(obj, GCHandleType.Pinned); Value = handle.AddrOfPinnedObject(); } ~SafeManagedObjectPointer() { Dispose(); } public void Dispose() { if (handle.IsAllocated) { handle.Free(); GC.SuppressFinalize(this); Value = IntPtr.Zero; } } public bool IsAllocated => handle.IsAllocated; public static implicit operator IntPtr(SafeManagedObjectPointer source) { return source.Value; } }