PHP 7.2 ionCube Opcode Dumper && Decoder

@dawwinci
Good job, we look forward to updates and improvements.

@dawwinci
These files will be more interesting for testing. No errors, no results. There is nothing in the output.
 

Вложения

  • ic72-test.rar
    647,8 KB · Просмотры: 4
Последнее редактирование:
Okay, but can you provide me with some files protected with IonCube dynamic key encoding?

New update — PHP 7.1 (ionCube loader v14.4)
Для просмотра ссылки Войди или Зарегистрируйся
  • Added logic to force dynamic-key decoding before dumping
  • Patched loader_win_7.1.dll for expiry/trial handling
 
Последнее редактирование:
New update — PHP 7.4 (ionCube loader v15.5)
Для просмотра ссылки Войди или Зарегистрируйся
  • Fix for ioncube.dynamickey protected method opcode dumping
  • Fix for obfuscated names of standard PHP functions
  • Fix for trial-expired encoded ionCube files
 
Copy these three files into the runtime folder, next to php.exe:
Код:
vcruntime140.dll
msvcp140.dll
concrt140.dll

After that, run:
Код:
runtime\php.exe -v

If PHP starts without the vcruntime140.dll warning, then dump.bat should work as well.
 

Вложения

  • runtime.rar
    260,1 KB · Просмотры: 3

Hi.
First of all — amazing work. Seriously. :ay:
I'm genuinely impressed by the amount of effort and knowledge that went into this decompiler. It's incredibly cool and deserves a lot of respect.
While analyzing one of the reconstructed files (simple.php), I noticed a couple of incorrectly reconstructed structures. I'm posting this mostly as debugging feedback in case the project is still being maintained or improved.

1. Incorrect curly brace reconstruction in dynamic method calls​

Some dynamic method/property access expressions were reconstructed with braces in incorrect positions.
I was able to fix the entire file using the following regex replacement:
Find: \{\$this\}->([\w\d]+\((["']+)[\w\d]+\2\))
Replace with: {$this->$1}

After this replacement, the affected expressions became syntactically correct.

2. Incorrect reconstruction of "switch ... case"​

The decompiler reconstructed the following code:
PHP:
    private function ijlj0($j0oli)
    {
        $oojji = "";
        switch ($j0oli) {
            case $this->il0jj("l0iji"):
                $oojji = defined($this->il0jj("joilj")) ? constant($this->il0jj("joilj")) : "";
                break;
            case $this->il0jj("ljjoj"):
                $oojji = defined($this->il0jj("ojli0")) ? constant($this->il0jj("ojli0")) : "";
                break;
            case $this->il0jj("oloji"):
                $oojji = isset($this->{$this->il0jj("i00l0")}->{$this->il0jj("oloji")}[$this->il0jj("jolol")]) ? $this->{$this->il0jj("i00l0")}->{$this->il0jj("oloji")}[$this->il0jj("jolol")] : constant($this->il0jj("joilj"));
                break;
            default:
                return $this->{$this->il0jj("oj0jo")}($oojji);
        }
    }

However, the actually working reconstructed version is:
PHP:
    private function ijlj0($j0oli)
    {
        $oojji = "";
        switch ($j0oli) {
            case $this->il0jj("l0iji"):
                $oojji = defined($this->il0jj("joilj")) ? constant($this->il0jj("joilj")) : "";
                break;
            case $this->il0jj("ljjoj"):
                $oojji = defined($this->il0jj("ojli0")) ? constant($this->il0jj("ojli0")) : "";
                break;
            case $this->il0jj("oloji"):
                $oojji = isset($this->{$this->il0jj("i00l0")}->{$this->il0jj("oloji")}[$this->il0jj("jolol")]) ? $this->{$this->il0jj("i00l0")}->{$this->il0jj("oloji")}[$this->il0jj("jolol")] : constant($this->il0jj("joilj"));
                break;
        }
        return $this->{$this->il0jj("oj0jo")}($oojji);
    }

After these modifications, the reconstructed file started working correctly.

CFG / opcode analysis confusion​

I tried to understand why the "switch" should be reconstructed this way by manually analyzing the opcode CFG.
Function hash / identifier: 696a6c6a30
Relevant dispatcher opcodes:
Код:
0-6    CASE #1 dispatcher
7-11   CASE #2 dispatcher
12-16  CASE #3 dispatcher
17     final dispatcher jump

Jump map:
Код:
6  -> 35 -> 95  (case 1)
11 -> 83        (case 2)
16 -> 50        (case 3)
17 -> 93        (exit / default)

What confused me is that:
  • "case 2" and "case 3" clearly jump into reachable handler regions,
  • but `case 1` appears to jump directly to block "35",
  • and block "35" itself immediately jumps toward function exit/end-switch logic.
Because of this, I could not understand how the handler body for: case $this->il0jj("l0iji")

Because of this, I ended up with two possible conclusions:
  • either my CFG / opcode flow analysis is incomplete or incorrect,
  • or the reconstructed opcodes themselves may not be fully accurate (which honestly seems unlikely).
So I suspect I'm probably missing some Zend VM / ZEND_CASE execution detail here.
If someone could explain how this specific "switch/case" flow works internally in Zend opcodes, I would really appreciate it.

I attached the corrected working reconstructed source file for reference.

Also, judging by the opcode structure and naming patterns, it looks like the original author additionally obfuscated the source before packing it with ionCube, which probably makes reconstruction significantly harder.
(And if anyone knows good automatic PHP deobfuscators for this kind of code — I'd appreciate recommendations.)
 

Вложения

  • simple.zip
    15,4 KB · Просмотры: 0
Назад
Сверху