2024年4月16日发(作者:)
The Shellcoder’s Handbook: Discovering and Exploiting Security Holes(七)
7
Windows Shellcode
本书一位作者的女友经常说写shellcode很容易,凭心而论,在Linux上是不太难,但
在Windows上还是有一定难度的,有时候会使人垂头丧气。在开始学习本章之前,我们将
先回顾一些shellcode的要点,然后研究Windows shellcode那令人着迷的特性。沿着这条主
线,我们还将讨论AT&T与Intel句法间的不同,某些Win32漏洞给我们带来的影响,并探
讨高级Windows shellcode的发展方向。
句法和过滤器
首先,不使用编码器/译码器又能工作,而体积又很小的windows Shellcode少之又少。
无论如何,如果有许多破解代码需要你去完成,你可能会想到在破解代码中采用编码器/译
码器 API来避免经常调整shellcode。例如Immunity CANVAS就使用了“附加的”编码器/
译码器。也就是说,它把shellcode视为一组unsigned long列表,把列表中的每个unsigned long
加上x(x可以通过不断重试随机数来找到),经过这样的处理,会得到一组新的没有坏字符
的unsigned long列表。虽然编码器/译码器工作得很好,但还是有人乐意使用XOR、character-
或word-based之类的方法。
重要的是:应该牢记译码器只是把x扩展到不同字符范围的y=f(x)函数。如果x仅仅包
含小写字母,那么可以把f(x) 看作是把小写字母转换成二进制字符并转到那里的函数;当
然,也可以把f(x) 看作是把小字字母转换成大字字母并转到那里的函数。换句话说,当你
遇到设置严密的过滤器(译注:现在有很多程序在接受用户输入时,会过滤一些恶意字符)
时,不要急于一次解决所有的问题,尝试使用多重解码,把攻击串分段转换为二进制等方法,
可能会更容易些。
在本章,我们不介绍编码器/译码器,并假设你知道怎样把二进制数据复制到进程空间
并跳到它。只要你会写Linux shellcode,就应该能编写x86汇编代码。我写windows shellcode
和写Linux shellcode一样,使用相同的工具。从长远来看,熟练掌握几种工具会使编写
shellcode变得更轻松。依我之见,不必花大把的钞票购买Visual Studio,免费的Cygwin
就不错。安装Cygwin可能有点慢,所以你可以试着运行某个程序(gcc,()
as,或其它),来确认安装是否完成。当然,有些人喜欢用NASM或其它的工具,但我认为
这些工具在编辑代码及测试时略有不便。
X86 AT&T 与Intel 句法的对比
在X86汇编代码格式里,AT&T与Intel句法有两个主要的不同点。第一个是AT&T句
法使用[助记符source,dest];而Intel使用[助记符dest,source]。当人们用GUN的gas(AT&T
使用),OllyDbg(Intel 使用)或其它的Windows工具查看汇编代码时,这种互相颠倒的形
欢迎访问,翻译@。
103
The Shellcoder’s Handbook: Discovering and Exploiting Security Holes(七)
式可能会使人摸不着头脑。当然,假如你可以灵活转换这种形式,那么在At&T与Intel之
间还有另外一个重要的不同点:寻址。
X86的寻址有如下的形式:两个寄存器,一个位移量,一个比例因子,如1,2,4或8。
所以,mov eax,[ecx+ebx*4+5000] (OllyDbg中的Intel 句法)等同于mov
5000(%ecx,%ebx,4),%eax (GUN AS中的AT&T句法)。
我建议大家学习AT&T句法,理由是它的句法清晰(译注:选择最适合自己的,而不
是人云亦云。)。考虑一下mov eax,[ecx+ebx],哪个是基址寄存器,哪个是变址寄存器?特别
是在缺少特征时(avoid character),更容易引起混淆。出现这种情形的主要原因还是因为寻
址的问题:两个寄存器似乎是一样的,可以互换;但实际上如果互换,生成的机器指令将完
全不同。
7.1 创建
我们在开始写Windows shellcode时,通常会碰到一个大问题,Win32不提供直接的系
统调用。而真正令人惊奇的是,这是由许多人讨论后决定的。正如Windows的一贯风格那
样,这个特性有令人讨厌的一面,也有值得称赞的一面。值得称赞的是,它使Win32系统
设计者在修复漏洞或扩展内部系统调用API时,不会影响现有的程序。
为了使shellcode在其它程序中运行,需要对其做适当修整,例如:
它必须可以找到需要的Win32 API函数,并生成调用表。
为了建立连接,它必须能加载需要的函数库。
它必须可以连接远程服务器,下载并执行后续的shellcode。
它必须确保自己可以正常退出,并使原来的进程继续运行或终止。
它必须能阻止其它线程对它的终止。
如果它想让后续的Win32调用继续使用堆,它还必须能修复一个或多个堆。
找到需要的Win32 API函数,一般是指在shellcode中硬编码这些函数的地址,或者是
硬编码Windows某个版本的GetProcAddressA()和LoadLibraryA()地址。编写Windows
shellcode最快速的方法之一是硬编码函数地址,当然,这种方法不适合特殊的可执行文件或
某些版本的Windows。正如SQL Slammer蠕虫显示的那样,硬编码函数地址有时候非常有
用。
注解:Slammer 的源代码在互联网上广为流传,它是非常好的、学习硬编码函数地址
的例子。
但在shellcode中硬编码函数地址,将使shellcode依赖特定的可执行文件或操作系统版
本。为了避免这种情形发生,我们只能改用其它方法。一种方法是在进程里模拟链接正常的
DLL,然后找出函数的位置。另一种方法是搜索函数使用的内存空间,通过寻
找进程环境块(Process environment block,PEB)(中国黑客经常用这个方法)找出函数的
位置。在随后的章节中,我们还将介绍怎样利用Windows异常处理系统(exception-handling
system)搜索整个内存空间。
欢迎访问,翻译@。
104


发布评论