首页
社区
课程
招聘
[翻译]编写Shellcode:寻找EIP/RIP
发表于: 2020-1-1 21:28 10733

[翻译]编写Shellcode:寻找EIP/RIP

2020-1-1 21:28
10733

当学习主动安全认证专家(OSCE)认证时,我花费了大量的时间用于研究如何编写自定义shellcode。本文是我计划要发布的一系列博文里的第一篇,这个系列详细介绍我在准备认证过程中所学习到的技术。本文的重点是描述如何找到EIP/RIP以及找到后做什么。OSCE专注于32位系统,作为继续学习的一部分,我会研究并记录下在64位系统上的方法。这超出了OSCE的需求,但这是我继续学习过程的一部分。

在本文中,我不会介绍到所有的可能找到EIPRIP的方法。我会介绍一些我熟悉并已经研究过的方法。如果你想找到更多的方法,我鼓励你开始你自己的研究并开始做自己的学习总结。在这里你看到的是我的学习总结,我希望它会在某些方面对你有帮助。我知道这些主题多年来已经被多次提到,也没有新的东西,但重点不是提出新的东西,而是在学习总结中提升自己,并且还可以帮助到刚开始学习的人。

从使用x86汇编(32位)指令定位EIP的方法开始。有两种方法可以用,根据你可能会遇到的情况和限制,两种方法可能都有用。两种方法都可以找到EIP。我要介绍的第一种方法比第二种方法小一个字节。两种方法都可以达到完全相同的目的,存储EIP的值到EAX寄存器中。两种方法都没有空(0x00)字节。之所以使用一种而不是另一种,是因为您可能会遇到大小或字符限制。

该方法最先由Aaron Adams在漏洞开发(Vulnerability Development)邮件列表中提出.他使用的这种方法利用x87浮点单元(FPU)寄存器来获取EIP值。以下的基础汇编代码将EIP的值存储到EAX寄存器中。

执行fldz指令来激活FPU寄存器。该指令将常数值+0.0压入FPU寄存器栈(ST(0))。在这个过程中,FPU寄存器被初始化,当前的EIP值被存储到FPU指令指针偏移(FIP)寄存器中。图 1描述了FPU寄存器的结构。该表拷贝自《Intel(r) 64 和 IA-32 架构软件开发者手册,卷1:基础架构PDF》.

图片描述
图1:内存中保护模式x87 FPU状态图片(32位格式)

下一条指令存储FPU寄存器在特殊的地址。因此使用fnstenv指令将EIP的值存储在便宜0x0C(12)的FIP寄存器中。ESP-0x0C的目标是固定的,所以FIP的值会存储在当前ESP地址处。接下来,该值会从栈中弹出,并存储到EAX寄存器中。由于在将EIP存储到FPU的FIP寄存器和弹到EAX的这段时间已经执行了几个字节的指令,所以需要调整该值来表示当前的EIP的值。用AL寄存器加0x7(7)完成此操作。AL寄存器被用来避免使用空字节。

要测试可以使用nasm来汇编代码:

随后插入字节到C中:

使用MingW来编译代码:

运行最终的PE文件在你喜欢的调试器中,并看它是如何运行的。你需要将.data 节标记为可执行。你可以使用调试器来实现或者使用如LordPE这样的工具。如果你不标记 .data 节为可执行,你会在执行你的shellcode时发生访问违例。我需要在PE文件中搜索call eax指令,并设置断点,第一个调用到EAX的会是shellcode。如果一切顺利,EAX会在执行add al,7指令时指向EIP,如图 2

图片描述
图 2:EAX指向EIP

** 方法1的替代——使用减法

感谢来自@TheColonial的建议,对EAX寄存器使用减法而不是add al指令可以再一些情况下避免错误,我添加了该替代方法。他正确的指出的问题,是通过对AL进行加法运算,如果加法导致了进位,EAX就会指向错误的地址。例如,如果AL寄存器包含大于或等于0xF9的任何数值,加上0x07后,进位的1会被丢弃掉。例如,如果EAX0x001234F9,我们把0x07加到AL寄存器(0xF9)后,结果会是0x00123400而不是我们需要的0x00123500

为了避免这个问题,并且避免空字节,可以让EAX减去一个负值。基本的数学运算,减去一个负值等于加上它(的绝对值)。简单却有效。正确的代码如下,还避开了空字节,并且只比之前大了1字节:

图 9中我们可以看出建议的代码正常运行并且会比原来的代码更加可靠。

图片描述
图 9 EAX指向EIP

