- Added full .NET 5 Support

- Added logging
master
VollRagm 3 years ago
parent 8732f358b1
commit 7919d230c6

@ -7,6 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Text; using System.Text;
using static apphost_extract_v2.Util;
namespace apphost_extract_v2 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 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"; private const string VERSION_SIGNATURE_MASK = "xxx???xxxx???xxxx???x";
public Analyzer(FileStream fs) public Analyzer(FileInfo fi)
{ {
File = fs; File = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read);
PEHeader = new PEHeaders(fs); PEHeader = new PEHeaders(File);
} }
public IApphostFile Open() public IApphostFile Open()
{ {
var textSegment = GetSegment(".text"); var textSegment = PEHeader.GetSegment(".text");
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var sigscanResults = Util.PatternScan(File, Log.Info("Scanning for version string pointer...");
var sigscanResults = PatternScan(File,
textSegment.PointerToRawData, textSegment.SizeOfRawData, textSegment.PointerToRawData, textSegment.SizeOfRawData,
VERSION_SIGNATURE, VERSION_SIGNATURE_MASK); VERSION_SIGNATURE, VERSION_SIGNATURE_MASK);
sw.Stop(); sw.Stop();
Log.Info($"Scan took {sw.ElapsedMilliseconds}ms | Version String Instructions -> {sigscanResults[0]:X8}");
if (sigscanResults.Length == 0) if (sigscanResults.Length == 0)
return null; return null;
var versionOffset = (int)BitConverter.ToUInt32(File.ReadBuffer(sigscanResults[0] + 3, 4)); 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( var versionString = Encoding.Unicode.GetString(
File.ReadBuffer( File.ReadBuffer(
versionStringPtr, 6)); 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) switch (versionString)
{ {
@ -58,43 +63,5 @@ namespace apphost_extract_v2
return null; 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();
}
} }
} }

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace apphost_extract_v2
{
public class HashChecker
{
}
}

@ -16,6 +16,7 @@ namespace apphost_extract_v2.Models
public ApphostFile30(FileStream fs, PEHeaders peheader) : base(fs, peheader) public ApphostFile30(FileStream fs, PEHeaders peheader) : base(fs, peheader)
{ {
Header = new AppHostFileHeader(); Header = new AppHostFileHeader();
Log.Info($"Reading header at 0x{HEADER_OFFSET_PTR:X8}...");
var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4)); var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4));
var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE); 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.Path = Encoding.UTF8.GetString(fs.ReadBuffer(headerAddress + HEADER_SIZE, 0xC));
Header.Manifest = ParseManifest(); Header.Manifest = ParseManifest();
} }
private AppHostManifest ParseManifest() private AppHostManifest ParseManifest()
{ {
AppHostManifest manifest = new AppHostManifest(); AppHostManifest manifest = new AppHostManifest();
var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8);
Log.Info($"Found {embeddedFileCount} embedded files.");
for (int i = 0; i < embeddedFileCount; i++) for (int i = 0; i < embeddedFileCount; i++)
{ {

@ -18,7 +18,7 @@ namespace apphost_extract_v2.Models
Header = new AppHostFileHeader(); Header = new AppHostFileHeader();
var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4)); var headerAddress = BitConverter.ToInt32(fs.ReadBuffer(HEADER_OFFSET_PTR, 4));
var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE); var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE);
Log.Info($"Reading header at 0x{HEADER_OFFSET_PTR:X8}...");
Header.Raw = headerBuffer; 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));
@ -30,8 +30,8 @@ namespace apphost_extract_v2.Models
{ {
AppHostManifest manifest = new AppHostManifest(); AppHostManifest manifest = new AppHostManifest();
var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8);
Log.Info($"Found {embeddedFileCount} embedded files.");
for(int i = 0; i< embeddedFileCount; i++) for (int i = 0; i< embeddedFileCount; i++)
{ {
manifest.FileEntries.Add(GetNextEntry()); manifest.FileEntries.Add(GetNextEntry());
} }

@ -1,40 +1,73 @@
using apphost_extract_v2.General; using apphost_extract_v2.General;
using System; using System;
using System.Collections.Generic; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Text; using System.Text;
using static apphost_extract_v2.Util;
namespace apphost_extract_v2.Models namespace apphost_extract_v2.Models
{ {
public class ApphostFile5 : IApphostFile 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 HEADER_SIZE = 0xD;
private const int FILE_ENTRY_SIZE = 0x12; private const int FILE_ENTRY_SIZE = 0x12;
public ApphostFile5(FileStream fs, PEHeaders peheader) : base(fs, peheader) public ApphostFile5(FileStream fs, PEHeaders peheader) : base(fs, peheader)
{ {
Header = new AppHostFileHeader(); 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); var headerBuffer = fs.ReadBuffer(headerAddress, HEADER_SIZE);
Header.Raw = headerBuffer; 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(); 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() private AppHostManifest ParseManifest()
{ {
//Seek over random bullshit that got added in .NET 5
FileStream.Seek(0x28, SeekOrigin.Current);
AppHostManifest manifest = new AppHostManifest(); AppHostManifest manifest = new AppHostManifest();
var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8); var embeddedFileCount = BitConverter.ToInt32(Header.Raw, 0x8);
Log.Info($"Found {embeddedFileCount} embedded files.");
for (int i = 0; i < embeddedFileCount; i++) for (int i = 0; i < embeddedFileCount; i++)
{
manifest.FileEntries.Add(GetNextEntry()); manifest.FileEntries.Add(GetNextEntry());
}
return manifest; return manifest;
} }
@ -59,7 +92,6 @@ namespace apphost_extract_v2.Models
} }
public override void Close() public override void Close()
{ {
FileStream.Close(); FileStream.Close();

@ -14,32 +14,5 @@ namespace apphost_extract_v2.General
public string Name { get; set; } public string Name { get; set; }
public byte[] Raw; 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;
//}
} }
} }

