PE文件结构

        PE文件由文件头部分,节表部分,还有节区部分组成。其中文件头包括PE文件头还有DOS文件头,节表主要是用来说明每个节的RVA地址(RVA地址就是文件被装载到内存后数据相对于文件起始位置的偏移量)还有节的尺寸的,当然还有一些其他的信息。而节区就是将属性相同的文件数据放在了一起,比如可执行的放一起,只读的放一起等等。

这是PE文件头的大概样子:

  • DOS文件头

  DOS头部分由MZ格式的文件头和可执行代码部分组成。而MZ格式的文件头是由一个IMAGE_DOS_HEADER结构定义的:

​

​

​

IMAE_DOS_HEADER STRUCT{             //DOS .EXE header                            位置  
     e_magic    WORD    ?           //DOS可执行文件标记,为"MZ"                    0x00  
     e_cblp     WORD    ?           //Bytes on last page of file                 0x02  
     e_cp       WORD    ?           //Pages in file                              0x04  
     e_crlc     WORD    ?           //Relocations                                0x06  
     e_cparhdr  WORD    ?           //Size of header in paragraphs               0x08  
     e_minalloc WORD    ?           //Minimum extra paragraphs needed            0x0A  
     e_maxalloc WORD    ?           //Maximum extra paragraphs needed            0x0C  
     e_ss       WORD    ?           //DOS代码初始化堆栈段                          0x0E  
     e_sp       WORD    ?           //DOS代码的初始化堆栈指针                      0x10  
     e_csum     WORD    ?           //Checksum                                   0x12  
     e_ip       WORD    ?           //DOS代码入口IP                               0x14  
     e_cs       WORD    ?           //DOS代码入口CS                               0x16  
     e_lfarlc   WORD    ?           //File address of relocation table           0x18  
     e_ovno     WORD    ?           //Overlay number                             0x1A  
     e_res      WORD    4 dup(?)    //Reserved words                             0x1C  
     e_oemid    WORD    ?           //OEM identifier (for e_oeminfo)             0x24  
     e_oeminfo  WORD    ?           //OEM information; e_oemid specific          0x26   
     e_res2     WORD    10 dup(?)   //Reserved words                             0x28  
     e_lfanew   DWORD   ?           //指向PE文件头                                0x3C  
IMAGE_DOS-HEADER ENDS

​

 其中比较重要的部分就是第一个字段的DOS可执行文件标记,它用来说明这是一个DOS文件头。还有就是最后一个字段的e_lfnew,它指向真正的PE文件头

  • PE文件头(NT文件头)

   由上图可知,NT文件头包括三个部分,分别是PE文件标识:Signature,FileHeader,OPtionalHeader。这三个结构都是在一个IMAGE_NT_HEADERS结构里面。

IMAGE_NT_HEADERS STRUCT                        ;NT文件头
    Signature        DWORD                ?    ;PE文件标识
    FileHeader       IMAGE_FILE_HEADER    <>    
    OptionalHeader   IMAGE_OPTIONAL_HEADER32    <>
IMAGE_NT_HEADERS ENDS

 第一个字段就是用来标识PE文件的,第二个字段的结构IMAGE_FILE_HEADER包含了很多信息:

IMAGE_FILE_HEADER    STRUCT
    Machine                WORD    ?    ;0004h - 运行平台
    NumberOfSections       WORD    ?    ;0006h - 文件的节区数目
    TimeDateStamp          DWORD   ?    ;0008h - 文件的创建日期和时间
    PointerToSymbolTable   DWORD   ?    ;000ch - 指向符号表(用于调试)
    NumberOfSymbols        DWORD   ?    ;0010h - 符号表中的符号数量(用于调试)
    SizeOfOptionalHeader   WORD    ?    ;0014h - IMAGE_OPTIONAL_HEADER32的结构长度
    Characteristics        WORD    ?    ;0016h - 文件属性
IMAGE_FILE_HEADER    ENDS

 里面对于我们来说比较重要的就是 NumberOfSections:文件的节区数目,IMAGE_OPTIONAL_HEADER32结构的长度以及Characteristics:文件属性。其中文件属性字段的长度是一个字,每一个位都表示一个不同的信息。其他字段看注释就能明白什么意思了。下面这张图就是Characteristics字段各个位的含义。

然后比较重要的一个结构就是IMAGE_OPTIONAL_HEADER32结构,这个结构虽然从英文的意思上来解读像是一个可选部分,但是其实包含了比IMAGE_FILE_HEADER结构更多的信息,并且也是存在于每个PE文件中。我们可以把这两个结构连在一起看,把他们当成连在一起的 PE文件头结构。

​

IMAGE_OPTIONAL_HEADER32 STRUCT
  
 Magic                        WORD   ?     ;0018h 107h=ROM Image,10Bh=exe Image
 MajorLinkerVersion           BYTE   ?     ;链接程序的主版本号
 MinorLinkerVersion           BYTE   ?     ;链接程序的次版本号
 SizeOfCode                   DWORD  ?     ;所有含代码的节的总大小
 SizeOfInitializedData        DWORD  ?     ;所有含已初始化数据的节的总大小
 SizeOfUninitializedData      DWORD  ?     ;所有含未初始化数据的节的大小
 AddressOfEntryPoint          DWORD  ?     ;程序执行入口RVA
 BaseOfCode                   DWORD  ?     ;代码的区块的起始RVA
 BaseOfData                   DWORD  ?     ;数据的区块的起始RVA
 ImageBase                    DWORD  ?     ;程序的首选装载地址
 SectionAlignment             DWORD  ?     ;内存中的区块的对齐大小
 FileAlignment                DWORD  ?     ;文件中的区块的对齐大小
 MajorOperatingSystemVersion  WORD   ?     ;要求操作系统最低版本号的主版本号
 MinorOperatingSystemVersion  WORD   ?     ;要求操作系统最低版本号的副版本号
 MajorImageVersion            WORD   ?     ;可运行于操作系统的主版本号
 MinorImageVersion            WORD   ?     ;可运行于操作系统的次版本号
 MajorSubsystemVersion        WORD   ?     ;要求最低子系统版本的主版本号
 MinorSubsystemVersion        WORD   ?     ;要求最低子系统版本的次版本号
 Win32VersionValue            DWORD  ?     ;未用
 SizeOfImage                  DWORD  ?     ;映像装入内存后的总尺寸
 SizeOfHeaders                DWORD  ?     ;所有头 + 节表的尺寸大小
 CheckSum                     DWORD  ?     ;映像的校检和
 Subsystem                    DWORD  ?     ;可执行文件期望的子系统
 DllCharacteristics           DWORD  ?     ;DllMain()函数何时被调用,默认为 0
 SizeOfStackReserve           DWORD  ?     ;初始化时的栈大小
 SizeOfStackCommit            DWORD  ?     ;初始化时实际提交的栈大小
 SizeOfHeapReserve            DWORD  ?     ;初始化时保留的堆大小
 SizeOfHeapCommit             DWORD  ?     ;初始化时实际提交的堆大小
 LoaderFlags                  DWORD  ?     ;与调试有关,默认为 0 
 NumberOfRvaAndSizes          DWORD  ?     ;下边数据目录结构的数量
 DataDirectory                IMAGE_DATA_DIRECTORY  16 dup(<>)   
 
IMAGE_OPTIONAL_HEADER32 ENDS

[点击并拖拽以移动]
​

可以看到这个IMAGE_OPTIONAL_HEADER32结构包含的信息非常多,但是其实我们只要了解那几个比较重要的就行了,所以这里挑几个重要的介绍一下。

AddressOfEntryPoint: 这个字段存储程序执行入口的RVA

SectionAlignment:内存中的节的对齐粒度,当PE文件被装载到内存中后节区部分需要对齐,也就是说每个节的装入地址都会是本字段的整数倍,每个节的大小都得是本字段的整数倍。加入对齐粒度是2,但是某个节的的大小是7,那么这个节被装载到内存后要填0扩展到8。(WIndows装载器在装载DOS部分,PE文件头部分和节表部分时不做任何处理,而装入节时要根据节的属性做不同处理)

FileAlignment:文件中节的对齐粒度,意思和上面的一样

Subsystem:文件的子系统。这个字段决定了系统如何为程序建立初始的界面,我们用Link链接器链接时的命令 link /subsystem:xxx  这个xxx选项就是这个字段的值。比如有Windows控制台界面Windows图形界面等

DataDirectory:数据目录。可以说这个字段是这个结构里面最重要的字段了。它由16个IMAGE_DATA_DIRECTORY结构组成。IMAGE_DATA_DIRECTORY结构指出了导出表,导入表,资源,重定位信息等数据的起始RVA和数据块的长度。结构的定义如下:

IMAGE_DATA_DIRECTORY STRUCT
    VirtualAddress    DWORD    ?    ;数据起始RVA
    isize             DWORD    ?    ;数据块的长度
IMAGE_DATA_DIRECTORY  ENDS

每个结构定义一个数据块的信息

            索    引             索引值在WIndows.inc中的预定义值                        对应的数据块
                 0IMAGE_DIRECTOYR_ENTRY_EXPORT导出表
                 1IMAGE_DIRECTOYR_ENTRY_IMPORT导入表
                 2IMAGE_DIRECTOYR_ENTRY_RESOURCE资源
                 3IMAGE_DIRECTOYR_ENTRY_EXCEPTION异常
                 4IMAGE_DIRECTOYR_ENTRY_SECURITY安全
                 5IMAGE_DIRECTOYR_ENTRY_BASERELOC重定位表
                 6IMAGE_DIRECTOYR_ENTRY_DEBUG调试信息
                 7IMAGE_DIRECTOYR_ENTRY_ARCHITECTURE版权信息
                 8IMAGE_DIRECTOYR_ENTRY_GLOBALPTR 
                 9IMAGE_DIRECTOYR_ENTRY_TLSThread Local Storage
                 10IMAGE_DIRECTOYR_ENTRY_LOAD_CONFIG 
                 11IMAGE_DIRECTOYR_ENTRY_IMPORT 
                 12IMAGE_DIRECTOYR_ENTRY_IAT导入函数地址表
                 13IMAGE_DIRECTOYR_ENTRY_DELAY_IMPORT 
                 14IMAGE_DIRECTOYR_ENTRY_DESCRIPTOR 
                 15未使用 

总之,如果我们要寻找某个数据的位置,就可以通过这个IMAGE_DATA_DIRECTORY结构来寻找。

  • 节表

文件头的最后一部分就是节表了,好像也叫区块表。节表也和数据目录一样是由很多个相同的结构组成的,节表里面的信息主要是指出它所指向的节区的RVA还有节区大小,节区属性,还有一些其他的重要信息。节表的结构如下:

​

IMAGE_SECTION_HEADER STRUCT
    Namel db IMAGE_SIZEOF_SHORT_NAME dup(?)    ;8个字节的节区名称
    union Misc
         PhysicalAddress    dd    ?
         VirtualSize        dd    ?            ;节区的尺寸
    ends
    VirtualAddress          dd    ?            ;节区的RVA地址
    SizeOfRawData           dd    ?            ;在文件中对齐后的尺寸
    PointerToRawData        dd    ?            ;在文件中的偏移
    PointerToRelocations    dd    ?            ;在OBJ文件中使用
    PointerToLinenumbers    dd    ?            ;行号表的位置(调试用的)
    NumberOfRelocations     dw    ?            ;在OBJ文件中使用
    Characteristics         dd    ?            ;节的属性
IMAGE_SECTION_HEADER  ENDS 

[点击并拖拽以移动]
​

Name1:第一个字段 ,注意,这后面是一,不是"l"。这个字段定义了节的名称,字段的长度为8个字节,名称小于8个字节后面就补0,但是如果达到了8个字节后面就没有0了。每个节的名字是唯一的,一个PE文件中不能有两个同名的节。节的名字并不决定节的属性,它只是一个标记。但是一般代码节的名字会被取为“.text”,可读数据的节会被名命为 “.data”等等。

VirtualSize:这个表示节在没有进行对齐时的实际大小

VirtualAddress:指出节被装载到内存后的RVA,这个地址是按照SectionAlignment对齐后的

SizeOfRawData:节区在文件中的尺寸(按照FileAlignment对齐后的)

PointerToRawData:一个从文件头开始算起的偏移,它指出节在磁盘文件中所处的位置

Characteristics:属性,不同的数据位代表了不同的属性。高三位从高到低分别表示映射到内存后包含 可读,可写,可执行属性。

知道了这些大概的信息后我们可以用WIN32汇编写一个显示PE文件头信息的小程序。

首先为了方便操作,用内存映射文件函数将要操作的文件映射到内存中,然后我们可以获得一个文件头起始位置的指针,通过这个指针我们可以定位到NT文件头的位置,然后在IMAGE_FILE_HEADER这里我们可以得到节区的数量,然后再定位到IMAGE_OPTIONAL_HEADER32结构这里,我们可以得到更多的信息,拿你想显示的就行。然后再往后定位可以得到节表的位置,这样就可以获取各个节的名称了。

但是注意:我们需要设置一个异常处理,因为可能我们选择的不是一个PE文件,那么我们将这个文件当成一个PE文件来读取时可能会引发内存读取错误。

(程序例子是琢石成器这本书上的)

整个程序是用一个对话框来显示信息的,下面是对话框资源的源码:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include		<resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define  ICO_MAIN		1000
#define	 DLG_MAIN		1000
#define	 IDC_INFO		1001
#define	 IDM_MAIN		2000
#define	 IDM_OPEN		2001
#define	 IDM_EXIT		2002
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN	ICON		"Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN	DIALOG	50,50,250,140
STYLE	DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION	"PE文件基本信息"
MENU	IDM_MAIN
FONT	9,"宋体"
BEGIN
	CONTROL "",IDC_INFO,"RichEdit20A",196 | ES_WANTRETURN | WS_CHILD | ES_READONLY
				| WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP,0,0,249,140
END
IDM_MAIN	menu		discardable
BEGIN
		popup	"文件(&F)"
		BEGIN
				menuitem			"打开文件(&O)...",IDM_OPEN
				menuitem			separator
				menuitem			"退出(&X)",IDM_EXIT
		END
END

然后是程序的汇编代码,分成两部分来写了,一部分是一个整体的框架,还有一部分是具体功能的实现。下面是大体框架:

				.386
				.model flat,stdcall
				option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;Include文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include			windows.inc 
include			user32.inc
includelib		user32.lib
include			kernel32.inc
includelib		kernel32.lib
include			comdlg32.inc
includelib		comdlg32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;Equ等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN		equ		1000
DLG_MAIN		equ		1000
IDC_INFO		equ		1001
IDM_MAIN		equ		2000
IDM_OPEN		equ		2001
IDM_EXIT		equ		2002
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				.data?
hInstance		dd		?
hRichEdit		dd		?
hWinMain		dd		?
hWinEdit		dd		?
szFileName		db		MAX_PATH dup (?)

				.const
szDllEdit		db		'RichEd20.dll',0
szClassEdit		db		'RichEdit20A',0
szFont			db		'宋体',0
szExtPe			db		'PE Files(*.*)',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0
				db		'ALl Files(*.*)',0,'*.*',0,0
szErr			db		'文件格式错误!',0
szErrFormat		db		'这个文件不是PE格式的文件!',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				.code
_AppendInfo		proc	_lpsz										;设置字符格式并显示字符在对话框内
				local	@stCR:CHARRANGE								;该结构指定位于富编辑器内的字符范围
				
				pushad
				invoke	GetWindowTextLength,hWinEdit				;该函数返回指定窗口的标题文本(如果存在)的字符长度。如果指定窗口是一个控件,函数将返回控制内文本的长度
				mov		@stCR.cpMin,eax								;所选范围的第一个字符的前一个位置
				mov		@stCR.cpMax,eax								;所选范围的最后一个字符的后一个位置
				invoke	SendMessage,hWinEdit,EM_EXSETSEL,0,addr @stCR			;在 Microsoft 丰富编辑控件中选择一系列字符或组件对象模型 (COM) 对象。
				invoke	SendMessage,hWinEdit,EM_REPLACESEL,FALSE,_lpsz			;将编辑控件或富编辑控件中的选定文本替换为指定文本
				popad
				ret
_AppendInfo		endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include			_ProcessPeFile.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Init			proc	;初始化
				local	@stCf:CHARFORMAT							;字符格式结构
				
				invoke	GetDlgItem,hWinMain,IDC_INFO				;获取对话框窗口控件的句柄
				mov		hWinEdit,eax
				invoke	LoadIcon,hInstance,ICO_MAIN
				invoke	SendMessage,hWinMain,WM_SETICON,ICON_BIG,eax
				invoke	SendMessage,hWinEdit,EM_SETTEXTMODE,TM_PLAINTEXT,0
				invoke	RtlZeroMemory,addr @stCf,sizeof	@stCf
				mov		@stCf.cbSize,sizeof @stCf
				mov		@stCf.yHeight,9*20
				mov		@stCf.dwMask,CFM_FACE or CFM_SIZE or CFM_BOLD
				invoke	lstrcpy,addr @stCf.szFaceName,addr szFont
				invoke	SendMessage,hWinEdit,EM_SETCHARFORMAT,0,addr @stCf		;设置字符格式
				invoke	SendMessage,hWinEdit,EM_EXLIMITTEXT,0,-1
				ret
				
_Init			endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;错误Handler
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Handler		proc	C _lpExceptionRecord,_lpSEH,\
						_lpContext,_lpDispatcherContext
				pushad
				mov		esi,_lpExceptionRecord
				mov		edi,_lpContext
				assume	esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT
				mov		eax,_lpSEH								;本线程注册回调函数的地址
				push	[eax + 0ch]
				pop		[edi].regEbp							;存储异常发生前的堆栈基址
				push	[eax + 8]
				pop		[edi].regEip							;修正程序位置
				push	eax
				pop		[edi].regEsp							;原来的Esp的位置
				assume	esi:nothing,edi:nothing
				popad
				mov		eax,ExceptionContinueExecution
				ret
_Handler		endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;打开文件,建立映射,读取文件数据,显示数据信息				
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_OpenFile		proc
				local	@stOF:OPENFILENAME
				local	@hFile,@dwFileSize,@hMapFile,@lpMemory
				
				invoke	RtlZeroMemory,addr @stOF,sizeof @stOF
				mov		@stOF.lStructSize,sizeof @stOF
				push	hWinMain
				pop		@stOF.hwndOwner
				mov		@stOF.lpstrFilter,offset szExtPe
				mov		@stOF.nMaxFile,MAX_PATH
				mov		@stOF.lpstrFile,offset szFileName							;选择的文件返回到这个缓冲区
				mov		@stOF.Flags,OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST
				invoke	GetOpenFileName,addr @stOF
				.if		!eax
						jmp		@F			;退出
				.endif
;*******************************************************************************************************************************
;打开并建立文件
;*******************************************************************************************************************************
				invoke	CreateFile,addr szFileName,GENERIC_READ,\					;以读的方式打开
						FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,\					;允许其他进程同时以读和写的方式打开文件
						OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL					;文件不存在时函数返回失败
				.if		eax != INVALID_HANDLE_VALUE
						mov		@hFile,eax
						invoke	GetFileSize,eax,NULL								;获取文件大小
						mov		@dwFileSize,eax	
						.if		eax
								invoke	CreateFileMapping,@hFile,\							;创建一个内存映射文件对象,保护类型为只读
										NULL,PAGE_READONLY,0,0,NULL
								.if		eax
										mov		@hMapFile,eax								;内存映射文件对象句柄
										invoke	MapViewOfFile,eax,\							;映射内存内存映射文件视图,映射整个文件
												FILE_MAP_READ,0,0,0
										.if		eax
												mov		@lpMemory,eax						;映射成功则返回映射到内存中的文件开始的指针
;*******************************************************************************************************************************
;创建用于错误处理的SHE结构
;*******************************************************************************************************************************
												assume	fs:nothing
												push	ebp									;存储异常发生前的堆栈基址
												push	offset _ErrFormat
												push	offset _Handler						;异常处理回调函数地址
												push	fs:[0]
												mov		fs:[0],esp
;*******************************************************************************************************************************
;检测PE文件是否有效
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
												mov		esi,@lpMemory
												assume	esi:ptr IMAGE_DOS_HEADER
												.if		[esi].e_magic != IMAGE_DOS_SIGNATURE							;检测是否是DOS文件头
													jmp		_ErrFormat
												.endif
												add		esi,[esi].e_lfanew											;到PE文件头的位置
												assume	esi:ptr IMAGE_NT_HEADERS
												.if		[esi].Signature != IMAGE_NT_SIGNATURE						;检测是否是PE文件头
														jmp		_ErrFormat
												.endif
												invoke	_ProcessPeFile,@lpMemory,esi,@dwFileSize
												jmp		_ErrorExit
_ErrFormat:
												invoke	MessageBox,hWinMain,addr szErrFormat,offset szErr,MB_OK				;显示出错信息
_ErrorExit:
												pop		fs:[0]
												add		esp,0ch														;平衡堆栈
												invoke	UnmapViewOfFile,@lpMemory									;解除映射关系
;******************************************************************************************************************************
										.endif
										invoke	CloseHandle,@hMapFile
								.endif
								invoke	CloseHandle,@hFile
						.endif
				.endif
@@:
				ret
_OpenFile		endp
						

_ProcDlgMain	proc	uses ebx edi esi hWnd,wMsg,wParam,lParam

				mov		eax,wMsg
				.if		eax == WM_CLOSE
						invoke	EndDialog,hWnd,NULL
				.elseif	eax == WM_INITDIALOG
						push		hWnd
						pop			hWinMain
						call		_Init
				.elseif	eax == WM_COMMAND
						mov		eax,wParam
						.if		ax == IDM_OPEN
								call	_OpenFile
						.elseif	ax == IDM_EXIT					;菜单退出
								invoke	EndDialog,hWnd,NULL
						.endif
				.else
						mov		eax,FALSE
						ret
				.endif
				mov		eax,TRUE
				ret
				
_ProcDlgMain	endp


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
				invoke	LoadLibrary,offset szDllEdit		;装载RichEd20.dll
				mov		hRichEdit,eax
				invoke	GetModuleHandle,NULL
				mov		hInstance,eax
				invoke	DialogBoxParam,hInstance,\
						DLG_MAIN,NULL,offset _ProcDlgMain,NULL
				invoke	FreeLibrary,hRichEdit
				invoke	ExitProcess,NULL
				
				end		start

下面是功能的具体实现部分:

				.const
szMsg	db	'文件名:%s',0dh,0ah
		db	'----------------------------------------------------------',0dh,0ah
		db	'运行平台:			0x%04X',0dh,0ah
		db	'节区数量:			%d',0dh,0ah
		db	'文件标记:			0x%04X',0dh,0ah
		db	'建议装入地址:		        0x%08X',0dh,0ah
		db	'程序执行入口RVA:		0x%08X',0dh,0ah,0,0
szMsgSection	db '----------------------------------------------------------',0dh,0ah
				db '节区名称  节区大小  虚拟地址  Raw_尺寸  Raw_偏移  节区属性',0dh,0ah
				db '----------------------------------------------------------',0dh,0ah,0
szFmtSection	db '%s  %08X  %08X  %08X  %08X  %08X',0dh,0ah,0
				.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcessPeFile	proc	_lpFile,_lpPeHead,_dwSize							;内存映射文件指针,PE文件头指针,文件大小
				local	@szBuffer[1024]:byte,@szSectionName[16]:byte
				
				pushad
				mov		edi,_lpPeHead
				assume	edi:ptr IMAGE_NT_HEADERS
;************************************************************************************************************
;显示PE文件头中的一些信息
;************************************************************************************************************
				movzx	ecx,[edi].FileHeader.Machine						;运行平台 
				movzx	edx,[edi].FileHeader.NumberOfSections				;节的数目
				movzx	ebx,[edi].FileHeader.Characteristics				;文件属性
				invoke	wsprintf,addr @szBuffer,addr szMsg,\
						addr szFileName,ecx,edx,ebx,\
						[edi].OptionalHeader.ImageBase,[edi].OptionalHeader.AddressOfEntryPoint
				invoke	SetWindowText,hWinEdit,addr @szBuffer				;设置对话框内容
;************************************************************************************************************
;循环显示每个节区的信息
;************************************************************************************************************
				invoke	_AppendInfo,addr szMsgSection						;设置字符格式并显示字符在对话框
				movzx	ecx,[edi].FileHeader.NumberOfSections
				add		edi,sizeof	IMAGE_NT_HEADERS
				assume	edi:ptr IMAGE_SECTION_HEADER						;现在EDI于节表位置
				.repeat
						push	ecx
;************************************************************************************************************
;获取节的名称,因为节区的名称不一定是以0结尾的,所以要进行一些处理
;************************************************************************************************************
						invoke	RtlZeroMemory,addr @szSectionName,\
								sizeof	@szSectionName
						push	esi
						push	edi
						mov		ecx,8
						mov		esi,edi
						lea		edi,@szSectionName
						cld
						@@:
						lodsb							;将ESI指向的存储单元读入AL
						.if		! al					;把0换成空格
								mov		al,' '
						.endif
						stosb							;将AL中的数据读入EDI所指的地址处
						loop	@B
						pop		edi
						pop		esi
						
						invoke	wsprintf,addr @szBuffer,addr szFmtSection,\
								addr @szSectionName,[edi].Misc.VirtualSize,\
								[edi].VirtualAddress,[edi].SizeOfRawData,\
								[edi].PointerToRawData,[edi].Characteristics
						invoke	_AppendInfo,addr @szBuffer
						add		edi,sizeof IMAGE_SECTION_HEADER						;在节表中到下一个节区的的节表结构位置

						pop		ecx
				.untilcxz
				assume	edi:nothing
				popad
				ret
				
_ProcessPeFile	endp
								
						

程序编译链接出来后运行是这个样子的:

文章转载于https://blog.csdn.net/weixin_43575859/article/details/104801073?utm_source=app

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值