diff --git a/README.md b/README.md index 5f068d5..ea49e7e 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | PNG | `image/png` | [`patterns/png.hexpat`](patterns/png.hexpat) | PNG image files | | PRODINFO | | [`patterns/prodinfo.hexpat`](patterns/prodinfo.hexpat) | Nintendo Switch PRODINFO | | Protobuf | | [`patterns/protobuf.hexpat`](patterns/protobuf.hexpat) | Google Protobuf encoding | +| PyInstaller | | [`patterns/pyinstaller.hexpat`](patterns/pyinstaller.hexpat) | PyInstaller binray files | | QBCL | | [`patterns/qbcl.hexpat`](patterns/qbcl.hexpat) | Qubicle voxel scene project file | | QOI | `image/qoi` | [`patterns/qoi.hexpat`](patterns/qoi.hexpat) | QOI image files | | Shell Link | `application/x-ms-shortcut` | [`patterns/lnk.hexpat`](patterns/lnk.hexpat) | Windows Shell Link file format | diff --git a/patterns/pyinstaller.hexpat b/patterns/pyinstaller.hexpat new file mode 100644 index 0000000..53bfb9a --- /dev/null +++ b/patterns/pyinstaller.hexpat @@ -0,0 +1,78 @@ +#pragma endian big + +#include + +// Reference: +// https://pyinstaller.org/en/stable/advanced-topics.html + +// typedef struct _cookie { +// char magic[8]; /* 'MEI\014\013\012\013\016' */ +// uint32_t len; /* len of entire package */ +// uint32_t TOC; /* pos (rel to start) of TableOfContents */ +// int TOClen; /* length of TableOfContents */ +// int pyvers; /* new in v4 */ +// char pylibname[64]; /* Filename of Python dynamic library. */ +// } COOKIE; + +u8 cookieStructLength = 88; + +struct Cookie { + char signature[8]; + u32 len; + u32 TOC[[comment("Table of Content")]]; + s32 TOClen; + s32 pyVers; + char pyLibName[64]; +}; + +u32 cookieOffset = std::mem::find_sequence(0, 'M', 'E', 'I', 0x0C, 0x0B, 0x0A, 0x0B, 0x0E); +Cookie cookie @ cookieOffset; + +u32 startOffset = cookieOffset + cookieStructLength - cookie.len; +u32 tocOffset = startOffset + cookie.TOC; + +struct ZlibArchive { + char pyzMagic[4]; + char pyMagic[4]; + u32 pyzTOCPos[[comment("Table of Content of the embedded PYZ")]]; +}; + +// typedef struct _toc { +// int structlen; /* len of this one - including full len of name */ +// uint32_t pos; /* pos rel to start of concatenation */ +// uint32_t len; /* len of the data (compressed) */ +// uint32_t ulen; /* len of data (uncompressed) */ +// char cflag; /* is it compressed (really a byte) */ +// char typcd; /* type code -'b' binary, 'z' zlib, 'm' module, +// * 's' script (v3),'x' data, 'o' runtime option */ +// char name[1]; /* the name to save it as */ +// /* starting in v5, we stretch this out to a mult of 16 */ +// } TOC; + +u32 tocStructLength = 18; + +enum TypeCode : u8 { + Binary = 98, + Zlib = 122, + Module = 109, + Script = 115, + Data = 120, + RuntimeOption = 111, +}; + +struct TOC { + s32 structLen; + u32 pos; + u32 len[[comment("len of the data (compressed)")]];; + u32 uLen[[comment("len of data (uncompressed)")]];; + bool cFlag[[comment("is it compressed")]];; + TypeCode typcd; + char name[this.structLen - tocStructLength]; + + if(typcd == TypeCode::Zlib) { + ZlibArchive zlibArchive @ startOffset + this.pos; + } +}; + + +TOC toc[while( $ < cookie.TOClen + tocOffset)] @ tocOffset; \ No newline at end of file diff --git a/tests/patterns/test_data/pyinstaller.hexpat.elf b/tests/patterns/test_data/pyinstaller.hexpat.elf new file mode 100755 index 0000000..522641a Binary files /dev/null and b/tests/patterns/test_data/pyinstaller.hexpat.elf differ