我在网上找到了以下代码
void main(void)
{
((void(*)()) "\xeb\x11\x59\x31\xc0\x31\xdb\x31\xd2\xb0\x04\xb3\x01\xb2\x36\xcd\x80\xeb\xf6\xe8\xea\xff\xff\xff\x48\x6f\x6c\x62\x65\x72\x74\x6f\x6e\x20\x3c\x33\x20\x40\x6b\x61\x6c\x6f\x75\x30\x30\x30\x20\x2b\x20\x40\x67\x61\x6e\x64\x69\x62\x61\x72\x20\x2b\x20\x40\x53\x74\x65\x70\x68\x61\x6e\x47\x61\x6e\x64\x69\x20\x3a\x29\x0a")();
}
编译并运行此代码正在连续打印以下内容:
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
您能告诉我这是怎么工作的吗?
我已经使用-m32标志对此进行了编译。看着strace输出,它不断调用write系统调用
write(1, "Holberton <3 @kalou000 + @gandib"..., 54Holberton <3 @kalou000 + @gandibar + @StephanGandi :)
在c中,您可以做不可思议的事情。这是其中之一。
您会看到一个简单的缓冲区,但事实并非如此。它是用十六进制字符串表示的机器代码。
此缓冲区强制转换为void函数,然后调用。
["\xeb\x11\x59\x31\xc0..."
是机器代码中的函数体,当转换为(void(*)())
时它成为代码中的函数,然后由();
调用
在c中,您可以将任何内容转换为任何内容。当编译器看到();
时,将跳转指令汇编到第一个字节,然后让CPU执行其余指令。
这就是它的工作方式。
如果您想成为此类程序的大师,可以查看ioccc competition。
在您粘贴的代码中,您将对象的指针转换为函数的指针,并在对象的地址处执行跳转。在那个位置有来自字符串对象的数据。您插入了实际上代表代码的字符串数据。
要查看该代码代表什么,我将其插入了反汇编程序,请参见here:
Disassembly
Raw Hex (zero bytes in bold):
EB115931C031DB31D2B004B301B236CD80EBF6E8EAFFFFFF486F6C626572746F6E203C3320406B616C6F75303030202B204067616E6469626172202B20405374657068616E47616E6469203A290A
String Literal:
"\xEB\x11\x59\x31\xC0\x31\xDB\x31\xD2\xB0\x04\xB3\x01\xB2\x36\xCD\x80\xEB\xF6\xE8\xEA\xFF\xFF\xFF\x48\x6F\x6C\x62\x65\x72\x74\x6F\x6E\x20\x3C\x33\x20\x40\x6B\x61\x6C\x6F\x75\x30\x30\x30\x20\x2B\x20\x40\x67\x61\x6E\x64\x69\x62\x61\x72\x20\x2B\x20\x40\x53\x74\x65\x70\x68\x61\x6E\x47\x61\x6E\x64\x69\x20\x3A\x29\x0A"
Array Literal:
{ 0xEB, 0x11, 0x59, 0x31, 0xC0, 0x31, 0xDB, 0x31, 0xD2, 0xB0, 0x04, 0xB3, 0x01, 0xB2, 0x36, 0xCD, 0x80, 0xEB, 0xF6, 0xE8, 0xEA, 0xFF, 0xFF, 0xFF, 0x48, 0x6F, 0x6C, 0x62, 0x65, 0x72, 0x74, 0x6F, 0x6E, 0x20, 0x3C, 0x33, 0x20, 0x40, 0x6B, 0x61, 0x6C, 0x6F, 0x75, 0x30, 0x30, 0x30, 0x20, 0x2B, 0x20, 0x40, 0x67, 0x61, 0x6E, 0x64, 0x69, 0x62, 0x61, 0x72, 0x20, 0x2B, 0x20, 0x40, 0x53, 0x74, 0x65, 0x70, 0x68, 0x61, 0x6E, 0x47, 0x61, 0x6E, 0x64, 0x69, 0x20, 0x3A, 0x29, 0x0A }
Disassembly:
0: eb 11 jmp 0x13
2: 59 pop ecx
3: 31 c0 xor eax,eax
5: 31 db xor ebx,ebx
7: 31 d2 xor edx,edx
9: b0 04 mov al,0x4
b: b3 01 mov bl,0x1
d: b2 36 mov dl,0x36
f: cd 80 int 0x80
11: eb f6 jmp 0x9
13: e8 ea ff ff ff call 0x2
18: 48 dec eax
19: 6f outs dx,DWORD PTR ds:[esi]
1a: 6c ins BYTE PTR es:[edi],dx
1b: 62 65 72 bound esp,QWORD PTR [ebp+0x72]
1e: 74 6f je 0x8f
20: 6e outs dx,BYTE PTR ds:[esi]
21: 20 3c 33 and BYTE PTR [ebx+esi*1],bh
24: 20 40 6b and BYTE PTR [eax+0x6b],al
27: 61 popa
28: 6c ins BYTE PTR es:[edi],dx
29: 6f outs dx,DWORD PTR ds:[esi]
2a: 75 30 jne 0x5c
2c: 30 30 xor BYTE PTR [eax],dh
2e: 20 2b and BYTE PTR [ebx],ch
30: 20 40 67 and BYTE PTR [eax+0x67],al
33: 61 popa
34: 6e outs dx,BYTE PTR ds:[esi]
35: 64 69 62 61 72 20 2b imul esp,DWORD PTR fs:[edx+0x61],0x202b2072
3c: 20
3d: 40 inc eax
3e: 53 push ebx
3f: 74 65 je 0xa6
41: 70 68 jo 0xab
43: 61 popa
44: 6e outs dx,BYTE PTR ds:[esi]
45: 47 inc edi
46: 61 popa
47: 6e outs dx,BYTE PTR ds:[esi]
48: 64 fs
49: 69 .byte 0x69
4a: 20 3a and BYTE PTR [edx],bh
4c: 29 0a sub DWORD PTR [edx],ecx
这种代码不可移植。
C语言,通过允许这种构造,是homoiconic language的示例-一种有效的语言范式,“代码是数据,数据是代码”(代码和数据之间没有区别)。如果您使用类似Lisp的语言进行编程,则这种编程非常普遍。但是在C语言中很少使用。