人间何所以,|

逆世混沌

园龄:3年2个月粉丝:6关注:5

逆向-第五次实验-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 @   逆世混沌  阅读(307)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 狂迪 卢广仲
狂迪 - 卢广仲
00:00 / 00:00
An audio error has occurred.

作词 : 卢广仲/討海人/黄少雍

作曲 : 卢广仲

在我眼前消失不见

今天的什么都不对

楼下的店听见熟悉的音乐

谁离开我谁爱着我

剩下一半的小火锅

别乱牵拖直到你出现

满天星光月亮出来了

神魂颠倒这是真的吗

管不住我自己的步伐

戒不掉你致命的魔法

谁教我蜿蜒的弹跳

谁教我崎岖的舞蹈

我爱你你知不知道

OH Dónde estás Bongria

想要见面有点抱歉

下午才跟你晚上约

请放轻松看着公园的落叶

摇摇晃晃我的形状

一边海洋一边天堂

你拉着我这一步叫做永远

满天星光月亮出来了

神魂颠倒这是真的吗

管不住我自己的步伐

戒不掉你致命的魔法

谁教我蜿蜒的弹跳

谁教我崎岖的舞蹈

我爱你你知不知道

OH Dónde estás Bongria

我丢掉太多的不必要

朝着有你的方向跑

跑到你眼前逗你笑

听到电影里的配乐响起

全场只为了等你说一句

说欸你要不要跟我一起

去教堂

Yes I do希望你也一样

先说好一起永保安康

每一天蜿蜒的弹跳

每一天崎岖的舞蹈

我爱你你知不知道 OH

如果爱我让我看见你

的脚