逆向-第五次实验-PE文件解析

前几天随便贴了点代码,实际上只是拿来作为网页读的(ai可以读网页html文本)

给自己留个坑而已

结构分析

PE文件解析,我拿py实现的,因为用了库,蛮简单的,C写肯定麻烦点,但是显然实验并不是这个目的,所以写两版,一个是用了pefile的,一个是没用的,思路是一致的

首先分析必须的结构与其作用:

1. DOS头:DOS头是PE文件第一个结构,长度是64字节,它包含一个DOSStub程序和PE文件的指针,使非PE识别的系统认为该文件是一个正常的可执行文件,可供DOS系统加载执行,是PE文件必不可少的部分。

2. PE文件头:PE文件头紧跟在DOS头之后,长度是20字节,它包含PE文件的标识和结构信息,如PE标记、区段数、时间日期戳等,这些信息是加载和解析PE文件必需的,所以PE文件头是必不可少的部分。

3. 可选头:可选头包含PE文件加载和执行所必需的信息,如代码区段大小、数据区段大小、目标系统版本等。这些信息是PE文件在Windows下正确加载和执行所必需的,所以可选头是必不可少的部分。

4. 区段表:区段表定义了PE文件中多个区段的信息,如区段名、起始RVA、大小、权限等。PE文件中实际的数据是通过区段来组织和映射的,区段表作为这个映射的基础,是解析实际数据必不可少的信息,所以它是PE文件必不可少的部分。

5. 导入表:导入表列出了PE文件引用的外部函数和DLL。PE文件需要调用导入表中列出的外部函数和DLL,否则无法正常执行,所以导入表是PE文件必不可少的部分。

6. 代码区段:代码区段包含了PE文件执行所需的代码和数据。没有代码区段,PE文件无法执行任何操作,所以代码区段是PE文件必不可少的部分。

接着分析解析的流程:

1. 解析PE文件头。读取前128字节,解析MAGIC,PE标记,文件大小等信息。

2. 解析可选头。读取可选头大小,解析可选头中的数据目录等。

3. 解析区段表。读取区段个数,对每个区段解析区段名、区段大小、区段偏移等信息。

4. 解析导出表。读取导出表目录入口的RVA和大小,解析导出表得到函数名、函数RVA等信息。

5. 解析导入表。读取导入表目录入口的RVA和大小,解析导入表得到DLL名、函数名、函数RVA等信息。

6. 解析代码区段。读取代码区段。RVA和Raw size,提取代码区段的数据。

7. 解析数据区段。读取数据区段RVA和大小,提取数据。

8. 解析调试信息。读取调试目录入口,解析调试信息中的代码View和数据View信息。

9. 解析资源。读取资源目录入口,解析各种资源类型、资源名和资源数据。

python实现PE文件解析1.py

实际无法使用,有点小问题,而且我都用py了得用库吧

filepath=input(":")
# DOS
def parse_dos_header(data):
    dos_header = {}
    dos_header['magic'] = data[:2]        # 魔数MZ
    dos_header['lastsize'] = data[2:4]    # 最后一页大小
    dos_header['nblocks'] = data[4:6]     # 内存块数
    dos_header['nrelocs'] = data[6:8]     # 重定位项数 
    dos_header['header_size'] = data[8:12] # 头部大小PE文件
    dos_header['min_extra_paragraphs'] = data[12:14] # 最小额外段数
    dos_header['max_extra_paragraphs'] = data[14:16] # 最大额外段数
    dos_header['ss'] = data[16:18]            # 初始(相对)SS值
    dos_header['sp'] = data[18:20]           # 初始SP值
    dos_header['checksum'] = data[20:24]     # 检验和
    dos_header['ip'] = data[24:26]           # 初始IP值
    dos_header['cs'] = data[26:28]           # 初始(相对)CS值  
    dos_header['lfarlc'] = data[28:32]      # 重定位表偏移地址
    dos_header['ovno'] = data[32:34]        # 叠加号
    return dos_header

# 解析PE文件头
def parse_pe_header(data):
    header = {}
    header['signature'] = data[:2]  # PE标记
    header['machine'] = data[4:6]   # 机器类型 
    header['sections'] = data[6:8]  # 区段数
    header['time'] = data[8:12]     # 时间日期戳
    return header

# 解析可选头   
def parse_optional_header(data):
    optional_header = {}
    optional_header['magic'] = data[:2]    # 魔数 0x10B
    optional_header['majorlinker'] = data[2:4] # Linker 版本号 主版本
    optional_header['minorlinker'] = data[4:6] # Linker 版本号 次版本
    optional_header['codesize'] = data[16:20] # 代码区段大小
    optional_header['initedata'] = data[20:24] # 已初始化数据大小
    optional_header['uninitedata'] = data[24:28]# 未初始化数据大小 
    return optional_header

