Skip to content

Instantly share code, notes, and snippets.

@extremecoders-re
Last active March 26, 2020 19:44
Show Gist options
  • Save extremecoders-re/4a93ae8bad3bbdfeb1474992948d6cbc to your computer and use it in GitHub Desktop.
Save extremecoders-re/4a93ae8bad3bbdfeb1474992948d6cbc to your computer and use it in GitHub Desktop.
PyInstaller Extractor notes

List of typecodes

pyinstaller/bootloader/src/pyi_archive.h

/* Types of CArchive items. */
#define ARCHIVE_ITEM_BINARY           'b'  /* binary */
#define ARCHIVE_ITEM_DEPENDENCY       'd'  /* runtime option */
#define ARCHIVE_ITEM_PYZ              'z'  /* zlib (pyz) - frozen Python code */
#define ARCHIVE_ITEM_ZIPFILE          'Z'  /* zlib (pyz) - frozen Python code */
#define ARCHIVE_ITEM_PYPACKAGE        'M'  /* Python package (__init__.py) */
#define ARCHIVE_ITEM_PYMODULE         'm'  /* Python module */
#define ARCHIVE_ITEM_PYSOURCE         's'  /* Python script (v3) */
#define ARCHIVE_ITEM_DATA             'x'  /* data */
#define ARCHIVE_ITEM_RUNTIME_OPTION   'o'  /* runtime option */

Module importing code

pyinstaller/bootloader/src/pyi_pythonlib.c

/*
 * Import modules embedded in the archive - return 0 on success
 */
int
pyi_pylib_import_modules(ARCHIVE_STATUS *status)
{
    PyObject *marshal;
    PyObject *marshaldict;
    PyObject *loadfunc;
    TOC *ptoc;
    PyObject *co;
    PyObject *mod;
    PyObject *meipass_obj;

    VS("LOADER: setting sys._MEIPASS\n");

    /* TODO extract function pyi_char_to_pyobject */
#ifdef _WIN32
    meipass_obj = PI_PyUnicode_Decode(status->mainpath,
                                      strlen(status->mainpath),
                                      "utf-8",
                                      "strict");
#else
    meipass_obj = PI_PyUnicode_DecodeFSDefault(status->mainpath);
#endif

    if (!meipass_obj) {
        FATALERROR("Failed to get _MEIPASS as PyObject.\n");
        return -1;
    }

    PI_PySys_SetObject("_MEIPASS", meipass_obj);

    VS("LOADER: importing modules from CArchive\n");

    /* Get the Python function marshall.load
     * Here we collect some reference to PyObject that we don't dereference
     * Doesn't matter because the objects won't be going away anyway.
     */
    marshal = PI_PyImport_ImportModule("marshal");
    marshaldict = PI_PyModule_GetDict(marshal);
    loadfunc = PI_PyDict_GetItemString(marshaldict, "loads");

    /* Iterate through toc looking for module entries (type 'm')
     * this is normally just bootstrap stuff (archive and iu)
     */
    ptoc = status->tocbuff;

    while (ptoc < status->tocend) {
        if (ptoc->typcd == ARCHIVE_ITEM_PYMODULE ||
            ptoc->typcd == ARCHIVE_ITEM_PYPACKAGE) {
            unsigned char *modbuf = pyi_arch_extract(status, ptoc);

            VS("LOADER: extracted %s\n", ptoc->name);

            /* .pyc/.pyo files have 8 bytes header. Skip it and load marshalled
             * data form the right point.
             */
            if (pyvers >= 37) {
                /* Python >= 3.7 the header: size was changed to 16 bytes. */
                co = PI_PyObject_CallFunction(loadfunc, "y#", modbuf + 16,
                                              ntohl(ptoc->ulen) - 16);
            }
            else {
                /* It looks like from python 3.3 the header */
                /* size was changed to 12 bytes. */
                co =
                    PI_PyObject_CallFunction(loadfunc, "y#", modbuf + 12, ntohl(
                                                 ptoc->ulen) - 12);
            };

            if (co != NULL) {
                VS("LOADER: callfunction returned...\n");
                mod = PI_PyImport_ExecCodeModule(ptoc->name, co);
            }
            else {
                /* TODO callfunctions might return NULL - find yout why and foor what modules. */
                VS("LOADER: callfunction returned NULL");
                mod = NULL;
            }

            /* Check for errors in loading */
            if (mod == NULL) {
                FATALERROR("mod is NULL - %s", ptoc->name);
            }

            if (PI_PyErr_Occurred()) {
                PI_PyErr_Print();
                PI_PyErr_Clear();
            }

            free(modbuf);
        }
        ptoc = pyi_arch_increment_toc_ptr(status, ptoc);
    }

    return 0;
}

Generating pyimod00_crypto_key.py

pyinstaller/PyInstaller/building/build_main.py

        if cipher:
            logger.info('Will encrypt Python bytecode with key: %s', cipher.key)
            # Create a Python module which contains the decryption key which will
            # be used at runtime by pyi_crypto.PyiBlockCipher.
            pyi_crypto_key_path = os.path.join(CONF['workpath'], 'pyimod00_crypto_key.py')
            with open_file(pyi_crypto_key_path, 'w', encoding='utf-8') as f:
                f.write('# -*- coding: utf-8 -*-\n'
                        'key = %r\n' % cipher.key)
            logger.info('Adding dependencies on pyi_crypto.py module')
            self.hiddenimports.append(pyz_crypto.get_crypto_hiddenimports())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment