前段时间在逛知乎的时候,偶然发现一道有意思的编程题
参考链接:hello world编程题你会吗?
Helllo World算是最简单也最基础的程序了,我们一般在编写c语言代码的时候,输入输出都会很自然地联想到frintf()和scanf(),这几道题非常地有意思,要求跳出常规思维输出’’Hello World’。
1.不用 “ “ 输出Hello,World!
不用” “输出,那么就不能用scanf()这个函数了,那么怎么办呢,想到c语言在处理字符常量的时候是把它当做数字来处理的,所以可以用普通char()函数把”Hello World”的每个字符的Ascii码输出。
这道题如果引伸为不用引号来输出(单引号和双引号都不能使用),其实也是一样的,putchar()函数可以直接用数字作为参数。
2.不用 ; 输出Hello,World!
不用;输出,也就是出代码中不能出现完整的语句,仔细思考下在c语言当中哪些位置可以不用;的,发现if语句刚好满足要求。
附上同时满足条件1和2的程序代码:
1 | void () |
这里需要说明的一点是,在最新的c标准中,已经不允许main函数的类型为void。
3.不用# 输出Hello,World!
这道题不得不感叹自己所学的知识的贫瘠,一直想着怎么在windows编译环境下怎么达到这个条件,看了原帖的回复,说是要重新定义printf()函数,然后在dev里试了试重新定义了一下printf函数,发现报错没有定义printf()这个函数。
后来才发现原来重新定义printf()函数在linux环境下是可以实现要求的。
代码如下:
1 | int printf(const char *format,...); |
4.不用括号输出Hello,World!(包括各种括号(),<>,{},[]都不能用 )
第四题出题人并不是为了输出”Hello World”出的题,本意在了解ELF文件格式。
附上知乎大佬的代码:
1 | const char main = 0x55, main1 = 0x48, main2 = 0x89, main3 = 0xe5, main4 = 0xb8, |
5.对上述代码的分析
其中最让我不能理解的是第四种方式输出的Hello ,World!
我用gcc编译器,把他编译后运行,的确能输出Hello ,World!
1 | ➜ gcc test.c -o test |
用ida反编译一下,可以看到start函数还是很正常的,
1 | .text:00000000004003E0 _start proc near ; DATA XREF: LOAD:0000000000400018↑o |
继续看main函数,就变得很不正常了,而且他是在data段。???
1 | .rodata:0000000000400564 ; int __cdecl main(int argc, const char **argv, const char **envp) |
接下来我用gdb动态调试一波,下断点到main函数的地方,
1 | pwndbg> b *0x400564 |
看到这里我明白了,上述代码其实是一段shellcode,gcc编译器是如何识别并编译的,这里我不得而知,看来还要再看一遍程序员的自我修养啊。
分析一下生成的汇编吧。0x400572 <main14> lea esi, [eip + 0x10]
其中这段汇编让我很感兴趣,
程序动态执行的时候总是把静态的数据用这种方式[eip + 0x10]
来传参
很显然里面存的是Hello world,然后syscall系统调用write函数输出,第二次syscall系统调用exit函数。
1 | pwndbg> x/10s 0x400579+0x10 |
6.模仿操作
用上述方式写 execve(“/bin/sh”)
1 | const char main = 0x6a, main1 = 0x42, main2 = 0x58, main3 = 0xfe, main4 = 0xc4, main5 = 0x48, main6 = 0x99, main7 = 0x52, main8 = 0x48, main9 = 0xbf,main10 = 0x2f, main11 = 0x62, main12 = 0x69, main13 = 0x6e, main14 = 0x2f, main15 = 0x2f, main16 = 0x73, main17 = 0x68, main18 = 0x57, main19 = 0x54,main20 = 0x5e, main21 = 0x49, main22 = 0x89, main23 = 0xd0, main24 = 0x49, main25 = 0x89, main26 = 0xd2, main27 = 0x0f, main28 = 0x05; |
orw flag
1 | const char main=0x48,main1=0xb8,main2=0x1,main3=0x1,main4=0x1,main5=0x1,main6=0x1,main7=0x1,main8=0x1,main9=0x1,main10=0x50,main11=0x48,main12=0xb8,main13=0x67,main14=0x2e,main15=0x67,main16=0x6d,main17=0x60,main18=0x66,main19=0x1,main20=0x1,main21=0x48,main22=0x31,main23=0x4,main24=0x24,main25=0x48,main26=0xb8,main27=0x2f,main28=0x68,main29=0x6f,main30=0x6d,main31=0x65,main32=0x2f,main33=0x63,main34=0x74,main35=0x50,main36=0x48,main37=0x89,main38=0xe7,main39=0x31,main40=0xd2,main41=0x31,main42=0xf6,main43=0x6a,main44=0x2,main45=0x58,main46=0xf,main47=0x5,main48=0x31,main49=0xc0,main50=0x6a,main51=0x3,main52=0x5f,main53=0x6a,main54=0x20,main55=0x5a,main56=0x48,main57=0x89,main58=0xe6,main59=0xf,main60=0x5,main61=0x6a,main62=0x1,main63=0x5f,main64=0x6a,main65=0x20,main66=0x5a,main67=0x48,main68=0x89,main69=0xe6,main70=0x6a,main71=0x1,main72=0x58,main73=0xf,main74=0x5; |
参考:
有趣的”Hello World”