private void LoadPEImage() { long fileDataSize = rdr.Bytes.Length - xexData.header.header_size; BeImageReader memRdr = new BeImageReader(xexData.memoryData); DOSHeader dosHeader = memRdr.ReadStruct <DOSHeader>(); dosHeader.Validate(); memRdr.Offset = dosHeader.e_lfanew; UInt32 peSignature = memRdr.ReadUInt32(); if (peSignature != 0x50450000) { throw new BadImageFormatException("PE: Invalid or Missing PE Signature"); } COFFHeader coffHeader = memRdr.ReadStruct <COFFHeader>(); if (coffHeader.Machine != 0x1F2) { throw new BadImageFormatException($"PE: Machine type does not match Xbox360 (found 0x{coffHeader.Machine:X})"); } if ((coffHeader.Characteristics & 0x0100) == 0) { throw new BadImageFormatException("PE: Only 32-bit images are supported"); } if (coffHeader.SizeOfOptionalHeader != 224) { throw new BadImageFormatException($"PE: Invalid size of optional header (got {coffHeader.SizeOfOptionalHeader}"); } PEOptHeader optHeader = memRdr.ReadStruct <PEOptHeader>(); if (optHeader.signature != 0x10b) { throw new BadImageFormatException($"PE: Invalid signature of optional header (got 0x{optHeader.signature})"); } if (optHeader.Subsystem != IMAGE_SUBSYSTEM_XBOX) { throw new BadImageFormatException($"PE: Invalid subsystem (got {optHeader.Subsystem})"); } xexData.peHeader = optHeader; uint extendedMemorySize = 0; uint numSections = coffHeader.NumberOfSections; List <PESection> peSections = new List <PESection>(); for (uint i = 0; i < numSections; i++) { COFFSection section = memRdr.ReadStruct <COFFSection>(); string sectionName = Encoding.ASCII.GetString(section.Name).Trim('\0'); uint lastMemoryAddress = section.VirtualAddress + section.VirtualSize; if (lastMemoryAddress > extendedMemorySize) { extendedMemorySize = lastMemoryAddress; } if (section.SizeOfRawData == 0) { decompilerEventListener.Info(new NullCodeLocation(""), $"Skipping empty section {sectionName}" ); continue; } byte[] sectionData = memRdr.ReadAt <byte[]>(section.PointerToRawData, rdr => rdr.ReadBytes(section.SizeOfRawData)); AccessMode acc = AccessMode.Read; if (section.Flags.HasFlag(PESectionFlags.IMAGE_SCN_MEM_WRITE)) { acc |= AccessMode.Write; } if (section.Flags.HasFlag(PESectionFlags.IMAGE_SCN_MEM_EXECUTE)) { acc |= AccessMode.Execute; } PESection managedSection = new PESection(section); peSections.Add(managedSection); ImageSegment seg = new ImageSegment(sectionName, new MemoryArea( new Address32(managedSection.PhysicalOffset + xexData.exe_address), sectionData ), acc); segments.Add(seg); } if (extendedMemorySize > xexData.memorySize) { decompilerEventListener.Info(new NullCodeLocation(""), $"PE: Image sections extend beyond virtual memory range loaded from file ({extendedMemorySize} > {xexData.memorySize}). Extending by {extendedMemorySize - xexData.memorySize} bytes." ); UInt32 oldMemorySize = xexData.memorySize; byte[] newMemoryData = new byte[extendedMemorySize]; Array.Copy(xexData.memoryData, newMemoryData, xexData.memorySize); xexData.memorySize = extendedMemorySize; xexData.memoryData = newMemoryData; for (int i = 0; i < peSections.Count; i++) { PESection section = peSections[i]; if (section.PhysicalSize == 0) { continue; } if (section.PhysicalSize + section.PhysicalOffset > fileDataSize) { decompilerEventListener.Warn(new NullCodeLocation(""), $"PE: Section '{section.Name}' lies outside any phyisical data we have {section.PhysicalOffset} (size {section.PhysicalSize})" ); continue; } if (section.VirtualOffset >= oldMemorySize) { uint sizeToCopy = section.PhysicalSize; if (section.VirtualSize < sizeToCopy) { sizeToCopy = section.VirtualSize; } Array.Copy( xexData.memoryData, section.PhysicalOffset, newMemoryData, section.VirtualOffset, sizeToCopy); } } } }
/// <summary> /// This function loads the header and PE header at the /// specified address in memory. /// </summary> /// <param name="process"></param> /// <param name="address"></param> public oHeaderReader(Process process, UInt64 address) { codeStartAddress = 0; codeLength = 0; invalidCodeAddresses = new List<ADDRESS_RANGE>(0); // Read in the Image Dos Header importTable = new List<IMPORT_FUNCTION>(0); byte[] headerData = oMemoryFunctions.readMemory(process, address, (uint)Marshal.SizeOf(typeof(IMAGE_DOS_HEADER))); dosHeader = (IMAGE_DOS_HEADER)oMemoryFunctions.RawDataToObject(ref headerData, typeof(IMAGE_DOS_HEADER)); // Load the PE Address UInt64 PE_header = 0; if (dosHeader.e_magic == 0x5A4D) { // Load the PE header address PE_header = dosHeader.e_lfanew + address; } else { PE_header = address; } // Read in the PE token byte[] PE_Token = oMemoryFunctions.readMemory(process, PE_header, 4); if (!(PE_Token[0] == 'P' & PE_Token[1] == 'E' & PE_Token[2] == 0 & PE_Token[3] == 0)) { // Problem, we are not pointing at a correct PE header. Abort. oConsole.printMessage("Failed to read PE header from block " + address.ToString("X") + " with PE header located at " + PE_header.ToString() + "."); return; } // Input the COFFHeader byte[] coffHeader_rawData = oMemoryFunctions.readMemory(process, PE_header + 4, (uint)Marshal.SizeOf(typeof(COFFHeader))); coffHeader = (COFFHeader)oMemoryFunctions.RawDataToObject(ref coffHeader_rawData, typeof(COFFHeader)); // Read in the PEOptHeader if it exists if (coffHeader.SizeOfOptionalHeader != 0) { if (coffHeader.SizeOfOptionalHeader != (ushort)Marshal.SizeOf(typeof(PEOptHeader))) { // Problem! oConsole.printMessage("Failed to read COFFHeader as a result of size mismatch. Size of expected COFFHeader, " + ((ushort)Marshal.SizeOf(typeof(PEOptHeader))).ToString() + ", does not equal size of SizeOfOptionalHeader, " + coffHeader.SizeOfOptionalHeader.ToString() + "."); return; } else { // Read in the optHeader byte[] optHeader_rawData = oMemoryFunctions.readMemory(process, PE_header + 4 + (uint)Marshal.SizeOf(typeof(COFFHeader)), (uint)Marshal.SizeOf(typeof(PEOptHeader))); optHeader = (PEOptHeader)oMemoryFunctions.RawDataToObject(ref optHeader_rawData, typeof(PEOptHeader)); // Confirm that it loaded correctly if (optHeader.signature != 267) { oConsole.printMessage("Failed to read optHeader; Expected signature of 267 does not match read in signature of " + optHeader.signature.ToString() + "."); return; } } } else { // No COFFHeader found oConsole.printMessage("Warning, no COFFHeader found for address " + address.ToString("X") + "."); return; } // Add all the directories as invalid code ranges try { if (optHeader.DataDirectory1_export.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory1_export.VirtualAddress), optHeader.DataDirectory1_export.Size)); if (optHeader.DataDirectory2_import.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory2_import.VirtualAddress), optHeader.DataDirectory2_import.Size)); if (optHeader.DataDirectory3.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory3.VirtualAddress), optHeader.DataDirectory3.Size)); if (optHeader.DataDirectory4.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory4.VirtualAddress), optHeader.DataDirectory4.Size)); if (optHeader.DataDirectory5.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory5.VirtualAddress), optHeader.DataDirectory5.Size)); if (optHeader.DataDirectory6.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory6.VirtualAddress), optHeader.DataDirectory6.Size)); if (optHeader.DataDirectory7.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory7.VirtualAddress), optHeader.DataDirectory7.Size)); if (optHeader.DataDirectory8.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory8.VirtualAddress), optHeader.DataDirectory8.Size)); if (optHeader.DataDirectory9.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory9.VirtualAddress), optHeader.DataDirectory9.Size)); if (optHeader.DataDirectory10.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory10.VirtualAddress), optHeader.DataDirectory10.Size)); if (optHeader.DataDirectory11.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory11.VirtualAddress), optHeader.DataDirectory11.Size)); if (optHeader.DataDirectory12.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory12.VirtualAddress), optHeader.DataDirectory12.Size)); if (optHeader.DataDirectory13.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory13.VirtualAddress), optHeader.DataDirectory13.Size)); if (optHeader.DataDirectory14.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory14.VirtualAddress), optHeader.DataDirectory14.Size)); if (optHeader.DataDirectory15.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory15.VirtualAddress), optHeader.DataDirectory15.Size)); if (optHeader.DataDirectory16.Size > 0) invalidCodeAddresses.Add( new ADDRESS_RANGE( oMemoryFunctions.addressAdd(address, optHeader.DataDirectory16.VirtualAddress), optHeader.DataDirectory16.Size)); }catch { // Ignore exceptions, this is not cretical. } // Extract the names of the functions and corresponding table entries uint importTableAddress = oMemoryFunctions.addressAdd(optHeader.DataDirectory2_import.VirtualAddress,(uint) address); uint exportTableAddress = oMemoryFunctions.addressAdd(optHeader.DataDirectory1_export.VirtualAddress, (uint) address); // Load the sections UInt64 sectionAddress = PE_header + 4 + (UInt64)Marshal.SizeOf(typeof(COFFHeader)) + coffHeader.SizeOfOptionalHeader; List<section> sections = new List<section>(coffHeader.NumberOfSections); for (int i = 0; i < coffHeader.NumberOfSections; i++) { sections.Add(new section( process, sectionAddress )); sectionAddress += (uint)Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER)); } // Load the import directory loadImportTable(process, importTableAddress, optHeader.DataDirectory2_import.Size, (uint)address); // Load the export directory loadExportTable(process, exportTableAddress, optHeader.DataDirectory1_export.Size, (uint) address, optHeader, sections); // Load the section structures int numSections = coffHeader.NumberOfSections; sections = new List<IMAGE_SECTION_HEADER_MOD>(numSections); for( uint i = 0; i < numSections; i++ ) { ulong sectionBase = PE_header + 4 + (ulong)Marshal.SizeOf(typeof(COFFHeader)) + (uint)Marshal.SizeOf(typeof(PEOptHeader)) + i * (uint)Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER)); byte[] sectionData = oMemoryFunctions.readMemory(process, sectionBase, (uint)Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER))); IMAGE_SECTION_HEADER section = (IMAGE_SECTION_HEADER)oMemoryFunctions.RawDataToObject(ref sectionData, typeof(IMAGE_SECTION_HEADER)); // Convert the raw section to a more friendly section type sections.Add(new IMAGE_SECTION_HEADER_MOD(section)); } }
private void loadExportTable(Process process, uint exportTableAddress, uint length, uint baseAddress, PEOptHeader optHeader, List<section> sections) { // Read in the first _IMAGE_EXPORT_DIRECTORY structure byte[] export_directory_rawData = MemoryFunctions.ReadMemory(process, exportTableAddress, (uint)Marshal.SizeOf(typeof(_IMAGE_EXPORT_DIRECTORY))); _IMAGE_EXPORT_DIRECTORY export_directory = (_IMAGE_EXPORT_DIRECTORY)MemoryFunctions.RawDataToObject(ref export_directory_rawData, typeof(_IMAGE_EXPORT_DIRECTORY)); exports = new Hashtable(20); UInt64 functionNameArray = (UInt64)export_directory.AddressOfNames + address; string exportName = MemoryFunctions.ReadString(process, (UInt64)export_directory.Name + address, MemoryFunctions.STRING_TYPE.ascii); UInt64 functionOrdinalArray = (UInt64)export_directory.AddressOfNameOrdinal + address; UInt64 functionAddressArray = (UInt64)export_directory.AddressOfFunctions + address; for (int i = 0; i < export_directory.NumberOfNames; i++) { int ordinal_relative = (int)MemoryFunctions.ReadMemoryUShort(process, functionOrdinalArray + (UInt64)i * 2); int ordinal = ordinal_relative + (int)export_directory.Base; if (ordinal_relative < export_directory.NumberOfFunctions) { // Continue with importing this function string name = ""; if (i < export_directory.NumberOfNames) name = MemoryFunctions.ReadString(process, (UInt64)MemoryFunctions.ReadMemoryDword(process, functionNameArray + (UInt64)i * 4) + address, MemoryFunctions.STRING_TYPE.ascii); else name = "oridinal" + ordinal.ToString(); // Lookup the function rva now try { UInt64 offset = (UInt64)MemoryFunctions.ReadMemoryDword(process, functionAddressArray + (UInt64)ordinal_relative * 4); // Check to see if this is a forwarded export if (offset < optHeader.DataDirectory1_export.VirtualAddress || offset > optHeader.DataDirectory1_export.VirtualAddress + optHeader.DataDirectory1_export.Size) { // Lookup privilege of heap to confirm it requests execute privileges. We only want to list exported functions, not variables. foreach (section exp in sections) { if (exp.Contains(offset)) { if (exp.IsExecutable()) { // Add this exported function export new_export = new export(offset + address, name, ordinal); exports.Add(name.ToLower(), new_export); } break; } } } else { // Forwarded export. Ignore it. } } catch (Exception e) { Console.WriteLine("Warning, failed to parse PE header export directory entry for name '" + name + "'."); } } } }
/// <summary> /// This checks whether the specified heap is /// a valid pe header. /// </summary> /// <param name="process"></param> /// <param name="address"></param> /// <returns></returns> public static bool isPeHeader(Process process, UInt64 address) { // Read in the Image Dos Header byte[] headerData = oMemoryFunctions.readMemory(process, address, (uint)Marshal.SizeOf(typeof(IMAGE_DOS_HEADER))); IMAGE_DOS_HEADER dosHeader = (IMAGE_DOS_HEADER)oMemoryFunctions.RawDataToObject(ref headerData, typeof(IMAGE_DOS_HEADER)); // Load the PE Address UInt64 PE_header = 0; if (dosHeader.e_magic == 0x5A4D) { // Load the PE header address PE_header = dosHeader.e_lfanew + address; } else { PE_header = address; return false; } if (PE_header <= 0x7FFFFFFF) { // Read in the PE token byte[] PE_Token = oMemoryFunctions.readMemory(process, PE_header, 4); if (!(PE_Token[0] == 'P' & PE_Token[1] == 'E' & PE_Token[2] == 0 & PE_Token[3] == 0)) { // Problem, we are not pointing at a correct PE header. Abort. return false; } // Input the COFFHeader byte[] coffHeader_rawData = oMemoryFunctions.readMemory(process, PE_header + 4, (uint)Marshal.SizeOf(typeof(COFFHeader))); COFFHeader coffHeader = (COFFHeader)oMemoryFunctions.RawDataToObject(ref coffHeader_rawData, typeof(COFFHeader)); // Read in the PEOptHeader if it exists if (coffHeader.SizeOfOptionalHeader != 0) { if (coffHeader.SizeOfOptionalHeader != (ushort)Marshal.SizeOf(typeof(PEOptHeader))) { // Problem! return false; } else { // Read in the optHeader byte[] optHeader_rawData = oMemoryFunctions.readMemory(process, PE_header + 4 + (uint)Marshal.SizeOf(typeof(COFFHeader)), (uint)Marshal.SizeOf(typeof(PEOptHeader))); PEOptHeader optHeader = (PEOptHeader)oMemoryFunctions.RawDataToObject(ref optHeader_rawData, typeof(PEOptHeader)); // Confirm that it loaded correctly if (optHeader.signature != 267) { return false; } if( optHeader.SizeOfCode == 0 ) return false; } } else { // No COFFHeader found return false; } return true; } else { return false; } }