windows下的shellcode剖析浅谈
来源:网络整理 网络用户发布,如有版权联系网管删除 2018-10-19
小编每日吐槽周一,Monday,comes again~又是新的一周,又是一次新的开始。这个周末,小编搬了家,哼哧哼哧的拉着个行李箱,一个轮子,彻底被磨坏。坐公交,转公交,再走路,耗时近2个小时,终于搬进新家。(想知道吗?就不告诉你。(~ ̄ ̄)~*)
今早立刻就体会到新家的好处路程短,耗时少。早早到达公司,还可以买个包子,吃个卤蛋,幸福就是这么简单~o(^^)o 元气满满,开始干活啦~

windows下的shellcode剖析浅谈说到shellcode可能都有些迷茫,不知它是什么东西,可能觉得也很神秘,对于它的专业解释也很少有人提及,今天我们就从以下几个方面来对windows下的shellcode做一个全剖析:
1. shellcode的发展历史以及定义
2. 现今常见的windows下的shellcode种类
3. 动手编写一个简单的shellcode
4. shellcode的存在形式以及编码方式
5. exploit中的shellcode
好了,下面我们就逐一来弄清楚这些问题吧!
1. shellcode的发展历史以及定义:
对于shellcode的发展历史,failewest兄的《0day安全:软件漏洞分析技术》一书中讲的很明白,这里就引用一小段:“1996年,Aleph One在Underground发表了著名论文《SMASHING THE STACK FOR FUN AND PROFIT》,其中详细描述了Linux系统中栈的结构和如何利用基于栈的缓冲区溢出。在这篇具有划时代意义的论文中,Aleph One演示了如何向进程中植入一段用于获得shell的代码,并在论文中称这段被植入进程的代码为’shellcode’。 ”
后来人们干脆统一用shellcode这个专用术语来通称缓冲区溢出攻击中植入进程的代码。这段代码可以是出于恶作剧目的的弹出一个消息框,也可以是出于攻击目的的删改重要文件、窃取数据、上传木马病毒并运行,甚至是出于破坏目的的格式化硬盘等等。”
其实,现在shellcode的应用也很广泛,甚至有些远程控制软件也把自己能做成一个shellcode形式,我们只要理解这个是溢出之后干坏事的一段代码。(文章中提及的shellcode也全是跟溢出相关的shellcode)
2. 现今常见的windows下的shellcode种类
3. 动手编写一个简单的shellcode
首先,让我们来理理思路,看图:

这个图是按shellcode的执行流程来画的,下面逐一来讲解。
其实shellcode就是一段自主的而且功能完善的代码,不过里面不能直接调用API函数,因为它不是运行在编译器环境下,没有include来声明函数,更没有应用程序的函数表,所以,shellcode得自己想办法找到自己调用的API函数的地址,然后强行调用了。
(1) 查找kernel32.dll基址:
shellcode里面用的API函数一般都是与用户界面无关的,因为它要干坏事,一般都是偷偷的,所以它一般用的都是kernel32.dll里面的函数。所以,我们必须先找到kernel32的基址才能进一步找到各API的地址具体地址。
关于获取api基址的方法很多,我这里就讲最简单的一种(这里面集合了众多高手的实践经验):
利用PEB查找kernel32基址:
代码:
可能上面这段代码大家看的不是很明白,现在画个示意图来看看:
(2) 查找API函数地址
通过上面找到了kernel32的基址,但是我们如何得到具体的api函数地址呢?这里就需要涉及到pe文件格式了。这里我只讲解如何从dll文件中找出其函数引出表中的函数地址的方法:(班门弄斧了,见笑~)
a. 在kernel32基址+0x3c处获取e_lfanewc地址,即可以得到PE头
b. 在PE头偏移的0x78处得到函数引出表地址
c. 在引出表的0x1c偏移处获取AddressOfFunctions、AddressOfNames、AddressOfNameOrdinalse
d. AddressOfFunctions和 AddressOfNames是函数地址和函数名通过AddressOfNameOrdinalse一一对应的两个数组
e. 是这样计算的:
搜索AddressOfNames,确定“GetProcAddress”所对应的index;
index = AddressOfNameOrdinalse [ index ];
函数地址 = AddressOfFunctions [ index ];
代码:

(3) 定位exe文件数据
API地址也找到了,现在剩下的就是实现功能,首先,想到的就是找到exe的数据在哪,然后我们把它ReadFile提出来然后CreateFile再WriteFile不就完了。但是,我们又面临以下两个问题:
* 如何找到exe数据呢?
这个问题很好回答,exe的数据就在我们的exploit文件中,接下来就有些难度了;
* 如何定位exploit文件呢?
我们可以考虑两种做法:
一是知道exploit文件的路径,那样就可以用CreateFile来打开它,从而获取数据,不过这种做法还面临一个困难,如何得到exploit文件的路径,当然,办法还是有的;
第二种方法就是找到exploit的文件句柄,这里先讨论一个逻辑关系,就是我们为什么可以利用这种方法,原因很简单,你的exploit其实已经打开了,只是你自己不知道它的句柄而已,这样,只要我们能群举出句柄,那么就可以直接通过句柄来读exploit里面的exe文件数据了。
上面两种方法的优缺点显而易见,第二种方法通用性更强,第一种方法虽然可以用更加巧妙的方式来实现,但是难度相对较高,而且不容易理解,所以我就以第二种方法为例来介绍,即群举句柄法:
代码:

这里还要办的一件事就是,在ReadFile和Writefile的时候需要申请一个空间来存放exe文件数据,这就由GlobalAlloc和GlobalFree来负责解决这个问题了,这里就不需要详细解释了。

(4) 生成并运行exe
这个就比较简单了,直接看代码:

(5) 打扫战场,闪人
当然首先是要把前面申请的内存空间释放掉,然后用个exitprocess来结束这一切吧,一是留点善心,二是省事:
代码:

4. shellcode的自动提取
上面写的shellcode是用汇编写的,我们不至于把它直接拷到exploit里面去执行吧,cpu认的是机器码,所以你控制了eip之后当然得把它指到cpu能识别的指令上去吧。所以,我们得把汇编转换成机器码,网上公开的方法很多,我这里介绍一种比较简便的方法吧:既然你是用汇编写的那么你的代码在.code段的内存中应该就直接是机器码了,只需要在开始和结尾打个标记,然后把它直接从那里导出来就完了。
看代码:

5. exploit中的shellcode
在exploit中,有时候由于隐蔽性、str的0x00断开限制、JavaScript等脚本中不同的字符串格式要求下,可能shellcode会需要以不同的形式来放到exploit中。下面逐一来说明:
(1) 隐蔽性
这个一般就是对shellcode进行简单的编码,比如异或等方式
(2) Str的0x00断开限制
有时候shellcode是作为一个问题函数的参数被传入的,这个时候就必须考虑传入shellcode的完整性了,因为在字符串中往往0x00会将字符串断开,所以必须向办法在shellcode中避免0x00的出现
(3) JavaScript中的unescape
在JavaScript中所有的变量基本都是以字符串的形式或者unescape的形式存在的,没有byte这种概念,因此对于一些诸如0x00,0x01等等这些非字符串是无法表示的,最好还是用它的unescape来存在,那么就必须把的shellcode转换成它的格式来放在exploit中了,这种多见于JavaScript溢出利用中。
不知不觉写了这么多,由于本人的知识水平和表达能力有限,所以其中不免有错误之处,希望大家能够批评指正!
*转载请注明来自看雪论坛@pediy.com

kill time, or kiss time.或消磨,或撕磨
看雪安全 看雪众测持续关注安全16年,专业为您服务!你一定要点二维码关注我

今早立刻就体会到新家的好处路程短,耗时少。早早到达公司,还可以买个包子,吃个卤蛋,幸福就是这么简单~o(^^)o 元气满满,开始干活啦~


1. shellcode的发展历史以及定义
2. 现今常见的windows下的shellcode种类
3. 动手编写一个简单的shellcode
4. shellcode的存在形式以及编码方式
5. exploit中的shellcode
好了,下面我们就逐一来弄清楚这些问题吧!
1. shellcode的发展历史以及定义:
对于shellcode的发展历史,failewest兄的《0day安全:软件漏洞分析技术》一书中讲的很明白,这里就引用一小段:“1996年,Aleph One在Underground发表了著名论文《SMASHING THE STACK FOR FUN AND PROFIT》,其中详细描述了Linux系统中栈的结构和如何利用基于栈的缓冲区溢出。在这篇具有划时代意义的论文中,Aleph One演示了如何向进程中植入一段用于获得shell的代码,并在论文中称这段被植入进程的代码为’shellcode’。 ”
后来人们干脆统一用shellcode这个专用术语来通称缓冲区溢出攻击中植入进程的代码。这段代码可以是出于恶作剧目的的弹出一个消息框,也可以是出于攻击目的的删改重要文件、窃取数据、上传木马病毒并运行,甚至是出于破坏目的的格式化硬盘等等。”
其实,现在shellcode的应用也很广泛,甚至有些远程控制软件也把自己能做成一个shellcode形式,我们只要理解这个是溢出之后干坏事的一段代码。(文章中提及的shellcode也全是跟溢出相关的shellcode)
2. 现今常见的windows下的shellcode种类
这里,我就直接从功能上来分类:
这是一个真正的原始意义上的shellcode,不得不讲
这个是最简单的一类shellcode,在网马中的应用也最广泛
为什么会有这么一类shellcode呢?试想想,制造漏洞的坏人如果把前面两类shellcode绑定到一个应用软件exploit里面就会出现一些意外情况:
a) 假如你的反弹行为被防火墙给拦截了怎么办?
b) 假如对方的防范意识比较高打开doc、pdf之类的文件的时候总是把网断了再打开怎么办?
3. 动手编写一个简单的shellcode
好了,前面说了这么多废话,说一会也该练练了。
首先,让我们来理理思路,看图:

这个图是按shellcode的执行流程来画的,下面逐一来讲解。
其实shellcode就是一段自主的而且功能完善的代码,不过里面不能直接调用API函数,因为它不是运行在编译器环境下,没有include来声明函数,更没有应用程序的函数表,所以,shellcode得自己想办法找到自己调用的API函数的地址,然后强行调用了。
(1) 查找kernel32.dll基址:
shellcode里面用的API函数一般都是与用户界面无关的,因为它要干坏事,一般都是偷偷的,所以它一般用的都是kernel32.dll里面的函数。所以,我们必须先找到kernel32的基址才能进一步找到各API的地址具体地址。
关于获取api基址的方法很多,我这里就讲最简单的一种(这里面集合了众多高手的实践经验):
利用PEB查找kernel32基址:
代码:

可能上面这段代码大家看的不是很明白,现在画个示意图来看看:

至于为什么这里放的就是kernel32的基址呢,这需要感谢那些经验丰富的大牛了,ms本来就是这么设计的,但是要找到这么通用的方法可不简单。同时,代码里面也对9x系统进行了判断,相信大家可以通过上面的图看明白是什么意思。
(2) 查找API函数地址
通过上面找到了kernel32的基址,但是我们如何得到具体的api函数地址呢?这里就需要涉及到pe文件格式了。这里我只讲解如何从dll文件中找出其函数引出表中的函数地址的方法:(班门弄斧了,见笑~)
a. 在kernel32基址+0x3c处获取e_lfanewc地址,即可以得到PE头
b. 在PE头偏移的0x78处得到函数引出表地址
c. 在引出表的0x1c偏移处获取AddressOfFunctions、AddressOfNames、AddressOfNameOrdinalse
d. AddressOfFunctions和 AddressOfNames是函数地址和函数名通过AddressOfNameOrdinalse一一对应的两个数组
e. 是这样计算的:
搜索AddressOfNames,确定“GetProcAddress”所对应的index;
index = AddressOfNameOrdinalse [ index ];
函数地址 = AddressOfFunctions [ index ];
代码:

(3) 定位exe文件数据
API地址也找到了,现在剩下的就是实现功能,首先,想到的就是找到exe的数据在哪,然后我们把它ReadFile提出来然后CreateFile再WriteFile不就完了。但是,我们又面临以下两个问题:
* 如何找到exe数据呢?
这个问题很好回答,exe的数据就在我们的exploit文件中,接下来就有些难度了;
* 如何定位exploit文件呢?
我们可以考虑两种做法:
一是知道exploit文件的路径,那样就可以用CreateFile来打开它,从而获取数据,不过这种做法还面临一个困难,如何得到exploit文件的路径,当然,办法还是有的;
第二种方法就是找到exploit的文件句柄,这里先讨论一个逻辑关系,就是我们为什么可以利用这种方法,原因很简单,你的exploit其实已经打开了,只是你自己不知道它的句柄而已,这样,只要我们能群举出句柄,那么就可以直接通过句柄来读exploit里面的exe文件数据了。
上面两种方法的优缺点显而易见,第二种方法通用性更强,第一种方法虽然可以用更加巧妙的方式来实现,但是难度相对较高,而且不容易理解,所以我就以第二种方法为例来介绍,即群举句柄法:
代码:

这里还要办的一件事就是,在ReadFile和Writefile的时候需要申请一个空间来存放exe文件数据,这就由GlobalAlloc和GlobalFree来负责解决这个问题了,这里就不需要详细解释了。

(4) 生成并运行exe
这个就比较简单了,直接看代码:

(5) 打扫战场,闪人
当然首先是要把前面申请的内存空间释放掉,然后用个exitprocess来结束这一切吧,一是留点善心,二是省事:
代码:

4. shellcode的自动提取
上面写的shellcode是用汇编写的,我们不至于把它直接拷到exploit里面去执行吧,cpu认的是机器码,所以你控制了eip之后当然得把它指到cpu能识别的指令上去吧。所以,我们得把汇编转换成机器码,网上公开的方法很多,我这里介绍一种比较简便的方法吧:既然你是用汇编写的那么你的代码在.code段的内存中应该就直接是机器码了,只需要在开始和结尾打个标记,然后把它直接从那里导出来就完了。
看代码:

5. exploit中的shellcode
在exploit中,有时候由于隐蔽性、str的0x00断开限制、JavaScript等脚本中不同的字符串格式要求下,可能shellcode会需要以不同的形式来放到exploit中。下面逐一来说明:
(1) 隐蔽性
这个一般就是对shellcode进行简单的编码,比如异或等方式
(2) Str的0x00断开限制
有时候shellcode是作为一个问题函数的参数被传入的,这个时候就必须考虑传入shellcode的完整性了,因为在字符串中往往0x00会将字符串断开,所以必须向办法在shellcode中避免0x00的出现
(3) JavaScript中的unescape
在JavaScript中所有的变量基本都是以字符串的形式或者unescape的形式存在的,没有byte这种概念,因此对于一些诸如0x00,0x01等等这些非字符串是无法表示的,最好还是用它的unescape来存在,那么就必须把的shellcode转换成它的格式来放在exploit中了,这种多见于JavaScript溢出利用中。
不知不觉写了这么多,由于本人的知识水平和表达能力有限,所以其中不免有错误之处,希望大家能够批评指正!
*转载请注明来自看雪论坛@pediy.com




查看评论 回复