{"id":18654904,"url":"https://github.com/jorianwoltjer/python-reassembler","last_synced_at":"2025-05-12T13:15:13.525Z","repository":{"id":228641829,"uuid":"774518945","full_name":"JorianWoltjer/python-reassembler","owner":"JorianWoltjer","description":"Re-assemble Python disassembly text to bytecode","archived":false,"fork":false,"pushed_at":"2024-04-05T12:52:51.000Z","size":12,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-12T13:14:56.755Z","etag":null,"topics":["bytecode","cli","decompiler","python","reassembler"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JorianWoltjer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-03-19T17:31:05.000Z","updated_at":"2025-04-19T11:11:20.000Z","dependencies_parsed_at":"2024-04-05T13:47:13.600Z","dependency_job_id":"78637dd8-18c4-4b02-a773-3df2500149b3","html_url":"https://github.com/JorianWoltjer/python-reassembler","commit_stats":null,"previous_names":["jorianwoltjer/python-reassembler"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JorianWoltjer%2Fpython-reassembler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JorianWoltjer%2Fpython-reassembler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JorianWoltjer%2Fpython-reassembler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JorianWoltjer%2Fpython-reassembler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JorianWoltjer","download_url":"https://codeload.github.com/JorianWoltjer/python-reassembler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253745196,"owners_count":21957319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode","cli","decompiler","python","reassembler"],"created_at":"2024-11-07T07:16:59.583Z","updated_at":"2025-05-12T13:15:13.473Z","avatar_url":"https://github.com/JorianWoltjer.png","language":"Python","readme":"Python re-assembler\n============================\n\n###### Re-assemble Python disassembly text to bytecode\n\n ---\n\nThis project serves a pretty unique use case, where you **have** output of the [`dis.dis()`](https://docs.python.org/3/library/dis.html#dis.dis) module in the form of disassembled bytecode:\n\n```\n  0 LOAD_GLOBAL              0 (print)\n  2 LOAD_CONST               1 ('Hello,')\n  4 LOAD_CONST               2 ('world!')\n  6 CALL_FUNCTION            2\n  8 POP_TOP\n 10 LOAD_CONST               0 (None)\n 12 RETURN_VALUE\n```\n\nUsing this tool, the above can be parsed and turned back into raw bytecode. This allows decompilers like [`uncompyle6`](https://github.com/rocky/python-uncompyle6/releases), [`decompyle3`](https://github.com/rocky/python-decompile3/blob/master/decompyle3/main.py) and [`pycdc`](https://github.com/zrax/pycdc) to work with the bytes. \n\n```Shell\n$ python-reassembler debug/example.txt -q\nOpening 'example.txt' and reassembling the code object...\n[SUCCESS] Code object written to 'output.pyc'\nNow use a decompiler like `uncompyle6`, `decompyle3`, or `pycdc` on the output to decompile it\n\n$ decompyle3 output.pyc\n# decompyle3 version 3.9.1\nprint(\"Hello,\", \"world!\")\n# okay decompiling output.pyc\n```\n\nThe inspiration for this tool came from a [PicoCTF 2024](https://picoctf.org/competitions/2024-spring.html) challenge named 'weirdSnake', where players received a text file containing disassembled Python bytecode. The easiest solution there would be to manually try and understand the code and became quite easy with some educated guesses. But this made me wonder, would it be possible to turn this back into clean readable source code?  \nThe answer: Yes! You can solve the challenge by re-assembling the input file at [debug/4.txt](debug/4.txt), and decompiling it with `decompyle3`:\n\n```Shell\n$ python-reassembler debug/4.txt\n[SUCCESS] Code object written to 'output.pyc'\n$ decompyle3 output.pyc | tee output.py\n# decompyle3 version 3.9.1\ninput_list = [\n 4,54,41,0,112,32,25,49,33,3,0,0,57,32,108,23,48,4,9,70,7,110,36,8,108,7,49,10,4,86,43,102,126,92,0,16,58,41,89,78]\nkey_str = \"J\"\nkey_str = \"_\" + key_str\nkey_str = key_str + \"o\"\nkey_str = key_str + \"3\"\nkey_str = \"t\" + key_str\nkey_list = [ord(char) for char in key_str]\nwhile len(key_list) \u003c len(input_list):\n    key_list.extend(key_list)\n\nresult = [a ^ b for a, b in zip(input_list, key_list)]\nresult_text = \"\".join(map(chr, result))\n# okay decompiling output.pyc\n$ python -i output.py\n\u003e\u003e\u003e result_text\n'picoCTF{...}'\n```\n\n## Installation\n\n```Bash\ngit clone https://github.com/JorianWoltjer/python-reassembler.git \u0026\u0026 cd python-reassembler\npython3 -m pip install -e .\n```\n\nThen use the `python-reassembler` binary on any file to re-assemble it.\n\n## Testing\n\nTo ensure correctness of the output this tool generates, several tests are included in the [`tests/`](tests/) folder. Using [pytest](https://docs.pytest.org/en/latest/index.html) these can be ran. \n\nFirst, it compares **real source code** against re-assembled and decompiled output to make sure the result is the same. Then it also compares **compiled and decompiled code** against the re-assembled and decompiled output, to make sure any issues aren't just bugs in the `decompyle3` decompiler.\n\nIf you find any input that does not re-assemble correctly, please write a new `.py` file into the [`tests/`](tests/) folder that fails to be re-assembled, and create an [Issue](https://github.com/JorianWoltjer/python-reassembler/issues) showing the input, and/or a [Pull Request](https://github.com/JorianWoltjer/python-reassembler/pulls) that fixes it!\n\n## Resources\n\n* Disassembler format: https://github.com/python/cpython/blob/3.12/Lib/dis.py#L304\n* Opcode list with examples and description: https://unpyc.sourceforge.net/Opcodes.html + Each Python version showing opcodes in source code: https://github.com/python/cpython/blob/3.12/Lib/opcode.py\n* Write `__code__` object to `.pyc` file: https://book.jorianwoltjer.com/languages/python#decompiling-co_code-bytecode\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorianwoltjer%2Fpython-reassembler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjorianwoltjer%2Fpython-reassembler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorianwoltjer%2Fpython-reassembler/lists"}