之前 De1CTF 的 web 扩展题,当时下载扩展 IDA 打开,然后看着反编译代码就懵了,不知道是个什么逻辑。
后来才知道题目是加入了简单的花指令来干扰 IDA 的分析,看了 writeup 之后打算自己动手解一下。
分析工具:IDA 7.0
下载扩展:https://raw.githubusercontent.com/De1ta-team/De1CTF2020/master/writeup/web/mixtrue/docker.zip
用 IDA 打开,按照开发 PHP 扩展的经验直接定位到 zif_Minclude 函数,F5 反编译一下,结果看到:
void __fastcall zif_Minclude(zend_execute_data *execute_data, zval *return_value){ ((void (__fastcall *)(zend_execute_data *, zval *, zend_execute_data *))l2)(execute_data, return_value, execute_data); JUMPOUT(*(_QWORD *)&byte_1245);}
这反编译出来的代码跟印象中的 PHP 扩展代码完全不一样,回去看反编译前的指令,看到一个红色错误:
zif_Minclude endp ; sp-analysis failed
说明这里可能存在花指令或者其他东西影响了 IDA 的分析,可以看到 IDA 看到 call l2,将 l2 当作了一个函数,然后认为函数返回之后的下一条指令是:
db 0EAh
这样一来反编译出来的代码就完全错误了,而机器执行的代码还是正常的,所以这里的花指令可以自己看汇编代码理清流程来去除,像我这样对汇编、栈和函数调用不是很熟悉的人就只能用 gdb 调试 php 扩展来看了。
修改 php.ini 加载扩展,然后 gdb 调试,要提一点的是 php 加载 so 扩展是运行时加载,所以给扩展里的函数要下断点需要在调用该函数之前让 php 停下来,比如在前面加个 var_dump 然后给 php_var_dump 下个断点,再在运行触发断点的时候给该函数下断点。
看一遍就会发现,根源就在于 call 之后的地方:
pop raxadd rax, 8push rax
这里 pop 出来的其实是 call l2 之后的返回地址,即 0x245,而修改之后再入栈则相当于将返回地址改为了 0x24D,而 IDA 静态分析不会发现返回地址被更改了,所以还是 0x245 的错误地址。
所以要去除这里的花指令,就是将 0x22E 到 0x24D 的指令都 nop 掉,然后 undefined,重新创建函数。
这时会发现另一个问题:
.text:00000000000012E2: The function has undefined instruction/data at the specified address.
定位过去看看,是一段无法识别的跳转指令,因为它的操作数需要四个字节的数据作为跳转的地址,而后面的数据已经被识别为了 next 标签的指令,这里我们将它直接 nop 掉,然后再创建。
然后发现,另外一个地址出现了同样的错误,阅读汇编代码之后会发现是个相同的花指令,去除之后再创建,可以发现创建成功了,再 F5 反编译:
void __fastcall zif_Minclude(zend_execute_data *execute_data, zval *return_value){ zval *v2; // r12 unsigned __int64 v3; // rsi FILE *v4; // rbx __int64 v5; // rax void *src; // [rsp+0h] [rbp-98h] size_t n; // [rsp+8h] [rbp-90h] char dest; // [rsp+10h] [rbp-88h] int v9; // [rsp+70h] [rbp-28h] char *v10; // [rsp+74h] [rbp-24h] v2 = return_value; memset(&dest, 0, 0x60uLL); v9 = 0; v10 = &dest; if ( (unsigned int)zend_parse_parameters(execute_data->This.u2.next, "s", &src, &n) != -1 ) { memcpy(&dest, src, n); php_printf("%s", &dest); php_printf("<br>", &dest); v3 = (unsigned __int64)"rb"; v4 = fopen(&dest, "rb"); if ( v4 ) { while ( !feof(v4) ) { v3 = (unsigned int)fgetc(v4); php_printf("%c", v3); } php_printf("n", v3); } else { php_printf("no filen", "rb"); } v5 = zend_strpprintf(0LL, "True"); v2->value.lval = v5; v2->u1.type_info = (*(_BYTE *)(v5 + 5) & 2u) < 1 ? 5126 : 6; }}
可以看到已经是正常的代码了。
参考文章:
https://wizardforcel.gitbooks.io/re-for-beginners/content/Part-III/Chapter-50.html
https://www.52pojie.cn/thread-1068444-1-1.html
https://www.anquanke.com/post/id/208682
https://xuanxuanblingbling.github.io/ctf/pwn/2020/05/05/mixture/
相关知识
逆向花指令入门
【逆向学习】花指令的去除
逆向分析“海莲花” APT木马的花指令反混淆工具
android 加花指令 花指令的作用
花指令总结
2024年网安最新Re 花指令学习
给dll加花指令
花指令手动清除
揭秘Android花指令:解锁手机潜能,安全使用指南
逆向分析基础
网址: 逆向花指令入门 https://m.huajiangbk.com/newsview2047879.html
上一篇: 不使用CHKDSK,手动重置或清 |
下一篇: excel表怎么清除查重的颜色 |