对于我们Lab2开发的shellcode,存在几个问题。
- 我们是写死了shellcode的起始地址,而程序每一次加载过程中,会出现栈帧移位现象,导致shellcode起始地址发生变化,导致shellcode失效
- 对于不同的机器,系统调用函数如MessageBoxA所在内存地址是不同,需要动态的查找其内存地址
- shellcode运行后,程序会直接崩溃,利用exit()函数终止程序
0x01 跳板技术jmp esp
我们注意到一个现象,在函数栈帧出栈后,ESP指向函数返回地址之后。所以可以将shellcode起始地址放在函数返回地址之后,返回位置覆盖为jmp esp指令的内存地址。因此我们还需要查找该指令内存地址,一个“万年不变”的内存地址是最好的。
查找跳转地址我们可以使用ollydbg插件ollyuni.dll,将其放在ollydbg安装plungins目录下即可使用,如图。不知道可不可以,在我电脑上运行直接卡死。
所以采用C++程序查找jmp esp这条指令存在地址,C程序如下。
#include <windows.h>
#include <stdio.h>
#define DLL_NAME "user32.dll"
main()
{
BYTE* ptr;
int position,address;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle=LoadLibrary(DLL_NAME); //加载动态链接库
if(!handle)
{
printf(" load dll erro !");
exit(0);
}
ptr = (BYTE*)handle; //ptr指向动态链接库基址
for(position = 0; !done_flag; position++) //根据偏移查找指令
{
try
{
// 以后查找其他其他指令修改以下字段值即可
if(ptr[position] == 0xFF && ptr[position+1] == 0xE4)
{
//0xFFE4 is the opcode of jmp esp
int address = (int)ptr + position;
printf("OPCODE found at 0x%x\n",address);
}
}
catch(...)
{
int address = (int)ptr + position;
printf("END OF 0x%x\n", address);
done_flag = true;
}
}
getchar();
}
随意选取一个地址即可,这里选的是0x76830e07
0x02 查找ExitProcess API
MessageBoxA是动态链接库user32.dll的导出函数,ExitProcess是kernel32.dll的导出函数。需要找到这两个函数的内存地址,通常使用的方法是使用depends工具获得动态链接库基址和函数偏移地址计算得到。由于安装的depends工具有点问题,还可以使用另一种方法,编译一段使用该函数的C程序,在Ollydbg中跟随查找到相应内存地址。
#include <stdio.h>
#include <windows.h>
int main()
{
MessageBox(NULL,TEXT("title"),TEXT("content"),MB_OK);
exit(0);
}
0x03 制作shellcode
MessageBoxA内存地址:0x767C7E60
exit(0)内存地址:0x764E6420
jmp esp内存地址:0x76830e07
将相应的汇编转换为机器码,然后修改输入文件,总共修改3处。
用ollydbg运行程序,查看栈区情况。与Lab2的shellcode不同的是,此时有效shellcode位于栈下方(高地址),通过jmp esp跳转执行。
这样的shellcode只能保证能在本机上准确执行,在其他机器上就行不通了,更进一步的shellcode需要实现动态查找API地址的功能。
0x05 通用shellcode和shellcode编码技术
关于这一部分,对汇编和windows编程基础过于薄弱,所以只是简单的了解。shellcode的编码原理如图,但是自己编写编码器和解码器是很麻烦的,所以通常可以借助metaspploit进行编码。