标题: ionCube逆向工程进展备忘
这是一篇吹牛式的、毫无意义的备忘,不要看,不值得看。
2021-05-21 16:48 scz
在XX期间有同事提出ionCube保护PHP源码比较结实,于是研究了一下。
此番与ionCube不期而遇,作为没有任何PHP基础的C/ASM程序员,确实感受了作者深 深的恶意以及随之而来巨大的逆向工程挑战。看了看PHP引擎源码,随便吐个槽,这 代码质量真特么烂。用IDA、GDB搞了一下ionCube,再吐个槽,其作者编码风格山寨 而奇葩。
在2021.4.23的日记中评论:
分析过敌对组织的某些样本,其使用了许多令人叹为观止的高级对抗技术,很是佩服。 敌方程序员应该是受过正规而系统的教育,虽然All In One,但逻辑框架一点不显混 乱。对于敌方开发维护,这是很好的现象,以前赞美过这样的对手。但从另一个角度 看,对于己方逆向工程,这也是很好的现象,对于敌方可能就是一种遗憾了。
今天说一个反例。最近在逆ionCube Loader,其逻辑框架显出一种迷之混乱,尽管我 理顺了它的主体流程。考虑到该代码是用于商业PHP加密的,必然包含反静态分析的 处理,但从IDA中看它,仍有大量山寨或者显得业余的代码痕迹,一种编译器想优化 都优化不过来的感觉。当然,这有一种可能,开发者在源代码级别有意进行了反逆向 工程干挠。或者另一种更小概率的可能,开发者智商太高,想哪写哪、信手填坑式开 发,嗯,这样的存在也是有的,比如二十年前的yuange。不确认ionCube Loader的开 发人员是水平太高,还是水平太渣,总之,那些外在山寨、业余的代码逻辑客观上给 逆向工程带来了障碍。我是该赞美TA们呢,还是喷TA们呢?
无论真相如何,此番真地被挑战到了。2019.10剁完Burp Pro之后说过一句话,有技 术追求的人应该去挑战那些崇山峻岭。毋庸置疑,不期而遇的ionCube就是新的崇山 峻岭。
搞了一阵之后,有些看法如下:
(a) ionCube 7.x确实很结实,作者应该与搞逆向工程的搏斗过多年,其实现很变态 (b) ionCube 7.x处理过的some_enc.php不含原始some.php,只有混淆过的opcode (c) 逆向工程技术路线必须分两步走,第一步还原zend_op_array,第二步反编译 (d) easytoyou网站很NB,PHP源码还原度很高,应该有一个强大的私有PHP反编译器
决定先努力达成第一步,即还原zend_op_array,今天阶段性目标达成。
some.php中含有类、多个函数,某类中某函数如下:
/ * func_0 comment */ public function func_0 ( $arg ) { try { $mode = func_1( $arg ); switch ( $mode ) { / * case 0 / case 0 : func_case_0( $mode, $arg ); break; case 1 : func_case_1( $mode ); break; default : / * default / func_default( $mode, " (unexpected)\n" ); throw new Exception( "\$mode is invalid" ); } } catch ( Exception $e ) { print_r( $e ); die; } finally { echo "Finally\n"; } }
写程序还原func_0()对应的zend_op_array并反汇编(不是反编译),效果如下:
/*\n * func_0 comment\n /
func_0(arg)
[0] (11) $arg = RECV(,)
[1] (15) = INIT_FCALL_BY_NAME(,"func_1")
[2] (15) = SEND_VAR_EX($arg,)
[3] (15) var_4 = DO_FCALL_BY_NAME(,)
[4] (15) = ASSIGN($mode,var_4)
[5] (21) tmp_6 = CASE($mode,0x0)
[6] (21) = JMPNZ(tmp_6,->10)
[7] (24) tmp_6 = CASE($mode,0x1)
[8] (24) = JMPNZ(tmp_6,->15)
[9] (24) = JMP(->19,)
[10] (22) = INIT_FCALL_BY_NAME(,"func_case_0")
[11] (22) = SEND_VAR_EX($mode,)
[12] (22) = SEND_VAR_EX($arg,)
[13] (22) = DO_FCALL_BY_NAME(,)
[14] (23) = JMP(->27,)
[15] (25) = INIT_FCALL_BY_NAME(,"func_case_1")
[16] (25) = SEND_VAR_EX($mode,)
[17] (25) = DO_FCALL_BY_NAME(,)
[18] (26) = JMP(->27,)
[19] (31) = INIT_FCALL_BY_NAME(,"func_default")
[20] (31) = SEND_VAR_EX($mode,)
[21] (31) = SEND_VAL_EX(" (unexpected)\n",)
[22] (31) = DO_FCALL_BY_NAME(,)
[23] (32) var_10 = NEW("Exception",)
[24] (32) = SEND_VAL_EX("$mode is invalid",)
[25] (32) = DO_FCALL(,)
[26] (32) = THROW(var_10,)
[27] (32) = JMP(->33,)
[28] (35) = CATCH("Exception",$e)
[29] (37) = INIT_FCALL_BY_NAME(,"print_r")
[30] (37) = SEND_VAR_EX($e,)
[31] (37) = DO_FCALL_BY_NAME(,)
[32] (38) = EXIT(,)
[33] (41) tmp_3 = FAST_CALL(->35,)
[34] (41) = JMP(->37,)
[35] (42) = ECHO("Finally\n",)
[36] (42) = FAST_RET(tmp_3,)
[37] (44) = RETURN(
[n]是opcode索引,(n)是源代码行号。
ionCube Encoder实际是个PHP编译器,应该是基于旧版PHP引擎改的,编译时优化不 够,未能紧跟相应版本PHP引擎;[8]、[9]处的JMPNZ、JMP如果由相应版本PHP引擎生 成,应该是合二为一的JMPZNZ。
反汇编结果中有两段注释丢了,因为它们未被保存到some_enc.php中。只要保存了的 注释,理论上都能还原出来。但easytoyou还原注释时有BUG,不是所有被保存的注释 都能还原出来,于是此处可设计CTF赛题,将flag置于被保存但easytoyou未成功还原 的注释中,简单、粗暴、可解。
目前还在早期原型阶段,只用两个样本测试,还有很多细节需要处理,只是反汇编成 功,第二步反编译器尚未动手。
2021-08-12
用Python完成一版PHP 7反编译器,用OPcache测试成功。应该有不少BUG等着迭代修 正,不过已覆盖很多语法场景,输出结果可用。接下来要与第一阶段的ionCube逆向 工程相结合,以此形成ionCube反编译器。
2021-08-25
完成一版ionCubeDecompile_x64_7.py,成功反编译经ionCube加密过的some_enc.php。
2021-08-26
《漫谈PHP反汇编器/反编译器》 https://scz.617.cn/web/202108261325.txt
2021-09-15
支持两个PHP 7.x版本,BinDiff省了我好多事。
2021-10-12
支持zend_property_info、namespace
2022-08-23
支持带key加密的情形