using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using RomCheater.Logging; using System.Runtime.InteropServices; using System.Diagnostics; namespace Sojaner.MemoryScanner { public class PEReader { public PEReader(FileInfo fi) : this(fi.FullName) { } public PEReader(string filename) { Exception ErrorInfo = null; try { this.Read(filename, out ErrorInfo); } catch (Exception ex) { logger.Error.WriteLine("PEReader: Failed to read process: {0}", filename); if (ErrorInfo != null) { //logger.Error.WriteLine(ErrorInfo.GetBaseException().ToString()); throw ErrorInfo; } else { //logger.Error.WriteLine(ex.GetBaseException().ToString()); throw ex; } } } #region marshalling private void Read(string filename, out Exception ErrorInfo) { ErrorInfo = null; try { logger.Debug.WriteLine("Reading Exe: {0}", filename); using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { try { byte[] data = new byte[] { }; GCHandle pinnedPacket = new GCHandle(); int size = 0; BinaryReader br = new BinaryReader(fs); #region IMAGE_DOS_HEADER size = Marshal.SizeOf(typeof(IMAGE_DOS_HEADER)); data = br.ReadBytes(size); pinnedPacket = GCHandle.Alloc(data, GCHandleType.Pinned); IMAGE_DOS_HEADER DOS_HEADER = (IMAGE_DOS_HEADER)Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(IMAGE_DOS_HEADER)); pinnedPacket.Free(); #endregion // skip the old dos stub br.BaseStream.Seek(DOS_HEADER.e_lfanew, SeekOrigin.Begin); #region IMAGE_NT_HEADERS size = Marshal.SizeOf(typeof(IMAGE_NT_HEADERS)); data = br.ReadBytes(size); pinnedPacket = GCHandle.Alloc(data, GCHandleType.Pinned); IMAGE_NT_HEADERS NT_HEADER = (IMAGE_NT_HEADERS)Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(IMAGE_NT_HEADERS)); pinnedPacket.Free(); #endregion StringBuilder section_header_string_builder = new StringBuilder(); List section_headers = new List(); section_header_string_builder.AppendFormat("Section headers:{0}", System.Environment.NewLine); for (int i = 0; i < NT_HEADER.FileHeader.NumberOfSections; i++) { size = Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER)); data = br.ReadBytes(size); pinnedPacket = GCHandle.Alloc(data, GCHandleType.Pinned); IMAGE_SECTION_HEADER SECTION_HEADER = (IMAGE_SECTION_HEADER)Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(IMAGE_SECTION_HEADER)); section_headers.Add(SECTION_HEADER); pinnedPacket.Free(); section_header_string_builder.AppendFormat("Section Header: {0}{1}", new string(SECTION_HEADER.Name).Replace("\0",""), System.Environment.NewLine); } logger.VerboseDebug.WriteLine(section_header_string_builder.ToString()); br.Close(); } catch (Exception ex) { ErrorInfo = ex; } } } catch (Exception ex) { ErrorInfo = ex; } } #endregion #region header support #region IMAGE_DATA_DIRECTORY [StructLayout(LayoutKind.Sequential)] public struct IMAGE_DATA_DIRECTORY { public UInt32 VirtualAddress; public UInt32 Size; public bool HasAddress { get { return (VirtualAddress != 0); } } public bool HasSize { get { return (Size > 0); } } } #endregion #region IMAGE_FILE_HEADER [StructLayout(LayoutKind.Sequential)] public struct IMAGE_FILE_HEADER { public MachineType Machine; public UInt16 NumberOfSections; public UInt32 TimeDateStamp; public UInt32 PointerToSymbolTable; public UInt32 NumberOfSymbols; public UInt16 SizeOfOptionalHeader; public DllCharacteristicsType Characteristics; } #endregion #region IMAGE_DOS_HEADER [StructLayout(LayoutKind.Sequential)] public struct IMAGE_DOS_HEADER { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public char[] e_magic; // Magic number public UInt16 e_cblp; // Bytes on last page of file public UInt16 e_cp; // Pages in file public UInt16 e_crlc; // Relocations public UInt16 e_cparhdr; // Size of header in paragraphs public UInt16 e_minalloc; // Minimum extra paragraphs needed public UInt16 e_maxalloc; // Maximum extra paragraphs needed public UInt16 e_ss; // Initial (relative) SS value public UInt16 e_sp; // Initial SP value public UInt16 e_csum; // Checksum public UInt16 e_ip; // Initial IP value public UInt16 e_cs; // Initial (relative) CS value public UInt16 e_lfarlc; // File address of relocation table public UInt16 e_ovno; // Overlay number [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public UInt16[] e_res1; // Reserved words public UInt16 e_oemid; // OEM identifier (for e_oeminfo) public UInt16 e_oeminfo; // OEM information; e_oemid specific [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public UInt16[] e_res2; // Reserved words public Int32 e_lfanew; // File address of new exe header private string _e_magic { get { return new string(e_magic); } } public bool isValid { get { return _e_magic == "MZ"; } } } #endregion #region IMAGE_NT_HEADERS [StructLayout(LayoutKind.Explicit)] public struct IMAGE_NT_HEADERS { [FieldOffset(0)] public uint Signature; [FieldOffset(4)] public IMAGE_FILE_HEADER FileHeader; [FieldOffset(24)] public IMAGE_OPTIONAL_HEADER OptionalHeader; private string _Signature { get { return Encoding.ASCII.GetString(BitConverter.GetBytes(Signature)); } } public bool isValid { get { return _Signature == "PE\0\0" && (OptionalHeader.Magic == MagicType.IMAGE_NT_OPTIONAL_HDR32_MAGIC || OptionalHeader.Magic == MagicType.IMAGE_NT_OPTIONAL_HDR64_MAGIC); } } } #endregion #region MachineType public enum MachineType : ushort { Native = 0, I386 = 0x014c, Itanium = 0x0200, x64 = 0x8664 } #endregion #region MagicType public enum MagicType : ushort { IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b, IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b } #endregion #region SubSystemType public enum SubSystemType : ushort { IMAGE_SUBSYSTEM_UNKNOWN = 0, IMAGE_SUBSYSTEM_NATIVE = 1, IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, IMAGE_SUBSYSTEM_POSIX_CUI = 7, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, IMAGE_SUBSYSTEM_EFI_ROM = 13, IMAGE_SUBSYSTEM_XBOX = 14 } #endregion #region DllCharacteristicsType [Flags] public enum DllCharacteristicsType : ushort { RES_0 = 0x0001, RES_1 = 0x0002, RES_2 = 0x0004, RES_3 = 0x0008, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100, IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, RES_4 = 0x1000, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 } #endregion #region IMAGE_OPTIONAL_HEADER [StructLayout(LayoutKind.Explicit)] public struct IMAGE_OPTIONAL_HEADER { [FieldOffset(0)] public MagicType Magic; [FieldOffset(2)] public byte MajorLinkerVersion; [FieldOffset(3)] public byte MinorLinkerVersion; [FieldOffset(4)] public uint SizeOfCode; [FieldOffset(8)] public uint SizeOfInitializedData; [FieldOffset(12)] public uint SizeOfUninitializedData; [FieldOffset(16)] public uint AddressOfEntryPoint; [FieldOffset(20)] public uint BaseOfCode; // PE32 contains this additional field [FieldOffset(24)] public uint BaseOfData; [FieldOffset(28)] public uint ImageBase; [FieldOffset(32)] public uint SectionAlignment; [FieldOffset(36)] public uint FileAlignment; [FieldOffset(40)] public ushort MajorOperatingSystemVersion; [FieldOffset(42)] public ushort MinorOperatingSystemVersion; [FieldOffset(44)] public ushort MajorImageVersion; [FieldOffset(46)] public ushort MinorImageVersion; [FieldOffset(48)] public ushort MajorSubsystemVersion; [FieldOffset(50)] public ushort MinorSubsystemVersion; [FieldOffset(52)] public uint Win32VersionValue; [FieldOffset(56)] public uint SizeOfImage; [FieldOffset(60)] public uint SizeOfHeaders; [FieldOffset(64)] public uint CheckSum; [FieldOffset(68)] public SubSystemType Subsystem; [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics; [FieldOffset(72)] public uint SizeOfStackReserve; [FieldOffset(76)] public uint SizeOfStackCommit; [FieldOffset(80)] public uint SizeOfHeapReserve; [FieldOffset(84)] public uint SizeOfHeapCommit; [FieldOffset(88)] public uint LoaderFlags; [FieldOffset(92)] public uint NumberOfRvaAndSizes; [FieldOffset(96)] public IMAGE_DATA_DIRECTORY ExportTable; [FieldOffset(104)] public IMAGE_DATA_DIRECTORY ImportTable; [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ResourceTable; [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ExceptionTable; [FieldOffset(128)] public IMAGE_DATA_DIRECTORY CertificateTable; [FieldOffset(136)] public IMAGE_DATA_DIRECTORY BaseRelocationTable; [FieldOffset(144)] public IMAGE_DATA_DIRECTORY Debug; [FieldOffset(152)] public IMAGE_DATA_DIRECTORY Architecture; [FieldOffset(160)] public IMAGE_DATA_DIRECTORY GlobalPtr; [FieldOffset(168)] public IMAGE_DATA_DIRECTORY TLSTable; [FieldOffset(176)] public IMAGE_DATA_DIRECTORY LoadConfigTable; [FieldOffset(184)] public IMAGE_DATA_DIRECTORY BoundImport; [FieldOffset(192)] public IMAGE_DATA_DIRECTORY IAT; [FieldOffset(200)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor; [FieldOffset(208)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; [FieldOffset(216)] public IMAGE_DATA_DIRECTORY Reserved; } #endregion #region IMAGE_EXPORT_DIRECTORY [StructLayout(LayoutKind.Sequential)] public struct IMAGE_EXPORT_DIRECTORY { public UInt32 Characteristics; public UInt32 TimeDateStamp; public UInt16 MajorVersion; public UInt16 MinorVersion; public UInt32 Name; public UInt32 Base; public UInt32 NumberOfFunctions; public UInt32 NumberOfNames; public UInt32 AddressOfFunctions; // RVA from base of image public UInt32 AddressOfNames; // RVA from base of image public UInt32 AddressOfNameOrdinals; // RVA from base of image } #endregion #endregion #region IMAGE_SECTION_HEADER [StructLayout(LayoutKind.Explicit)] public struct IMAGE_SECTION_HEADER { [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public char[] Name; [FieldOffset(8)] public UInt32 VirtualSize; [FieldOffset(12)] public UInt32 VirtualAddress; [FieldOffset(16)] public UInt32 SizeOfRawData; [FieldOffset(20)] public UInt32 PointerToRawData; [FieldOffset(24)] public UInt32 PointerToRelocations; [FieldOffset(28)] public UInt32 PointerToLinenumbers; [FieldOffset(32)] public UInt16 NumberOfRelocations; [FieldOffset(34)] public UInt16 NumberOfLinenumbers; [FieldOffset(36)] public DataSectionFlags Characteristics; public string Section { get { return new string(Name); } } } #endregion #region DataSectionFlags [Flags] public enum DataSectionFlags : uint { /// /// Reserved for future use. /// TypeReg = 0x00000000, /// /// Reserved for future use. /// TypeDsect = 0x00000001, /// /// Reserved for future use. /// TypeNoLoad = 0x00000002, /// /// Reserved for future use. /// TypeGroup = 0x00000004, /// /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. /// TypeNoPadded = 0x00000008, /// /// Reserved for future use. /// TypeCopy = 0x00000010, /// /// The section contains executable code. /// ContentCode = 0x00000020, /// /// The section contains initialized data. /// ContentInitializedData = 0x00000040, /// /// The section contains uninitialized data. /// ContentUninitializedData = 0x00000080, /// /// Reserved for future use. /// LinkOther = 0x00000100, /// /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only. /// LinkInfo = 0x00000200, /// /// Reserved for future use. /// TypeOver = 0x00000400, /// /// The section will not become part of the image. This is valid only for object files. /// LinkRemove = 0x00000800, /// /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files. /// LinkComDat = 0x00001000, /// /// Reset speculative exceptions handling bits in the TLB entries for this section. /// NoDeferSpecExceptions = 0x00004000, /// /// The section contains data referenced through the global pointer (GP). /// RelativeGP = 0x00008000, /// /// Reserved for future use. /// MemPurgeable = 0x00020000, /// /// Reserved for future use. /// Memory16Bit = 0x00020000, /// /// Reserved for future use. /// MemoryLocked = 0x00040000, /// /// Reserved for future use. /// MemoryPreload = 0x00080000, /// /// Align data on a 1-byte boundary. Valid only for object files. /// Align1Bytes = 0x00100000, /// /// Align data on a 2-byte boundary. Valid only for object files. /// Align2Bytes = 0x00200000, /// /// Align data on a 4-byte boundary. Valid only for object files. /// Align4Bytes = 0x00300000, /// /// Align data on an 8-byte boundary. Valid only for object files. /// Align8Bytes = 0x00400000, /// /// Align data on a 16-byte boundary. Valid only for object files. /// Align16Bytes = 0x00500000, /// /// Align data on a 32-byte boundary. Valid only for object files. /// Align32Bytes = 0x00600000, /// /// Align data on a 64-byte boundary. Valid only for object files. /// Align64Bytes = 0x00700000, /// /// Align data on a 128-byte boundary. Valid only for object files. /// Align128Bytes = 0x00800000, /// /// Align data on a 256-byte boundary. Valid only for object files. /// Align256Bytes = 0x00900000, /// /// Align data on a 512-byte boundary. Valid only for object files. /// Align512Bytes = 0x00A00000, /// /// Align data on a 1024-byte boundary. Valid only for object files. /// Align1024Bytes = 0x00B00000, /// /// Align data on a 2048-byte boundary. Valid only for object files. /// Align2048Bytes = 0x00C00000, /// /// Align data on a 4096-byte boundary. Valid only for object files. /// Align4096Bytes = 0x00D00000, /// /// Align data on an 8192-byte boundary. Valid only for object files. /// Align8192Bytes = 0x00E00000, /// /// The section contains extended relocations. /// LinkExtendedRelocationOverflow = 0x01000000, /// /// The section can be discarded as needed. /// MemoryDiscardable = 0x02000000, /// /// The section cannot be cached. /// MemoryNotCached = 0x04000000, /// /// The section is not pageable. /// MemoryNotPaged = 0x08000000, /// /// The section can be shared in memory. /// MemoryShared = 0x10000000, /// /// The section can be executed as code. /// MemoryExecute = 0x20000000, /// /// The section can be read. /// MemoryRead = 0x40000000, /// /// The section can be written to. /// MemoryWrite = 0x80000000 } #endregion } }