From 7919d230c67467141df1290fd695c05eb9bbb00b Mon Sep 17 00:00:00 2001 From: VollRahm Date: Wed, 24 Feb 2021 19:38:14 +0100 Subject: [PATCH] - Added full .NET 5 Support - Added logging --- .../apphost-extract-v2/Analyzer.cs | 59 ++++--------------- .../apphost-extract-v2/HashChecker.cs | 11 ++++ .../Models/ApphostFile30.cs | 3 +- .../Models/ApphostFile31.cs | 6 +- .../apphost-extract-v2/Models/ApphostFile5.cs | 48 ++++++++++++--- .../Models/General/AppHostFileEntry.cs | 27 --------- .../Models/General/AppHostManifest.cs | 10 ---- .../apphost-extract-v2/Program.cs | 52 +++++----------- .../apphost-extract-v2/Util.cs | 40 +++++++++++++ 9 files changed, 123 insertions(+), 133 deletions(-) create mode 100644 src/apphost-extract/apphost-extract-v2/HashChecker.cs diff --git a/src/apphost-extract/apphost-extract-v2/Analyzer.cs b/src/apphost-extract/apphost-extract-v2/Analyzer.cs index 1679ca7..9a893d8 100644 --- a/src/apphost-extract/apphost-extract-v2/Analyzer.cs +++ b/src/apphost-extract/apphost-extract-v2/Analyzer.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Text; +using static apphost_extract_v2.Util; namespace apphost_extract_v2 { @@ -18,31 +19,35 @@ namespace apphost_extract_v2 private readonly byte[] VERSION_SIGNATURE = new byte[] { 0x4C, 0x8D, 0x05, 0x0, 0x0, 0x0, 0x0, 0x48, 0x8D, 0x15, 0x0, 0x0, 0x0, 0x0, 0x48, 0x8D, 0x0D, 0x0, 0x0, 0x0, 0x0 }; private const string VERSION_SIGNATURE_MASK = "xxx???xxxx???xxxx???x"; - public Analyzer(FileStream fs) + public Analyzer(FileInfo fi) { - File = fs; - PEHeader = new PEHeaders(fs); + File = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read); + PEHeader = new PEHeaders(File); } public IApphostFile Open() { - var textSegment = GetSegment(".text"); + var textSegment = PEHeader.GetSegment(".text"); + var sw = Stopwatch.StartNew(); - var sigscanResults = Util.PatternScan(File, + Log.Info("Scanning for version string pointer..."); + + var sigscanResults = PatternScan(File, textSegment.PointerToRawData, textSegment.SizeOfRawData, VERSION_SIGNATURE, VERSION_SIGNATURE_MASK); sw.Stop(); - Log.Info($"Scan took {sw.ElapsedMilliseconds}ms | Version String Instructions -> {sigscanResults[0]:X8}"); + if (sigscanResults.Length == 0) return null; var versionOffset = (int)BitConverter.ToUInt32(File.ReadBuffer(sigscanResults[0] + 3, 4)); - var versionStringPtr = AddVirtualOffset(sigscanResults[0] + 7, versionOffset); + var versionStringPtr = PEHeader.AddVirtualOffset(sigscanResults[0] + 7, versionOffset); var versionString = Encoding.Unicode.GetString( File.ReadBuffer( versionStringPtr, 6)); - Log.Info($"Translated virtual offset -> {versionStringPtr:X8}: {versionString}"); + Log.Info($"Found version string at 0x{versionStringPtr:X8} in {sw.ElapsedMilliseconds}ms -> {versionString}"); + Console.WriteLine(); switch (versionString) { @@ -58,43 +63,5 @@ namespace apphost_extract_v2 return null; } } - - private int AddVirtualOffset(int fileAddress, int offset) - { - return VirtualAddressToFileOffset(FileOffsetToVirtualAddress(fileAddress) + offset); - } - - private int FileOffsetToVirtualAddress(int offset) - { - var section = FindSection(offset, true); - return offset + (section.VirtualAddress - section.PointerToRawData); - } - - private int VirtualAddressToFileOffset(int address) - { - var section = FindSection(address, false); - return address - (section.VirtualAddress - section.PointerToRawData); - } - - public SectionHeader GetSegment(string name) - { - var section = PEHeader.SectionHeaders.Where(x => x.Name == name).FirstOrDefault(); - return section; - } - - public SectionHeader FindSection(int address, bool fileOffset) - { - foreach (var section in PEHeader.SectionHeaders) - { - if (fileOffset) - { - if (section.PointerToRawData < address && section.PointerToRawData + section.SizeOfRawData > address) return section; - } - else - if (section.VirtualAddress < address && section.VirtualAddress + section.VirtualSize > address) return section; - } - - return new SectionHeader(); - } } } diff --git a/src/apphost-extract/apphost-extract-v2/HashChecker.cs b/src/apphost-extract/apphost-extract-v2/HashChecker.cs new file mode 100644 index 0000000..c846a8e --- /dev/null +++ b/src/apphost-extract/apphost-extract-v2/HashChecker.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace apphost_extract_v2 +{ + public class HashChecker + { + + } +} diff --git a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile30.cs b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile30.cs index f0af0bb..03f3e7a 100644 --- a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile30.cs +++ b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile30.cs @@ -16,6 +16,7 @@ namespace apphost_extract_v2.Models public ApphostFile30(FileStream fs, PEHeaders peheader) : base(fs, peheader) { Header = new AppHostFileHeader(); + Log.Info($"Reading header at 0x{HEADER_OFFSET_PTR:X8}..."); var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4)); var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE); @@ -23,13 +24,13 @@ namespace apphost_extract_v2.Models Header.Path = Encoding.UTF8.GetString(fs.ReadBuffer(headerAddress + HEADER_SIZE, 0xC)); Header.Manifest = ParseManifest(); - } private AppHostManifest ParseManifest() { AppHostManifest manifest = new AppHostManifest(); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); + Log.Info($"Found {embeddedFileCount} embedded files."); for (int i = 0; i < embeddedFileCount; i++) { diff --git a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile31.cs b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile31.cs index 422ba2d..f6d6785 100644 --- a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile31.cs +++ b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile31.cs @@ -18,7 +18,7 @@ namespace apphost_extract_v2.Models Header = new AppHostFileHeader(); var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4)); var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE); - + Log.Info($"Reading header at 0x{HEADER_OFFSET_PTR:X8}..."); Header.Raw = headerBuffer; Header.Path = Encoding.UTF8.GetString(fs.ReadBuffer(headerAddress + HEADER_SIZE, 0xC)); @@ -30,8 +30,8 @@ namespace apphost_extract_v2.Models { AppHostManifest manifest = new AppHostManifest(); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); - - for(int i = 0; i< embeddedFileCount; i++) + Log.Info($"Found {embeddedFileCount} embedded files."); + for (int i = 0; i< embeddedFileCount; i++) { manifest.FileEntries.Add(GetNextEntry()); } diff --git a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile5.cs b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile5.cs index ee0d5cb..75924df 100644 --- a/src/apphost-extract/apphost-extract-v2/Models/ApphostFile5.cs +++ b/src/apphost-extract/apphost-extract-v2/Models/ApphostFile5.cs @@ -1,40 +1,73 @@ using apphost_extract_v2.General; using System; -using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection.PortableExecutable; using System.Text; +using static apphost_extract_v2.Util; namespace apphost_extract_v2.Models { public class ApphostFile5 : IApphostFile { - private const int HEADER_OFFSET_PTR = 0x20268; + private readonly byte[] HEADER_OFFSET_SIG = { 0xE8, 0x0, 0x0, 0x0, 0x0, 0x48, 0x8B, 0x05, 0x0, 0x0, 0x0, 0x0, 0x48, 0x85, 0xC0 }; + private const string HEADER_OFFSET_MASK = "x????xxx????xxx"; + private const int HEADER_SIZE = 0xD; private const int FILE_ENTRY_SIZE = 0x12; public ApphostFile5(FileStream fs, PEHeaders peheader) : base(fs, peheader) { Header = new AppHostFileHeader(); - var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4)); + var headerAddress = FindHeaderOffset(); + + if(headerAddress == 0) + Log.Fatal("Unable to located bundle header :/"); + var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE); Header.Raw = headerBuffer; - Header.Path = Encoding.UTF8.GetString(fs.ReadBuffer(headerAddress + HEADER_SIZE, 0xC)); + Header.Path = Encoding.UTF8.GetString( + fs.ReadBuffer(headerAddress + HEADER_SIZE, 0xC)); Header.Manifest = ParseManifest(); } + private uint FindHeaderOffset() + { + var textSegment = PEHeader.GetSegment(".text"); + Log.Info("Scanning for the .NET 5 Bundle header..."); + var sw = Stopwatch.StartNew(); + + var sigscanResults = PatternScan(FileStream, + textSegment.PointerToRawData, textSegment.SizeOfRawData, + HEADER_OFFSET_SIG, HEADER_OFFSET_MASK); + + sw.Stop(); + + if (sigscanResults.Length == 0) return 0; + + var headerOffset = (int)BitConverter.ToUInt32( + FileStream.ReadBuffer(sigscanResults[0] + 8, 4)); + + var headerPtr = PEHeader.AddVirtualOffset(sigscanResults[0] + 12, headerOffset); + var headerAddress = BitConverter.ToUInt32(FileStream.ReadBuffer(headerPtr, 4)); + + Log.Info($"Found bundle header offset at 0x{headerPtr:X8} in {sw.ElapsedMilliseconds}ms -> {headerAddress:X8}"); + return headerAddress; + } + private AppHostManifest ParseManifest() { + //Seek over random bullshit that got added in .NET 5 + FileStream.Seek(0x28, SeekOrigin.Current); + AppHostManifest manifest = new AppHostManifest(); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); - + Log.Info($"Found {embeddedFileCount} embedded files."); for (int i = 0; i < embeddedFileCount; i++) - { manifest.FileEntries.Add(GetNextEntry()); - } return manifest; } @@ -59,7 +92,6 @@ namespace apphost_extract_v2.Models } - public override void Close() { FileStream.Close(); diff --git a/src/apphost-extract/apphost-extract-v2/Models/General/AppHostFileEntry.cs b/src/apphost-extract/apphost-extract-v2/Models/General/AppHostFileEntry.cs index cd28d83..9d96fb3 100644 --- a/src/apphost-extract/apphost-extract-v2/Models/General/AppHostFileEntry.cs +++ b/src/apphost-extract/apphost-extract-v2/Models/General/AppHostFileEntry.cs @@ -14,32 +14,5 @@ namespace apphost_extract_v2.General public string Name { get; set; } public byte[] Raw; - - - //public AppHostFileEntry() - //{ - // FileStream = File; - // byte[] entryBuffer = new byte[FILE_ENTRY_SIZE]; - // File.Read(entryBuffer, 0, entryBuffer.Length); - // Raw = entryBuffer; - - // Offset = BitConverter.ToInt64(Raw, 0); - - // //hopefully nobody embeds a file larger than 2GB :D - // Size = (int)BitConverter.ToInt64(Raw, 0x8); - - // byte[] stringBuffer = new byte[Raw[0x11]]; - // File.Read(stringBuffer, 0, stringBuffer.Length); - // Name = Encoding.UTF8.GetString(stringBuffer); - //} - - //public byte[] Read() - //{ - // //jumps to the offsets and reads the bytes - // byte[] buffer = new byte[Size]; - // FileStream.Seek(Offset, SeekOrigin.Begin); - // FileStream.Read(buffer, 0, Size); - // return buffer; - //} } } diff --git a/src/apphost-extract/apphost-extract-v2/Models/General/AppHostManifest.cs b/src/apphost-extract/apphost-extract-v2/Models/General/AppHostManifest.cs index bdc32e6..da73dae 100644 --- a/src/apphost-extract/apphost-extract-v2/Models/General/AppHostManifest.cs +++ b/src/apphost-extract/apphost-extract-v2/Models/General/AppHostManifest.cs @@ -14,16 +14,6 @@ namespace apphost_extract_v2.General public AppHostManifest() { FileEntries = new List(); - //List entries = new List(); - - //for (int i = 0; i < embeddedFileCount; i++) - //{ - // AppHostFileEntry entry = new AppHostFileEntry(File); - // entries.Add(entry); - //} - //FileEntries = entries.AsReadOnly(); - - //Log.Info("Manifest parsed successfully!"); } } } diff --git a/src/apphost-extract/apphost-extract-v2/Program.cs b/src/apphost-extract/apphost-extract-v2/Program.cs index a4e071e..55f8b95 100644 --- a/src/apphost-extract/apphost-extract-v2/Program.cs +++ b/src/apphost-extract/apphost-extract-v2/Program.cs @@ -10,51 +10,27 @@ namespace apphost_extract_v2 { static void Main(string[] args) { - var file = "net5-fd.exe"; - var fs = new FileStream(file, FileMode.Open, FileAccess.Read); - var anal = new Analyzer(fs); - var af = anal.Open(); - var path = new FileInfo(file); - var directory = Path.Combine(path.DirectoryName, path.Name.Remove(path.Name.Length - path.Extension.Length) + "_extracted"); - af.ExtractAll(directory); - Console.ReadLine(); - //RunTest("net30-sc.exe"); - //RunTest("net5-sc.exe"); - //Log.Info("Scanning for pattern..."); - //Stopwatch sw = Stopwatch.StartNew(); - //var res = Util.PatternScan(fs, 0, (int)fs.Length, pattern, mask); - //sw.Stop(); - //Log.Info("Found pattern at " + res[0].ToString("X8") + $" in {sw.ElapsedMilliseconds}ms"); - - + Log.Info("apphost-extract-v2 by VollRagm\n", ConsoleColor.Yellow); - /* Log.Info("apphost-extract by VollRagm\n", ConsoleColor.Yellow); - var path = GetPath(args); + var fileInfo = GetFileInfo(args); - // var file = AppHostFile.Open(path2);//path.FullName); - // Log.Info($"{file.Header.Manifest.FileEntries.Count} embedded file(s) found."); + var apphostAnalyzer = new Analyzer(fileInfo); + var apphost = apphostAnalyzer.Open(); + + if(apphost == null) + Log.Fatal("Unable to determine apphost version."); - var directory = Path.Combine(path.DirectoryName, path.Name.Remove(path.Name.Length - path.Extension.Length) + "_extracted"); - Console.WriteLine(); - Log.Info("Extracting..."); + Log.Info("File parsed successfully, extracting contents..."); + Console.WriteLine(); - //file.ExtractAll(directory); + var directory = Path.Combine(fileInfo.DirectoryName, fileInfo.Name.Remove(fileInfo.Name.Length - fileInfo.Extension.Length) + "_extracted"); + apphost.ExtractAll(directory); - Console.WriteLine(); - Log.Info("Done."); - // file.Close(); - Console.ReadLine();*/ + Log.Info("Done."); + Console.ReadLine(); } - //static void RunTest(string file) - //{ - // Log.Info("Running test on " + file + ", scanning for version sig..."); - - - // var d = new Analyzer(fs).GetVersion(); - //} - - static FileInfo GetPath(string[] args) + static FileInfo GetFileInfo(string[] args) { try { diff --git a/src/apphost-extract/apphost-extract-v2/Util.cs b/src/apphost-extract/apphost-extract-v2/Util.cs index 1ce1340..60fb6b9 100644 --- a/src/apphost-extract/apphost-extract-v2/Util.cs +++ b/src/apphost-extract/apphost-extract-v2/Util.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection.PortableExecutable; using System.Text; namespace apphost_extract_v2 @@ -45,5 +47,43 @@ namespace apphost_extract_v2 fs.Read(buff, 0, length); return buff; } + + public static int AddVirtualOffset(this PEHeaders header, int fileAddress, int offset) + { + return header.VirtualAddressToFileOffset(header.FileOffsetToVirtualAddress(fileAddress) + offset); + } + + public static int FileOffsetToVirtualAddress(this PEHeaders header, int offset) + { + var section = header.FindSection(offset, true); + return offset + (section.VirtualAddress - section.PointerToRawData); + } + + public static int VirtualAddressToFileOffset(this PEHeaders header, int address) + { + var section = header.FindSection(address, false); + return address - (section.VirtualAddress - section.PointerToRawData); + } + + public static SectionHeader GetSegment(this PEHeaders header, string name) + { + var section = header.SectionHeaders.Where(x => x.Name == name).FirstOrDefault(); + return section; + } + + public static SectionHeader FindSection(this PEHeaders header, int address, bool fileOffset) + { + foreach (var section in header.SectionHeaders) + { + if (fileOffset) + { + if (section.PointerToRawData < address && section.PointerToRawData + section.SizeOfRawData > address) return section; + } + else + if (section.VirtualAddress < address && section.VirtualAddress + section.VirtualSize > address) return section; + } + + return new SectionHeader(); + } } }