您的位置 首页 > 数码极客

『如何将汇编转换为shellcode』c语言如何转换汇编!

翻译:myswsun

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

0x00 前言


本文的主题是基础的shellcode概念、汇编级编码器/解码器的设计和一些绕过反利用解决方案(如微软的EMET)的方法。为了理解本文的内容,读者至少需要有较好的x86汇编知识,并熟悉COFF及PE的文件格式,还可以阅读(Art of Anti Detection 1 – Introduction to AV & Detection Techniques 和Art of Anti Detection 2 – PE Backdoor Manufacturing)帮助你理解AV产品使用的基本的检测技术的内部细节和本文中的术语。

0x01 术语


进程环境块(PEB):PEB是Windows NT操作系统家族中的一个数据结构。它是由操作系统内部使用的一个不透明的数据结构,它的大部分字段不适用与操作系统之外。[1]微软MSDN文档(其中只包含了部分字段)说明这个结构可能随着操作系统版本不同而变化。[2]PEB包含全局上下文、启动参数、程序映像加载器的数据结构、映像基址和进程内提供互斥的同步对象。

导入地址表(IAT):当应用程序在不同模块中调用一个函数时,地址表被用来作为一个查询表。它包括两种导入形式(序号和名字)。因为一个编译好的程序无法知道依赖库的内存位置,当调用API时需要间接跳转。因为动态链接器加载模块并将它们连接在一起,它将真实的地址写入IAT, 因此它们指向相应库函数的内存位置。

数据执行保护(DEP):DEP是用来校验内存来帮助阻止恶意代码执行的一组硬件和软件的技术。在微软Windows XP SP2和Windows XP Tablet PC Edition 2005版本中,DEP通过硬件和软件实现。DEP的好处是阻止代码从数据页执行。典型的,代码不能在默认堆和栈中执行。硬件增强的DEP检测这些位置的代码运行,当执行发生时抛出异常。软件增强的DEP帮助阻止恶意代码利用Windows的异常处理机制。

地址空间布局随机化(ASLR):它是一种避免缓冲区溢出攻击的保护措施。为了阻止攻击者固定的跳转,例如,一个特别的内存漏洞利用,ASLR能随机分配进程内关键区域的地址,包括可执行文件的基地址和栈、堆、动态库的位置。

stdcall调用约定:stdcall调用约定由pascal约定演变而来,被调用者负责清理栈,但是参数从右向左的顺序压栈(和_cdecl调用约定一样)。寄存器EAX,ECX,EDX在函数中使用。返回结果保存在EAX中。stdcall是微软win32 API和open Watcom C++的标准调用约定。

0x02 介绍


shellcode在安全领域扮演了很重要的角色,他们在很多恶意软件和利用中都有使用。

因此,什么是shellcode?shellcode以一系列字节为基础,其组成CPU指令,编写shellcode的主要目的是利用漏洞(如溢出漏洞)来允许在系统中执行任意代码。因为shellcode能直接在内存中运行,导致大量恶意软件使用它。命名为shellcode的原因是通常运行shellcode后都会返回一个命令行shell,但是随着时间推移意义也改变了。在今天几乎所有的编译器生成的程序都能转化为shellcode。因为编写shellcode涉及到深入理解目标架构和操作系统的汇编语言,本文假设读者可以在Windows和Linux环境中使用汇编编写程序。在网络上有很多开源的shellcode,但是对于新利用和不同的漏洞,每个安全研究员应该都能编写他自己的shellcode,同时编写你自己的shellcode能很大程度上帮助你理解操作系统的关键东西。本文的目标是介绍基本的shellcode概念,降低shellcode被检测的概率,和绕过一些反利用缓解措施。

0x03 基本的shellcode编程


为不同的操作系统编写shellcode需要不同的方法,不像Windows,基于Unix的操作系统提供了一种通过int 0x80接口与内核通信的方式,基于Unix的操作系统的所有的系统调用都有一个唯一的调用号,通过80中断来调用(int 0x80),内核通过被提供的调用号和参数执行系统调用,但是这里有个问题,Windows没有一个直接调用内核的接口,意味着不得不有精准的函数指针(内存地址)以便调用它们。不幸的是,硬编码函数地址不能完全解决问题,Windows中的每个函数地址在每个版本中都会改变,使用硬编码的shellcode高度依赖版本,在Windows上编写不依赖版本的shellcode是可能的,只要解决地址问题,这个能通过在运行时动态获取地址解决。

0x04 解决地址问题


随着时间的推移,shellcode编写者找到了聪明的方法能在运行时找到Windows API函数的地址,在本文中我们主要关注一种称为解析PEB的方法,这个方法使用PEB数据结构来定位加载的动态库的地址,并解析导出地址表得到函数地址。在metasploit框架中,几乎所有的不依赖版本的shellcode都是用这个技术得到Windows API函数地址。使用这个方法充分利用了FS段寄存器,在Windows中这个寄存器指向线程环境块(TEB)地址,TEB包含了很多有用的数据,包括我们寻找的PEB结构,当shellcode在内存中执行时,我们需要从TEB块向前偏移48字节,

