现在在前面三篇译文中我们对Shellcode有了大概的了解,其实编译Shellcode是有一定的技巧的,只要堆栈平衡,那么我们就可以用少量甚至不规范的汇编代码编写Shellcode了,下面我以一个MessageBoxA的函数来说明。

首先编写一个弹出信息框的程序,编译器用的VC++6.0

下面这段程序是弹出标题为“test”的信息框

1
2
3
4
5
6
#include "windows.h"
int main()
{
MessageBox(NULL,"test","TEST",NULL);
return 0;
}

在MessageBox处下断点,如图

24

我们来来看下MessageBox函数调用的反汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
省略无关代码....
5: MessageBox(NULL,"test","TEST",NULL);
00401028 mov esi,esp
0040102A push 0
0040102C push offset string "TEST" (00422024)
00401031 push offset string "test" (0042201c)
00401036 push 0
00401038 call dword ptr [__imp__MessageBoxA@16 (0042a2ac)]
0040103E cmp esi,esp
00401040 call __chkesp (00401070)
6:
7: return 0;

我们知道,MessageBox函数有四个参数,我们在这里只填写了标题和内容部分,可以看到其他参数的值为0。

程序首先push压入4个参数,然后调用Messagebox函数地址,执行函数,这里我们来寻找下MessageBox的调用函数地址,在call这一行我们可以看到这个字符串(0042a2ac),这个就是MessageBox的函数地址,在查看内存中我们输入这个地址查找地址。

25

找到这个地址(AE FD 21 77),因为Windows指令是小端存储的,所以我们要把它反过来读取,逆序为(7721fdae),这个就是正确的MessageBox函数地址,但是这个是在作者本人的电脑上的地址,每台电脑的地址可能会有所不同,如果要正确查找函数地址,详情请见前三篇译文。

下面我们来编写shellcode

首先把eax入栈,然后把4个参数入栈,为了简单,就不添加内容了,把MessageBox的函数地址赋值给eax,最后调用MessageBox函数,恢复eax,保持堆栈平衡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "windows.h"

int main()
{
_asm
{
push eax
push 0
push 0
push 0
push 0
mov eax,0x7721fdae ;MessageBox
call eax
pop eax
}
return 0;
}

执行效果

26

汇编直接转换为机器码就是Shellcode了。

所有的Shellcode都可以这么编写,寄存器入栈–参数入栈–调用函数–恢复寄存器,这么编写出来的Shellcode就可以直接利用了,我们在分析或者看别人写的Shellcode里都可以看到相似之处,所以,只有理解了,那么一切就都顺其自然了。