第二种方法仅比第一种方法长一个字节,也完成了将EIP的值存储到EAX中的目标。我从 Phrack issue 62,Phile 7标题为History and Advances in Windows Shellcode中学到的这种方法。这篇文章由SK Chong所写。为了获得EIP这种方法使用了一系列的跳转和调用,最终EIP的值被存储在EAX中。SK Chong文章的原始文件已经找不到了,但是,我找到了其中的一些。在他的例子中,它使用了db项来硬编码了那些跳转和调用。我要提供的版本读起来更容易并且以及更容易使用nasm汇编。

上面的代码执行了跳转到label2,一个call指令调用了label1。当call被执行时,返回地址,即下一条指令的地址,被压入栈中。执行,随后跳转到getEIP,会弹出返回地址到EAX。不要小题大作(No muss, no fuss.)。

你可以使用测试方法 1的测试方法来测试方法2.当执行完,结果看起来如图 3

图片描述
图 3:EAX指向EIP

查看是否32位架构中运行的方法在64位世界中是否运行是一个好的开始。如果只需做些微修改,为什么还要重新造轮子?

使用FPU指令可以运行,因为指令在64位下仍然生效。首先,找出未对32位模式运行下的指令进行修改或微小修改发生的情况。要做到这点,我使用x64dbg并使其执行notepad.exe,运行直到命中EntryPoing。使用Assemble指令,我手动替换了开始的一些指令位32-bit的指令。这样做的结果如图 4。这里有一些需要注意。首先,67:出现在fnstenv指令前。根据Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z 章节2.1.1,67h前缀是地址大小覆写。这一定会出现的,因为我之前使用的是ESP寄存器而不是RSP。第二,我不能使用POP EAX指令,我需要使用POP RAX
图片描述
图 4:使用FPU寻找RIP,尝试1

运行该序列会部分成功(见图5)。当加法执行时,RAX指向RIP之前的指令。这是必须使用67h地址大小前缀的原因。使用0x08替代0x07就能很容易的使之平衡。继续,自己尝试下,看看它是否有效。
图片描述
图 5:使用FPU指令寻找RIP,尝试1的结果

既然已经证明了使用FPU寄存器来获取RIP的值时可行的。我的下一步是看看是否能够写一些汇编代码,可以在调试器中手工输入命令可以使之平衡。经过一些调整后,这是我想出的代码:

当使用Nasm汇编时,上面指令中的代码结果如图 6。为了测试那些指令,我对用x64dbg打开的64位进程的第一条指令做二进制粘贴。如你所看到的,在add rax,0x07指令前有了新的48h前缀。根据Intel® 64 and IA-32 Architectures Software Developer’s Manual, Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4 章节3.7.2.1,这是一个REX前缀,允许指令寻址8位寄存器。汇编器可能够以这种方式丢到空字节。而且,由于当使用RSP而不是ESP时,对于fnstenv指令,地址大小前缀是非必须的,所以,RAX加上0x07字节的结果是RAX包含了RIP的地址。
图片描述
图 6:使用FPU'指令寻找RIP,终结

在开始测试该方法前,我想做些微小的修改。我想有必要将EAX替换成RAX。这种跳转和调用和手工干扰到调试器中有一些不同,所以我选择先开始编写一些汇编代码。下面的代码就是我选择用来最开始尝试的:

执行了使用汇编的代码覆盖的notepad的入口点的结果并可以在图 7中看到。这种方法依旧没有空字节,长度上仅仅10个字节。
图片描述
图 7:使用跳转和调用寻找RIP,终结


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (6)
雪    币: 8854
活跃值: (5086)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jgs
2
 坐沙发收藏学习
2020-1-1 21:33
0
雪    币: 6535
活跃值: (4491)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
3
有用,收藏备用
2020-1-1 21:46
0
雪    币: 300
活跃值: (2442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2020-1-1 22:20
0
雪    币: 2107
活跃值: (1429)
能力值: ( LV8,RANK:126 )
在线值:
发帖
回帖
粉丝
5
可以的,分析的时候遇到过很多第二种jmp获取地址的,没有太发散,第一种算是学到了
2020-1-2 08:58
0
雪    币: 26785
活跃值: (63217)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
6
感谢分享!
2020-1-11 15:57
0
雪    币: 3176
活跃值: (1781)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
有关获取EIP地址的方式 大约有7种,其中有些需要指令集支持的
最后于 2020-1-14 10:45 被MsScotch编辑 ,原因:
2020-1-14 10:45
0
游客
登录 | 注册 方可回帖
返回
//