Created
April 17, 2024 18:02
-
-
Save motebaya/8e2abd463294c52cb9889bab8a2643e9 to your computer and use it in GitHub Desktop.
simple script that help me to RE python bytecode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3.11 | |
# it help me for RE, @github.com/motebaya | |
# useless tool :) tools 17.04.2024 | |
from typing import Dict | |
from xdis import magics | |
import re, os, types, marshal, argparse | |
class Asmtool: | |
def __init__(self) -> None: | |
self.magic = [] | |
self.index = 1 | |
def dump_magic(self) -> Dict[str, int]: | |
"""dump magic header from | |
:return Dict[str, int]: magics list | |
""" | |
m = magics.magics | |
for vers in map(lambda x: (str(x[0]),magics.magic2int(x[1])), zip(m.keys(), m.values())): | |
if re.sub(r'([\./])*?', str(), vers[0]).isdigit() and vers[0].startswith('3'): | |
self.magic.append(vers) | |
del m | |
self.magic = dict(self.magic) | |
return self.magic | |
def make_header(self, vers: str) -> bytes: | |
"""create magic headers by py version | |
it based from: | |
- https://github.com/python/cpython/blob/fda8cd1fd38a12e17b1db1775d54ff0fa4d886b8/Lib/py_compile.py#L79 | |
- https://github.com/python/cpython/blob/fda8cd1fd38a12e17b1db1775d54ff0fa4d886b8/Lib/importlib/_bootstrap_external.py#L764 | |
:param str vers: py version str | |
:return bytes: header | |
""" | |
pack = (lambda x: (int(x) & 0xFFFFFFFF).to_bytes(4, 'little')) | |
stat = os.stat(__file__) | |
if len(self.magic) == 0: | |
self.dump_magic() | |
data = bytearray(magics.int2magic(self.magic[str(vers)])) | |
data.extend(pack(0)) | |
data.extend(pack(stat.st_ctime)) | |
data.extend(pack(stat.st_size)) | |
return bytes(data) | |
def quick_header(self, vers: str) -> bytes: | |
"""quick get created headers/ | |
:param str vers: py str version | |
:return bytes: header | |
""" | |
match float(vers): | |
case 3.8: | |
return b'U\r\r\n\x00\x00\x00\x00:M?^\x99\x02\x00\x00' | |
case 3.9: | |
return b'a\r\r\n\x00\x00\x00\x00B4\x04`\xa8\x00\x00\x00' | |
case 3.10: | |
return b'o\r\r\n\x00\x00\x00\x00\xf0\xc1\xdaau\x00\x00\x00' | |
case 3.11: | |
return b'\xa7\r\r\n\x00\x00\x00\x00\x06Wie\xd3\x00\x00\x00' | |
def loadcode(self, pycfile: str) -> types.CodeType: | |
"""load python bytecode file to code object | |
:param str pycfile: pycfile | |
:raises FileNotFoundError: no file | |
:raises RuntimeError: potential bad marshal | |
:return types.CodeType: code | |
""" | |
if not os.path.exists(pycfile): | |
raise FileNotFoundError(f'no file {pycfile}') | |
try: | |
return marshal.loads(open(pycfile, 'rb').read().strip()[16:]) | |
except ValueError as e: | |
exit( | |
f" {e}\n try change shebang on: {__file__} to current python version!" | |
) | |
def dump(self, code: types.CodeType, fpath: str) -> None: | |
"""save code object to pyc(bytecode) file's. | |
:param types.CodeType code: from consts attribute/pyc file. | |
:param str fpath: path for save | |
""" | |
for code in code.co_consts: | |
if isinstance(code, types.CodeType): | |
name = '{}/{}.pyc'.format(fpath, code.co_name) | |
print(f" -- ({self.index}) saving to -> {name}") | |
with open(name, 'wb') as f: | |
f.write(self.quick_header(3.11) + marshal.dumps(code)) | |
self.index +=1 | |
self.dump(code, fpath) | |
else: | |
continue | |
def dump_code(self, pycfile: str) -> None: | |
"""recursive save code object to pycfile. | |
:param str pycfile: pycfile | |
""" | |
fpath = 'object_{}'.format(pycfile[:pycfile.find('.pyc')]) | |
if not os.path.exists(fpath): | |
os.mkdir(fpath) | |
self.dump(self.loadcode(pycfile), fpath) | |
if __name__=="__main__": | |
asm = Asmtool() | |
asm.dump_magic() | |
args = argparse.ArgumentParser( | |
formatter_class=argparse.RawTextHelpFormatter, | |
description="\n Util RE: gist.github.com/motebaya \n") | |
args.add_argument('-f','--func',help='dump all code object to pyc files',metavar='') | |
args.add_argument('--quick-header', help="get quick header pyc: 3.8,3.9,3.10,3.11", metavar="") | |
args.add_argument('--make-header', help='create pyc header based python version: [{}..{}]'.format( | |
list(asm.magic.keys())[0], list(asm.magic.keys())[-1] | |
), metavar="") | |
arg = args.parse_args() | |
if arg.func: | |
asm.dump_code( | |
arg.func | |
) | |
elif arg.quick_header: | |
print("```\n{}\n```".format( | |
asm.quick_header( | |
arg.quick_header | |
) | |
)) | |
exit(1) | |
elif arg.make_header: | |
print("```\n{}\n```".format( | |
asm.make_header( | |
arg.make_header | |
) | |
)) | |
exit(1) | |
else: | |
args.print_help() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment