对于我们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 espint 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进行编码。
