0x0 唠叨
许久未更新 blog,最近由于要考研忙得很,但是还是挤出时间来研究了一下 shellcode。
研究这个的原因是因为 CNSS 又一次把不用任何形式的括号写 helloworld 搬上了招新测试,本着搞事的原则,我决定花点时间来玩一玩。
0x1 思路
不用任何形式的括号,大概的思路就是直接把机器码写到一个变量里,然后再运行这个变量里的机器码。
想到这里,你可能会一把梭的想到这样的一段汇编:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 .text
_start
_start:
mov edx,len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
mov eax,1
int 0x80
.data
msg db 'Hello, world!', 0xa
len equ $ - msg
嗯,这是一段很简单的 x86 hello world ,调用 linux 的系统调用然后打印输出数据段里的 msg 变量。
那我们试着把它变成 shellcode 来试试看。
0x2 错误的尝试
那么我们先用汇编器把它变成 object file。
1 | nasm -f elf blog.asm |
看起来很不错,然后我们把它放到 C 语言里面,然后用 attribute 指定变量放到 text 段里。1
2char* shellcode __attribute__((section(".text"))) =
"\xba\x0e\x00\x00\x00\xb9\x00\x00\x00\x00\xbb\x01\x00\x00\x00\xb8\x04\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xcd\x80";
然后我们编译运行,似乎我们就要成功了,然后结果你肯定得到的程序无法运行,这是为什么呢?
我们来简单的 trace 一下我们生成的程序运行时的情况。1
2
3
4
5
6 strace ./blog
execve("./blog", ["./blog"], 0x7ffe0ad18d60 /* 37 vars */) = 0
strace: [ Process PID=18003 runs in 32 bit mode. ]
write(1, NULL, 14) = -1 EFAULT (Bad address)
exit(1) = ?
+++ exited with 1 +++
哇,为什么我们的地址变成 NULL 了呢?
这是因为在这种情况下,我们没有办法获得到 data 段里的地址。
0x3 重新来过
这时候我们就需要魔改一下我们的汇编。
大家都知道,在汇编中进行 call 的时候,当前运行到的地址会被 push 到栈中,利用这个特性,我们可以在我们定义变量之前进行一个 call,然后在 call 的这个函数里面 pop 出变量的地址。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 _start
.text
_start:
jmp short wrapper
shellcode:
xor eax, eax
mov al, 0x4
xor ebx, ebx
mov bl, 0x1
pop ecx
xor edx, edx
mov dl, 11
int 0x80
xor eax, eax
mov al, 0x1
xor ebx, ebx
mov bl, 0
int 0x80
wrapper:
call shellcode
message: db "bye world!", 10
让我们再来试试:1
2char* main __attribute__((section(".text"))) =
"\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x00\xcd\x80\xe8\xe2\xff\xff\xff\x62\x79\x65\x20\x77\x6f\x72\x6c\x64\x21\x0a";
哇,我们成功了!
尾声
所以这样我们就成功了,是不是很棒呢!
这只是 shellcode 的简单应用,具体如何真正应用 shellcode 去搞事情,等我哪天再有兴趣了再去研究。
(所以我最后还是用了小括号)逃)