# 解析区段表  
def parse_section_table(data):
    section_table = []
    for i in range(len(data) // 40):  # 每个区段描述占用40字节
        section = {}
        section['name'] = data[i*40:(i+1)*40][:8].decode('utf-8').strip('\x00')
        section['virtualsize'] = data[i*40+8:i*40+12]  # 区段虚拟大小
        section['virtualaddress'] = data[i*40+12:i*40+16] # 区段虚拟地址
        section['sizeofraw'] = data[i*40+16:i*40+20]     # 原始大小
        section['pointertoraw'] = data[i*40+20:i*40+24]   # 文件指针
        section_table.append(section)
    return section_table

# 解析导入表
def parse_import_table(data):
    import_table = {}
    import_table['importlookup'] = data[:4]   # 导入查找表RVA
    # 遍历导入描述表,每个表占用20字节
    for i in range(len(data[4:]) // 20):  
        import_desc = {}
        import_desc['origfirstthunk'] = data[4+i*20:8+i*20]   # 未绑定的导入地址表RVA
        import_desc['timedatestamp'] = data[8+i*20:12+i*20]   # 时间日期戳 
        import_desc['forwarderchain'] = data[12+i*20:16+i*20] # 链到下一个导入描述表
        import_desc['name'] = data[16+i*20:20+i*20]           # 模块名地址RVA
        import_desc['firstthunk'] = data[20+i*20:24+i*20]     # 绑定的导入地址表RVA
        import_table[i] = import_desc
    # 解析导入查找表,每个元素占用4字节
    for j in range(len(data[import_table['importlookup']:]) // 4): 
        thunk_data = {}
        thunk_data['lookup'] =  data[import_table['importlookup']+j*4:import_table['importlookup']+4+j*4]
        thunk_data['name'] = data[import_table[0]['name']+thunk_data['lookup']-import_table['importlookup']+2: 
                                        data.find(b'\x00', import_table[0]['name']+thunk_data['lookup']-import_table['importlookup'])].decode('utf-8')       
        import_table[j] = thunk_data
    return import_table

# 解析导出表  
def parse_export_table(data):
    export_table = {}
    export_table['exportflags'] = data[:4]    # 导出标志 
    export_table['timedatestamp'] = data[4:8] # 时间日期戳
    export_table['majorversion'] = data[8:12]   # 主版本
    export_table['minorversion'] = data[12:16]   # 次版本
    export_table['name'] = data[16:32].decode('utf-8').strip('\x00')  # 表名
    export_table['ordinalbase'] = data[32:36]       # 基础序号 
    export_table['numberofnames'] = data[36:40]      # 名称个数
    export_table['numberOfFunctions'] = data[40:44] # 函数个数
    export_table['addressoffunctions'] = data[44:48] # 函数地址索引的RVA
    export_table['nameptrs'] = data[48:52]           # 名称指针RVA 
    export_table['ordinals'] = data[52:56]            # 序数表RVA
    return export_table  

    
# 主函数
def main():
    with open(filepath, 'rb') as f:
        data = f.read()
    dos_header = parse_dos_header(data)
    # 解析PE头   
    header = parse_pe_header(data)
    # 解析各个表和区段
    optional_header = parse_optional_header(data[128:])
    import_table=parse_import_table(data)
    section_table = parse_section_table(data[128+224:])
    export_table = parse_export_table(data[section_table[0]['pointerto raw']:])

if __name__ == '__main__':
    main()

以上代码是一个不算完整但还可以的代码(指运行起来还需要改很多),下面这个就很一般了,只是加了pefile和一些用户体验感(主要问题在于用py不用库很难受)

Python实现PE文件解析代码2.py

import pefile
import os
filepath= input("input your exe path:")
pe = pefile.PE(filepath)
names=[]
start_line=None
with open(f"PEreader.txt", 'w', encoding='utf8') as f:
        
    f.write(pe.dump_info())
with open(f"PEreader.txt",'r',encoding="utf8") as f:
    lines = f.readlines()
    for line in lines:
        if line.startswith('----------'):  
            names.append(line)
    while(1):
        for name in names:
            print(name)
        name=  input('Enter your find name(or Enter all): ')
        if name == "all":
            for line in lines:
                print(line)
        else:
            judge=0
            for line in lines:
                if line.startswith('----------'):
                    if name in line:
                        start_line = lines.index(line)
                        for line in lines[start_line:]:
                            if line.startswith('----------'):
                                judge=judge+1
                                if judge==2:
                                    break   
                            print(line)
            if judge == 2:
                print("succes to find")
                print()
            else:
                print("failed to find")
                print()

 py打包挺头疼的,不如直接编译,我用了pyinstaller这个库,当然,使用的时候也是蛮难受的,因为需要保持你拥有这个库的同时还拥有你的py文件权限,意思是cd到文件夹那里,因为使用的是anaconda,所以要在conda 的命令行运行

 大约就是这样,然后你在文件夹里找dist文件夹,里面会有exe文件,点开就有了

 

posted @ 2023-05-08 22:12  逆世混沌  阅读(366)  评论(0)    收藏  举报