一个表情包引发的悬案!
2023-2-15 15:39:50 Author: 寰宇卫士(查看原文) 阅读量:12 收藏

大家好,我是轩辕。

今天给大家看个有趣的编程知识。

看看下面几行代码:

大家觉得运行后会输出什么?

一进main函数后就进入一个while死循环,理论上来说,这程序就死在这里了。

但有人居然运行打出了“hello world”,你敢信吗?

这是著名的安全论坛——看雪论坛的一位网友(LeadroyaL)的研究过程,强烈推荐大家看看,gcc编译器到底干了什么事!

以下是该网友的原贴:


某日在群里看到这张很怪的表情包,想着这不是扯淡么,一看到运行结果顿时傻眼了。。。

尝试复现

#include <stdio.h>
int main(){
    while(1) ;
}
void unreachable(){
    printf("HelloWorld\n");
}

先放一个在线编译C代码的网站:https://godbolt.org/

初步结果:只有在比较新的C++上才会触发,稳定复现,因此选定 clang++ test.cpp -O1 作为目标进行研究。

分析问题引入位置

objdump/gdb 误入歧途

通过 objdump -d a.out 可以看到,_start 函数直接调用了 unreachable 函数。(一口盐汽水就喷了在屏幕上。。。)

gdb 调试,很诡异,也直接就断在了 unreachable 里,说明二进制确实执行到了这里。

什么?还有程序没有main、从别的函数开始执行?这连接器是抽什么风?

readelf 初见端倪

说实话,我还是第一次见到 “没有main函数、但是能运行” 的程序,于是查看符号表,发现main函数还在,和unreachable指向同一个偏移,但函数大小为0。

也就是说,main函数的函数体被清空了,恰好指向了unreachable的位置,“越界”执行了下一个函数的代码。

查看汇编

clang++ -c -S test.cpp -O1 得到 test.s 汇编文件。

我去掉了汇编大部分不必要的注释,但故意保留了一部分,才能让读者知道,他读的是汇编 。可以看到,main函数里一条指令都没有,全部都是点开头的或者井号开头的注释。

去除全部注释后就是,main 和 _Z11unreachablev 指向同一段汇编代码:

查看IR

clang++ -emit-llvm -S -c test.cpp -O1 得到 test.ll IR文件。可以看出,确实在IR阶段main的内容就发生了变换,由原先的死循环被替换成了 unreachable。注意,这个unreachable是一条名叫UnreachableInstruction的指令,并非函数名。

对比 -O0 的输出结果:

最终,可以得到结论,这个反常的现象是在IR层面的编译器优化引起的。

原文来自「编程技术宇宙」|侵删

中电运行是专业专注培养能源企业IT工匠和提供IT整体解决方案的服务商,也是能源互联网安全专家。我们每天都会分享各种IT相关内容,如果您有任何关于IT疑问,欢迎给我们留言

小白必读!寰宇卫士手把手教你栈溢出(上)

手把手教你栈溢出(中)

手把手教你栈溢出(下)

《信息安全知识》之法律关键常识汇总

CTF经验分享|带你入门带你飞!


文章来源: http://mp.weixin.qq.com/s?__biz=MzIwMzU0NDY5OA==&mid=2247496375&idx=1&sn=0537bb3abfcf6b79a989e2ced4f438f0&chksm=96cf73a2a1b8fab4fc6cface2be75126125e76bca337ec137bd0521b421a3294cd60eef9e4f0#rd
如有侵权请联系:admin#unsafe.sh