diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0c6485ae..986c2472 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,7 +11,7 @@
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Test/bin/Debug/net6.0/Test.dll",
- "args": [],
+ "args": ["R:\\BurnOutSharp Testing Files\\PackerType\\Wise Installer\\_Undetected\\InstallCollapseII.exe"],
"cwd": "${workspaceFolder}/Test",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
diff --git a/BurnOutSharp.Builders/PortableExecutable.cs b/BurnOutSharp.Builders/PortableExecutable.cs
index d34a116b..fdc830a6 100644
--- a/BurnOutSharp.Builders/PortableExecutable.cs
+++ b/BurnOutSharp.Builders/PortableExecutable.cs
@@ -300,7 +300,7 @@ namespace BurnOutSharp.Builders
// Try to parse the resource directory table
data.Seek(resourceTableAddress, SeekOrigin.Begin);
- var resourceDirectoryTable = ParseResourceDirectoryTable(data, data.Position, executable.SectionTable, optionalHeader.ResourceTable.Size, true);
+ var resourceDirectoryTable = ParseResourceDirectoryTable(data, data.Position, executable.SectionTable, true);
if (resourceDirectoryTable == null)
return null;
@@ -1195,10 +1195,9 @@ namespace BurnOutSharp.Builders
/// Stream to parse
/// Initial offset to use in address comparisons
/// Section table to use for virtual address translation
- /// Indicates the size of the section, only used for top-level
/// Indicates if this is the top level or not
/// Filled resource directory table on success, null on error
- private static ResourceDirectoryTable ParseResourceDirectoryTable(Stream data, long initialOffset, SectionHeader[] sections, uint size = 0, bool topLevel = false)
+ private static ResourceDirectoryTable ParseResourceDirectoryTable(Stream data, long initialOffset, SectionHeader[] sections, bool topLevel = false)
{
// TODO: Use marshalling here instead of building
var resourceDirectoryTable = new ResourceDirectoryTable();
@@ -1293,6 +1292,13 @@ namespace BurnOutSharp.Builders
if (!topLevel)
return resourceDirectoryTable;
+ // If we're not aligned to a section
+ if (!sections.Any(s => s.PointerToRawData == initialOffset))
+ return resourceDirectoryTable;
+
+ // Get the section size
+ int size = (int)sections.First(s => s.PointerToRawData == initialOffset).SizeOfRawData;
+
// Align to the 1024-byte boundary
while (data.Position - initialOffset < size && data.Position % 1024 != 0)
_ = data.ReadByteValue();
@@ -1301,7 +1307,7 @@ namespace BurnOutSharp.Builders
if (data.Position - initialOffset < size)
{
Array.Resize(ref resourceDirectoryTable.Entries, totalEntryCount + 1);
- int length = (int)(data.Position - initialOffset);
+ int length = (int)(size - data.Position - initialOffset);
resourceDirectoryTable.Entries[totalEntryCount] = new ResourceDirectoryEntry
{
diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs
index 5921e956..b4e2f296 100644
--- a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs
+++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace BurnOutSharp.Wrappers
{
- public class MicrosoftCabinet : WrapperBase
+ public partial class MicrosoftCabinet : WrapperBase
{
#region Pass-Through Properties
diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs
new file mode 100644
index 00000000..eefacc9b
--- /dev/null
+++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs
@@ -0,0 +1,1017 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using static BurnOutSharp.Wrappers.CabinetConstants;
+using static BurnOutSharp.Wrappers.FDIConstants;
+
+namespace BurnOutSharp.Wrappers
+{
+ public partial class MicrosoftCabinet
+ {
+
+ }
+
+ ///
+ #region fdi.h
+
+ ///
+ internal class ERF
+ {
+ ///
+ /// FCI/FDI error code - see {FCI,FDI}ERROR_XXX for details.
+ ///
+ public int erfOper;
+
+ ///
+ /// Optional error value filled in by FCI/FDI.
+ ///
+ public int erfType;
+
+ ///
+ /// TRUE => error present
+ ///
+ public bool fError;
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal static class FDIConstants
+ {
+ public const ushort CB_MAX_CHUNK = 32768;
+ public const uint CB_MAX_DISK = 0x7fffffff;
+ public const int CB_MAX_FILENAME = 256;
+ public const int CB_MAX_CABINET_NAME = 256;
+ public const int CB_MAX_CAB_PATH = 256;
+ public const int CB_MAX_DISK_NAME = 256;
+
+ /**********************************************************************/
+
+ ///
+ /// Mask for compression type
+ ///
+ public const ushort tcompMASK_TYPE = 0x000F;
+
+ ///
+ /// No compression
+ ///
+ public const ushort tcompTYPE_NONE = 0x0000;
+
+ ///
+ /// MSZIP
+ ///
+ public const ushort tcompTYPE_MSZIP = 0x0001;
+
+ ///
+ /// Quantum
+ ///
+ public const ushort tcompTYPE_QUANTUM = 0x0002;
+
+ ///
+ /// LZX
+ ///
+ public const ushort tcompTYPE_LZX = 0x0003;
+
+ ///
+ /// Unspecified compression type
+ ///
+ public const ushort tcompBAD = 0x000F;
+
+ ///
+ /// Mask for LZX Compression Memory
+ ///
+ public const ushort tcompMASK_LZX_WINDOW = 0x1F00;
+
+ ///
+ /// Lowest LZX Memory (15)
+ ///
+ public const ushort tcompLZX_WINDOW_LO = 0x0F00;
+
+ ///
+ /// Highest LZX Memory (21)
+ ///
+ public const ushort tcompLZX_WINDOW_HI = 0x1500;
+
+ ///
+ /// Amount to shift over to get int
+ ///
+ public const ushort tcompSHIFT_LZX_WINDOW = 8;
+
+ ///
+ /// Mask for Quantum Compression Level
+ ///
+ public const ushort tcompMASK_QUANTUM_LEVEL = 0x00F0;
+
+ ///
+ /// Lowest Quantum Level (1)
+ ///
+ public const ushort tcompQUANTUM_LEVEL_LO = 0x0010;
+
+ ///
+ /// Highest Quantum Level (7)
+ ///
+ public const ushort tcompQUANTUM_LEVEL_HI = 0x0070;
+
+ ///
+ /// Amount to shift over to get int
+ ///
+ public const ushort tcompSHIFT_QUANTUM_LEVEL = 4;
+
+ ///
+ /// Mask for Quantum Compression Memory
+ ///
+ public const ushort tcompMASK_QUANTUM_MEM = 0x1F00;
+
+ ///
+ /// Lowest Quantum Memory (10)
+ ///
+ public const ushort tcompQUANTUM_MEM_LO = 0x0A00;
+
+ ///
+ /// Highest Quantum Memory (21)
+ ///
+ public const ushort tcompQUANTUM_MEM_HI = 0x1500;
+
+ ///
+ /// Amount to shift over to get int
+ ///
+ public const ushort tcompSHIFT_QUANTUM_MEM = 8;
+
+ ///
+ /// Reserved bits (high 3 bits)
+ ///
+ public const ushort tcompMASK_RESERVED = 0xE000;
+
+ /**********************************************************************/
+
+ public const byte _A_NAME_IS_UTF = 0x80;
+
+ public const byte _A_EXEC = 0x40;
+
+ /**********************************************************************/
+
+ ///
+ /// FDI does detection
+ ///
+ public const int cpuUNKNOWN = -1;
+
+ ///
+ /// '286 opcodes only
+ ///
+ public const int cpu80286 = 0;
+
+ ///
+ /// '386 opcodes used
+ ///
+ public const int cpu80386 = 1;
+
+ /**********************************************************************/
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal enum FDIERROR
+ {
+ FDIERROR_NONE,
+ FDIERROR_CABINET_NOT_FOUND,
+ FDIERROR_NOT_A_CABINET,
+ FDIERROR_UNKNOWN_CABINET_VERSION,
+ FDIERROR_CORRUPT_CABINET,
+ FDIERROR_ALLOC_FAIL,
+ FDIERROR_BAD_COMPR_TYPE,
+ FDIERROR_MDI_FAIL,
+ FDIERROR_TARGET_FILE,
+ FDIERROR_RESERVE_MISMATCH,
+ FDIERROR_WRONG_CABINET,
+ FDIERROR_USER_ABORT,
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal class FDICABINETINFO
+ {
+ ///
+ /// Total length of cabinet file
+ ///
+ public long cbCabinet;
+
+ ///
+ /// Count of folders in cabinet
+ ///
+ public ushort cFolders;
+
+ ///
+ /// Count of files in cabinet
+ ///
+ public ushort cFiles;
+
+ ///
+ /// Cabinet set ID
+ ///
+ public ushort setID;
+
+ ///
+ /// Cabinet number in set (0 based)
+ ///
+ public ushort iCabinet;
+
+ ///
+ /// TRUE => RESERVE present in cabinet
+ ///
+ public bool fReserve;
+
+ ///
+ /// TRUE => Cabinet is chained prev
+ ///
+ public bool hasprev;
+
+ ///
+ /// TRUE => Cabinet is chained next
+ ///
+ public bool hasnext;
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal enum FDIDECRYPTTYPE
+ {
+ ///
+ /// New cabinet
+ ///
+ fdidtNEW_CABINET,
+
+ ///
+ /// New folder
+ ///
+ fdidtNEW_FOLDER,
+
+ ///
+ /// Decrypt a data block
+ ///
+ fdidtDECRYPT,
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal class FDIDECRYPT
+ {
+ ///
+ /// Command type (selects union below)
+ ///
+ public FDIDECRYPTTYPE fdidt;
+
+ ///
+ /// Decryption context
+ ///
+ public object pvUser;
+
+ #region DUMMYUNIONNAME
+
+ ///
+ /// fdidtNEW_CABINET
+ ///
+ public DUMMYUNIONNAMEcabinet cabinet;
+
+ ///
+ /// fdidtNEW_FOLDER
+ ///
+ public DUMMYUNIONNAMEfolder folder;
+
+ ///
+ /// fdidtDECRYPT
+ ///
+ public DUMMYUNIONNAMEdecrypt decrypt;
+
+ ///
+ /// fdidtNEW_CABINET
+ ///
+ public class DUMMYUNIONNAMEcabinet
+ {
+ ///
+ /// RESERVE section from CFHEADER
+ ///
+ public object pHeaderReserve;
+
+ ///
+ /// Size of pHeaderReserve
+ ///
+ public ushort cbHeaderReserve;
+
+ ///
+ /// Cabinet set ID
+ ///
+ public ushort setID;
+
+ ///
+ /// Cabinet number in set (0 based)
+ ///
+ public int iCabinet;
+ }
+
+ ///
+ /// fdidtNEW_FOLDER
+ ///
+ public class DUMMYUNIONNAMEfolder
+ {
+ ///
+ /// RESERVE section from CFFOLDER
+ ///
+ public object pFolderReserve;
+
+ ///
+ /// Size of pFolderReserve
+ ///
+ public ushort cbFolderReserve;
+
+ ///
+ /// Folder number in cabinet (0 based)
+ ///
+ public ushort iFolder;
+ }
+
+ ///
+ /// fdidtDECRYPT
+ ///
+ public class DUMMYUNIONNAMEdecrypt
+ {
+ ///
+ /// RESERVE section from CFDATA
+ ///
+ public object pDataReserve;
+
+ ///
+ /// Size of pDataReserve
+ ///
+ public ushort cbDataReserve;
+
+ ///
+ /// Data buffer
+ ///
+ public object pbData;
+
+ ///
+ /// Size of data buffer
+ ///
+ public ushort cbData;
+
+ ///
+ /// TRUE if this is a split data block
+ ///
+ public bool fSplit;
+
+ ///
+ /// 0 if this is not a split block, or the first piece of a split block;
+ /// Greater than 0 if this is the second piece of a split block.
+ ///
+ public ushort cbPartial;
+ }
+
+ #endregion
+ }
+
+ /**********************************************************************/
+
+ ///
+ internal class FDINOTIFICATION
+ {
+ public long cb;
+ public char[] psz1;
+ public char[] psz2;
+
+ ///
+ /// Points to a 256 character buffer
+ ///
+ public char[] psz3;
+
+ ///
+ /// Value for client
+ ///
+ public object pv;
+
+ public IntPtr hf;
+
+ public ushort date;
+ public ushort time;
+ public ushort attribs;
+
+ ///
+ /// Cabinet set ID
+ ///
+ public ushort setID;
+
+ ///
+ /// Cabinet number (0-based)
+ ///
+ public ushort iCabinet;
+
+ ///
+ /// Folder number (0-based)
+ ///
+ public ushort iFolder;
+
+ public FDIERROR fdie;
+ }
+
+ ///
+ internal enum FDINOTIFICATIONTYPE
+ {
+ ///
+ /// General information about cabinet
+ ///
+ fdintCABINET_INFO,
+
+ ///
+ /// First file in cabinet is continuation
+ ///
+ fdintPARTIAL_FILE,
+
+ ///
+ /// File to be copied
+ ///
+ fdintCOPY_FILE,
+
+ ///
+ /// Close the file, set relevant info
+ ///
+ fdintCLOSE_FILE_INFO,
+
+ ///
+ /// File continued to next cabinet
+ ///
+ fdintNEXT_CABINET,
+
+ ///
+ /// Enumeration status
+ ///
+ fdintENUMERATE,
+ }
+
+ ///
+ internal class FDINOTIFY
+ {
+ public FDINOTIFICATIONTYPE fdint;
+
+ public FDINOTIFICATION pfdin;
+ }
+
+ ///
+ internal class FDISPILLFILE
+ {
+ ///
+ /// Set to { '*', '\0' }
+ ///
+ public char[] ach = new char[2];
+
+ ///
+ /// Required spill file size
+ ///
+ public long cbFile;
+ }
+
+ #endregion
+
+ ///
+ #region cabinet.h
+
+ ///
+ internal static class CabinetConstants
+ {
+ public const int CAB_SPLITMAX = (10);
+
+ public const int CAB_SEARCH_SIZE = (32 * 1024);
+
+ /* structure offsets */
+ public const byte cfhead_Signature = (0x00);
+ public const byte cfhead_CabinetSize = (0x08);
+ public const byte cfhead_FileOffset = (0x10);
+ public const byte cfhead_MinorVersion = (0x18);
+ public const byte cfhead_MajorVersion = (0x19);
+ public const byte cfhead_NumFolders = (0x1A);
+ public const byte cfhead_NumFiles = (0x1C);
+ public const byte cfhead_Flags = (0x1E);
+ public const byte cfhead_SetID = (0x20);
+ public const byte cfhead_CabinetIndex = (0x22);
+ public const byte cfhead_SIZEOF = (0x24);
+ public const byte cfheadext_HeaderReserved = (0x00);
+ public const byte cfheadext_FolderReserved = (0x02);
+ public const byte cfheadext_DataReserved = (0x03);
+ public const byte cfheadext_SIZEOF = (0x04);
+ public const byte cffold_DataOffset = (0x00);
+ public const byte cffold_NumBlocks = (0x04);
+ public const byte cffold_CompType = (0x06);
+ public const byte cffold_SIZEOF = (0x08);
+ public const byte cffile_UncompressedSize = (0x00);
+ public const byte cffile_FolderOffset = (0x04);
+ public const byte cffile_FolderIndex = (0x08);
+ public const byte cffile_Date = (0x0A);
+ public const byte cffile_Time = (0x0C);
+ public const byte cffile_Attribs = (0x0E);
+ public const byte cffile_SIZEOF = (0x10);
+ public const byte cfdata_CheckSum = (0x00);
+ public const byte cfdata_CompressedSize = (0x04);
+ public const byte cfdata_UncompressedSize = (0x06);
+ public const byte cfdata_SIZEOF = (0x08);
+
+ /* flags */
+ public const ushort cffoldCOMPTYPE_MASK = (0x000f);
+ public const ushort cffoldCOMPTYPE_NONE = (0x0000);
+ public const ushort cffoldCOMPTYPE_MSZIP = (0x0001);
+ public const ushort cffoldCOMPTYPE_QUANTUM = (0x0002);
+ public const ushort cffoldCOMPTYPE_LZX = (0x0003);
+ public const ushort cfheadPREV_CABINET = (0x0001);
+ public const ushort cfheadNEXT_CABINET = (0x0002);
+ public const ushort cfheadRESERVE_PRESENT = (0x0004);
+ public const ushort cffileCONTINUED_FROM_PREV = (0xFFFD);
+ public const ushort cffileCONTINUED_TO_NEXT = (0xFFFE);
+ public const ushort cffileCONTINUED_PREV_AND_NEXT = (0xFFFF);
+ public const byte cffile_A_RDONLY = (0x01);
+ public const byte cffile_A_HIDDEN = (0x02);
+ public const byte cffile_A_SYSTEM = (0x04);
+ public const byte cffile_A_ARCH = (0x20);
+ public const byte cffile_A_EXEC = (0x40);
+ public const byte cffile_A_NAME_IS_UTF = (0x80);
+
+ /****************************************************************************/
+ /* our archiver information / state */
+
+ /* MSZIP stuff */
+
+ ///
+ /// window size
+ ///
+ public const ushort ZIPWSIZE = 0x8000;
+
+ ///
+ /// bits in base literal/length lookup table
+ ///
+ public const int ZIPLBITS = 9;
+
+ ///
+ /// bits in base distance lookup table
+ ///
+ public const int ZIPDBITS = 6;
+
+ ///
+ /// maximum bit length of any code
+ ///
+ public const int ZIPBMAX = 16;
+
+ ///
+ /// maximum number of codes in any set
+ ///
+ public const int ZIPN_MAX = 288;
+
+ /* LZX stuff */
+
+ /* some constants defined by the LZX specification */
+ public const int LZX_MIN_MATCH = (2);
+ public const int LZX_MAX_MATCH = (257);
+ public const int LZX_NUM_CHARS = (256);
+
+ ///
+ /// also blocktypes 4-7 invalid
+ ///
+ public const int LZX_BLOCKTYPE_INVALID = (0);
+ public const int LZX_BLOCKTYPE_VERBATIM = (1);
+ public const int LZX_BLOCKTYPE_ALIGNED = (2);
+ public const int LZX_BLOCKTYPE_UNCOMPRESSED = (3);
+ public const int LZX_PRETREE_NUM_ELEMENTS = (20);
+
+ ///
+ /// aligned offset tree #elements
+ ///
+ public const int LZX_ALIGNED_NUM_ELEMENTS = (8);
+
+ ///
+ /// this one missing from spec!
+ ///
+ public const int LZX_NUM_PRIMARY_LENGTHS = (7);
+
+ ///
+ /// length tree #elements
+ ///
+ public const int LZX_NUM_SECONDARY_LENGTHS = (249);
+
+ /* LZX huffman defines: tweak tablebits as desired */
+ public const int LZX_PRETREE_MAXSYMBOLS = (LZX_PRETREE_NUM_ELEMENTS);
+ public const int LZX_PRETREE_TABLEBITS = (6);
+ public const int LZX_MAINTREE_MAXSYMBOLS = (LZX_NUM_CHARS + 50 * 8);
+ public const int LZX_MAINTREE_TABLEBITS = (12);
+ public const int LZX_LENGTH_MAXSYMBOLS = (LZX_NUM_SECONDARY_LENGTHS + 1);
+ public const int LZX_LENGTH_TABLEBITS = (12);
+ public const int LZX_ALIGNED_MAXSYMBOLS = (LZX_ALIGNED_NUM_ELEMENTS);
+ public const int LZX_ALIGNED_TABLEBITS = (7);
+
+ public const int LZX_LENTABLE_SAFETY = (64); /* we allow length table decoding overruns */
+
+ /* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed
+ * blocks have zero growth. MSZIP guarantees that it won't grow above
+ * uncompressed size by more than 12 bytes. LZX guarantees it won't grow
+ * more than 6144 bytes.
+ */
+ public const int CAB_BLOCKMAX = (32768);
+ public const int CAB_INPUTMAX = (CAB_BLOCKMAX + 6144);
+ }
+
+ /* MSZIP stuff */
+
+ ///
+ internal class Ziphuft
+ {
+ ///
+ /// number of extra bits or operation
+ ///
+ public byte e;
+
+ ///
+ /// number of bits in this code or subcode
+ ///
+ public byte b;
+
+ #region v
+
+ ///
+ /// literal, length base, or distance base
+ ///
+ public ushort n;
+
+ ///
+ /// pointer to next level of table
+ ///
+ public Ziphuft t;
+
+ #endregion
+ }
+
+ ///
+ internal class ZIPstate
+ {
+ ///
+ /// current offset within the window
+ ///
+ public uint window_posn;
+
+ ///
+ /// bit buffer
+ ///
+ public uint bb;
+
+ ///
+ /// bits in bit buffer
+ ///
+ public uint bk;
+
+ ///
+ /// literal/length and distance code lengths
+ ///
+ public uint[] ll = new uint[288 + 32];
+
+ ///
+ /// bit length count table
+ ///
+ public uint[] c = new uint[ZIPBMAX + 1];
+
+ ///
+ /// memory for l[-1..ZIPBMAX-1]
+ ///
+ public uint[] lx = new uint[ZIPBMAX + 1];
+
+ ///
+ /// table stack
+ ///
+ public Ziphuft[] u = new Ziphuft[ZIPBMAX];
+
+ ///
+ /// values in order of bit length
+ ///
+ public uint[] v = new uint[ZIPN_MAX];
+
+ ///
+ /// bit offsets, then code stack
+ ///
+ public uint[] x = new uint[ZIPBMAX + 1];
+
+ public int inpos; // byte*
+ }
+
+ /* Quantum stuff */
+
+ ///
+ internal class QTMmodelsym
+ {
+ public ushort sym;
+
+ public ushort cumfreq;
+ }
+
+ ///
+ internal class QTMmodel
+ {
+ public int shiftsleft;
+
+ public int entries;
+
+ public QTMmodelsym[] syms;
+
+ public ushort[] tabloc = new ushort[256];
+ }
+
+ ///
+ internal class QTMstate
+ {
+ ///
+ /// the actual decoding window
+ ///
+ public byte[] window;
+
+ ///
+ /// window size (1Kb through 2Mb)
+ ///
+ public uint window_size;
+
+ ///
+ /// window size when it was first allocated
+ ///
+ public uint actual_size;
+
+ ///
+ /// current offset within the window
+ ///
+ public uint window_posn;
+
+ public QTMmodel model7;
+ public QTMmodelsym[] m7sym = new QTMmodelsym[7 + 1];
+
+ public QTMmodel model4;
+ public QTMmodel model5;
+ public QTMmodel model6pos;
+ public QTMmodel model6len;
+ public QTMmodelsym[] m4sym = new QTMmodelsym[0x18 + 1];
+ public QTMmodelsym[] m5sym = new QTMmodelsym[0x24 + 1];
+ public QTMmodelsym[] m6psym = new QTMmodelsym[0x2a + 1];
+ public QTMmodelsym[] m6lsym = new QTMmodelsym[0x1b + 1];
+
+ public QTMmodel model00;
+ public QTMmodel model40;
+ public QTMmodel model80;
+ public QTMmodel modelC0;
+ public QTMmodelsym[] m00sym = new QTMmodelsym[0x40 + 1];
+ public QTMmodelsym[] m40sym = new QTMmodelsym[0x40 + 1];
+ public QTMmodelsym[] m80sym = new QTMmodelsym[0x40 + 1];
+ public QTMmodelsym[] mC0sym = new QTMmodelsym[0x40 + 1];
+ }
+
+ /* LZX stuff */
+
+ ///
+ internal class LZXstate
+ {
+ ///
+ /// the actual decoding window
+ ///
+ public byte[] window;
+
+ ///
+ /// window size (32Kb through 2Mb)
+ ///
+ public uint window_size;
+
+ ///
+ /// window size when it was first allocated
+ ///
+ public uint actual_size;
+
+ ///
+ /// current offset within the window
+ ///
+ public uint window_posn;
+
+ ///
+ /// for the LRU offset system
+ ///
+ public uint R0, R1, R2;
+
+ ///
+ /// number of main tree elements
+ ///
+ public ushort main_elements;
+
+ ///
+ /// have we started decoding at all yet?
+ ///
+ public int header_read;
+
+ ///
+ /// type of this block
+ ///
+ public ushort block_type;
+
+ ///
+ /// uncompressed length of this block
+ ///
+ public uint block_length;
+
+ ///
+ /// uncompressed bytes still left to decode
+ ///
+ public uint block_remaining;
+
+ ///
+ /// the number of CFDATA blocks processed
+ ///
+ public uint frames_read;
+
+ ///
+ /// magic header value used for transform
+ ///
+ public int intel_filesize;
+
+ ///
+ /// current offset in transform space
+ ///
+ public int intel_curpos;
+
+ ///
+ /// have we seen any translatable data yet?
+ ///
+ public int intel_started;
+
+ public ushort[] tblPRETREE_table = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS << 1)];
+ public ushort[] tblMAINTREE_table = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS << 1)];
+ public ushort[] tblLENGTH_table = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS << 1)];
+ public ushort[] tblALIGNED_table = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS << 1)];
+ }
+
+ ///
+ internal class lzx_bits
+ {
+ public uint bb;
+
+ public int bl;
+
+ public byte[] ip; // byte*
+ }
+
+ /****************************************************************************/
+
+ ///
+ internal class cab_file
+ {
+ ///
+ /// next file in sequence
+ ///
+ public cab_file next;
+
+ ///
+ /// folder that contains this file
+ ///
+ public cab_folder folder;
+
+ ///
+ /// output name of file
+ ///
+ public string filename;
+
+ ///
+ /// open file handle or NULL
+ ///
+ public Stream fh;
+
+ ///
+ /// uncompressed length of file
+ ///
+ public uint length;
+
+ ///
+ /// uncompressed offset in folder
+ ///
+ public uint offset;
+
+ ///
+ /// magic index number of folder
+ ///
+ public ushort index;
+
+ ///
+ /// MS-DOS time/date/attributes
+ ///
+ public ushort time, date, attribs;
+ }
+
+ ///
+ internal class cab_folder
+ {
+ ///
+ /// next folder in sequence
+ ///
+ public cab_folder next;
+
+ ///
+ /// cabinet(s) this folder spans
+ ///
+ public cabinet[] cab = new cabinet[CAB_SPLITMAX];
+
+ ///
+ /// offset to data blocks
+ ///
+ public uint[] offset = new uint[CAB_SPLITMAX];
+
+ ///
+ /// compression format/window size
+ ///
+ public ushort comp_type;
+
+ ///
+ /// compressed size of folder
+ ///
+ public uint comp_size;
+
+ ///
+ /// number of split blocks + 1
+ ///
+ public byte num_splits;
+
+ ///
+ /// total number of blocks
+ ///
+ public ushort num_blocks;
+
+ ///
+ /// the first split file
+ ///
+ public cab_file contfile;
+ }
+
+ ///
+ internal class cabinet
+ {
+ ///
+ /// next cabinet in sequence
+ ///
+ public cabinet next;
+
+ ///
+ /// input name of cabinet
+ ///
+ public string filename;
+
+ ///
+ /// open file handle or NULL
+ ///
+ public Stream fh;
+
+ ///
+ /// length of cabinet file
+ ///
+ public uint filelen;
+
+ ///
+ /// offset to data blocks in file
+ ///
+ public uint blocks_off;
+
+ ///
+ /// multipart cabinet chains
+ ///
+ public cabinet prevcab, nextcab;
+
+ ///
+ /// and their filenames
+ ///
+ public string prevname, nextname;
+
+ ///
+ /// and their visible names
+ ///
+ public string previnfo, nextinfo;
+
+ ///
+ /// first folder in this cabinet
+ ///
+ public cab_folder folders;
+
+ ///
+ /// first file in this cabinet
+ ///
+ public cab_file files;
+
+ ///
+ /// reserved space in datablocks
+ ///
+ public byte block_resv;
+
+ ///
+ /// header flags
+ ///
+ public byte flags;
+ }
+
+ // TODO: Left off at `struct cds_forward `
+
+ #endregion
+}
\ No newline at end of file
diff --git a/BurnOutSharp/PackerType/EmbeddedExecutable.cs b/BurnOutSharp/PackerType/EmbeddedExecutable.cs
new file mode 100644
index 00000000..1304ce99
--- /dev/null
+++ b/BurnOutSharp/PackerType/EmbeddedExecutable.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Linq;
+using BurnOutSharp.Interfaces;
+using BurnOutSharp.Matching;
+using BurnOutSharp.Wrappers;
+
+namespace BurnOutSharp.PackerType
+{
+ ///
+ /// Though not technically a packer, this detection is for any executables that include
+ /// others in their resources in some uncompressed manner to be used at runtime.
+ ///
+ public class EmbeddedExecutable : IPortableExecutableCheck, IScannable
+ {
+ ///
+ public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
+ {
+ // Get the sections from the executable, if possible
+ var sections = pex?.SectionTable;
+ if (sections == null)
+ return null;
+
+ // Get the resources that have an executable signature
+ if (pex.ResourceData?.Any(kvp => kvp.Value is byte[] ba && ba.StartsWith(Models.MSDOS.Constants.SignatureBytes)) == true)
+ return "Embedded Executable";
+
+ return null;
+ }
+
+ ///
+ public ConcurrentDictionary> Scan(Scanner scanner, string file)
+ {
+ if (!File.Exists(file))
+ return null;
+
+ using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ return Scan(scanner, fs, file);
+ }
+ }
+
+ ///
+ public ConcurrentDictionary> Scan(Scanner scanner, Stream stream, string file)
+ {
+ // Parse into an executable again for easier extraction
+ PortableExecutable pex = PortableExecutable.Create(stream);
+ if (pex?.ResourceData == null)
+ return null;
+
+ // Get the resources that have an executable signature
+ var resources = pex.ResourceData
+ .Where(kvp => kvp.Value != null && kvp.Value is byte[])
+ .Where(kvp => (kvp.Value as byte[]).StartsWith(Models.MSDOS.Constants.SignatureBytes))
+ .ToList();
+
+ // If any of the individual extractions fail
+ try
+ {
+ string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ Directory.CreateDirectory(tempPath);
+
+ for (int i = 0; i < resources.Count; i++)
+ {
+ // Get the resource data
+ var resource = resources[i];
+ byte[] data = resource.Value as byte[];
+
+ // Create the temp filename
+ string tempFile = $"embedded_resource_{i}.bin";
+ tempFile = Path.Combine(tempPath, tempFile);
+
+ // Write the resource data to a temp file
+ using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
+ {
+ tempStream.Write(data, 0, data.Length);
+ }
+ }
+
+ // Collect and format all found protections
+ var protections = scanner.GetProtections(tempPath);
+
+ // If temp directory cleanup fails
+ try
+ {
+ Directory.Delete(tempPath, true);
+ }
+ catch (Exception ex)
+ {
+ if (scanner.IncludeDebug) Console.WriteLine(ex);
+ }
+
+ // Remove temporary path references
+ Utilities.Dictionary.StripFromKeys(protections, tempPath);
+
+ return protections;
+ }
+ catch (Exception ex)
+ {
+ if (scanner.IncludeDebug) Console.WriteLine(ex);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/README.md b/README.md
index 15de8794..0d14b0c5 100644
--- a/README.md
+++ b/README.md
@@ -115,31 +115,32 @@ Below is a list of protections detected by BurnOutSharp. The two columns explain
Below is a list of executable packers detected by BurnOutSharp. The three columns explain what sort of checks are performed to determine how the protection is detected as well as if the contents can be extracted.
-| Protection Name | Content Check | Path Check | Extractable |
-| --------------- | ------------- | ---------- | ----------- |
-| 7-zip SFX | Yes | No | No |
-| Advanced Installer / Caphyon Advanced Installer | Yes | No | No |
-| Armadillo | Yes | No | No |
-| ASPack | Yes | No | No |
-| AutoPlay Media Studio | Yes | No | No |
-| CExe | Yes | No | Yes |
-| dotFuscator | Yes | No | No |
-| EXE Stealth | Yes | No | No |
-| Gentee Installer | Yes | No | No |
-| Inno Setup | Yes | No | No |
-| InstallAnywhere | Yes | No | No |
-| Installer VISE | Yes | No | No |
-| Intel Installation Framework | Yes | No | No |
-| Microsoft CAB SFX | Yes | No | No |
-| NSIS | Yes | No | No |
-| PECompact | Yes | No | No |
-| PEtite | Yes | No | No |
-| Setup Factory | Yes | No | No |
-| Shrinker | Yes | No | No |
-| UPX and UPX (NOS Variant) | Yes | No | No |
-| WinRAR SFX | Yes | No | Yes |
-| WinZip SFX | Yes | No | Yes |
-| WISE Installer | Yes | No | Yes |
+| Protection Name | Content Check | Path Check | Extractable | Notes |
+| --------------- | ------------- | ---------- | ----------- | ----- |
+| 7-zip SFX | Yes | No | No | |
+| Advanced Installer / Caphyon Advanced Installer | Yes | No | No | |
+| Armadillo | Yes | No | No | |
+| ASPack | Yes | No | No | |
+| AutoPlay Media Studio | Yes | No | No | |
+| CExe | Yes | No | Yes | |
+| dotFuscator | Yes | No | No | |
+| Embedded Executable | Yes | No | Yes | Not technically a packer |
+| EXE Stealth | Yes | No | No | |
+| Gentee Installer | Yes | No | No | |
+| Inno Setup | Yes | No | No | |
+| InstallAnywhere | Yes | No | No | |
+| Installer VISE | Yes | No | No | |
+| Intel Installation Framework | Yes | No | No | |
+| Microsoft CAB SFX | Yes | No | No | |
+| NSIS | Yes | No | No | |
+| PECompact | Yes | No | No | |
+| PEtite | Yes | No | No | |
+| Setup Factory | Yes | No | No | |
+| Shrinker | Yes | No | No | |
+| UPX and UPX (NOS Variant) | Yes | No | No | |
+| WinRAR SFX | Yes | No | Yes | |
+| WinZip SFX | Yes | No | Yes | |
+| WISE Installer | Yes | No | Yes | |
## Container Formats