121.xor eax, eax2.mov edx, [fs:eax+48]

现在我们得到了PEB结构,

在得到PEB结构指针后,我们在PEB块中向前移12字节,以便得到ldr数据结构的地址。

1

1.mov edx, [edx+12]

ldr结构包含了进程加载模块的信息,在ldr结构向前偏移20字节,我们得到InMemoryOrderModuleList中的第一个模块,

1

1.mov edx, [edx+20]

现在我们的指针指向InMemoryOrderModuleList是一个LIST_ENTRY结构,Windows定义这个为包含进程模块的双向列表。这个列表中的每项是指向LDR_DATA_TABLE_ENTRY结构的指针,这个结构是我们主要的目标,包含加载模块的所有地址和名字,因为模块加载的顺序可能改变,我们应该校验全名,以便选择包含我们要查找的函数的动态库,这能简单的通过在LDR_DATA_TABLE_ENTRY向前移40字节做到,如果名字匹配了,则正是我们寻找的那个,我们在结构中向前移16字节,能得到加载模块的地址。

1

1.mov edx, [edx+16]

得到函数地址的第一步完成了,现在我们有了包含要寻找的函数地址的DLL的基地址,我们不得不解析模块的导出地址表,以便能找到需要的函数地址,导出地址表能在PE可选头中定位到,从基址向前移60字节,我们有了DLL的PE头的内存地址,

最后我们需要通过公式(模块基地址+PE头地址+120字节)计算导出地址表的地址,我们能得到导出地址表(EAT)的地址,在得到EAT地址后我们能访问DLL导出的所有函数,微软描述IMAGE_EXPORT_DIRECTORY结构如下,

这个结构包含地址,名字和导出函数的数量,使用想通大小计算遍历技术能得到函数的地址,当然导出函数的顺序在每个版本中可能有变化,因此获取函数地址和名字前应该校验。你可以把这个方法理解为计算几个Windows数据结构的大小,并在内存中遍历,这里真正的挑战是建立一种稳定的名字比较机制来选择正确的DLL和函数,如果PEB解析技术太难实现,不用担心,有更简单的方法做到这个。

0x05 Hash API


metasploit项目中所有的shellcode几乎都使用了称为Hash API的汇编块,它是由Stephen Fewer编写的一个代码片段,且从2009年以来在metasploit中主要用于Windows shellcode,这个汇编代码块使得解析PEB结构变得容易,它使用基本的PEB解析逻辑和一些额外的哈希方法,使用函数和模块名的ROR13哈希计算法来快速查找需要的函数,使用这个块非常简单,它使用stdcall调用约定,唯一的不同是压栈的参数,需要使用ROR13计算函数名和DLL名的哈希。在压入必须的参数和函数哈希后,像之前解释的解析PEB块并找到模块名。在找到模块名后,计算ROR13哈希且将它保存到栈上,然后移到DLL的导出地址表中,计算每个函数的哈希和模块名哈希,如果匹配到我们要找的哈希,意味着想要的函数被找到了,最后Hash API使用栈上的参数跳转到找到的函数地址执行。它是一段非常优雅的代码,但是它的日子到头了,因为它的流行和广泛使用,一些AV产品和反利用缓解措施有专门针对这个代码块的检测,甚至一些AV产品使用Hash API使用的ROR13哈希作为识别恶意文件的特征,因为最近操作系统中的反利用缓解措施的改进,Hash API只剩下很短的生命周期,但是有其他方法来找到API函数地址,同时针对这种方法使用一些编码机制也能绕过主要的AV产品。

0x06 编码器/解码器设计


在开始设计前,读者应该知道的事实是只使用这个编码器不能生成完全躲避检测的shellcode,在执行shellcode后,解码器将直接运行并解码整段shellcode为它的原始格式,这个不能绕过AV产品的动态分析机制。

解码器的逻辑非常简单,它使用一个随机生成的多字节XOR密钥来解码shellcode,在解码操作完成后将执行它,在将shellcode放置在解码器头之前应该使用多字节的XOR密钥加密,且shellcode和XOR密钥应该在“<Shellcode>”,”<Key>”标签内,

因为代码非常好理解,我将不浪费时间逐行解释它了,使用JMP/CALL技巧能在运行时得到shellcode和密钥的地址,然后在shellcode和密钥的每个字节之间执行一个逻辑XOR操作,每次解密密钥到末尾,它将重置密钥为它的起始地址,在完成解码操作后,将跳转到shellcode,使用更长的XOR密钥能提高shellcode的随机性,但是也提高了代码块的熵,因此要避免使用太长的解码密钥,使用基础的逻辑操作(如XOR,NOT,ADD,SUB,ROR,ROL)能有几百种方式编码shellcode,在每种编码器中有无穷可能的shellcode输出,AV产品在解码序列之前检测到任何shellcode的特征的概率很低,因为这种AV产品也开发了启发式引擎,它能够检测解密和代码块中的解码循环。当编写shellcode编码器时,几乎没有用于绕过用于检测解码循环的静态方法的有效方式。

不常见的寄存器的使用:

在x86架构中,所有的寄存器有一个特定的目的,例如ECX表示扩展计数寄存器,且它通常用于循环计数。当我们编写一个基础的循环条件时,编译器可能使用ECX寄存器作为循环计数器变量,在一个代码块中找到连续增长的ECX寄存器强烈暗示了一个循环,这个问题的解决方案非常简单,不使用ECX作为循环计数器,这只是一个例子,但是它对于所有的其它类型的代码片段(如函数epilogue/prologue等)也非常有效。大量的代码识别机制依赖寄存器的使用,使用不常见的寄存器编写汇编代码将减小被检测率。

垃圾代码填充:

在代码块中可能有几百种方法识别解码器,且几乎每个AV产品使用不同的方式,但是最终他们都不得不将可能的解码器的代码块生成一个特征,在解码器代码中使用随机的NOP指令是绕过静态特征分析的一种非常好的方式,不一定要使用NOP指令,可以是任何维持原有功能的其他指令,目标是增加垃圾指令以便破环代码块的恶意的特征。另一个关于编写shellcode重要的事是大小,因此避免使用太大的垃圾混淆代码否则将增加整体大小。

实现这种方法的代码如下:

唯一的改变在于EAX和ECX寄存器,现在在shellcode中负责计数的寄存器是EAX,且在每个XOR和MOV指令之间插入一些NOP填充,通过这个教程使用的shellcode是Windows Meterpreter反向TCP,在使用一个10字节长的随机XOR密钥加密shellcode后,一起放置在解码器中,使用nasm –f bin Decoder.asm命令汇编解码器为二进制格式(不要忘了移除shellcode中的换行符,否则nasm不能汇编它)。

下面是编码前shellcode的AV扫描结果,

如你所见,大量的AV扫描器识别了shellcode。下面是shellcode编码后的扫描结果。

0x07 对抗利用缓解措施


绕过AV产品有很多中方法,但是对抗利用缓解措施导致形式变了,2009年微软宣称EMET,它是一个工具包,来帮助阻止在软件中的漏洞利用,它有下面几种机制:

动态数据执行保护(DEP)

结构化异常处理覆盖保护(SEHOP)

NullPage分配

堆喷射保护

导出地址表地址过滤(EAF)

强制ASLR

导出地址表地址过滤增强版(EAF+)

ROP缓解措施

加载库校验

内存保护校验

调用者校验

模拟执行流

Stack pivot

Attack Surface Reduction(ASR)

在这些缓解措施中EAF,EAF+和调用者校验使我们最关心的。正如早前解释的,在metasploit框架中几乎所有的shellcode使用Stephen Fewer的HASH API,且因为Hash API使用了PEB/EAT解析技术,EMET能简单的检测到并阻止shellcode的执行。

0x08 绕过EMET


在EMET中的调用者校验检查进程中的Windows API调用,它能阻止API函数中的RET和JMP指令,以便阻止使用ROP方式的所有的利用,在HASH API中,在找到需要的API函数地址后,使用JMP指令执行函数,不幸的是这将触发EMET调用者校验,为了绕过调用者校验,应该避免使用JMP和RET指令指向API函数,使用CALL代替JMP指令执行API函数,Hash API应该绕过调用者校验,但是当我们看到EAF/EAF+缓解机制时,它们根据调用的代码阻止访问导出地址表(EAT),并且它检查栈寄存器是否在允许的边界内,或者它尝试读MZ/PE头和KERNELBASE,对于阻止EAT解析技术这是非常有效的缓解措施,但是EAT不是唯一一个包含函数地址的结构,导入地址表(IAT)也保存程序使用的API函数的地址,如果应用程序也使用我们需要的函数,在IAT结构中获得函数地址是可能的,一个叫Joshua Pitts的安全研究员最近开发一种新的IAT解析的方法,它在IAT中找到LoadLibraryA和GetProcAddress的地址,在获得这些函数的地址,能从任何库中得到任何函数,他也为Stephen Fewer的Hash API写了一个称为fibo的工具,且使用他写的IAT解析代码代替,如果你想阅读这种方法的更多细节,参见这里。

0x09 参考


责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“如何将汇编转换为shellcode,c语言如何转换汇编,如何将汇编转换为c51”边界阅读