这是一篇吹牛式的、毫无意义的备忘,不要看,不值得看。
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(<IS_NULL>,)
[n]是opcode索引,(n)是源代码行号。反汇编结果中有两段注释丢了,因为它们未被保存到some_enc.php中。
只要保存了的注释,理论上都能还原出来。但easytoyou还原注释时有BUG,不是所有被保存的注释都能还原出来,于是此处可设计CTF赛题,将flag置于easytoyou未成功还原的注释中,简单、粗暴、可解。
目前还在早期原型阶段,只用两个样本测试,还有很多细节需要处理,只是反汇编成功,第二步反编译器尚未动手。
记录进展备忘于此。