def process_pyinstaller_archive(ar, prog): # See utils/cliutils/archive_viewer.py # If PyInstaller is not installed, do nothing try: from PyInstaller.archive.readers import CArchiveReader, NotAnArchiveError except ImportError: return # Attempt to open the program as PyInstaller archive try: pyi_ar = CArchiveReader(prog) except: # Silence all PyInstaller exceptions here return logging.info("Opened PyInstaller archive!") # Create a temporary directory # TODO PY3: Use tempfile.TemporaryDirectory and cleanup() tmpdir = tempfile.mkdtemp(prefix='staticx-pyi-') try: # Process the archive, looking at all shared libs for n, item in enumerate(pyi_ar.toc.data): (dpos, dlen, ulen, flag, typcd, name) = item # Only process binary files # See xformdict in PyInstaller.building.api.PKG if typcd != 'b': continue # Extract it to a temporary location x, data = pyi_ar.extract(n) tmppath = os.path.join(tmpdir, name) directory = os.path.dirname(tmppath) if not os.path.exists(directory): os.makedirs(directory) logging.debug("Extracting to {}".format(tmppath)) with open(tmppath, 'wb') as f: f.write(data) # Silence "you do not have execution permission" warning from ldd make_executable(tmppath) # Add any missing libraries to our archive for libpath in get_shobj_deps(tmppath): lib = os.path.basename(libpath) if lib in ar.libraries: logging.debug("{} already in staticx archive".format(lib)) continue if pyi_ar.toc.find(lib) != -1: logging.debug( "{} already in pyinstaller archive".format(lib)) continue ar.add_library(libpath) finally: shutil.rmtree(tmpdir)
def print_archive_items(executable): logger.info('PyInstaller bundle is "%s"', executable) path = os.path.join(os.path.basename(executable) + '_extracted') logger.info('Extracted bundle files to "%s"', path) makedirs(path, exist_ok=True) logger.info('Got items from PKG') arch = CArchiveReader(executable) pyzlist = [] for toc in arch.toc: logger.debug('toc: %s', toc) dpos, dlen, ulen, flag, typcd, nm = toc if nm.endswith('.pyz') and typcd in ('z', 'Z'): pathnm = os.path.join(path, nm) makedirs(os.path.dirname(pathnm), exist_ok=True) with arch.lib: arch.lib.seek(arch.pkg_start + dpos) with open(pathnm, 'wb') as f: f.write(arch.lib.read(dlen)) pyzlist.append(pathnm) else: logger.info(' %s (%d)', nm, ulen) for pyz in pyzlist: logger.info('Got items from "%s"', pyz) arch = ZlibArchive(pyz) for name in arch.toc: logger.info(' %s', name)
def get_archive(name): if not stack: if name[-4:].lower() == '.pyz': return ZlibArchive(name) return CArchiveReader(name) parent = stack[-1][1] try: return parent.openEmbedded(name) except KeyError: return None except (ValueError, RuntimeError): ndx = parent.toc.find(name) dpos, dlen, ulen, flag, typcd, name = parent.toc[ndx] x, data = parent.extract(ndx) tempfilename = tempfile.mktemp() cleanup.append(tempfilename) open(tempfilename, 'wb').write(data) if typcd == 'z': return ZlibArchive(tempfilename) else: return CArchiveReader(tempfilename)
def extract_pyc_header(self, arch: CArchiveReader) -> bytes: ndx = arch.toc.find('pyimod01_os_path') x, data = arch.extract(ndx) with io.BytesIO(data) as buffer: buffer.seek(0) float_version, _, _, _, _, _, _ = load_module_from_file_object( buffer) if float_version >= 3.7: return data[:16] if float_version >= 3.3: return data[:12] return data[:8]
def extract_to(self, extracted_dir: Path): extracted_dir.mkdir(parents=True, exist_ok=True) arch = CArchiveReader(self.path) pyc_header = self.extract_pyc_header(arch) for dpos, dlen, ulen, flag, typcd, nm in arch.toc.data: ndx = arch.toc.find(nm) x, data = arch.extract(ndx) """ 'm': 'PYMODULE', 's': 'PYSOURCE', 'b': 'EXTENSION', 'z': 'PYZ', 'a': 'PKG', 'x': 'DATA', 'b': 'BINARY', 'Z': 'ZIPFILE', 'b': 'EXECUTABLE', 'd': 'DEPENDENCY', 'o': 'OPTION', """ name = nm if typcd in ('s', 'm'): name += '.pyc' print(name) file_path = extracted_dir / name file_path.parent.mkdir(parents=True, exist_ok=True) with (file_path).open('wb+') as fp: if typcd == 's': fp.write(pyc_header) fp.write(data) if typcd in ('s', 'm'): self.extract_source(fp, extracted_dir / (nm + '.py'))
def repacker(executable, obfpath, entry=None): logger.info('Repack PyInstaller bundle "%s"', executable) obfpath = os.path.normpath(obfpath) logger.info('Obfuscated scripts in the path "%s"', obfpath) name, ext = os.path.splitext(os.path.basename(executable)) entry = name if entry is None else entry logger.info('Entry script name: %s', entry) arch = CArchiveReader(executable) logic_toc = [] obfentry = os.path.join(obfpath, entry + '.py') if not os.path.exists(obfentry): raise RuntimeError('No obfuscated script "%s" found', obfentry) path = os.path.join(name + '_extracted') logger.info('Extracted bundle files to "%s"', path) if not os.path.exists(path): os.makedirs(path) for item in arch.toc: logger.debug('toc: %s', item) dpos, dlen, ulen, flag, typcd, nm = item pathnm = os.path.join(path, nm) with arch.lib: arch.lib.seek(arch.pkg_start + dpos) with open(pathnm, 'wb') as f: f.write(arch.lib.read(dlen)) if nm.endswith('.pyz') and typcd in ('z', 'Z'): logger.info('Extract pyz file "%s"', pathnm) repack_pyz(pathnm, obfpath) patched = 1 elif name == nm: patched = 1 pathnm = obfentry else: patched = 0 logic_toc.append((patched, dlen, ulen, flag, typcd, nm, pathnm)) append_runtime_files(logic_toc, obfpath) obfname = os.path.join(name + '_obf' + ext) shutil.copy2(executable, obfname) repack_exe(path, obfname, logic_toc, obfentry)
def process_pyinstaller_archive(ctx): # See utils/cliutils/archive_viewer.py # If PyInstaller is not installed, do nothing try: from PyInstaller.archive.readers import CArchiveReader, NotAnArchiveError except ImportError: return # Attempt to open the program as PyInstaller archive try: pyi_ar = CArchiveReader(ctx.orig_prog) except: # Silence all PyInstaller exceptions here return logging.info("Opened PyInstaller archive!") with PyInstallHook(ctx.archive, pyi_ar) as h: h.process()
def repacker(executable, items): logger.info('Repack PyInstaller bundle "%s"', executable) name, ext = os.path.splitext(os.path.basename(executable)) arch = CArchiveReader(executable) logic_toc = [] path = os.path.join(name + '_extracted') logger.info('Extracted bundle files to "%s"', path) makedirs(path, exist_ok=True) for toc in arch.toc: logger.debug('toc: %s', toc) dpos, dlen, ulen, flag, typcd, nm = toc pathnm = os.path.join(path, nm) makedirs(os.path.dirname(pathnm), exist_ok=True) with arch.lib: arch.lib.seek(arch.pkg_start + dpos) with open(pathnm, 'wb') as f: f.write(arch.lib.read(dlen)) if nm.endswith('.pyz') and typcd in ('z', 'Z'): logger.info('Extract pyz file "%s"', pathnm) patched = repack_pyz(pathnm, items) elif nm in items: patched = 1 pathnm = items[nm] else: patched = 0 logic_toc.append((patched, dlen, ulen, flag, typcd, nm, pathnm)) output = os.path.join(name + '-patched' + ext) logger.info('Copy "%s" to "%s"', executable, output) shutil.copy2(executable, output) repack_exe(path, output, logic_toc)