标题: 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加密的情形