@ -14,16 +14,6 @@ namespace apphost_extract_v2.General
public AppHostManifest() public AppHostManifest()
{ {
FileEntries = new List<AppHostFileEntry>(); FileEntries = new List<AppHostFileEntry>();
//List<AppHostFileEntry> entries = new List<AppHostFileEntry>();
//for (int i = 0; i < embeddedFileCount; i++)
//{
// AppHostFileEntry entry = new AppHostFileEntry(File);
// entries.Add(entry);
//}
//FileEntries = entries.AsReadOnly();
//Log.Info("Manifest parsed successfully!");
} }
} }
} }

@ -10,51 +10,27 @@ namespace apphost_extract_v2
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
var file = "net5-fd.exe"; Log.Info("apphost-extract-v2 by VollRagm\n", ConsoleColor.Yellow);
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 by VollRagm\n", ConsoleColor.Yellow); var fileInfo = GetFileInfo(args);
var path = GetPath(args);
// var file = AppHostFile.Open(path2);//path.FullName); var apphostAnalyzer = new Analyzer(fileInfo);
// Log.Info($"{file.Header.Manifest.FileEntries.Count} embedded file(s) found."); 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"); Log.Info("File parsed successfully, extracting contents...");
Console.WriteLine(); Console.WriteLine();
Log.Info("Extracting...");
//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.");
Log.Info("Done."); Console.ReadLine();
// file.Close();
Console.ReadLine();*/
} }
//static void RunTest(string file) static FileInfo GetFileInfo(string[] args)
//{
// Log.Info("Running test on " + file + ", scanning for version sig...");
// var d = new Analyzer(fs).GetVersion();
//}
static FileInfo GetPath(string[] args)
{ {
try try
{ {

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text; using System.Text;
namespace apphost_extract_v2 namespace apphost_extract_v2
@ -45,5 +47,43 @@ namespace apphost_extract_v2
fs.Read(buff, 0, length); fs.Read(buff, 0, length);
return buff; 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();
}
} }
} }

Loading…
Cancel
Save