PeView 是一款基于C/C++开发的命令行版PE文件解析工具,专门用于解析Windows可执行文件并提供详尽的文件结构和交互式查询功能,帮助用户理解和分析目标程序的内部构成,是逆向分析和软件调试中的重要工具,本次分享工具源代码及使用方法,读者可根据自己的需要参考学习,并以此来更好的理解PE文件格式的构成。
该命令行工具提供了广泛的功能,可以查看文件结构(如DOS头、NT头、数据目录、节表)、导入导出表信息、重定位表内容,以及检测资源和执行地址转换。此外,工具还包括内置计算器功能(加法、减法)、检查文件自身的保护方式或签名验证,并提供特征识别和获取DLL函数地址的能力,满足各种PE文件分析和调试需求。
查询DOS/NT结构
DOS头和NT头是PE文件格式的关键组成部分。DOS头位于文件开头,以“MZ”标识,包含指向PE头的偏移量。NT头紧随其后,包含文件签名、文件头(如目标架构和节数量)以及可选头(如入口点地址和映像基地址)。这两部分共同提供了Windows操作系统加载和执行可执行文件及动态链接库所需的结构信息。
1. DOS头(DOS Header)
- 位置:PE文件的开头。
- 标识符:以“MZ”开头,表示可执行文件。
- 主要字段
- e_lfanew:指向NT头的偏移量,帮助定位后续的PE结构。
2. NT头(NT Header)
- 位置:紧跟在DOS头后。
- 组成部分
- Signature:通常为“PE\0\0”,确认文件格式为PE。
- File Header:
- Machine:目标架构(如x86、x64)。
- NumberOfSections:节的数量。
- TimeDateStamp:文件的时间戳,标识文件创建或修改的时间。
- PointerToSymbolTable:符号表的指针(通常为0)。
- NumberOfSymbols:符号数量(通常为0)。
- SizeOfOptionalHeader:可选头的大小。
- Characteristics:文件特性,如是否可执行、是否为DLL等。
- Optional Header:
- Magic:指示头的类型(32位或64位)。
- AddressOfEntryPoint:程序的入口点地址,加载时会从这里开始执行。
- ImageBase:映像基地址,指明程序在内存中加载的起始地址。
- SectionAlignment、FileAlignment:节的对齐方式,确保在内存中和文件中结构的一致性。
- SizeOfImage、SizeOfHeaders:映像和头部的总大小,用于内存分配。
3. 查询DOS/NT结构
DOS/NT头的查询非常简单,首先可以使用Open
命令打开对应的可执行文件,当文件被打开后,使用Dos
命令可查询DOS头部数据,使用Nt
命令则可查询NT头部数据,如下输出所示;
C:\>peview
[Pe View] # Open --path d://lyshark.exe
[+] 已读入文件
[Pe View] # Dos
----------------------------------------------------------------------
十六进制 十进制
----------------------------------------------------------------------
DOS标志: 00005A4D 00023117
文件最后一页的字节数: 00000090 00000144
文件中的页面: 00000003 00000003
重定位: 00000000 00000000
段落中标题的大小: 00000004 00000004
至少需要额外段落: 00000000 00000000
所需的最大额外段落数: 0000FFFF 00065535
初始(相对)SS值: 00000000 00000000
初始SP值: 000000B8 00000184
校验和: 00000000 00000000
初始IP值: 00000000 00000000
初始(相对)CS值: 00000000 00000000
重新定位表的文件地址: 00000040 00000064
叠加编号: 00000000 00000000
保留字: 0010001C 01048604
OEM标识符 00000000 00000000
OEM信息 00100028 01048616
PE指针: 00000100 00000256
----------------------------------------------------------------------
[Pe View] # Nt
----------------------------------------------------------------------
十六进制 十进制
----------------------------------------------------------------------
NT标志: 0x00004550 00017744
运行平台: 0x0000014C 00000332
区段数目: 0x00000005 00000005
时间日期标志: 0x669481D8 1721008600
特征值: 0x00000102 00000258
可选头部大小: 0x000000E0 00000224
文件符号标志: 0x00000000 00000000
文件符号指针: 0x00000000 00000000
入口点: 0x0000158B 00005515
镜像基址: 0x00400000 04194304
镜像大小: 0x00007000 00028672
代码基址: 0x00001000 00004096
内存对齐: 0x00001000 00004096
文件对齐: 0x00000200 00000512
子系统: 0x00000002 00000002
首部大小: 0x00000400 00001024
校验和: 0x00000000 00000000
RVA 数及大小: 0x00000010 00000016
主操作系统版本: 0x00000006 00000006
从操作系统版本: 0x00000000 00000000
主映像版本: 0x00000000 00000000
从映像版本: 0x00000000 00000000
主子系统版本: 0x00000006 00000006
从子系统版本: 0x00000000 00000000
Win32版本: 0x00000000 00000000
DLL标识: 0x00008140 00033088
SizeOfStackReserve: 0x00100000 01048576
SizeOfStackCommit: 0x00001000 00004096
SizeOfHeapReserve: 0x00100000 01048576
SizeOfHeapCommit: 0x00001000 00004096
LoaderFlags: 0x00000000 00000000
----------------------------------------------------------------------
查询数据目录表结构
数据目录表位于可选头中。它包含一系列指向各种重要数据结构的指针,如导入表、导出表、资源表、异常表和证书表等。每个条目都定义了对应数据的虚拟地址和大小,帮助操作系统在加载时找到和管理这些数据,确保程序的正常运行和功能调用。
1. 位置
- 可选头:数据目录表通常位于PE文件的可选头部分,具体在NT头之后。
2. 结构
数据目录表由一组结构体组成,每个结构体表示一个特定的数据目录项。每个条目包含两个字段:
- Virtual Address:指向数据在内存中的虚拟地址。
- Size:数据的大小(以字节为单位)。
3. 常见数据目录项
以下是一些常见的数据目录项:
目录索引 | 数据目录项 | 描述 |
---|---|---|
0 | Export Table | 导出表,用于列出可用的外部函数 |
1 | Import Table | 导入表,记录外部依赖 |
2 | Resource Table | 资源表,包含图标、字符串等资源 |
3 | Exception Table | 异常表,存储异常处理信息 |
4 | Certificate Table | 证书表,用于存储签名信息 |
5 | Base Relocation | 重定位表,处理地址重定位 |
6 | Debug Directory | 调试目录,用于调试信息 |
7 | Architecture | 架构表,针对特定架构的信息 |
8 | Global Pointer | 全局指针表 |
9 | TLS Directory | 线程局部存储目录 |
10 | Load Configuration | 加载配置表 |
4. 查询数据目录表结构
查询数据目录表,可在文件被打开状态下,通过执行DataDirectory
命令获取,其输出信息中包括了,目录RVA
、目录FOA
、Size长度
、功能描述
等基本信息,如下输出所示;
[Pe View] # DataDirectory
--------------------------------------------------------------------------------------------------------------
编号 目录RVA 目录FOA Size长度(十进制) Size长度(十六进制) 功能描述
--------------------------------------------------------------------------------------------------------------
001 0x00000000 0xFFFFFFFF 00000000 0x00000000 Export symbols
002 0x000022A4 0x000012A4 00000080 0x00000050 Import symbols
003 0x00004000 0x00001A00 00007600 0x00001DB0 Resources
004 0x00000000 0xFFFFFFFF 00000000 0x00000000 Exception
005 0x00000000 0xFFFFFFFF 00000000 0x00000000 Security
006 0x00006000 0x00003800 00000388 0x00000184 Base relocation
007 0x00002110 0x00001110 00000056 0x00000038 Debug
008 0x00000000 0xFFFFFFFF 00000000 0x00000000 Copyright string
009 0x00000000 0xFFFFFFFF 00000000 0x00000000 Globalptr
010 0x00000000 0xFFFFFFFF 00000000 0x00000000 Thread local storage (TLS)
011 0x00002150 0x00001150 00000064 0x00000040 Load configuration
012 0x00000000 0xFFFFFFFF 00000000 0x00000000 Bound Import
013 0x00002000 0x00001000 00000232 0x000000E8 Import Address Table
014 0x00000000 0xFFFFFFFF 00000000 0x00000000 Delay Import
015 0x00000000 0xFFFFFFFF 00000000 0x00000000 COM descriptor
016 0x00000000 0xFFFFFFFF 00000000 0x00000000 NoUse
--------------------------------------------------------------------------------------------------------------
查询节表结构
节表位于NT头之后,包含多个节的描述信息。每个节由一个节头定义,提供有关节的名称、虚拟地址、大小、类型和属性等信息。节表帮助操作系统识别和加载不同类型的数据和代码,例如代码节、数据节和资源节,以确保程序在内存中的正确组织和访问。
节表同样是PE文件的重要组成部分,其定义了文件中的各个节(section),每个节包含特定类型的数据和代码。以下是对节表结构的详细分析:
1. 位置
- 节表位于NT头之后,紧接在可选头后面。
2. 结构
每个节由一个节头(Section Header)描述,节头通常包含以下字段:
字段名称 | 描述 |
---|---|
Name | 节的名称,通常为8字节,使用ASCII表示。 |
Virtual Size | 节在内存中的大小(以字节为单位)。 |
Virtual Address | 节在内存中的虚拟地址。 |
Size of Raw Data | 节在文件中的大小(以字节为单位)。 |
Pointer to Raw Data | 节在文件中的偏移量,指向实际数据。 |
Pointer to Relocations | 指向重定位信息的偏移量(通常为0)。 |
Pointer to Line Numbers | 指向行号信息的偏移量(通常为0)。 |
Number of Relocations | 重定位条目的数量(通常为0)。 |
Number of Line Numbers | 行号条目的数量(通常为0)。 |
Characteristics | 节的特性标志,如可执行、可读、可写等。 |
3. 常见节类型
- .text:存放可执行代码,通常为只读。
- .data:存放全局变量和静态数据,通常为可读可写。
- .rsrc:存放程序资源,如图标和字符串。
- .bss:未初始化的数据,系统会自动分配空间。
- .reloc:存放重定位信息,用于地址修正。
4. 查询节表结构
程序中的节表查询可通过使用Section
获取,其输出结果中包含了程序中所有的节的名称及该节在内存和文件中的偏移地址,如下输出所示;
[Pe View] # Section
----------------------------------------------------------------------------------------------------
编号 节区名称 虚拟偏移 虚拟大小 实际偏移 实际大小 节区属性
----------------------------------------------------------------------------------------------------
1 .text 0x00001000 0x00000B44 0x00000400 0x00000C00 0x60000020
2 .rdata 0x00002000 0x000007BA 0x00001000 0x00000800 0x40000040
3 .data 0x00003000 0x00000518 0x00001800 0x00000200 0xC0000040
4 .rsrc 0x00004000 0x00001DB0 0x00001A00 0x00001E00 0x40000040
5 .reloc 0x00006000 0x00000184 0x00003800 0x00000200 0x42000040
----------------------------------------------------------------------------------------------------
查询导入表结构
导入表位于数据目录表中,其记录了可执行文件所依赖的外部DLL及其函数信息。每个导入表项包含DLL名称和对应的导入函数列表,帮助操作系统在加载时解析并链接这些外部依赖,以确保程序能够正确调用所需的功能和资源。
以下是导入表的基本结构和分析:
1.导入表结构
- 导入描述符(Import Descriptor)
- 每个导入表项由一个或多个导入描述符组成。
- 包含以下字段:
Characteristics
:描述符的特征(通常为0)。TimeDateStamp
:时间戳,标识DLL的版本。ForwarderChain
:指向转发导入的链表,通常为0。Name
:DLL的名称,以字符串形式表示。ImportAddressTable
(IAT):存储函数地址的表。ImportNameTable
(INT):存储函数名称的表。
- DLL名称
- 存储所依赖的DLL的名称,通常以空字符结尾的字符串形式表示。
- 导入函数列表
- 包含DLL中所需的函数,通常包括函数的序号或名称(通过名称或按序号查找)。
2.查询导入表结构
导入表的查询有多个命令,首先我们需要查询当前程序中导入了哪些动态链接库,可通过调用ImportDll
命令来获取到,如下输出所示;
[Pe View] # ImportDll
----------------------------------------------------------------------
序号 文件偏移FOA 相对偏移RVA DLL名称
----------------------------------------------------------------------
1 0x00001516 0x00000000 USER32.dll
2 0x0000167A 0x00000000 MSVCR120.dll
3 0x000017AC 0x00000000 KERNEL32.dll
----------------------------------------------------------------------
接着我们需要查询USER32.dll
模块内导入了哪些函数,此时可以使用ImportByName
命令,该命令接收一个动态链接库名称字符串,并返回该动态链接库中所导入的函数信息,如下输出所示;
[Pe View] # ImportByName --dll USER32.dll
------------------------------------------------------------------------------------------
序号 文件偏移FOA 相对偏移RVA 导入函数 [ 当前模块: USER32.dll ]
------------------------------------------------------------------------------------------
231 0x0000150A 0x0000250A EndDialog
625 0x000014F8 0x000024F8 PostQuitMessage
233 0x000014EC 0x000024EC EndPaint
14 0x000014DE 0x000024DE BeginPaint
161 0x000014CC 0x000024CC DefWindowProcW
173 0x000014BC 0x000024BC DestroyWindow
178 0x000014AA 0x000024AA DialogBoxParamW
855 0x0000149A 0x0000249A UpdateWindow
800 0x0000148C 0x0000248C ShowWindow
113 0x0000147A 0x0000247A CreateWindowExW
649 0x00001466 0x00002466 RegisterClassExW
545 0x00001458 0x00002458 LoadCursorW
547 0x0000144C 0x0000244C LoadIconW
181 0x00001438 0x00002438 DispatchMessageW
831 0x00001424 0x00002424 TranslateMessage
829 0x0000140C 0x0000240C TranslateAcceleratorW
371 0x000013FE 0x000023FE GetMessageW
539 0x000013EA 0x000023EA LoadAcceleratorsW
560 0x000013DC 0x000023DC LoadStringW
------------------------------------------------------------------------------------------
当需要查询所有导入的动态链接库及函数信息时可以使用ImportAll
命令来实现,如下输出所示;
[Pe View] # ImportAll
--------------------------------------------------------------------------------------------------------------
Hint值 API序号 文件RVA VA地址 函数名称 模块: [ USER32.dll ]
--------------------------------------------------------------------------------------------------------------
[ 231] 000009482 0000150A 0040250A EndDialog
[ 625] 000009464 000014F8 004024F8 PostQuitMessage
[ 233] 000009452 000014EC 004024EC EndPaint
[ 14] 000009438 000014DE 004024DE BeginPaint
[ 161] 000009420 000014CC 004024CC DefWindowProcW
[ 173] 000009404 000014BC 004024BC DestroyWindow
[ 178] 000009386 000014AA 004024AA DialogBoxParamW
[ 855] 000009370 0000149A 0040249A UpdateWindow
[ 800] 000009356 0000148C 0040248C ShowWindow
--------------------------------------------------------------------------------------------------------------
Hint值 API序号 文件RVA VA地址 函数名称 模块: [ MSVCR120.dll ]
--------------------------------------------------------------------------------------------------------------
[ 634] 000009954 000016E2 004026E2 _except_handler4_common
[ 579] 000009938 000016D2 004026D2 _controlfp_s
[ 788] 000009920 000016C0 004026C0 _invoke_watson
[ 1082] 000009910 000016B6 004026B6 _onexit
[ 430] 000009896 000016A8 004026A8 __dllonexit
[ 558] 000009882 0000169A 0040269A _calloc_crt
[ 1284] 000009872 00001690 00402690 _unlock
--------------------------------------------------------------------------------------------------------------
Hint值 API序号 文件RVA VA地址 函数名称 模块: [ KERNEL32.dll ]
--------------------------------------------------------------------------------------------------------------
[ 254] 000010140 0000179C 0040279C DecodePointer
[ 726] 000010114 00001782 00402782 GetSystemTimeAsFileTime
[ 526] 000010092 0000176C 0040276C GetCurrentThreadId
[ 522] 000010070 00001756 00402756 GetCurrentProcessId
[ 1069] 000010044 0000173C 0040273C QueryPerformanceCounter
[ 877] 000010016 00001720 00402720 IsProcessorFeaturePresent
[ 871] 000009996 0000170C 0040270C IsDebuggerPresent
[ 289] 000009980 000016FC 004026FC EncodePointer
有时我们需要验证特定的函数是否被导入,此时可以使用ImportByFunction
来获取,若函数被导入则输出导入的详细信息,如下输出所示;
[Pe View] # ImportByFunction --function DialogBoxParamW
----------------------------------------------------------------------------------------------------
序号 FOA地址 VA地址 所在DLL
----------------------------------------------------------------------------------------------------
[ 178] 000014AA 004024AA USER32.dll
----------------------------------------------------------------------------------------------------
[Pe View] #
[Pe View] # ImportByFunction --function PostQuitMessage
----------------------------------------------------------------------------------------------------
序号 FOA地址 VA地址 所在DLL
----------------------------------------------------------------------------------------------------
[ 625] 000014F8 004024F8 USER32.dll
----------------------------------------------------------------------------------------------------
[Pe View] #
[Pe View] # ImportByFunction --function QueryPerformanceCounter
----------------------------------------------------------------------------------------------------
序号 FOA地址 VA地址 所在DLL
----------------------------------------------------------------------------------------------------
[ 1069] 0000173C 0040273C KERNEL32.dll
----------------------------------------------------------------------------------------------------
查询导出表结构
导出表同样位于数据目录表中,其用于列出可执行文件或DLL所提供的外部函数和变量。每个导出表项包含函数的名称、地址和序号,使其他程序能够调用这些导出的功能。导出表提供了模块间的接口和动态链接,使得共享代码和资源变得可行。
以下是导出表的基本结构和分析。
1.导出表的基本组成
- 导出描述符(Export Directory)
- 包含指向导出表的主要信息。
- 主要字段包括:
Characteristics
:通常为0。TimeDateStamp
:时间戳,标识导出的版本。MajorVersion
:导出表的主版本号。MinorVersion
:导出表的次版本号。Name
:指向DLL名称的指针。Base
:导出函数的基地址。NumberOfFunctions
:导出函数的数量。NumberOfNames
:导出名称的数量。AddressOfFunctions
:指向函数地址的表。AddressOfNames
:指向函数名称的表。AddressOfNameOrdinals
:指向名称序号表的指针。
- DLL名称
- 存储DLL的名称,以空字符结尾的字符串形式表示。
- 导出函数列表
- 函数的地址和名称,可以通过名称或序号进行引用。
2.关键字段分析
- Characteristics:通常为0,用于标识导出表的有效性。
- TimeDateStamp:指示最后修改的时间,可以用于检查DLL的版本。
- Name:DLL的名称,有助于识别模块。
- Base:定义函数地址的起始点,为地址计算提供基础。
- NumberOfFunctions:表示可导出函数的总数,重要的统计信息。
- NumberOfNames:表示可导出名称的数量,通常与函数数量相同,但可以不同。
- AddressOfFunctions:实际导出函数的地址数组,程序调用时需要用到。
- AddressOfNames:函数名称的指针数组,提供名称与地址之间的映射。
- AddressOfNameOrdinals:指向名称序号的数组,帮助在导出时找到具体函数。
3.查询导出表结构
查询导出表可使用Export
命令,一般的EXE可执行程序中并不会包含导出表,所以在查询时可自行切换到DLL模块中查询,如下输出所示;
[Pe View] # Open --path d://asmjit.dll
[+] 已读入文件
[Pe View] # Export
------------------------------------------------------------------------------------------
序号 导出RVA地址 导出VA地址 导出FOA地址 导出函数
------------------------------------------------------------------------------------------
1 00013670 0x10013670 0x00012A70 AsmParser@asmtk
2 00016520 0x10016520 0x00015920 0AsmTokenizer
3 000136D0 0x100136D0 0x00012AD0 1AsmParser@asmtk@@QAE@XZ
4 00016550 0x10016550 0x00015950 1AsmTokenizer@asmtk@@QAE@XZ
------------------------------------------------------------------------------------------
查询重定位表结构
重定位表用于在程序加载时调整地址以适应不同的内存布局。它包含重定位条目,指定了哪些地址需要被修改,通常在执行时针对非基地址加载的情况。重定位表保证了可执行文件在内存中的正确定位和地址修正,尤其对于共享库和动态链接来说非常重要。
以下是重定位表的基本结构和分析。
1.重定位表的基本组成
- 重定位目录(Relocation Directory)
- 包含重定位信息的主要字段。
- 主要字段包括:
VirtualAddress
:重定位块的虚拟地址。Size
:重定位块的大小(以字节为单位)。
- 重定位条目(Relocation Entry)
- 每个重定位块包含多个重定位条目。
- 重定位条目的格式通常包括:
Offset
:相对于重定位块的偏移量,指示需要修改的位置。Type
:重定位类型,指示如何处理该地址。
2.关键字段分析
-
VirtualAddress:指示重定位信息所对应的内存地址,加载时根据此地址进行修正。
-
Size:重定位块的大小,用于确定处理的范围。
-
Offset:重定位条目的偏移量,具体指定需要修正的指令或数据位置。
-
Type:重定位类型,常见类型包括:
- IMAGE_REL_BASED_ABSOLUTE:不需要重定位。
- IMAGE_REL_BASED_HIGH:高16位重定位。
- IMAGE_REL_BASED_LOW:低16位重定位。
- IMAGE_REL_BASED_HIGHLOW:完整32位重定位。
- IMAGE_REL_BASED_DIR64:64位重定位(在64位PE文件中)。
3.查询重定位表结构
首先我们来查询重定位表的分页情况,可使用FixRelocPage
命令得到所有的分页,如下输出所示;
[Pe View] # Open --path d://lyshark.exe
[+] 已读入文件
[Pe View] #
[Pe View] # FixRelocPage
----------------------------------------------------------------------
映像基址: 00400000 虚拟偏移: 00006000 重定位表基址: 00113800
----------------------------------------------------------------------
起始RVA: 00001000 块长度: 0352 重定位个数: 0172
起始RVA: 00002000 块长度: 0036 重定位个数: 0014
----------------------------------------------------------------------
如上所示在程序中有两块重定位分页。接着我们可以使用FixRelocRVA
命令,该命令可通过传入一个重定位起始页RVA地址,自动输出该重定位页内的重定位项,如下输出所示;
[Pe View] # FixRelocPage
----------------------------------------------------------------------
映像基址: 00400000 虚拟偏移: 00006000 重定位表基址: 00113800
----------------------------------------------------------------------
起始RVA: 00001000 块长度: 0352 重定位个数: 0172
起始RVA: 00002000 块长度: 0036 重定位个数: 0014
----------------------------------------------------------------------
[Pe View] #
[Pe View] # FixRelocRVA --rva 00002000
----------------------------------------------------------------------------------------------------
起始RVA 类型 重定位RVA 重定位地址 修正RVA
----------------------------------------------------------------------------------------------------
00002000 3 000020EC 00401393 00001393
00002000 3 000020F8 004012DA 000012DA
00002000 3 000020FC 0040190D 0000190D
00002000 3 00002100 0040170E 0000170E
00002000 3 00002148 00403038 00003038
00002000 3 0000214C 00403088 00003088
00002000 3 0000218C 00403000 00003000
00002000 3 00002190 00402230 00002230
00002000 3 0000225C 0040153B 0000153B
00002000 3 00002260 0040154F 0000154F
00002000 3 0000227C 00401809 00001809
00002000 3 00002280 0040181C 0000181C
00002000 3 000022A0 004019E6 000019E6
00002000 0 00002000 0000279C FFC0279C
----------------------------------------------------------------------------------------------------
若要查询程序中所有的重定位项,可使用FixReloc
命令,如下输出所示;
[Pe View] # FixReloc
----------------------------------------------------------------------------------------------------
起始RVA 类型 重定位RVA 重定位地址 修正RVA
----------------------------------------------------------------------------------------------------
00001000 3 00001009 004020E0 000020E0
00001000 3 00001014 00403438 00003438
00001000 3 00001020 00403370 00003370
00001000 3 0000104B 00403438 00003438
00002000 3 000020EC 00401393 00001393
00002000 3 000020F8 004012DA 000012DA
00002000 3 000020FC 0040190D 0000190D
00002000 3 00002100 0040170E 0000170E
查询资源表结构
资源表用于存储程序所需的各种资源,如图标、位图、对话框和字符串等。资源表以层次结构组织,包含资源类型、名称和实际数据的指针,允许程序在运行时访问和管理这些外部资源。通过资源表,开发者可以轻松地在应用程序中使用多种用户界面元素和本地化内容。
以下是资源表的基本结构和分析。
1.资源表的基本组成
- 资源目录(Resource Directory)
- 包含资源的层级结构和指向具体资源的指针。
- 主要字段包括:
Characteristics
:通常为0,用于指示资源的特征。TimeDateStamp
:时间戳,标识资源的最后修改时间。MajorVersion
:资源表的主版本号。MinorVersion
:资源表的次版本号。NumberOfIdEntries
:ID项的数量。NumberOfNamedEntries
:命名项的数量。
- 资源项(Resource Entry)
- 每个资源目录可以包含多个资源项。
- 每个资源项包括:
Name
或Id
:资源的名称或ID。Type
:资源的类型(如图标、位图、字符串等)。OffsetToData
:指向资源数据的偏移量。Size
:资源数据的大小。
- 资源数据(Resource Data)
- 实际的资源内容,如位图的像素数据或字符串的文本。
2.关键字段分析
- Characteristics:指示资源的特征,通常为0,可能用于标识某些特性。
- TimeDateStamp:指示资源的最后修改时间,有助于版本控制。
- MajorVersion / MinorVersion:用于表示资源版本,便于管理不同版本的资源。
- NumberOfIdEntries / NumberOfNamedEntries:帮助管理资源目录中的条目数量,便于遍历。
- Name / Id:唯一标识资源的名称或ID,是资源查找的关键。
- Type:指示资源的类型,如
RT_ICON
(图标)、RT_BITMAP
(位图)、RT_STRING
(字符串)等。 - OffsetToData:指向实际资源数据的位置,程序加载资源时需要使用。
- Size:资源数据的大小,便于分配和管理内存。
3.查询资源表结构
查询资源表可以使用Resource
命令获取,该命令仅用于获取进程中所加载的资源信息概述,如下输出所示;
[Pe View] # Resource
------------------------------------------------------------
资源类型ID 类型
------------------------------------------------------------
00000003 图标
00000004 菜单
00000005 对话框
00000006 字符串列表
00000009 快捷键
0000000E 图标组
00000018 24
------------------------------------------------------------
若要查询完整的资源表结构,可通过调用ResourceAll
命令获取,如下输出所示;
[Pe View] # ResourceAll
--------------------------------------------------------------------------------
资源类型ID 类型 资源语言ID 语言 资源数据偏移地址
--------------------------------------------------------------------------------
00000003 图标 00000003 Unknown
资源二层目录偏移地址: FFFFFFFF
-> 资源ID: 0000B800 类型: 47104 数据偏移地址: 00000000
-> 资源ID: 00004000 类型: 16384 数据偏移地址: 00000000
-> 资源ID: 00000000 类型: (null) 数据偏移地址: 00000000
-> 资源ID: 00000000 类型: (null) 数据偏移地址: 00000000
-> 资源ID: 000021CD 类型: 8653 数据偏移地址: 5421CD4C
-> 资源ID: 00006968 类型: 26984 数据偏移地址: 676F7270
-> 资源ID: 00006172 类型: 24946 数据偏移地址: 6E6E6163
-> 资源ID: 0000746F 类型: 29807 数据偏移地址: 75722065
-> 资源ID: 0000206E 类型: 8302 数据偏移地址: 534F4420
-> 资源ID: 00006D20 类型: 27936 数据偏移地址: 0D0D2E65
文件查询与反汇编
1.查询十六进制文本
通过调用GetHexAscii
命令,该命令接收两个参数,分别是文件地址及需要向下遍历的长度,当参数被传入后则可输出特定程序中特定位置的十六进制机器码,如下输出所示;
[Pe View] # Open --path d://lyshark.exe
[+] 已读入文件
[Pe View] # GetHexAscii --offset 0 --len 100
-------------------------------------------------------------------------------
Offset | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ASCII
-------------------------------------------------------------------------------
00000000 | 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 | MZ?
00000016 | B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 | ? @
00000032 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
00000048 | 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 |
00000064 | 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 | ? ? ?!?L?!Th
00000080 | 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F | is program canno
[Pe View] #
[Pe View] # GetHexAscii --offset 1024 --len 100
-------------------------------------------------------------------------------
Offset | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ASCII
-------------------------------------------------------------------------------
00001024 | 55 8B EC 83 EC 1C 56 8B 35 E0 20 40 00 57 8B 7D | U????V?5? @ W?}
00001040 | 08 6A 64 68 38 34 40 00 6A 67 57 FF D6 6A 64 68 |jdh84@ jgW?jdh
00001056 | 70 33 40 00 6A 6D 57 FF D6 8B CF E8 C0 00 00 00 | p3@ jmW?????
00001072 | 6A 00 57 6A 00 6A 00 6A 00 68 00 00 00 80 6A 00 | j Wj j j h €j
00001088 | 68 00 00 00 80 68 00 00 CF 00 68 38 34 40 00 68 | h €h ? h84@ h
00001104 | 70 33 40 00 6A 00 89 3D 00 35 40 00 FF 15 BC 20 | p3@ j ?= 5@ ?
2.查询文件反汇编区域
通过调用DasmFoa
命令,该命令接收两个参数,分别是文件地址及需要向下遍历的长度,用于对特定文本区域进行反汇编,如下输出所示;
[Pe View] # DasmFoa --offset 1024 --len 30
--------------------------------------------------------------------------------
文件偏移 反汇编指令集
--------------------------------------------------------------------------------
0x00000400 | push ebp
0x00000401 | mov ebp, esp
0x00000403 | sub esp, 0x1c
0x00000406 | push esi
0x00000407 | mov esi, dword ptr [0x4020e0]
0x0000040D | push edi
0x0000040E | mov edi, dword ptr [ebp + 8]
0x00000411 | push 0x64
0x00000413 | push 0x403438
0x00000418 | push 0x67
0x0000041A | push edi
0x0000041B | call esi
[Pe View] #
[Pe View] # DasmFoa --offset 2000 --len 20
--------------------------------------------------------------------------------
文件偏移 反汇编指令集
--------------------------------------------------------------------------------
0x000007D0 | add byte ptr [ebp + 0x6a0879c0], al
0x000007D6 | or al, ch
0x000007D8 | inc esi
0x000007D9 | add eax, dword ptr [eax]
0x000007DB | add byte ptr [ecx - 0x3d], bl
0x000007DE | push 0x14
3.扫描内存挂钩状态
检查内存汇编代码是否被挂钩可以通过调用ScanPE
命令实现,该命令需要读入一个可执行文件并运行一个与之相对应的进程,当执行命令后会将内存中的反汇编代码与磁盘中的反汇编代码进行比对若两者不同则说明被挂钩了,若相同则说明正常,如下输出所示;
[Pe View] # Open --path D:\\lyshark.exe
[+] 已读入文件
[Pe View] #
[Pe View] # ScanPE
[+] 读入文件长度: 14848 bytes 基址: 0172DAF8
[+] 进程模块句柄: 15532032
[+] 进程句柄: 000000E4
[+] PE读入状态: 1
[+] 拉伸后长度: 28672 bytes 基址: 01731500
[+] 修正重定位基址: 15532032
[+] 代码段数量: 1
[+] 虚拟地址: 4096 长度: 3072 实际地址: 1024
0x00ED0000 | 文件汇编: push ebp | 内存汇编: push ebp |
0x00ED0001 | 文件汇编: mov ebp, esp | 内存汇编: mov ebp, esp |
0x00ED0003 | 文件汇编: sub esp, 0x1c | 内存汇编: sub esp, 0x1c |
0x00ED0006 | 文件汇编: push esi | 内存汇编: push esi |
0x00ED0007 | 文件汇编: mov esi, dword ptr [0xed20e0] | 内存汇编: mov esi, dword ptr [0xed20e0] |
0x00ED000D | 文件汇编: push edi | 内存汇编: push edi |
0x00ED000E | 文件汇编: mov edi, dword ptr [ebp + 8] | 内存汇编: mov edi, dword ptr [ebp + 8] |
其他扩展
检查函数内存地址: 用于验证特定模块中的内存地址,使用GetProcAddr
命令验证。
[Pe View] # GetProcAddr --dll user32.dll --function MessageBoxA
0x75361F70
[Pe View] # GetProcAddr --dll user32.dll --function MessageBoxW
0x753623A0
[Pe View] # GetProcAddr --dll user32.dll --function MessageBox
0x0
检查保护模式: 用于检查当前打开进程所开启的保护模式,使用CheckSelf
命令验证。
[Pe View] # Open --path d:\\lyshark.exe
[+] 已读入文件
[Pe View] #
[Pe View] # CheckSelf
--------------------------------------------------
基址随机化: 是
DEP保护兼容: 是
强制完整性: 否
SEH异常保护: 否
证书签名: 否
--------------------------------------------------
目标指纹识别: 使用Fingerprint
命令检测目标程序硬盘特征指纹,从而判断是那个编译器生成的程序,目前特征库不全仅用于演示。
[Pe View] # Fingerprint --path d://lyshark.exe
--------------------------------------------------------------------------------------------------------------
原始数据: 55 8B EC 81 EC C4 00 00 00 53 56 57 8D BD 3C FF FF FF B9 31 00 00 00 B8 CC CC CC CC F3 AB 8B 45
磁盘映像: 35 40 00 87 01 83 3D 14 35 40 00 00 74 1B 68 14 35 40 00 E8 C8 02 00 00 59 85 C0 74 0C 6A 00 6A
检测结果: 无指纹特征
--------------------------------------------------------------------------------------------------------------
原始数据: CC CC CC CC CC E9 86 02 00 00 E9 31 05 00 00 E9 6C 01 00 00 E9 57 03 00 00 E9 22 00 00 00 CC CC
磁盘映像: 55 8B EC 83 EC 1C 56 8B 35 E0 20 40 00 57 8B 7D 08 6A 64 68 38 34 40 00 6A 67 57 FF D6 6A 64 68
检测结果: 无指纹特征
--------------------------------------------------------------------------------------------------------------
十六进制计算器: 用于计算两个十六进制数的加减法。
[Pe View] # Add --x 1c --y 2d
1c + 2d =>
HEX= 00000049
DEC= 73
OCT= 111
BIN= 1001001
[Pe View] #
[Pe View] # Sub --x 1c --y 2d
1c - 2d =>
HEX= FFFFFFEF
DEC= -17
OCT= 37777777757
BIN= 11111111111111111111111111101111
文件地址转虚拟地址: 将当前打开程序机器码所在地址转换成载入内存中的虚拟地址。
[Pe View] # Open --path d:\\lyshark.exe
[+] 已读入文件
[Pe View] #
[Pe View] # FoaToVa --foa 1024
--------------------------------------------------------------------------------
基址: 0x00400000 文件偏移开始: 0x00001000 文件偏移结束: 0x00001800
--------------------------------------------------------------------------------
FOA地址: 0x00001024
---> RVA地址: 0x00002024
---> VA地址: 0x00402024
--------------------------------------------------------------------------------
虚拟地址转文件地址: 将当前打开程序的内存虚拟地址转换为所在文件地址。
[Pe View] # VaToFoa --va 0x00402024
--------------------------------------------------------------------------------
基址: 0x00400000 所在节区: .rdata 节开始地址: 0x00402000 节结束地址: 0x004027BA
--------------------------------------------------------------------------------
VA地址: 0x00402024
---> RVA地址: 0x00002024
---> FOA地址: 0x00001024
--------------------------------------------------------------------------------
相对地址转文件地址: 将当前打开程序的 RVA 相对内存地址,转换成一个文件偏移地址。
[Pe View] # RvaToFoa --rva 0x00002024
--------------------------------------------------------------------------------
基址: 0x00400000 所在节区: .rdata 节开始地址: 0x00002000 节结束地址: 0x000027BA
--------------------------------------------------------------------------------
RVA地址: 0x00002024
---> VA地址: 0x00402024
---> FOA地址: 0x00001024
--------------------------------------------------------------------------------
标签:00,PeView,PE,00000000,地址,命令行,Pe,--,View
From: https://blog.csdn.net/lyshark_csdn/article/details/143346651