2.3 PEComp的实现
PEComp是PE文件比较器。功能是按照PE文件格式的数据结构按字段对两个指定的PE文件进行比对,以获取两个PE文件结构中的不相同的信息。在实际应用中,我们可以通过对比病毒感染前后PE文件在相关字段上发生的变化来判断病毒的感染方式,从而确定清理病毒的方法。
2.3.1 编程思路
PEComp的功能是在通用框架pe.asm的基础上,实现两个PE文件的对比,并通过图形界面将不同之处形象地表示出来。编码的大致思路如下:
步骤1 打开要比较的两个文件,分别进行文件的内存映射,获取内存起始地址。
步骤2 线性搜索,根据文件头部内容确定该文件是否为PE文件,不是则退出。
步骤3 将esi指向要操作的第一个文件的相关字段处,将edi指向第二个要操作的文件的相同字段处,同时获取该位置的指定个数的字节到内存,比较并显示,如果不同,则显示时使用红色背景以示区别。
下面开始具体的设计过程,首先根据构思的最终显示效果定义资源文件。
2.3.2 定义资源文件
将2.1.2节中的资源文件pe.rc复制到pecomp.rc,并在pecomp.rc文件中增加一个对话框,该对话框中包括用户选择的参与对比的两个PE文件文本框ID_TEXT1和ID_TEXT2、两个浏览按钮、一个显示结果用的表格IDC_MODULETABLE和一个执行按钮,增加的对话框脚本定义如下所示:
RESULT_MODULE DIALOG 76,10,630,480
STYLE DS_MODALFRAME | WS_POPUP |WS_VISIBLE | WS_CAPTION |WS_SYSMENU
CAPTION "PE文件对比结果"
FONT 9,"宋体"
BEGIN
LTEXT "您选定的第一个文件为:",ID_STATIC,10,13,200,15
EDITTEXT ID_TEXT1,130,13,440,15
PUSHBUTTON "浏览...",IDC_BROWSE1,570,13,50,14
LTEXT "您选定的第二个文件为:",ID_STATIC1,10,35,200,15
EDITTEXT ID_TEXT2,130,35,440,15
PUSHBUTTON "浏览...",IDC_BROWSE2,570,35,50,14
CONTROL "", IDC_MODULETABLE, "SysListView32",13 | WS_CHILD | WS_VISIBLE |
WS_BORDER | WS_TABSTOP, 10,60,610,390
AUTOCHECKBOX "只显示不同的值" IDC_THESAME,10,460,100,14
PUSHBUTTON "执行...(&R)",IDC_OK,570,460,50,14
END
2.3.3 PEComp编码
复制pe.asm到PEComp.asm,并从代码中的窗口回调函数的菜单项响应部分开始编码,增加内容有以下4个部分。
1.菜单项响应代码
在主窗口的回调函数中,定义对鼠标点击菜单项"文件"|"打开"所引发的消息处理程序,添加如下代码:
.elseif eax==IDM_OPEN ;打开PE文件对比对话框
invoke DialogBoxParam,hInstance,RESULT_MODULE,hWnd,\
offset _resultProcMain,0
invoke InvalidateRect,hWnd,NULL,TRUE
invoke UpdateWindow,hWnd
当用户选择了"打开"菜单选项时,会弹出资源文件里定义的对话框RESULT_MODULE。通过定义该对话框的回调函数,可以处理对话框控件发出的消息。该对话框的回调函数是_resultProcMain,其代码如代码清单2-4所示。
代码清单2-4 PE文件比较器的窗口回调函数_resultProcMain实现(chapter2\pecomp.asm)
;-----------------------
; 弹出PE对比窗口回调函数
;-----------------------
_resultProcMain proc uses ebx edi esi
hProcessModuleDlg:HWND,wMsg,wParam,lParam 5 mov eax,wMsg
.if eax==WM_CLOSE
invoke EndDialog,hProcessModuleDlg,NULL
.elseif eax==WM_INITDIALOG
invoke GetDlgItem,hProcessModuleDlg,IDC_MODULETABLE
mov hProcessModuleTable,eax
invoke GetDlgItem,hProcessModuleDlg,ID_TEXT1
mov hText1,eax
invoke GetDlgItem,hProcessModuleDlg,ID_TEXT2
mov hText2,eax
;定义表格外观
invoke SendMessage,hProcessModuleTable,LVM_SETEXTENDEDLISTVIEWSTYLE,\
0,LVS_EX_GRIDLINES or LVS_EX_FULLROWSELECT
invoke ShowWindow,hProcessModuleTable,SW_SHOW
;清空表格内容
invoke _clearResultView
.elseif eax==WM_NOTIFY
mov eax,lParam
mov ebx,lParam
;更改各控件状态
mov eax,[eax+NMHDR.hwndFrom]
.if eax==hProcessModuleTable
mov ebx,lParam
.if [ebx+NMHDR.code]==NM_CUSTOMDRAW ;绘画时
mov ebx,lParam
assume ebx:ptr NMLVCUSTOMDRAW
.if [ebx].nmcd.dwDrawStage==CDDS_PREPAINT
invoke SetWindowLong,hProcessModuleDlg,DWL_MSGRESULT,\
CDRF_NOTIFYITEMDRAW
mov eax,TRUE
.elseif [ebx].nmcd.dwDrawStage==CDDS_ITEMPREPAINT
;当每一单元格内容被重新绘制前,判断
;两列的值是否一致
invoke _GetListViewItem,hProcessModuleTable,\
[ebx].nmcd.dwItemSpec,1,addr bufTemp1
invoke _GetListViewItem,hProcessModuleTable,\
[ebx].nmcd.dwItemSpec,2,addr bufTemp2
invoke lstrlen,addr bufTemp1
invoke _MemCmp,addr bufTemp1,addr bufTemp2,eax
;如果一致,则将文本的背景色设置为浅红色,否则为黑色
.if eax==1
mov [ebx].clrTextBk,0a0a0ffh
.else
mov [ebx].clrTextBk,0ffffffh
.endif
invoke SetWindowLong,hProcessModuleDlg,DWL_MSGRESULT,\
CDRF_DODEFAULT
mov eax,TRUE
.endif
.elseif [ebx+NMHDR.code]==NM_CLICK
assume ebx:ptr NMLISTVIEW
.endif
.endif
.elseif eax==WM_COMMAND
mov eax,wParam
.if ax==IDC_OK ;执行对比
invoke _openFile
.elseif ax==IDC_BROWSE1
invoke _OpenFile1 ;用户选择第一个文件
.elseif ax==IDC_BROWSE2
invoke _OpenFile2 ;用户选择第二个文件
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_resultProcMain endp
PE文件比较器窗口回调函数中最主要的代码集中在第31~58行。由窗口回调函数注册的监听器一旦发现由表格控件引发了绘画消息,则判断该绘画是否为表格项重画(第38行)。如果是,则获取要重画的项目当前所在行的第1列和第2列的值。这两个值来自于两个不同PE文件的同一个字段,通过判断二者是否相等来决定重画时使用的背景色。如果不相等,则将重画时的背景色设置为0a0a0ffh(浅红色),否则设置为0ffffffh(黑色)。
如上所示,当用户选择了两个要参与对比的PE文件以后,点击执行对比按钮,系统会调用函数_openFile。
2. _openFile函数
_openFile函数的功能是把两个PE文件数据结构中的相关字段的值取出,分别放到表格的第1列和第2列,判断两个值是否相等的代码在回调函数_resultProcMain中已经给出。
与前面的思路一样,程序还是使用了内存映射函数来操作参与对比的两个PE文件,所不同的是这里需要定义两个指针。一个指针指向第一个文件的内存映射函数的起始位置,另一个指针指向第二个文件的内存映射函数的起始位置。假设该工作已经完成,接下来就是把两个PE文件按照第3章里描述的所有字段的值取出来显示到表格中,完成该功能的代码如代码清单2-5所示。
;到此为止,两个内存文件的指针已经获取到了
;@lpMemory和@lpMemory1分别指向两个文件头
;下面是从这个文件头开始,先找出各数据结构的字段值,然后进行比较
;调整esi,edi指向DOS头
mov esi,@lpMemory
assume esi:ptr IMAGE_DOS_HEADER
mov edi,@lpMemory1
assume edi:ptr IMAGE_DOS_HEADER
invoke _Header1
;调整esi,dei指针指向PE文件头
add esi,[esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
add edi,[edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
invoke _Header2
movzx ecx,word ptr [esi+6]
movzx eax,word ptr [edi+6]
.if eax>ecx
mov ecx,eax
.endif
;调整esi,edi指针指向节表
add esi,sizeof IMAGE_NT_HEADERS
add edi,sizeof IMAGE_NT_HEADERS
mov eax,1
.repeat
invoke _Header3
dec ecx
inc eax
.break .if ecx==0
add esi,sizeof IMAGE_SECTION_HEADER
add edi,sizeof IMAGE_SECTION_HEADER
.until FALSE
代码清单2-5 _openFile函数实现的部分代码
由于编码比较长,这里只以结构IMAGE_DOS_HEADER中的字段e_lpanew为例介绍程序设计的流程:将esi和edi分别赋值到两个PE文件的IMAGE_DOS_HEADER后,调用函数_Header1,处理数据结构DOS头的相关字段(第5~10行)。
3._Header1函数
_Header1函数完成了DOS头部分的字段比较,此部分的详细代码如代码清单2-6所示。
代码清单2-6 DOS头部分的字段比较函数_Header1(chapter2\pecomp.asm)
;--------------------------------------------
; IMAGE_DOS_HEADER头信息
;-------------------------------------------
_Header1 proc
pushad
invoke _addLine,addr szRec1,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec2,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec3,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec4,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec5,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec6,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec7,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec8,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec9,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec10,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec11,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec12,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec13,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec14,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec15,esi,edi,8
add esi,8
add edi,8
invoke _addLine,addr szRec16,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec17,esi,edi,2
add esi,2
add edi,2
invoke _addLine,addr szRec18,esi,edi,20
add esi,20
add edi,20
invoke _addLine,addr szRec19,esi,edi,4
popad
ret
_Header1 endp
DOS头结构中字段e_lpanew的处理在第59~61行。首先,将esi和edi指向该字段所在内存位置;然后,调用_addLine函数将两个PE文件对应字段的值加入到表格中。
invoke _addLine,para1,para2,para3,para4
_addLine的参数描述如下:
para1:字段名称字符串所在地址。
para2:PE文件1该字段值所在内存地址。
para3:PE文件2该字段值所在内存地址。
para4:该字段的长度(即字节数)。
4. _addLine函数
该函数完成了在表格中增加一行的操作,具体定义如代码清单2-7所示。
代码清单2-7 在表格中增加一行的函数_addLine( chapter2\pecomp.asm)
;--------------------------------------------
; 在表格中增加一行
; _lpSZ为第一行要显示的字段名
; _lpSP1为第一个文件该字段的位置
; _lpSP2为第二个文件该字段的位置
; _Size为该字段的字节长度
;--------------------------------------------
_addLine proc _lpSZ,_lpSP1,_lpSP2,_Size
pushad
invoke _ListViewSetItem,hProcessModuleTable,dwCount,-1,\
_lpSZ ;在表格中新增加一行
mov dwCount,eax
xor ebx,ebx
invoke _ListViewSetItem,hProcessModuleTable,dwCount,ebx,\
_lpSZ ;显示字段名
invoke RtlZeroMemory,addr szBuffer,50
invoke MemCopy,_lpSP1,addr bufTemp2,_Size
invoke _Byte2Hex,_Size
;将指定字段按照十六进制显示,格式:一个字节+一个空格
invoke lstrcat,addr szBuffer,addr bufTemp1
inc ebx
invoke _ListViewSetItem,hProcessModuleTable,dwCount,ebx,\
addr szBuffer ;第一个文件中的值
invoke RtlZeroMemory,addr szBuffer,50
invoke MemCopy,_lpSP2,addr bufTemp2,_Size
invoke _Byte2Hex,_Size
invoke lstrcat,addr szBuffer,addr bufTemp1
inc ebx
invoke _ListViewSetItem,hProcessModuleTable,dwCount,ebx,\
addr szBuffer ;第二个文件中的值
popad
ret
_addLine endp
显示字段值的同时,会在消息处理函数中调用字节比对函数_MemCmp以确定值是否相同,如果不相同则将表格行的背景色设置为红色以示区别。其他字段的处理方式与字段IMAGE_DOS_HEADER. e_lpanew的处理方式类似,不再一一陈述。
2.3.4 运行PEComp
编译资源文件PEComp.rc,编译链接PEComp.asm生成最终的PEComp.exe程序;将随书文件中目录chapter2下的两个测试用文件peinfoNor.bin和peinfoVir.bin重命名,其扩展名都改为exe,然后运行PEComp.exe程序。
依次选择菜单"文件"→"打开",弹出PE对比对话框窗口,在对话框中选择并打开刚才重命名的两个EXE文件,然后进行对比,运行效果如图2-5所示。

图2-5 PEComp运行效果
注意 请不要运行以上两个文件,因为peinfoVir.exe是个病毒文件。测试完以后请将扩展名改回原来的"bin"。
通过对比可以看出,在两个PE文件的文件头部结构中,字段不相同的部分来自最后一个节的描述。可以初步断定,该病毒程序是通过修改正常程序的最后一个节的相关字段的值来实现病毒代码携带的。
笔记:
peComp.rc文件:
#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
#define IDM_1 4000
#define IDM_2 4001
#define IDM_3 4002
#define IDM_4 4003
#define RESULT_MODULE 5000
#define ID_TEXT1 5001
#define ID_TEXT2 5002
#define IDC_MODULETABLE 5003
#define IDC_OK 5004
#define ID_STATIC 5005
#define ID_STATIC1 5006
#define IDC_BROWSE1 5007
#define IDC_BROWSE2 5008
#define IDC_THESAME 5009
ICO_MAIN ICON "main.ico"
DLG_MAIN DIALOG 50,50,544,399
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "PEComp by Scott"
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,540,396
END
RESULT_MODULE DIALOG 76,10,630,480
STYLE DS_MODALFRAME | WS_POPUP |WS_VISIBLE | WS_CAPTION |WS_SYSMENU
CAPTION "PE文件对比结果"
FONT 9,"宋体"
BEGIN
LTEXT "您选定的第一个文件为:",ID_STATIC,10,13,200,15
EDITTEXT ID_TEXT1,130,13,440,15
PUSHBUTTON "浏览...",IDC_BROWSE1,570,13,50,14
LTEXT "您选定的第二个文件为:",ID_STATIC1,10,35,200,15
EDITTEXT ID_TEXT2,130,35,440,15
PUSHBUTTON "浏览...",IDC_BROWSE2,570,35,50,14
CONTROL "", IDC_MODULETABLE, "SysListView32",13 | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 10,60,610,390
AUTOCHECKBOX "只显示不同的值" IDC_THESAME,10,460,100,14
PUSHBUTTON "执行...(&R)",IDC_OK,570,460,50,14
END
IDM_MAIN menu discardable
BEGIN
POPUP "文件(&F)"
BEGIN
menuitem "打开PE对比对话框",IDM_OPEN
menuitem "---",IDM_1
menuitem "---",IDM_2
menuitem "---",IDM_3 CHECKED
menuitem separator
menuitem "退出(&x)",IDM_EXIT
END
POPUP "编辑(&E)"
BEGIN
menuitem separator
END
POPUP "格式(&O)"
BEGIN
menuitem separator
END
POPUP "查看(&V)"
BEGIN
menuitem "源文件",IDM_1
menuitem "窗口透明度",IDM_2
menuitem separator
menuitem "大小",IDM_3
menuitem "宽度",IDM_4
END
POPUP "帮助(&H)"
BEGIN
menuitem separator
END
END
peComp.asm文件:
;peComp.asm 通用程序框架
;使用 nmake 或下列命令进行编译和链接:
;ml -c -coff peComp.asm
;rc -r peComp.rc
;link -subsystem:windows peComp.obj peComp.res
.386
.model flat, stdcall
option casemap:none
include c:/masm32/include/windows.inc
include c:/masm32/include/user32.inc
include c:/masm32/include/kernel32.inc
include c:/masm32/include/comdlg32.inc
include c:/masm32/include/gdi32.inc
include c:/masm32/include/comctl32.inc
include c:/masm32/include/comdlg32.inc
include c:/masm32/include/advapi32.inc
include c:/masm32/include/shell32.inc
include c:/masm32/include/masm32.inc
include c:/masm32/include/netapi32.inc
include c:/masm32/include/winmm.inc
include c:/masm32/include/ws2_32.inc
include c:/masm32/include/psapi.inc
include c:/masm32/include/mpr.inc ;WNetCancelConnection2
include c:/masm32/include/iphlpapi.inc ;SendARP
includelib c:/masm32/lib/user32.lib
includelib c:/masm32/lib/kernel32.lib
includelib c:/masm32/lib/comdlg32.lib
includelib c:/masm32/lib/comdlg32.lib
includelib c:/masm32/lib/gdi32.lib
includelib c:/masm32/lib/comctl32.lib
includelib c:/masm32/lib/comdlg32.lib
includelib c:/masm32/lib/advapi32.lib
includelib c:/masm32/lib/shell32.lib
includelib c:/masm32/lib/masm32.lib
includelib c:/masm32/lib/netapi32.lib
includelib c:/masm32/lib/winmm.lib
includelib c:/masm32/lib/ws2_32.lib
includelib c:/masm32/lib/psapi.lib
includelib c:/masm32/lib/mpr.lib
includelib c:/masm32/lib/iphlpapi.lib
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_INFO equ 1001
IDM_MAIN equ 2000
IDM_OPEN equ 2001
IDM_EXIT equ 2002
IDM_1 equ 4000
IDM_2 equ 4001
IDM_3 equ 4002
RESULT_MODULE equ 5000
ID_TEXT1 equ 5001
ID_TEXT2 equ 5002
IDC_MODULETABLE equ 5003
IDC_OK equ 5004
ID_STATIC equ 5005
ID_STATIC1 equ 5006
IDC_BROWSE1 equ 5007
IDC_BROWSE2 equ 5008
IDC_THESAME equ 5009
.data
hInstance dword ?
hRichEdit dword ?
hWinMain dword ?
hWinEdit dword ?
dwCount dword ?
dwColorRed dword ?
hText1 dword ?
hText2 dword ?
hFile dword ?
hProcessModuleTable dword ?
szFileName byte MAX_PATH dup(?)
szFileNameOpen1 byte MAX_PATH dup(0)
szFileNameOpen2 byte MAX_PATH dup(0)
szResultColName1 byte 'PE数据结构相关字段',0
szResultColName2 byte '文件1的值(H)',0
szResultColName3 byte '文件2的值(H)',0
szBuffer byte 256 dup(0), 0
bufTemp1 byte 200 dup(0), 0
bufTemp2 byte 200 dup(0), 0
szFilter1 byte 'Excutable Files',0,'*.exe;*.com',0
byte 0
.const
szDllEdit byte 'RichEd20.dll', 0
szClassEdit byte 'RichEdit20A', 0
szFont byte '宋体', 0
szExtPe byte 'PE File',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0
byte 'All Files(*.*)',0,'*.*',0,0
szErr byte '文件格式错误!',0
szErrFormat byte '这个文件不是PE格式的文件!',0
szSuccess byte '恭喜你,程序执行到这里是成功的。',0
szNotFound byte '无法查找',0
szRec1 byte 'IMAGE_DOS_HEADER.e_magic',0
szRec2 byte 'IMAGE_DOS_HEADER.e_cblp',0
szRec3 byte 'IMAGE_DOS_HEADER.e_cp',0
szRec4 byte 'IMAGE_DOS_HEADER.e_crlc',0
szRec5 byte 'IMAGE_DOS_HEADER.e_cparhdr',0
szRec6 byte 'IMAGE_DOS_HEADER.e_minalloc',0
szRec7 byte 'IMAGE_DOS_HEADER.e_maxalloc',0
szRec8 byte 'IMAGE_DOS_HEADER.e_ss',0
szRec9 byte 'IMAGE_DOS_HEADER.e_sp',0
szRec10 byte 'IMAGE_DOS_HEADER.e_csum',0
szRec11 byte 'IMAGE_DOS_HEADER.e_ip',0
szRec12 byte 'IMAGE_DOS_HEADER.e_cs',0
szRec13 byte 'IMAGE_DOS_HEADER.e_lfarlc',0
szRec14 byte 'IMAGE_DOS_HEADER.e_ovno',0
szRec15 byte 'IMAGE_DOS_HEADER.e_res',0
szRec16 byte 'IMAGE_DOS_HEADER.e_oemid',0
szRec17 byte 'IMAGE_DOS_HEADER.e_oeminfo',0
szRec18 byte 'IMAGE_DOS_HEADER.e_res2',0
szRec19 byte 'IMAGE_DOS_HEADER.e_lfanew',0
szRec20 byte 'IMAGE_NT_HEADERS.Signature',0
szRec21 byte 'IMAGE_FILE_HEADER.Machine',0
szRec22 byte 'IMAGE_FILE_HEADER.NumberOfSections',0
szRec23 byte 'IMAGE_FILE_HEADER.TimeDateStamp',0
szRec24 byte 'IMAGE_FILE_HEADER.PointerToSymbolTable',0
szRec25 byte 'IMAGE_FILE_HEADER.NumberOfSymbols',0
szRec26 byte 'IMAGE_FILE_HEADER.SizeOfOptionalHeader',0
szRec27 byte 'IMAGE_FILE_HEADER.Characteristics',0
szRec28 byte 'IMAGE_OPTIONAL_HEADER32.Magic',0
szRec29 byte 'IMAGE_OPTIONAL_HEADER32.MajorLinkerVersion',0
szRec30 byte 'IMAGE_OPTIONAL_HEADER32.MinorLinkerVersion',0
szRec31 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfCode',0
szRec32 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfInitializedData',0
szRec33 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData',0
szRec34 byte 'IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint',0
szRec35 byte 'IMAGE_OPTIONAL_HEADER32.BaseOfCode',0
szRec36 byte 'IMAGE_OPTIONAL_HEADER32.BaseOfData',0
szRec37 byte 'IMAGE_OPTIONAL_HEADER32.ImageBase',0
szRec38 byte 'IMAGE_OPTIONAL_HEADER32.SectionAlignment',0
szRec39 byte 'IMAGE_OPTIONAL_HEADER32.FileAlignment',0
szRec40 byte 'IMAGE_OPTIONAL_HEADER32.MajorOperatingSystemVersion',0
szRec41 byte 'IMAGE_OPTIONAL_HEADER32.MinorOperatingSystemVersion',0
szRec42 byte 'IMAGE_OPTIONAL_HEADER32.MajorImageVersion',0
szRec43 byte 'IMAGE_OPTIONAL_HEADER32.MinorImageVersion',0
szRec44 byte 'IMAGE_OPTIONAL_HEADER32.MajorSubsystemVersion',0
szRec45 byte 'IMAGE_OPTIONAL_HEADER32.MinorSubsystemVersion',0
szRec46 byte 'IMAGE_OPTIONAL_HEADER32.Win32VersionValue',0
szRec47 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfImage',0
szRec48 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfHeaders',0
szRec49 byte 'IMAGE_OPTIONAL_HEADER32.CheckSum',0
szRec50 byte 'IMAGE_OPTIONAL_HEADER32.Subsystem',0
szRec51 byte 'IMAGE_OPTIONAL_HEADER32.DllCharacteristics',0
szRec52 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfStackReserve',0
szRec53 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfStackCommit',0
szRec54 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfHeapReserve',0
szRec55 byte 'IMAGE_OPTIONAL_HEADER32.SizeOfHeapCommit',0
szRec56 byte 'IMAGE_OPTIONAL_HEADER32.LoaderFlags',0
szRec57 byte 'IMAGE_OPTIONAL_HEADER32.NumberOfRvaAndSizes',0
szRec58 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Export)',0
szRec59 byte 'IMAGE_DATA_DIRECTORY.isize(Export)',0
szRec60 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Import)',0
szRec61 byte 'IMAGE_DATA_DIRECTORY.isize(Import)',0
szRec62 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Resource)',0
szRec63 byte 'IMAGE_DATA_DIRECTORY.isize(Resource)',0
szRec64 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Exception)',0
szRec65 byte 'IMAGE_DATA_DIRECTORY.isize(Exception)',0
szRec66 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Security)',0
szRec67 byte 'IMAGE_DATA_DIRECTORY.isize(Security)',0
szRec68 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(BaseReloc)',0
szRec69 byte 'IMAGE_DATA_DIRECTORY.isize(BaseReloc)',0
szRec70 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Debug)',0
szRec71 byte 'IMAGE_DATA_DIRECTORY.isize(Debug)',0
szRec72 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Architecture)',0
szRec73 byte 'IMAGE_DATA_DIRECTORY.isize(Architecture)',0
szRec74 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(GlobalPTR)',0
szRec75 byte 'IMAGE_DATA_DIRECTORY.isize(GlobalPTR)',0
szRec76 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(TLS)',0
szRec77 byte 'IMAGE_DATA_DIRECTORY.isize(TLS)',0
szRec78 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Load_Config)',0
szRec79 byte 'IMAGE_DATA_DIRECTORY.isize(Load_Config)',0
szRec80 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Bound_Import)',0
szRec81 byte 'IMAGE_DATA_DIRECTORY.isize(Bound_Import)',0
szRec82 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(IAT)',0
szRec83 byte 'IMAGE_DATA_DIRECTORY.isize(IAT)',0
szRec84 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Delay_Import)',0
szRec85 byte 'IMAGE_DATA_DIRECTORY.isize(Delay_Import)',0
szRec86 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Com_Descriptor)',0
szRec87 byte 'IMAGE_DATA_DIRECTORY.isize(Com_Descriptor)',0
szRec88 byte 'IMAGE_DATA_DIRECTORY.VirtualAddress(Reserved)',0
szRec89 byte 'IMAGE_DATA_DIRECTORY.isize(Reserved)',0
szRec90 byte 'IMAGE_SECTION_HEADER%d.Name1',0
szRec91 byte 'IMAGE_SECTION_HEADER%d.VirtualSize',0
szRec92 byte 'IMAGE_SECTION_HEADER%d.VirtualAddress',0
szRec93 byte 'IMAGE_SECTION_HEADER%d.SizeOfRawData',0
szRec94 byte 'IMAGE_SECTION_HEADER%d.PointerToRawData',0
szRec95 byte 'IMAGE_SECTION_HEADER%d.PointerToRelocations',0
szRec96 byte 'IMAGE_SECTION_HEADER%d.PointerToLinenumbers',0
szRec97 byte 'IMAGE_SECTION_HEADER%d.NumberOfRelocations',0
szRec98 byte 'IMAGE_SECTION_HEADER%d.NumberOfLinenumbers',0
szRec99 byte 'IMAGE_SECTION_HEADER%d.Characteristics',0
szOut1 byte '%02x',0
szOut2 byte '%04x',0
lpszHexArr byte '0123456789ABCDEF',0
.data?
stLVC LV_COLUMN<?>
stLVI LV_ITEM<?>
.code
;初始化窗口程序
_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 _lpExceptionRecord, _lpSEH, \
_lpContext, _lpDispathcerContext
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
assume esi:nothing, edi:nothing
popad
mov eax, ExceptionContinueExecution
ret
_Handler endp
;-------------------------------
; 将内存偏移量RVA转换为文件偏移
;-------------------------------
_RVAToOffset proc _lpFileHead, _dwRVA
local @dwReturn
pushad
mov esi, _lpFileHead
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
mov edi, _dwRVA
mov edx, esi
add edx, sizeof IMAGE_NT_HEADERS
assume edx:ptr IMAGE_SECTION_HEADER
movzx ecx, [esi].FileHeader.NumberOfSections
;遍历节表
.repeat
mov eax, [edx].VirtualAddress
add eax, [edx].SizeOfRawData ;计算该节结束RVA 相对虚拟地址
.if (edi >= [edx].VirtualAddress) && (edi < eax)
mov eax, [edx].VirtualAddress
sub edi, eax ;计算RVA在节中的偏移
mov eax, [edx].PointerToRawData
add eax, edi ;加上节在文件中的的起始位置
jmp @F
.endif
add edx, sizeof IMAGE_SECTION_HEADER
.untilcxz
assume edx:nothing
assume esi:nothing
mov eax, -1
@@:
mov @dwReturn, eax
popad
mov eax, @dwReturn
ret
_RVAToOffset endp
;------------------------
; 获取RVA所在节的名称
;------------------------
_getRVASectionName proc _lpFileHead, _dwRVA
local @dwReturn
pushad
mov esi, _lpFileHead
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
mov edi, _dwRVA
mov edx, esi
add edx, sizeof IMAGE_NT_HEADERS
assume edx:ptr IMAGE_SECTION_HEADER
movzx ecx, [esi].FileHeader.NumberOfSections
;遍历节表
.repeat
mov eax, [edx].VirtualAddress
add eax,[edx].SizeOfRawData ;计算该节结束RVA
.if (edi>=[edx].VirtualAddress) && (edi < eax)
mov eax, edx
jmp @F
.endif
add edx, sizeof IMAGE_SECTION_HEADER
.untilcxz
assume edx:nothing
assume esi:nothing
mov eax, offset szNotFound
@@:
mov @dwReturn, eax
popad
mov eax, @dwReturn
ret
_getRVASectionName endp
;---------------------------------
;在ListView中增加一个列
;输入:_dwColumn = 增加的列编号
; _dwWidth = 列的宽度
; _lpszHead = 列的标题字符串
;---------------------------------
_ListViewAddColumn proc uses ebx ecx _hWinView, _dwColumn, _dwWidth, _lpszHead
local @stLVC:LV_COLUMN
invoke RtlZeroMemory, addr @stLVC, sizeof LV_COLUMN
mov @stLVC.imask, LVCF_TEXT or LVCF_WIDTH or LVCF_FMT
mov @stLVC.fmt, LVCFMT_LEFT
push _lpszHead
pop @stLVC.pszText
push _dwWidth
pop @stLVC.lx
push _dwColumn
pop @stLVC.iSubItem
invoke SendMessage, _hWinView, LVM_INSERTCOLUMN, _dwColumn, addr @stLVC
ret
_ListViewAddColumn endp
;----------------------------------------------------------------------
; 在ListView中新增一行,或修改一行中某个字段的内容
; 输入:_dwItem = 要修改的行的编号
; _dwSubItem = 要修改的字段的编号,-1表示插入新的行,>=1表示字段的编号
;-----------------------------------------------------------------------
_ListViewSetItem proc uses ebx ecx _hWinView, _dwItem, _dwSubItem, _lpszText
invoke RtlZeroMemory, addr stLVI, sizeof LV_ITEM
invoke lstrlen, _lpszText
mov stLVI.cchTextMax, eax
mov stLVI.imask, LVIF_TEXT
push _lpszText
pop stLVI.pszText
push _dwItem
pop stLVI.iItem
push _dwSubItem
pop stLVI.iSubItem
.if _dwSubItem == -1
mov stLVI.iSubItem, 0
invoke SendMessage, _hWinView, LVM_INSERTITEM, NULL, addr stLVI
.else
invoke SendMessage, _hWinView, LVM_SETITEM, NULL, addr stLVI
.endif
ret
_ListViewSetItem endp
;----------------------
; 清除ListView中的内容
; 删除所有的行和所有的列
;----------------------
_ListViewClear proc uses ebx ecx _hWinView
invoke SendMessage, _hWinView, LVM_DELETEALLITEMS, 0, 0
.while TRUE
invoke SendMessage, _hWinView, LVM_DELETECOLUMN, 0, 0
.break .if !eax
.endw
ret
_ListViewClear endp
;---------------------
; 返回指定行列的值
; 结果在szBuffer中
;---------------------
_GetListViewItem proc _hWinView:DWORD, _dwLine:DWORD, _dwCol:DWORD, _lpszText
local @stLVI:LV_ITEM
invoke RtlZeroMemory, addr @stLVI, sizeof LV_ITEM
invoke RtlZeroMemory, _lpszText, 512
mov @stLVI.cchTextMax, 512
mov @stLVI.imask, LVIF_TEXT
push _lpszText
pop @stLVI.pszText
push _dwCol
pop @stLVI.iSubItem
invoke SendMessage, _hWinView, LVM_GETITEMTEXT, _dwLine, addr @stLVI
ret
_GetListViewItem endp
;---------------------
; 初始化结果表格
;---------------------
_clearResultView proc uses ebx ecx
invoke _ListViewClear, hProcessModuleTable
;添加表头
mov ebx, 1
mov eax, 200
lea ecx, szResultColName1
invoke _ListViewAddColumn, hProcessModuleTable, ebx, eax, ecx
mov ebx, 2
mov eax, 400
lea ecx, szResultColName2
invoke _ListViewAddColumn, hProcessModuleTable, ebx, eax, ecx
mov ebx, 3
mov eax, 400
lea ecx, szResultColName3
invoke _ListViewAddColumn, hProcessModuleTable, ebx, eax, ecx
mov dwCount, 0
ret
_clearResultView endp
;------------------------------------------
; 打开输入文件
;------------------------------------------
_OpenFile1 proc
local @stOF:OPENFILENAME ;openfilename
local @stES:EDITSTREAM ;editstream
;如果打开之前还有文件句柄存在,则先关闭再赋值
.if hFile
invoke CloseHandle, hFile
mov hFile, 0
.endif
;显示"打开文件"对话框
invoke RtlZeroMemory, addr @stOF, sizeof @stOF
mov @stOF.lStructSize, sizeof @stOF
push hWinMain
pop @stOF.hwndOwner
push hInstance
pop @stOF.hInstance
mov @stOF.lpstrFilter, offset szFilter1
mov @stOF.lpstrFile, offset szFileNameOpen1
mov @stOF.nMaxFile, MAX_PATH
mov @stOF.Flags, OFN_FILEMUSTEXIST or \
OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
invoke GetOpenFileName, addr @stOF
.if eax
invoke SetWindowText, hText1, addr szFileNameOpen1
.endif
ret
_OpenFile1 endp
;------------------------------------------
; 打开输入文件
;------------------------------------------
_OpenFile2 proc
local @stOF:OPENFILENAME
local @stES:EDITSTREAM
;如果打开之前还有文件句柄存在,则先关闭再赋值
.if hFile
invoke CloseHandle, hFile
mov hFile, 0
.endif
;显示"打开文件"对话框
invoke RtlZeroMemory, addr @stOF, sizeof @stOF
mov @stOF.lStructSize, sizeof @stOF
push hWinMain
pop @stOF.hwndOwner
push hInstance
pop @stOF.hInstance
mov @stOF.lpstrFilter, offset szFilter1
mov @stOF.lpstrFile, offset szFileNameOpen2
mov @stOF.nMaxFile, MAX_PATH
mov @stOF.Flags, OFN_FILEMUSTEXIST or \
OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
invoke GetOpenFileName, addr @stOF
.if eax
invoke SetWindowText, hText2, addr szFileNameOpen2
.endif
ret
_OpenFile2 endp
;-------------------------------------------------
; 将_lpPoint位置处_dwSize个字节转换为16进制的字符串
; bufTemp1处为转换后的字符串
;-------------------------------------------------
_Byte2Hex proc _dwSize
local @dwSize:dword
pushad
mov esi, offset bufTemp2
mov edi, offset bufTemp1
mov @dwSize, 0
.repeat
mov al, byte ptr [esi]
mov bl, al
xor edx, edx
xor eax, eax
mov al, bl
mov cx, 16
div cx ;结果高位在al中,余数在dl中
xor bx, bx
mov bl, al
movzx edi, bx
mov bl, byte ptr lpszHexArr[edi]
mov eax, @dwSize
mov byte ptr bufTemp1[eax], bl
inc @dwSize
xor bx, bx
mov bl, dl
movzx edi, bx
;invoke wsprintf,addr szBuffer,addr szOut2,edx
;invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
mov bl, byte ptr lpszHexArr[edi]
mov eax, @dwSize
mov byte ptr bufTemp1[eax], bl
inc @dwSize
mov bl, 20h
mov eax, @dwSize
mov byte ptr bufTemp1[eax], bl
inc @dwSize
inc esi
dec _dwSize
.break .if _dwSize == 0
.until FALSE
mov bl, 0
mov eax, @dwSize
mov byte ptr bufTemp1[eax],bl
popad
ret
_Byte2Hex endp
_MemCmp proc _lp1, _lp2, _size
local @dwResult:dword
pushad
mov esi, _lp1
mov edi, _lp2
mov ecx, _size
.repeat
mov al, byte ptr [esi]
mov bl, byte ptr [edi]
.break .if al != bl
inc esi
inc edi
dec ecx
.break .if ecx == 0
.until FALSE
.if ecx != 0
mov @dwResult, 1
.else
mov @dwResult, 0
.endif
popad
mov eax, @dwResult
ret
_MemCmp endp
;--------------------------------------------
; 在表格中增加一行
; _lpSZ为第一行要显示的字段名
; _lpSP1为第一个文件该字段的位置
; _lpSP2为第二个文件该字段的位置
; _Size为该字段的字节长度
;--------------------------------------------
_addLine proc _lpSZ, _lpSP1, _lpSP2, _Size
pushad
invoke _ListViewSetItem, hProcessModuleTable, dwCount, -1, \
_lpSZ ;在表格中新增加一行
mov dwCount, eax
xor ebx, ebx
invoke _ListViewSetItem, hProcessModuleTable, dwCount, ebx, \
_lpSZ ;显示字段名
invoke RtlZeroMemory, addr szBuffer, 50
invoke MemCopy, _lpSP1, addr bufTemp2, _Size
invoke _Byte2Hex, _Size
;将指定字段按照十六进制显示,格式:一个字节+一个空格
invoke lstrcat, addr szBuffer, addr bufTemp1
inc ebx
invoke _ListViewSetItem, hProcessModuleTable, dwCount, ebx, \
addr szBuffer ;第一个文件中的值
invoke RtlZeroMemory, addr szBuffer, 50
invoke MemCopy, _lpSP2, addr bufTemp2, _Size
invoke _Byte2Hex, _Size
invoke lstrcat, addr szBuffer, addr bufTemp1
inc ebx
invoke _ListViewSetItem, hProcessModuleTable, dwCount, ebx, \
addr szBuffer ;第二个文件中的值
popad
ret
_addLine endp
;-----------------------
; IMAGE_DOS_HEADER头信息
;-----------------------
_Header1 proc
pushad
invoke _addLine, addr szRec1, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec2, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec3, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec4, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec5, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec6, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec7, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec8, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec9, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec10, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec11, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec12, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec13, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec14, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec15, esi, edi, 8
add esi, 8
add edi, 8
invoke _addLine, addr szRec16, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec17, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec18, esi, edi, 20
add esi,20
add edi,20
invoke _addLine,addr szRec19,esi,edi,4
popad
ret
_Header1 endp
;-----------------------
; IMAGE_DOS_HEADER头信息
;-----------------------
_Header2 proc
pushad
invoke _addLine, addr szRec20, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec21, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec22, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec23, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec24, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec25, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec26, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec27, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec28, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec29, esi, edi, 1
add esi, 1
add edi, 1
invoke _addLine, addr szRec30, esi, edi, 1
add esi, 1
add edi, 1
invoke _addLine, addr szRec31, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec32, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec33, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec34, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec35, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec36, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec37, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec38, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec39, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec40, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec41, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec42, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec43, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec44, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec45, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec46, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec47, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec48, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec49, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec50, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec51, esi, edi, 2
add esi, 2
add edi, 2
invoke _addLine, addr szRec52, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec53, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec54, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec55, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec56, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec57, esi, edi, 4
;IMAGE_DATA_DIRECTORY
add esi, 4
add edi, 4
invoke _addLine, addr szRec58, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec59, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec60, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec61, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec62, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec63, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec64, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec65, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec66, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec67, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec68, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec69, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec70, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec71, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec72, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec73, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec74, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec75, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec76, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec77, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec78, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec79, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec80, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec81, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec82, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec83, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec84, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec85, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec86, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec87, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec88, esi, edi, 4
add esi, 4
add edi, 4
invoke _addLine, addr szRec89, esi, edi, 4
popad
ret
_Header2 endp
;---------------------------------------
; 节表
; eax=节序号
;---------------------------------------
_Header3 proc
local _dwValue:dword
pushad
mov _dwValue, eax
invoke wsprintf, addr szBuffer, addr szRec90, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 8
add esi, 8
add edi, 8
invoke wsprintf, addr szBuffer, addr szRec91, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec92, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec93, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec94, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec95, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec96, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
add esi, 4
add edi, 4
invoke wsprintf, addr szBuffer, addr szRec97, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 2
add esi, 2
add edi, 2
invoke wsprintf, addr szBuffer, addr szRec98, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 2
add esi, 2
add edi, 2
invoke wsprintf, addr szBuffer, addr szRec99, _dwValue
invoke _addLine, addr szBuffer, esi, edi, 4
popad
ret
_Header3 endp
;_goHere
;--------------------
; 打开PE文件并处理
;--------------------
_openFile proc
local @stOF:OPENFILENAME
local @hFile, @dwFileSize, @hMapFile, @lpMemory
local @hFile1, @dwFileSize1, @hMapFile1, @lpMemory1
local @bufTemp1[10]:byte
local @dwTemp:dword
invoke CreateFile, addr szFileNameOpen1, 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 ;获得文件在内存的映象起始位置
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 ;判断是否有MZ字样
jmp _ErrFormat
.endif
add esi, [esi].e_lfanew ;调整ESI指针指向PE文件头
assume esi:ptr IMAGE_NT_HEADERS
.if [esi].Signature != IMAGE_NT_SIGNATURE ;判断是否有PE字样
jmp _ErrFormat
.endif
.endif
.endif
.endif
.endif
invoke CreateFile, addr szFileNameOpen2, GENERIC_READ, \
FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, \
OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL
.if eax != INVALID_HANDLE_VALUE
mov @hFile1, eax
invoke GetFileSize, eax, NULL
mov @dwFileSize1, eax
.if eax
invoke CreateFileMapping, @hFile1, \ ;内存映射文件
NULL, PAGE_READONLY, 0, 0, NULL
.if eax
mov @hMapFile1, eax
invoke MapViewOfFile, eax, \
FILE_MAP_READ, 0, 0, 0
.if eax
mov @lpMemory1, eax ;获得文件在内存的映象起始位置
assume fs:nothing
push ebp
push offset _ErrFormat1
push offset _Handler
push fs:[0]
mov fs:[0], esp
;检测PE文件是否有效
mov esi, @lpMemory1
assume esi:ptr IMAGE_DOS_HEADER
.if [esi].e_magic != IMAGE_DOS_SIGNATURE ;判断是否有MZ字样
jmp _ErrFormat1
.endif
add esi, [esi].e_lfanew ;调整ESI指针指向PE文件头
assume esi:ptr IMAGE_NT_HEADERS
.if [esi].Signature != IMAGE_NT_SIGNATURE ;判断是否有PE字样
jmp _ErrFormat1
.endif
.endif
.endif
.endif
.endif
;到此为止,两个内存文件的指针已经获取到了。
;@lpMemory和@lpMemory1分别指向两个文件头
;下面是从这个文件头开始,找出各数据结构的字段值,进行比较。
;调整ESI,EDI指向DOS头
mov esi, @lpMemory
assume esi:ptr IMAGE_DOS_HEADER
mov edi, @lpMemory1
assume edi:ptr IMAGE_DOS_HEADER
invoke _Header1
;调整ESI,EDI指针指向PE文件头
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
add edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
invoke _Header2
movzx ecx, word ptr [esi+6]
movzx eax, word ptr [edi+6]
.if eax > ecx
mov ecx, eax
.endif
;调整ESI,EDI指针指向节表
add esi, sizeof IMAGE_NT_HEADERS
add edi, sizeof IMAGE_NT_HEADERS
mov eax, 1
.repeat
invoke _Header3
dec ecx
inc eax
.break .if ecx == 0
add esi, sizeof IMAGE_SECTION_HEADER
add edi, sizeof IMAGE_SECTION_HEADER
.until FALSE
jmp _ErrorExit ;正常退出
_ErrFormat:
invoke MessageBox, hWinMain, offset szErrFormat, NULL, MB_OK
_ErrorExit:
pop fs:[0]
add esp, 0ch
invoke UnmapViewOfFile, @lpMemory
invoke CloseHandle, @hMapFile
invoke CloseHandle, @hFile
jmp @F
_ErrFormat1:
invoke MessageBox, hWinMain, offset szErrFormat, NULL, MB_OK
_ErrorExit1:
pop fs:[0]
add esp, 0ch
invoke UnmapViewOfFile, @lpMemory1
invoke CloseHandle, @hMapFile1
invoke CloseHandle, @hFile1
@@:
ret
_openFile endp
;-----------------------
; 弹出PE对比窗口回调函数
;----------------------
_resultProcMain proc uses ebx edi esi hProcessModuleDlg:HWND, wMsg, wParam, lParam
mov eax, wMsg
.if eax == WM_CLOSE
invoke EndDialog, hProcessModuleDlg, NULL
.elseif eax == WM_INITDIALOG
invoke GetDlgItem, hProcessModuleDlg, IDC_MODULETABLE
mov hProcessModuleTable, eax
invoke GetDlgItem, hProcessModuleDlg, ID_TEXT1
mov hText1, eax
invoke GetDlgItem, hProcessModuleDlg, ID_TEXT2
mov hText2, eax
;定义表格外观
invoke SendMessage, hProcessModuleTable, LVM_SETEXTENDEDLISTVIEWSTYLE, \
0, LVS_EX_GRIDLINES or LVS_EX_FULLROWSELECT
invoke ShowWindow, hProcessModuleTable, SW_SHOW
;清空表格内容
invoke _clearResultView
.elseif eax == WM_NOTIFY
mov eax, lParam
mov ebx, lParam
;更改各控件状态
mov eax, [eax+NMHDR.hwndFrom]
.if eax == hProcessModuleTable
mov ebx, lParam
.if [ebx+NMHDR.code] == NM_CUSTOMDRAW ;绘画时
mov ebx, lParam
assume ebx:ptr NMLVCUSTOMDRAW
.if [ebx].nmcd.dwDrawStage == CDDS_PREPAINT
invoke SetWindowLong, hProcessModuleDlg, DWL_MSGRESULT, \
CDRF_NOTIFYITEMDRAW
mov eax, TRUE
.elseif [ebx].nmcd.dwDrawStage == CDDS_ITEMPREPAINT
;当每一单元格内容预画时,判断
;两列的值是否一致
invoke _GetListViewItem, hProcessModuleTable, \
[ebx].nmcd.dwItemSpec, 1, addr bufTemp1
invoke _GetListViewItem, hProcessModuleTable, \
[ebx].nmcd.dwItemSpec, 2, addr bufTemp2
invoke lstrlen, addr bufTemp1
invoke _MemCmp, addr bufTemp1, addr bufTemp2, eax
;如果一致,则将文本的背景色设置为浅红色,否则黑色
.if eax == 1
mov [ebx].clrTextBk, 0a0a0ffh
.else
mov [ebx].clrTextBk, 0ffffffh
.endif
invoke SetWindowLong, hProcessModuleDlg, DWL_MSGRESULT, \
CDRF_DODEFAULT
mov eax, TRUE
.endif
.elseif [ebx+NMHDR.code] == NM_CLICK
assume ebx:ptr NMLISTVIEW
.endif
.endif
.elseif eax == WM_COMMAND
mov eax, wParam
.if ax == IDC_OK ;刷新
invoke _openFile
.elseif ax == IDC_BROWSE1
invoke _OpenFile1 ;用户选择第一个文件
.elseif ax == IDC_BROWSE2
invoke _OpenFile2 ;用户选择第二个文件
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
_resultProcMain 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 eax == IDM_EXIT ;退出
invoke EndDialog, hWnd, NULL
.elseif eax == IDM_OPEN ;打开PE对比对话框
invoke DialogBoxParam, hInstance, RESULT_MODULE, hWnd, \
offset _resultProcMain, 0
invoke InvalidateRect, hWnd, NULL, TRUE
invoke UpdateWindow, hWnd
.elseif eax == IDM_1
.elseif eax == IDM_2
.elseif eax == IDM_3
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
_ProcDlgMain endp
start:
invoke InitCommonControls
invoke LoadLibrary, offset szDllEdit
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
Makefile文件:
NAME = pecomp
OBJS = $(NAME).obj
RES = $(NAME).res
LINK_FLAG = /subsystem:windows
ML_FLAG = /c /coff
$(NAME).exe: $(OBJS) $(RES)
Link $(LINK_FLAG) $(OBJS) $(RES)
.asm.obj:
ml $(ML_FLAG) $<
.rc.res:
rc $<
clean:
del *.obj
del *.res
编译:

