PE文件结构的基础概念,用32位汇编写一个程序读取PE文件头 您所在的位置:网站首页 dos代码 PE文件结构的基础概念,用32位汇编写一个程序读取PE文件头

PE文件结构的基础概念,用32位汇编写一个程序读取PE文件头

2023-09-21 08:06| 来源: 网络整理| 查看: 265

    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 //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #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

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

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有