{"id":13611417,"url":"https://github.com/goatmilkkk/nuitka-helper","last_synced_at":"2025-08-07T06:14:31.684Z","repository":{"id":247192251,"uuid":"823561949","full_name":"goatmilkkk/nuitka-helper","owner":"goatmilkkk","description":"Symbol Recovery Tool for Nuitka Binaries","archived":false,"fork":false,"pushed_at":"2024-07-07T07:58:55.000Z","size":1222,"stargazers_count":27,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-08-01T19:57:30.065Z","etag":null,"topics":["ida","ida-plugin","ida-pro","idapython","malware-analysis","nuitka","python","reverse-engineering"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/goatmilkkk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"publiccode":null,"codemeta":null}},"created_at":"2024-07-03T09:10:07.000Z","updated_at":"2024-07-23T11:30:33.000Z","dependencies_parsed_at":"2024-07-07T08:57:10.575Z","dependency_job_id":"bc5eb089-1599-462e-a642-8e7741bbad6d","html_url":"https://github.com/goatmilkkk/nuitka-helper","commit_stats":null,"previous_names":["goatmilkkk/nuitka-helper"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goatmilkkk%2Fnuitka-helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goatmilkkk%2Fnuitka-helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goatmilkkk%2Fnuitka-helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goatmilkkk%2Fnuitka-helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goatmilkkk","download_url":"https://codeload.github.com/goatmilkkk/nuitka-helper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223568212,"owners_count":17166620,"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":["ida","ida-plugin","ida-pro","idapython","malware-analysis","nuitka","python","reverse-engineering"],"created_at":"2024-08-01T19:01:55.067Z","updated_at":"2025-04-13T04:34:22.019Z","avatar_url":"https://github.com/goatmilkkk.png","language":"Python","funding_links":[],"categories":["Python","Deobfuscators"],"sub_categories":["Manual analysis"],"readme":"# nuitka-helper\n\nnuitka-helper is a collection of IDAPython scripts to help analyze Nuitka binaries. It is **intended to be used in conjunction with its accompanying blog post [here](https://www.notion.so/goatmilkk/Nuitka-a3ac9ee7f3f240f3baa345c17f2b8aa3?pvs=4)**. If you have queries, the answers are probably in the blog.\n\n![Analysis Methodology for Nuitka Binaries using `nuitka-helper`](files/images/analysis-flow.png)\n\n# Main Features\n\n### Library Code Recovery\n\n![lib.png](files/images/recover_libs.png)\n\n### User Code Recovery\n\n![func.png](files/images/recover_funcs.png)\n\n### Constants Recovery\n\n\u003cimg src=\"files/images/recover_consts.png\" width=\"80%\"/\u003e\n\n\n\n# Setup\n\n\u003e See blog for detailed information\n\n1. Unpack Nuitka binary\n2. Create Nuitka header file\n    - Optional since `nuitka.h` is provided, but some structs might become obsolete in the future\n3. Create FLIRT signature\n    - Optional but highly recommended as other heuristics may not be as reliable\n      - Use `get_nuitka_version.py` to help\n\n# Usage\n\n\u003e Some scripts use the debugger so run them in a VM if needed\n\n1. Run `nuitka-helper.py` on unpacked binary\n\n2. Organize functions by module\n   \n    ![organize-folders.png](files/images/organize-folders.png)\n    \n3. View logged constants in `constants.log`\n   \n    ![Constants are sorted by the module they belong to](files/images/log_constants.png)\n\n# Plugins\n\n- Parse additional constants using `parse_module_constants.py`\n  - Particularly, we can use it to trace the module dictionary (while debugging)\n\n    ![trace-module-dict.png](files/images/trace-module-dict.png)\n\n- Hook functions using `hook_module_functions.py`\n  \n  - View function trace in `trace.log`\n    \n    ![trace](files/images/log_trace.png)\n  \n- Get function definitions by injecting `get_module_functions.py` into Python process\n\n  - This can be done using a Python injector like [pynject](https://github.com/acureau/pynject)\n\n    - Inject **after** target modules get imported for best results\n\n  - View function definitions in `functions.log`\n  \n    ![trace](files/images/log_functions.png)\n\n# Directory Structure\n\n```jsx\n├───files\n    │   nuitka.h (Nuitka header file)\n    │\n    └───flake (output files for flake.exe)\n            flake.sig\n            solve.py\n \n├───examples\n    ├───types: (test cases for parse_module_constants.py)\n    │       scalar\n    │       collections\n    │\n    └───constructs: (code constructs that are harder to recognize)\n            loops\n            try-except\n \n├───scripts\n    ├───setup\n    │       get_nuitka_version.py\n    │\n    ├───symbol recovery\n    │       nuitka-helper.py\n    │       recover_library_code.py\n    │       recover_modules.py\n    │       recover_constants.py\n    │       recover_functions.py\n    │\n    └───plugins\n            get_module_functions.py\n            hook_module_functions.py\n            parse_module_constants.py\n```\n\n# Supported Platforms\n\n- Windows (not tested on other platforms)\n\n# Known Issues\n\n- Script crashes sometimes due to some Appcall/debugger bug (internal error 40731/unhandled c++ exception)\n\n    ![error](files/images/error.png)\n\n    - Temporary Fix:\n        - Increase the sleep timer in `recover_constants.py`\n        - Re-run `nuitka-helper.py`\n\n# Future Work\n\n\u003e Work that never gets done\n\n- `recover_constants.py`\n  - Fix Appcall/debugger bug (not sure why but this occurs occasionally for certain samples)\n- `recover_library_code.py`\n  - Load structs as type library instead of header file\n- `recover_module_constants.py`\n  - Comment gets cut off if its too long\n    - e.g. module dictionary -\u003e gets printed as fallback for now\n  - [IDA crashes if name is too long](https://hex-rays.com/products/ida/support/sdkdoc/name_8hpp.html)\n\n# FAQ\n\nThe answers are in the blog for these questions:\n\n- Q: How can I manually identify `Nuitka_Function_New`?\n- Q: How do I identify the main module dictionary `moduledict___main__`?\n- Q: Where do I find the code logic in the Nuitka module/function?\n\n---\n\n- Q: Why did you choose to parse the constants dynamically (instead of statically)?\n  - A: First, doing so enables me to trace the module dictionary, which changes at different points of the program. Second, I want the parsing algorithm to be independent of how Nuitka loads its constants, in case it gets updated in the future.\n- Q: What Nuitka versions does this tool support?\n  - A: I only tested the tool on `flake` (1.8.0) \u0026 `GhostLocker` (1.8.4), but I think it should (somewhat) work for other versions too. I did not test the tool on any commercial Nuitka binaries.\n- Q: What can I do if the tool breaks?\n  - A: Here are some things you can try:\n    1. Manually identify `modulecode__main__` using the `Loaded %s` string instead of `__main__`\n    2. Manually identify important library functions (i.e. `loadConstantsBlob`, `Nuitka_Function_New`)\n    3. `ida_typeinf.get_arg_addrs` might be broken (function typed wrongly/remote debugging buggy)\n    4. Let the binary automatically load the constants instead of forcibly loading them","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoatmilkkk%2Fnuitka-helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoatmilkkk%2Fnuitka-helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoatmilkkk%2Fnuitka-helper/lists"}