运行:

打开文件:

2.4 PEInfo的实现
PEInfo是PE文件结构查看器,它将PE中的字节码以形象的描述语言显示出来,塑造一个整体的PE形象。通过编写PEInfo,可以锻炼我们使用数据结构定位特定PE信息的能力。
2.4.1 编程思路
这个小工具开发起来也不难,只是过程复杂了一些而已,其编程思路如下:
步骤1 打开文件,判断是否为PE文件。
判断方法非常简单,首先查看IMAGE_DOS_HEADER. e_magic字段,然后查看IMAGE_NT_HEADER.Signature字段;如果符合PE文件定义,则视为合法PE文件。事实上,操作系统在装载PE文件时,对PE文件的检测远比此方法复杂得多。
步骤2 将指针定位到相关数据结构,获取字段内容并以更人性化的方式显示相关内容。
提示 由于我们还没有正式开始学习PE文件格式,而PEInfo编程中涉及PE头部的大量数据结构,所以该部分代码的阅读最好等学习完第3章以后再进行。
2.4.2 PEInfo编码
编写PEInfo不需要额外的资源文件,复制一份pe.rc到PEInfo.rc即可,源代码依然来自pe.asm。与pe.asm不同的是,我们需要在PEInfo.asm的窗口回调函数中,为菜单项"文件"|"打开"的消息响应代码加入调用_openFile函数的代码。如下所示:
.elseif eax==IDM_OPEN ;打开文件
call _openFile
下面来看_openFile函数和_getMainInfo函数。
1. _openFile函数
_openFile函数完成了显示PE结构的所有功能,该部分代码如代码清单2-8所示。
代码清单2-8 _openFile函数实现(chapter2\peinfo.asm)
;------------------------------
; 打开PE文件并处理
;------------------------------
_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.lpstrFile,offset szFileName
mov @stOF.nMaxFile,MAX_PATH
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
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
;判断是否有MZ字样
.if [esi].e_magic!=IMAGE_DOS_SIGNATURE
jmp _ErrFormat
.endif
;调整esi指针指向PE文件头
add esi,[esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
;判断是否有PE字样
.if [esi].Signature!=IMAGE_NT_SIGNATURE
jmp _ErrFormat
.endif
;到此为止,该文件的验证已经完成。是PE结构文件
;接下来分析文件映射到内存中的数据,并显示相关信息
invoke _getMainInfo,@lpMemory,esi,@dwFileSize
;显示导入表
invoke _getImportInfo,@lpMemory,esi,@dwFileSize
;显示导出表
invoke _getExportInfo,@lpMemory,esi,@dwFileSize
;显示重定位信息
invoke _getRelocInfo,@lpMemory,esi,@dwFileSize
;显示其他信息
jmp _ErrorExit
_ErrFormat:
invoke MessageBox,hWinMain,offset szErrFormat,\
NULL,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
第44~59行代码的功能是检测打开的文件是否符合PE标准。第61~69行的代码的功能是调用不同的函数显示PE的相关信息。例如,PE的主要信息的显示调用了函数_getMainInfo:
invoke _getMainInfo,@lpMemory,esi,@dwFileSize
2._getMainInfo函数
该函数接收三个参数:
❑ _lpFile (内存映射文件的起始地址)
❑ _lpPeHead (数据结构IMAGE_NT_HEADERS在内存中的起始位置)
❑ _dwSize (PE文件大小)
该函数获取PE文件的头部信息并显示, 由于实现很简单,就不再详细分析了,如代码清单2-9所示。
代码清单2-9 获取PE文件主要信息的函数_getMainInfo(chapter2\peinfo.asm)
;----------------------------------------------
; 从内存中获取PE文件的主要信息
;----------------------------------------------
_getMainInfo proc _lpFile,_lpPeHead,_dwSize
local @szBuffer[1024]:byte
local @szSecName[16]:byte
pushad
mov edi,_lpPeHead
assume edi:ptr IMAGE_NT_HEADERS
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 szMsgSec
movzx ecx,[edi].FileHeader.NumberOfSections
add edi,sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
.repeat
push ecx
;获取节的名称,注意:长度为8的名称,并且不以0结尾
invoke RtlZeroMemory,addr @szSecName,sizeof @szSecName
push esi
push edi
mov ecx,8
mov esi,edi
lea edi,@szSecName
cld
@@:
lodsb
.if !al ;如果名称为0,则显示为空格
mov al,' '
.endif
stosb
loop @B
pop edi
pop esi
;获取节的主要信息
invoke wsprintf,addr @szBuffer,addr szFmtSec,\
addr @szSecName,[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
_getMainInfo endp
其他信息(如导入表、导出表、资源表等)的显示代码将在后续的章节中详细介绍。
2.4.3 运行PEInfo
编译链接生成PEInfo.exe,然后运行。用该程序打开第1章生成的HelloWorld.exe程序,运行效果见图2-6。

图2-6 PEInfo运行效果
至此,三个小工具的开发工作就完成了,在后续的章节中,会陆续使用这些小工具完成对PE格式的分析和学习。
笔记:
peinfo.rc文件
#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
#define IDM_1 4000
#define IDM_2 4001
#define IDM_3 4002
#define IDM_4 4003
ICO_MAIN ICON "main.ico"
DLG_MAIN DIALOG 50,50,544,399
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "PEInfo by Scott"
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,540,396
END
IDM_MAIN menu discardable
BEGIN
POPUP "文件(&F)"
BEGIN
menuitem "打开文件(&O)...",IDM_OPEN
menuitem separator
menuitem "退出(&x)",IDM_EXIT
END
POPUP "编辑(&E)"
BEGIN
menuitem separator
END
POPUP "格式(&O)"
BEGIN
menuitem separator
END
POPUP "查看(&V)"
BEGIN
menuitem "源文件",IDM_1
menuitem "窗口透明度",IDM_2
menuitem separator
menuitem "大小",IDM_3
menuitem "宽度",IDM_4
END
POPUP "帮助(&H)"
BEGIN
menuitem separator
END
END
peinfo.asm文件
;peinfo.asm 通用程序框架
;使用 nmake 或下列命令进行编译和链接:
;ml -c -coff peinfo.asm
;rc -r peinfo.rc
;link -subsystem:windows peinfo.obj peinfo.res
.386
.model flat, stdcall
option casemap:none
include c:/masm32/include/windows.inc
include c:/masm32/include/user32.inc
includelib c:/masm32/lib/user32.lib
include c:/masm32/include/kernel32.inc
includelib c:/masm32/lib/kernel32.lib
include c:/masm32/include/comdlg32.inc
includelib c:/masm32/lib/comdlg32.lib
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_INFO equ 1001
IDM_MAIN equ 2000
IDM_OPEN equ 2001
IDM_EXIT equ 2002
IDM_1 equ 4000
IDM_2 equ 4001
IDM_3 equ 4002
.data
hInstance dword ?
hRichEdit dword ?
hWinMain dword ?
hWinEdit dword ?
szFileName byte MAX_PATH dup(?)
.const
szDllEdit byte 'RichEd20.dll', 0
szClassEdit byte 'RichEdit20A', 0
szFont byte '宋体', 0
szExtPe byte 'PE File',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0
byte 'All Files(*.*)',0,'*.*',0,0
szErr byte '文件格式错误!',0
szErrFormat byte '这个文件不是PE格式的文件!',0
szSuccess byte '恭喜你,程序执行到这里是成功的。',0
szNotFound byte '无法查找',0
szMsg byte '文件名:%s',0dh,0ah
byte '-----------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
byte '运行平台: 0x%04x (014c:Intel 386 014dh:Intel 486 014eh:Intel 586)',0dh,0ah
byte '节的数量: %d',0dh,0ah
byte '文件属性: 0x%04x (大尾-禁止多处理器-DLL-系统文件-禁止网络运行-禁止优盘运行-无调试-32位-小尾-X-X-X-无符号-无行-可执行-无重定位)',0dh,0ah
byte '建议装入基地址: 0x%08x',0dh,0ah
byte '文件执行入口(RVA地址): 0x%04x',0dh,0ah,0dh,0ah,0
szMsgSec byte '---------------------------------------------------------------------------------',0dh,0ah
byte '节的属性参考:',0dh,0ah
byte ' 00000020h 包含代码',0dh,0ah
byte ' 00000040h 包含已经初始化的数据,如.const',0dh,0ah
byte ' 00000080h 包含未初始化数据,如 .data?',0dh,0ah
byte ' 02000000h 数据在进程开始以后被丢弃,如.reloc',0dh,0ah
byte ' 04000000h 节中数据不经过缓存',0dh,0ah
byte ' 08000000h 节中数据不会被交换到磁盘',0dh,0ah
byte ' 10000000h 数据将被不同进程共享',0dh,0ah
byte ' 20000000h 可执行',0dh,0ah
byte ' 40000000h 可读',0dh,0ah
byte ' 80000000h 可写',0dh,0ah
byte '常见的代码节一般为:60000020h,数据节一般为:c0000040h,常量节一般为:40000040h',0dh,0ah
byte '---------------------------------------------------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
byte '节的名称 未对齐前真实长度 内存中的偏移(对齐后的) 文件中对齐后的长度 文件中的偏移 节的属性',0dh,0ah
byte '---------------------------------------------------------------------------------------------',0dh,0ah,0
szFmtSec byte '%s %08x %08x %08x %08x %08x',0dh,0ah,0dh,0ah,0dh,0ah,0
szMsg1 byte 0dh,0ah,0dh,0ah,0dh,0ah
byte '---------------------------------------------------------------------------------------------',0dh,0ah
byte '导入表所处的节:%s',0dh,0ah
byte '---------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgImport byte 0dh,0ah,0dh,0ah
byte '导入库:%s',0dh,0ah
byte '-----------------------------',0dh,0ah,0dh,0ah
byte 'OriginalFirstThunk %08x',0dh,0ah
byte 'TimeDateStamp %08x',0dh,0ah
byte 'ForwarderChain %08x',0dh,0ah
byte 'FirstThunk %08x',0dh,0ah
byte '-----------------------------',0dh,0ah,0dh,0ah,0
szMsg2 byte '%08u %s',0dh,0ah,0
szMsg3 byte '%08u(无函数名,按序号导入)',0dh,0ah,0
szErrNoImport byte 0dh,0ah,0dh,0ah
byte '未发现该文件有导入函数',0dh,0ah,0dh,0ah,0
szMsgExport byte 0dh,0ah,0dh,0ah,0dh,0ah
byte '---------------------------------------------------------------------------------------------',0dh,0ah
byte '导出表所处的节:%s',0dh,0ah
byte '---------------------------------------------------------------------------------------------',0dh,0ah
byte '原始文件名:%s',0dh,0ah
byte 'nBase %08x',0dh,0ah
byte 'NumberOfFunctions %08x',0dh,0ah
byte 'NuberOfNames %08x',0dh,0ah
byte 'AddressOfFunctions %08x',0dh,0ah
byte 'AddressOfNames %08x',0dh,0ah
byte 'AddressOfNameOrd %08x',0dh,0ah
byte '-------------------------------------',0dh,0ah,0dh,0ah
byte '导出序号 虚拟地址 导出函数名称',0dh,0ah
byte '-------------------------------------',0dh,0ah,0
szMsg4 byte '%08x %08x %s',0dh,0ah,0
szExportByOrd byte '(按照序号导出)',0
szErrNoExport byte 0dh,0ah,0dh,0ah
byte '未发现该文件有导出函数',0dh,0ah,0dh,0ah,0
szMsgReloc1 byte 0dh,0ah,'重定位表所处的节:%s',0dh,0ah,0
szMsgReloc2 byte 0dh,0ah
byte '--------------------------------------------------------------------------------------------',0dh,0ah
byte '重定位基地址: %08x',0dh,0ah
byte '重定位项数量: %d',0dh,0ah
byte '--------------------------------------------------------------------------------------------',0dh,0ah
byte '需要重定位的地址列表(ffffffff表示对齐用,不需要重定位)',0dh,0ah
byte '--------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgReloc3 byte '%08x ',0
szCrLf byte 0dh,0ah,0
szMsgReloc4 byte 0dh,0ah,'未发现该文件有重定位信息.',0dh,0ah,0
.code
;初始化窗口程序
_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 _lpExceptionRecord, _lpSEH, \
_lpContext, _lpDispathcerContext
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
assume esi:nothing, edi:nothing
popad
mov eax, ExceptionContinueExecution
ret
_Handler endp
;---------------------------------
; 将内存偏移量RVA转换为文件偏移
; lp_FileHead为文件头的起始地址
; _dwRVA为给定的RVA地址
;---------------------------------
_RVAToOffset proc _lpFileHead, _dwRVA
local @dwReturn
pushad
mov esi, _lpFileHead
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
mov edi, _dwRVA
mov edx, esi
add edx, sizeof IMAGE_NT_HEADERS
assume edx:ptr IMAGE_SECTION_HEADER
movzx ecx, [esi].FileHeader.NumberOfSections
;遍历节表
.repeat
mov eax, [edx].VirtualAddress
;计算该节结束RVA,不用Misc的主要原因是有些段的Misc值是错误的!
add eax, [edx].SizeOfRawData
.if (edi >= [edx].VirtualAddress) && (edi < eax)
mov eax, [edx].VirtualAddress
;计算RVA在节中的偏移
sub edi, eax
mov eax, [edx].PointerToRawData
;加上节在文件中的的起始位置
add eax, edi
jmp @F
.endif
add edx, sizeof IMAGE_SECTION_HEADER
.untilcxz
assume edx:nothing
assume esi:nothing
mov eax, -1
@@:
mov @dwReturn, eax
popad
mov eax, @dwReturn
ret
_RVAToOffset endp
;-------------------------------------------
; 将距离文件头的文件偏移转换为内存偏移量RVA
; lp_FileHead为文件头的起始地址
; _dwOffset为给定的文件偏移地址
;-------------------------------------------
_OffsetToRVA proc _lpFileHead, _dwOffset
local @dwReturn
pushad
mov esi, _lpFileHead
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
mov edi, _dwOffset
mov edx, esi
add edx, sizeof IMAGE_NT_HEADERS
assume edx:ptr IMAGE_SECTION_HEADER
movzx ecx, [esi].FileHeader.NumberOfSections
;遍历节表
.repeat
mov eax, [edx].PointerToRawData
;计算该节结束RVA,不用Misc的主要原因是有些段的Misc值是错误的!
add eax, [edx].SizeOfRawData
.if (edi >= [edx].PointerToRawData) && (edi < eax)
mov eax, [edx].PointerToRawData
;计算RVA在节中的偏移
sub edi, eax
mov eax, [edx].VirtualAddress
;加上节在文件中的的起始位置
add eax, edi
jmp @F
.endif
add edx, sizeof IMAGE_SECTION_HEADER
.untilcxz
assume edx:nothing
assume esi:nothing
mov eax, -1
@@:
mov @dwReturn, eax
popad
mov eax, @dwReturn
ret
_OffsetToRVA endp
;------------------------
; 获取RVA所在节的名称
;------------------------
_getRVASectionName proc _lpFileHead, _dwRVA
local @dwReturn
pushad
mov esi, _lpFileHead
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
mov edi, _dwRVA
mov edx, esi
add edx, sizeof IMAGE_NT_HEADERS
assume edx:ptr IMAGE_SECTION_HEADER
movzx ecx, [esi].FileHeader.NumberOfSections
;遍历节表
.repeat
mov eax, [edx].VirtualAddress
add eax, [edx].SizeOfRawData ;计算该节结束RVA
.if (edi >= [edx].VirtualAddress) && (edi < eax)
mov eax, edx
jmp @F
.endif
add edx, sizeof IMAGE_SECTION_HEADER
.untilcxz
assume edx:nothing
assume esi:nothing
mov eax, offset szNotFound
@@:
mov @dwReturn,eax
popad
mov eax, @dwReturn
ret
_getRVASectionName endp
;-------------------------------
; 获取指定字符串的API函数的调用地址
; 入口参数:_hModule为动态链接库的基址,_lpApi为API函数名的首址
; 出口参数:eax为函数在虚拟地址空间中的真实地址
;-------------------------------
_getApi proc _hModule, _lpApi
local @ret
local @dwLen
pushad
mov @ret, 0
;计算API字符串的长度,含最后的零
mov edi, _lpApi
mov ecx, -1
xor al, al
cld
repnz scasb
mov ecx, edi
sub ecx, _lpApi
mov @dwLen, ecx
;从pe文件头的数据目录获取导出表地址
mov esi, _hModule
add esi, [esi+3ch]
assume esi:ptr IMAGE_NT_HEADERS
mov esi, [esi].OptionalHeader.DataDirectory.VirtualAddress
add esi, _hModule
assume esi:ptr IMAGE_EXPORT_DIRECTORY
;查找符合名称的导出函数名
mov ebx, [esi].AddressOfNames
add ebx, _hModule
xor edx, edx
.repeat
push esi
mov edi, [ebx]
add edi, _hModule
mov esi, _lpApi
mov ecx, @dwLen
repz cmpsb
.if ZERO?
pop esi
jmp @F
.endif
pop esi
add ebx, 4
inc edx
.until edx >= [esi].NumberOfNames
jmp _ret
@@:
;通过API名称索引获取序号索引再获取地址索引
sub ebx, [esi].AddressOfNames
sub ebx, _hModule
shr ebx, 1
add ebx, [esi].AddressOfNameOrdinals
add ebx, _hModule
movzx eax, word ptr [ebx]
shl eax, 2
add eax, [esi].AddressOfFunctions
add eax, _hModule
;从地址表得到导出函数的地址
mov eax, [eax]
add eax, _hModule
mov @ret, eax
_ret:
assume esi:nothing
popad
mov eax, @ret
ret
_getApi endp
;---------------------
; 往文本框中追加文本
;---------------------
_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
invoke SendMessage, hWinEdit, EM_REPLACESEL, FALSE, _lpsz
popad
ret
_appendInfo endp
;--------------------
; 从内存中获取PE文件的主要信息
;--------------------
_getMainInfo proc _lpFile, _lpPeHead, _dwSize
local @szBuffer[1024]:byte
local @szSecName[16]:byte
pushad
mov edi, _lpPeHead
assume edi:ptr IMAGE_NT_HEADERS
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 szMsgSec
movzx ecx, [edi].FileHeader.NumberOfSections
add edi, sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
.repeat
push ecx
;获取节的名称,注意长度为8的名称并不以0结尾
invoke RtlZeroMemory, addr @szSecName, sizeof @szSecName
push esi
push edi
mov ecx, 8
mov esi, edi
lea edi, @szSecName
cld
@@:
lodsb ;从内存中加载一个字节到 AL 寄存器的指令,同时它会自动更新源指针(通常是 ESI 寄存器)。
.if !al ;如果名称为0,则显示为空格
mov al, ' '
.endif
stosb
loop @B
pop edi
pop esi
;获取节的主要信息
invoke wsprintf, addr @szBuffer, addr szFmtSec, \
addr @szSecName, [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
_getMainInfo endp
;--------------------
; 获取PE文件的导入表
;--------------------
_getImportInfo proc _lpFile, _lpPeHead, _dwSize
local @szBuffer[1024]:byte
local @szSectionName[16]:byte
pushad
mov edi, _lpPeHead
assume edi:ptr IMAGE_NT_HEADERS
mov eax, [edi].OptionalHeader.DataDirectory[8].VirtualAddress
.if !eax
invoke _appendInfo, addr szErrNoImport
jmp _Ret
.endif
invoke _RVAToOffset, _lpFile, eax
add eax, _lpFile
mov edi, eax ;计算引入表所在文件偏移位置
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
invoke _getRVASectionName, _lpFile, [edi].OriginalFirstThunk
invoke wsprintf, addr @szBuffer, addr szMsg1, eax ;显示节名
invoke _appendInfo, addr @szBuffer
.while [edi].OriginalFirstThunk || [edi].TimeDateStamp || \
[edi].ForwarderChain || [edi].Name1 || \
[edi].FirstThunk
invoke _RVAToOffset, _lpFile, [edi].Name1
add eax, _lpFile
invoke wsprintf, addr @szBuffer, addr szMsgImport, eax, \
[edi].OriginalFirstThunk, [edi].TimeDateStamp, \
[edi].ForwarderChain, [edi].FirstThunk
invoke _appendInfo, addr @szBuffer
;获取IMAGE_THUNK_DATA列表到EBX
.if [edi].OriginalFirstThunk
mov eax, [edi].OriginalFirstThunk
.else
mov eax, [edi].FirstThunk
.endif
invoke _RVAToOffset, _lpFile, eax
add eax, _lpFile
mov ebx, eax
.while dword ptr [ebx]
;按序号导入
.if dword ptr [ebx] & IMAGE_ORDINAL_FLAG32
mov eax, dword ptr [ebx]
and eax, 0ffffh
invoke wsprintf, addr @szBuffer, addr szMsg3, eax
.else ;按名称导入
invoke _RVAToOffset, _lpFile, dword ptr [ebx]
add eax, _lpFile
assume eax:ptr IMAGE_IMPORT_BY_NAME
movzx ecx, [eax].Hint
invoke wsprintf, addr @szBuffer, \
addr szMsg2, ecx, addr [eax].Name1
assume eax:nothing
.endif
invoke _appendInfo, addr @szBuffer
add ebx, 4
.endw
add edi, sizeof IMAGE_IMPORT_DESCRIPTOR
.endw
_Ret:
assume edi:nothing
popad
ret
_getImportInfo endp
;--------------------
; 获取PE文件的导出表
;--------------------
_getExportInfo proc _lpFile, _lpPeHead, _dwSize
local @szBuffer[1024]:byte
local @szSectionName[16]:byte
local @lpAddressOfNames, @dwIndex, @lpAddressOfNameOrdinals
pushad
mov esi, _lpPeHead
assume esi:ptr IMAGE_NT_HEADERS
mov eax, [esi].OptionalHeader.DataDirectory[0].VirtualAddress
.if !eax
invoke _appendInfo, addr szErrNoExport
jmp _Ret
.endif
invoke _RVAToOffset, _lpFile, eax
add eax, _lpFile
mov edi, eax ;计算导出表所在文件偏移位置
assume edi:ptr IMAGE_EXPORT_DIRECTORY
invoke _RVAToOffset, _lpFile, [edi].nName
add eax, _lpFile
mov ecx, eax
invoke _getRVASectionName, _lpFile, [edi].nName
invoke wsprintf, addr @szBuffer, addr szMsgExport, \
eax, ecx, [edi].nBase, [edi].NumberOfFunctions, \
[edi].NumberOfNames, [edi].AddressOfFunctions, \
[edi].AddressOfNames, [edi].AddressOfNameOrdinals
invoke _appendInfo, addr @szBuffer
invoke _RVAToOffset, _lpFile, [edi].AddressOfNames
add eax, _lpFile
mov @lpAddressOfNames, eax
invoke _RVAToOffset, _lpFile, [edi].AddressOfNameOrdinals
add eax, _lpFile
mov @lpAddressOfNameOrdinals, eax
invoke _RVAToOffset, _lpFile, [edi].AddressOfFunctions
add eax, _lpFile
mov esi, eax ;函数的地址表
mov ecx, [edi].NumberOfFunctions
mov @dwIndex, 0
@@:
pushad
mov eax, @dwIndex
push edi
mov ecx, [edi].NumberOfNames
cld
mov edi, @lpAddressOfNameOrdinals
repnz scasw
.if ZERO? ;找到函数名称
sub edi, @lpAddressOfNameOrdinals
sub edi, 2
shl edi, 1
add edi, @lpAddressOfNames
invoke _RVAToOffset, _lpFile, dword ptr[edi]
add eax, _lpFile
.else
mov eax, offset szExportByOrd
.endif
pop edi
;序号在ecx中
mov ecx, @dwIndex
add ecx, [edi].nBase
invoke wsprintf, addr @szBuffer, addr szMsg4, \
ecx, dword ptr [esi], eax
invoke _appendInfo, addr @szBuffer
popad
add esi, 4
inc @dwIndex
loop @B
_Ret:
assume esi:nothing
assume edi:nothing
popad
ret
_getExportInfo endp
;-----------------------
; 获取PE文件的重定位信息
;-----------------------
_getRelocInfo proc _lpFile, _lpPeHead, _dwSize
local @szBuffer[1024]:byte
local @szSectionName[16]:byte
pushad
mov esi, _lpPeHead
assume esi:ptr IMAGE_NT_HEADERS
mov eax, [esi].OptionalHeader.DataDirectory[8*5].VirtualAddress
.if !eax
invoke _appendInfo, addr szMsgReloc4
jmp _ret
.endif
push eax
invoke _RVAToOffset, _lpFile, eax
add eax, _lpFile
mov esi, eax
pop eax
invoke _getRVASectionName, _lpFile, eax
invoke wsprintf, addr @szBuffer, addr szMsgReloc1, eax
invoke _appendInfo, addr @szBuffer
assume esi:ptr IMAGE_BASE_RELOCATION
;循环处理每个重定位块
.while [esi].VirtualAddress
cld
lodsd ;eax=[esi].VirtualAddress
mov ebx, eax
lodsd ;eax=[esi].SizeofBlock
sub eax, sizeof IMAGE_BASE_RELOCATION ;块总长度-两个dd
shr eax, 1 ;然后除以2,得到重定位项数量
;除以2是因为重定位项是word
push eax
invoke wsprintf, addr @szBuffer, addr szMsgReloc2, ebx, eax
invoke _appendInfo, addr @szBuffer
pop ecx ;重定位项数量
xor edi, edi
.repeat
push ecx
lodsw
mov cx, ax
and cx, 0f000h ;得到高四位
.if cx == 03000h ;重定位地址指向的双字的32位都需要休正
and ax, 0fffh
movzx eax, ax
add eax, ebx ;得到修正以前的偏移,
;该偏移加上装入时的基址就是绝对地址
.else ;该重定位项无意义,仅用来作为对齐
mov eax, -1
.endif
invoke wsprintf, addr @szBuffer, addr szMsgReloc3, eax
inc edi
.if edi == 8 ;每显示8个项目换行
invoke lstrcat, addr @szBuffer, addr szCrLf
xor edi, edi
.endif
invoke _appendInfo, addr @szBuffer
pop ecx
.untilcxz
.if edi
invoke _appendInfo, addr szCrLf
.endif
.endw
_ret:
assume esi:nothing
popad
ret
_getRelocInfo endp
;--------------------
; 打开PE文件并处理
;--------------------
_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.lpstrFile, offset szFileName
mov @stOF.nMaxFile, MAX_PATH
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
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
;判断是否有MZ字样
.if [esi].e_magic != IMAGE_DOS_SIGNATURE
jmp _ErrFormat
.endif
;调整ESI指针指向PE文件头
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
;判断是否有PE字样
.if [esi].Signature != IMAGE_NT_SIGNATURE
jmp _ErrFormat
.endif
;到此为止,该文件的验证已经完成。为PE结构文件
;接下来分析分件映射到内存中的数据,并显示主要参数
invoke _getMainInfo, @lpMemory, esi, @dwFileSize
;显示导入表
invoke _getImportInfo, @lpMemory, esi, @dwFileSize
;显示导出表
invoke _getExportInfo, @lpMemory, esi, @dwFileSize
;显示重定位信息
invoke _getRelocInfo, @lpMemory, esi, @dwFileSize
jmp _ErrorExit
_ErrFormat:
invoke MessageBox, hWinMain, offset szErrFormat, \
NULL, 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 eax == IDM_EXIT ;退出
invoke EndDialog, hWnd, NULL
.elseif eax == IDM_OPEN ;打开文件
call _openFile
.elseif eax == IDM_1
invoke MessageBox, NULL, offset szErrFormat, offset szErr, MB_ICONWARNING
.elseif eax == IDM_2
invoke MessageBox, NULL, offset szErrFormat, offset szErr, MB_ICONQUESTION
.elseif eax == IDM_3
invoke MessageBox, NULL, offset szErrFormat, offset szErr, MB_YESNOCANCEL
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
_ProcDlgMain endp
start:
invoke LoadLibrary, offset szDllEdit
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
Makefile文件
NAME = peinfo
OBJS = $(NAME).obj
RES = $(NAME).res
LINK_FLAG = /subsystem:windows
ML_FLAG = /c /coff
$(NAME).exe: $(OBJS) $(RES)
Link $(LINK_FLAG) $(OBJS) $(RES)
.asm.obj:
ml $(ML_FLAG) $<
.rc.res:
rc $<
clean:
del *.obj
del *.res
编译:

运行:


2.5 小结
本章主要学习了如何通过汇编语言来编写基于PE操作的三个小工具,在后面对PE文件的分析中会经常使用这三个小工具。后面会讲到如何利用PEInfo遍历PE文件的导入表和导出表,那时还会用到本章的源代码。大家也可以对这些小工具进行扩展或整合,编写属于自己的PE分析工具。
值得一提的是,随编译器分发的可执行程序中有一个基于命令行的PE文件结构分析工具dumpbin.exe,它是公认的最好的PE分析工具之一,如果你喜欢,也可以用它来代替我们的小程序。