标题: 去除ioncube_loader_lin_7.x.so的36小时限制 创建: 2021-04-19 14:28 更新: 2021-04-28 16:40 链接: https://scz.617.cn/web/202104191428.txt https://www.52pojie.cn/thread-1422295-1-1.html ionCube,一款PHP加密工具。有试用版Encoder,Decoder是免费的。试用版Encoder 本身有14天试用期,很容易剁,参看: 《去除试用版ioncube_encoder_xxx_64的14天限制》 https://scz.617.cn/web/202104141608.txt $ ./ioncube_encoder_xxx_64_scz hello.php -o hello_enc.php 这样生成的hello_enc.php只有36小时可用,可以剁。可能有多种办法,此处简述其 中一种逆向思路。 $ php -c php.ini -f hello_enc.php PHP Fatal error: The encoded file hello_enc.php has expired. in Unknown on line 0 strace -v -i -f -ff -o hello_enc.log php -c php.ini -f hello_enc.php 从strace日志看,只有一个write(2,x),可以设断 catch syscall write 不用针对句柄设条件断点。 gdb -q -nx -x /tmp/gdbinit_x64.txt -x "/tmp/ShellPipeCommand.py" -x "/tmp/GetOffset.py" -ex 'display/5i $pc' php catch syscall write r -c php.ini -f hello_enc.php (gdb) bt #0 0x00007ffff4e2ba90 in __write_nocancel () from /lib64/libc.so.6 #1 0x00007ffff4db62f3 in _IO_new_file_write () from /lib64/libc.so.6 #2 0x00007ffff4db6b90 in __GI__IO_file_xsputn () from /lib64/libc.so.6 #3 0x00007ffff4d89f1d in buffered_vfprintf () from /lib64/libc.so.6 #4 0x00007ffff4d8486e in vfprintf () from /lib64/libc.so.6 #5 0x00007ffff4e52215 in __fprintf_chk () from /lib64/libc.so.6 #6 0x0000555555618c5e in php_log_err_with_severity #7 0x0000555555618f87 in php_error_cb #8 0x000055555561ad8f in zend_error #9 0x00007fffed33cb35 in ?? () from ioncube_loader_lin_7.x.so #10 0x00007fffed33957c in ?? () from ioncube_loader_lin_7.x.so #11 0x00007fffed2f2662 in ?? () from ioncube_loader_lin_7.x.so #12 0x00005555557f1eac in zend_execute_scripts #13 0x000055555578d730 in php_execute_script #14 0x0000555555899c8c in do_cli #15 0x000055555561e0aa in main (gdb) i r rdi rsi rdx rdi 0x2 2 rsi 0x7fffffff8050 140737488322640 rdx 0x67 103 (gdb) x/s $rsi 0x7fffffff8050: "PHP Fatal error: \nThe encoded file hello_enc.php has expired.\n in Unknown on line 0\n" 通过调用栈回溯中0x00007fffed33957c定位: -------------------------------------------------------------------------- 00007FFFED338EB9 48 8B 3D 00 2B 17 00 mov rdi, cs:off_7FFFED4AB9C0 00007FFFED338EC0 48 81 C7 28 01 00 00 add rdi, 128h ; env 00007FFFED338EC7 E8 8C C3 F5 FF call __setjmp -------------------------------------------------------------------------- 看到setjmp(),这种比较讨嫌,是从别处longjmp()过来的,一般都不在本函数中。 在IDA中去找匹配的longjmp,可能需要一些*nix编程基本功,最终定位: -------------------------------------------------------------------------- 00007FFFED32DB80 somefunc ... 00007FFFED32DC2A 48 8B 3D 8F DD 17 00 mov rdi, cs:off_7FFFED4AB9C0 00007FFFED32DC31 BE 01 00 00 00 mov esi, 1 ; val 00007FFFED32DC36 48 89 9F F0 01 00 00 mov [rdi+1F0h], rbx 00007FFFED32DC3D 48 81 C7 28 01 00 00 add rdi, 128h ; env 00007FFFED32DC44 E8 0F 7E F6 FF call _longjmp -------------------------------------------------------------------------- gdb -q -nx -x /tmp/gdbinit_x64.txt -x "/tmp/ShellPipeCommand.py" -x "/tmp/GetOffset.py" -ex 'display/5i $pc' php catch load ioncube_loader r -c php.ini -f hello_enc.php 断点命中后对somefunc()增设断点。因为ioncube_loader_lin_7.x.so是动态加载的, 无法在php的e_entry处直接对位于ioncube_loader_lin_7.x.so中的地址设断,gdb好 像没有windbg的延迟断点一说? b *0x7fffed32db80 c (gdb) bt #0 0x00007fffed32db80 in ?? () from ioncube_loader_lin_7.x.so #1 0x00007fffed32ea3c in ?? () from ioncube_loader_lin_7.x.so #2 0x00007fffed338dc6 in ?? () from ioncube_loader_lin_7.x.so #3 0x00007fffed338f0a in ?? () from ioncube_loader_lin_7.x.so #4 0x00007fffed2f2662 in ?? () from ioncube_loader_lin_7.x.so #5 0x00005555557f1eac in zend_execute_scripts #6 0x000055555578d730 in php_execute_script #7 0x0000555555899c8c in do_cli #8 0x000055555561e0aa in main (gdb) x/s $rdi 0x7fffffff6150: "\nThe encoded file hello_enc.php has expired.\n" 可以不对somefunc()设断,直接对longjmp()设断,后者有符号,本例调用 libc!siglongjmp()。忘了为什么最初我没这么干,补做个实验。 gdb -q -nx -x /tmp/gdbinit_x64.txt -x "/tmp/ShellPipeCommand.py" -x "/tmp/GetOffset.py" -ex 'display/5i $pc' php catch load ioncube_loader r -c php.ini -f hello_enc.php 断点命中后增设断点 b *siglongjmp c (gdb) bt #0 0x00007ffff4d72230 in siglongjmp () from /lib64/libc.so.6 #1 0x00007fffed32dc49 in ?? () from ioncube_loader_lin_7.x.so #2 0x00007fffed32ea3c in ?? () from ioncube_loader_lin_7.x.so #3 0x00007fffed338dc6 in ?? () from ioncube_loader_lin_7.x.so #4 0x00007fffed338f0a in ?? () from ioncube_loader_lin_7.x.so #5 0x00007fffed2f2662 in ?? () from ioncube_loader_lin_7.x.so #6 0x00005555557f1eac in zend_execute_scripts #7 0x000055555578d730 in php_execute_script #8 0x0000555555899c8c in do_cli #9 0x000055555561e0aa in main 通过调用栈回溯中0x00007fffed338dc6可以定位一些检查时间的代码逻辑,其中一处 是: -------------------------------------------------------------------------- 00007FFFED3383B1 48 8B 91 00 02 00 00 mov rdx, [rcx+200h] 00007FFFED3383B8 48 81 C2 80 51 01 00 add rdx, 86400 00007FFFED3383BF 48 39 C2 cmp rdx, rax -------------------------------------------------------------------------- gdb -q -nx -x /tmp/gdbinit_x64.txt -x "/tmp/ShellPipeCommand.py" -x "/tmp/GetOffset.py" -ex 'display/5i $pc' php catch load ioncube_loader commands $bpnum tb *0x7fffed3383b1 commands $bpnum silent set *(unsigned int*)($rcx+0x200)=$rax c end c end r -c php.ini -f hello_enc.php 通过断点热Patch,hello_enc.php得到执行。可以静态Patch: $ rasm2 -a x86 -b 64 -s intel -o 0x7fffed3383b1 "mov [rcx+0x200],rax;jmp 0x7fffed3383c8" 48898100020000eb0e $ rasm2 -a x86 -b 64 -s intel -o 0x7fffed3383b1 -D 48898100020000eb0e 0x7fffed3383b1 7 48898100020000 mov qword [rcx + 0x200], rax 0x7fffed3383b8 2 eb0e jmp 0x7fffed3383c8 $ fc /b old new XXXXXXX2: 8B 89 XXXXXXX3: 91 81 XXXXXXX8: 48 EB XXXXXXX9: 81 0E 静态Patch只需要改4字节。 vi php.ini -------------------------------------------------------------------------- zend_extension = ioncube_loader_lin_7.x_scz.so -------------------------------------------------------------------------- $ php -c php.ini -f hello_enc.php Hello World 为减少不必要的麻烦,本文只简述了逆向思路。如有疑问,请勿发问,就当没看过吧。 关于ionCube有很多传说,比如some_enc.php中含有some.php的内容。只说7.x版本吧, 没这回事,通过Hook从半路上截获原始PHP的想法注定徒劳无功。easytoyou必然是恢 复了各原始zend_op_array,然后有自己的PHP反编译器,对付ionCube的唯一技术路 线,真是禽兽。5.x不予考虑。