第五章 导出表
typedef struct _IMAGE_NT_HEADERS
{
+00h DWORD Signature
+04h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS, *PIMAGE_NT_HEADERS32;
IMAGE_NT_HEADERS=4BYTEPE标识符 + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER32 总共240字节 = 4 + 20 + FileHeader.SizeOfOptionalHeader
Windows加载器将与进程相关的DLL加载到虚拟地址空间以后,会根据导入表中登记的与该动态链接库相关的由INT指向的名称或编号来编译DLL所在虚拟地址空间,
通过函数名或编号查找导出表结构,从而确定该导出函数在虚拟地址空间中起始地址VA,并将该VA覆盖导入表的IAT相关项。
IMAGE_EXPORT_DIRECTORY STRUCT{
Characteristics DWORD ?
TimeDateStamp DWORD ?
MajorVersion WORD ?
MinorVersion WORD ?
nName DWORD ? ;000ch - 指向该导出表的文件名字字符串
nBase DWORD ? ;10导出函数的起始序号
NumberOfFunctions DWORD ? ;14所有的导出函数个数
NumberOfNames DWORD ? ;18以函数名导出的函数个数
AddressOfFuctions DWORD ? ;1c导出函数地址表RVA 指向全部导出函数的入口地址的起始
AddressOfNames DWORD ? ;20函数名称地址表RVA
AddressOfNameOrdinal DWORD ? ;24函数序号地址表 AddressOfNameOrdinals指向的是单字数组
}
AddressOfFuctions DWORD ? ;导出函数地址表RVA 指向全部导出函数的入口地址的起始
nBase 并不是从0开始
AddressOfName 指向的位置是一连串的双字值,这些值指向对应定义了函数名的函数的字符串地址。
AddressOfNameOrdinal 与AddressOfName一一对应,指向AddressOfName中对应函数在AddressOfNameOrdinal的索引值 分别的偏移相等
编写DLL
1.winResult.asm // ml /c /coff winResult.asm
2.winResult.def
// link -dll -subsystem:widnows -def:winResult.def winResult.obj
上面生成 winResult.dll winResult.lib
使用上面的DLL
1.winResult.inc
现在就可以用1.winResult.inc;2. winResult.dll winResult.lib
2.FistWindow.asm
//include winResult.inc
//includelib winResult.lib
操作导出表内容可以覆盖、调整导出函数,还可以增加导出信息来导出dll中出导出的私有数据。
;------------------------
; DLL动态链接库
; 提供了几个窗口效果
; 戚利
; 2010.6.27
;------------------------
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
MAX_XYSTEPS equ 50
DELAY_VALUE equ 50 ; 动画效果使用的步长
X_STEP_SIZE equ 10
Y_STEP_SIZE equ 9
X_START_SIZE equ 20
Y_START_SIZE equ 10
LMA_ALPHA equ 2
LMA_COLORKEY equ 1
WS_EX_LAYERED equ 80000h
;数据段
.data
dwCount dd ?
Value dd ?
Xsize dd ?
Ysize dd ?
sWth dd ?
sHth dd ?
Xplace dd ?
Yplace dd ?
counts dd ?
pSLWA dd ?
User32 db 'user32.dll',0
SLWA db 'SetLayeredWindowAttributes',0
;代码段
.code
;------------------
; DLL入口
;------------------
DllEntry proc _hInstance,_dwReason,_dwReserved
mov eax,TRUE
ret
DllEntry endp
;-------------------------------
; 私有函数
;-------------------------------
TopXY proc wDim:DWORD,sDim:DWORD
shr sDim,1
shr wDim,1
mov eax,wDim
sub sDim,eax
mov eax,sDim
ret
TopXY endp
;-----------------------------------------------------------
; 窗口抖动进入效果
;-----------------------------------------------------------
AnimateOpen proc hWin:DWORD
LOCAL Rct:RECT
invoke GetWindowRect,hWin,ADDR Rct
mov Xsize,X_START_SIZE
mov Ysize,Y_START_SIZE
invoke GetSystemMetrics,SM_CXSCREEN
mov sWth,eax
invoke TopXY,Xsize,eax
mov Xplace,eax
invoke GetSystemMetrics,SM_CYSCREEN
mov sHth,eax
invoke TopXY,Ysize,eax
mov Yplace,eax
mov counts,MAX_XYSTEPS
aniloop:
invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,FALSE
invoke ShowWindow,hWin,SW_SHOWNA
invoke Sleep,DELAY_VALUE
invoke ShowWindow,hWin,SW_HIDE
add Xsize,X_STEP_SIZE
add Ysize,Y_STEP_SIZE
invoke TopXY,Xsize,sWth
mov Xplace,eax
invoke TopXY,Ysize,sHth
mov Yplace,eax
dec counts
jnz aniloop
mov eax,Rct.left
mov ecx,Rct.right
sub ecx,eax
mov Xsize,ecx
mov eax,Rct.top
mov ecx,Rct.bottom
sub ecx,eax
mov Ysize,ecx
invoke TopXY,Xsize,sWth
mov Xplace,eax
invoke TopXY,Ysize,sHth
mov Yplace,eax
invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,TRUE
invoke ShowWindow,hWin,SW_SHOW
ret
AnimateOpen endp
;-------------------------
; 窗口抖动退出效果
;-------------------------
AnimateClose proc hWin:DWORD
LOCAL Rct:RECT
invoke ShowWindow,hWin,SW_HIDE
invoke GetWindowRect,hWin,ADDR Rct
mov eax,Rct.left
mov ecx,Rct.right
sub ecx,eax
mov Xsize,ecx
mov eax,Rct.top
mov ecx,Rct.bottom
sub ecx,eax
mov Ysize,ecx
invoke GetSystemMetrics,SM_CXSCREEN
mov sWth,eax
invoke TopXY,Xsize,eax
mov Xplace,eax
invoke GetSystemMetrics,SM_CYSCREEN
mov sHth,eax
invoke TopXY,Ysize,eax
mov Yplace,eax
mov counts,MAX_XYSTEPS
aniloop:
invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,FALSE
invoke ShowWindow,hWin,SW_SHOWNA
invoke Sleep,DELAY_VALUE
invoke ShowWindow,hWin,SW_HIDE
sub Xsize,X_STEP_SIZE
sub Ysize,Y_STEP_SIZE
invoke TopXY,Xsize,sWth
mov Xplace,eax
invoke TopXY,Ysize,sHth
mov Yplace,eax
dec counts
jnz aniloop
ret
AnimateClose endp
;--------------------------------------------
; 窗口淡入效果,仅运行在2000/XP以上操作系统
;--------------------------------------------
FadeInOpen proc hWin:DWORD
invoke GetWindowLongA,hWin,GWL_EXSTYLE
or eax,WS_EX_LAYERED
invoke SetWindowLongA,hWin,GWL_EXSTYLE,eax
invoke GetModuleHandleA,ADDR User32
invoke GetProcAddress,eax,ADDR SLWA
mov pSLWA,eax
push LMA_ALPHA
push 0
push 0
push hWin
call pSLWA
mov Value,90
invoke ShowWindow,hWin,SW_SHOWNA
doloop:
push LMA_COLORKEY + LMA_ALPHA
push Value
push Value
push hWin
call pSLWA
invoke Sleep,DELAY_VALUE
add Value,15
cmp Value,255
jne doloop
push LMA_ALPHA
push 255
push 0
push hWin
call pSLWA
ret
FadeInOpen endp
;--------------------------------------------
; 窗口淡出效果,仅运行在2000/XP以上操作系统
;--------------------------------------------
FadeOutClose proc hWin:DWORD
invoke GetWindowLongA,hWin,GWL_EXSTYLE
or eax,WS_EX_LAYERED
invoke SetWindowLongA,hWin,GWL_EXSTYLE,eax
invoke GetModuleHandleA,ADDR User32
invoke GetProcAddress,eax,ADDR SLWA
mov pSLWA,eax
push LMA_ALPHA
push 255
push 0
push hWin
call pSLWA
mov Value,255
doloop:
push LMA_COLORKEY + LMA_ALPHA
push Value
push Value
push hWin
call pSLWA
invoke Sleep,DELAY_VALUE
sub Value,15
cmp Value,0
jne doloop
ret
FadeOutClose endp
End DllEntry
//inc file
AnimateOpen proto :dword
AnimateClose proto :dword
FadeInOpen proto :dword
FadeOutClose proto :dword
;------------------------
.386
.model flat,stdcall
option casemap:none
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include winResult.inc
includelib winResult.lib
;数据段
.data?
hInstance dd ?
hWinMain dd ?
;常量定义
.const
szClassName db 'MyClass',0
szCaptionMain db '窗口特效演示',0
szText db '你好,认识我吗?^_^',0
;代码段
.code
;------------------
; 窗口消息处理子程序
;------------------
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
local @stPs:PAINTSTRUCT
local @stRect:RECT
local @hDc
mov eax,uMsg
.if eax==WM_PAINT
invoke BeginPaint,hWnd,addr @stPs
mov @hDc,eax
invoke GetClientRect,hWnd,addr @stRect
invoke DrawText,@hDc,addr szText,-1,\
addr @stRect,\
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd,addr @stPs
.elseif eax==WM_CLOSE ;关闭窗口
invoke FadeOutClose,hWinMain
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
_ProcWinMain endp
;----------------------
; 主窗口程序
;----------------------
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;注册窗口类
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW+1
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
;建立并显示窗口
invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
offset szClassName,offset szCaptionMain,\
WS_OVERLAPPEDWINDOW,\
100,100,600,400,\
NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke FadeInOpen,hWinMain
;invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain ;更新客户区,即发送WM_PAINT消息
;消息循环
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax==0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
start:
call _WinMain
invoke ExitProcess,NULL
end start
;-------------------------------
; 获取指定字符串的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 ;set DF=0(向高地址增长) std is set DF=1
repnz scasb ;scasb 在edi向高地址增长方向找al字符,找到设置ZF=1
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 ;cmpsb是用 DS:[SI] 所指的字节单元内容,减去 ES:[DI] 所指的字节单元的内容。
.if ZERO?
pop esi ;//if esi="fun" edi="func"感觉要比较下长度
jmp @F
.endif
pop esi
add ebx,4
inc edx
.until edx>=[esi].NumberOfNames
jmp _ret
@@:
;通过API名称索引获取序号索引再获取地址索引
sub ebx,[esi].AddressOfNames ;AddressOfNames指向是的双字RVA数组
sub ebx,_hModule ;找到相对AddressOfNames的RVA
shr ebx,1 ;DWORD 步长变为WORD步长
add ebx,[esi].AddressOfNameOrdinals ;AddressOfNameOrdinals指向的是单字RVA数组
add ebx,_hModule
movzx eax,word ptr [ebx] ;无符号扩展Mov 从ebx指向地址取出索引号
shl eax,2 ;索引号*4变为AddressOfFunctions偏移量
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
;--------------------
; 获取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 ;ecx 指向该导出表的文件名字字符串的首地址
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 ;scasW 在edi向高地址增长方向找aX字符,找到设置ZF=1
.if ZERO? ;找到函数名称
sub edi,@lpAddressOfNameOrdinals
sub edi,2 ;lpAddressOfNameOrdinals指向是单字索引数组,减2是从零编号
shl edi,1 ;AddressOfNameOrdinals单字步长变为AddressOfNames双字步长
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
//peinfo.asm 整个文件
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include comdlg32.inc
includelib 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 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 File',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0
db 'All Files(*.*)',0,'*.*',0,0
szErr db '文件格式错误!',0
szErrFormat db '这个文件不是PE格式的文件!',0
szSuccess db '恭喜你,程序执行到这里是成功的。',0
szNotFound db '无法查找',0
szMsg db '文件名:%s',0dh,0ah
db '-----------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
db '运行平台: 0x%04x (014c:Intel 386 014dh:Intel 486 014eh:Intel 586)',0dh,0ah
db '节的数量: %d',0dh,0ah
db '文件属性: 0x%04x (大尾-禁止多处理器-DLL-系统文件-禁止网络运行-禁止优盘运行-无调试-32位-小尾-X-X-X-无符号-无行-可执行-无重定位)',0dh,0ah
db '建议装入基地址: 0x%08x',0dh,0ah
db '文件执行入口(RVA地址): 0x%04x',0dh,0ah,0dh,0ah,0
szMsgSec db '---------------------------------------------------------------------------------',0dh,0ah
db '节的属性参考:',0dh,0ah
db ' 00000020h 包含代码',0dh,0ah
db ' 00000040h 包含已经初始化的数据,如.const',0dh,0ah
db ' 00000080h 包含未初始化数据,如 .data?',0dh,0ah
db ' 02000000h 数据在进程开始以后被丢弃,如.reloc',0dh,0ah
db ' 04000000h 节中数据不经过缓存',0dh,0ah
db ' 08000000h 节中数据不会被交换到磁盘',0dh,0ah
db ' 10000000h 数据将被不同进程共享',0dh,0ah
db ' 20000000h 可执行',0dh,0ah
db ' 40000000h 可读',0dh,0ah
db ' 80000000h 可写',0dh,0ah
db '常见的代码节一般为:60000020h,数据节一般为:c0000040h,常量节一般为:40000040h',0dh,0ah
db '---------------------------------------------------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
db '节的名称 未对齐前真实长度 内存中的偏移(对齐后的) 文件中对齐后的长度 文件中的偏移 节的属性',0dh,0ah
db '---------------------------------------------------------------------------------------------',0dh,0ah,0
szFmtSec db '%s %08x %08x %08x %08x %08x',0dh,0ah,0dh,0ah,0dh,0ah,0
szMsg1 db 0dh,0ah,0dh,0ah,0dh,0ah
db '---------------------------------------------------------------------------------------------',0dh,0ah
db '导入表所处的节:%s',0dh,0ah
db '---------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgImport db 0dh,0ah,0dh,0ah
db '导入库:%s',0dh,0ah
db '-----------------------------',0dh,0ah,0dh,0ah
db 'OriginalFirstThunk %08x',0dh,0ah
db 'TimeDateStamp %08x',0dh,0ah
db 'ForwarderChain %08x',0dh,0ah
db 'FirstThunk %08x',0dh,0ah
db '-----------------------------',0dh,0ah,0dh,0ah,0
szMsg2 db '%08u %s',0dh,0ah,0
szMsg3 db '%08u(无函数名,按序号导入)',0dh,0ah,0
szErrNoImport db 0dh,0ah,0dh,0ah
db '未发现该文件有导入函数',0dh,0ah,0dh,0ah,0
szMsgExport db 0dh,0ah,0dh,0ah,0dh,0ah
db '---------------------------------------------------------------------------------------------',0dh,0ah
db '导出表所处的节:%s',0dh,0ah
db '---------------------------------------------------------------------------------------------',0dh,0ah
db '原始文件名:%s',0dh,0ah
db 'nBase %08x',0dh,0ah
db 'NumberOfFunctions %08x',0dh,0ah
db 'NuberOfNames %08x',0dh,0ah
db 'AddressOfFunctions %08x',0dh,0ah
db 'AddressOfNames %08x',0dh,0ah
db 'AddressOfNameOrd %08x',0dh,0ah
db '-------------------------------------',0dh,0ah,0dh,0ah
db '导出序号 虚拟地址 导出函数名称',0dh,0ah
db '-------------------------------------',0dh,0ah,0
szMsg4 db '%08x %08x %s',0dh,0ah,0
szExportByOrd db '(按照序号导出)',0
szErrNoExport db 0dh,0ah,0dh,0ah
db '未发现该文件有导出函数',0dh,0ah,0dh,0ah,0
szMsgReloc1 db 0dh,0ah,'重定位表所处的节:%s',0dh,0ah,0
szMsgReloc2 db 0dh,0ah
db '--------------------------------------------------------------------------------------------',0dh,0ah
db '重定位基地址: %08x',0dh,0ah
db '重定位项数量: %d',0dh,0ah
db '--------------------------------------------------------------------------------------------',0dh,0ah
db '需要重定位的地址列表(ffffffff表示对齐用,不需要重定位)',0dh,0ah
db '--------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgReloc3 db '%08x ',0
szCrLf db 0dh,0ah,0
szMsgReloc4 db 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
add eax,[edx].SizeOfRawData ;计算该节结束RVA,不用Misc的主要原因是有些段的Misc值是错误的!
.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
;-------------------------------
; 获取指定字符串的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 ;set DF=0(向高地址增长) std is set DF=1
repnz scasb ;scasb 在edi向高地址增长方向找al字符,找到设置ZF=1
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 ;cmpsb是用 DS:[SI] 所指的字节单元内容,减去 ES:[DI] 所指的字节单元的内容。
.if ZERO?
pop esi ;//if esi="fun" edi="func"感觉要比较下长度
jmp @F
.endif
pop esi
add ebx,4
inc edx
.until edx>=[esi].NumberOfNames
jmp _ret
@@:
;通过API名称索引获取序号索引再获取地址索引
sub ebx,[esi].AddressOfNames ;AddressOfNames指向是的双字RVA数组
sub ebx,_hModule ;找到相对AddressOfNames的RVA
shr ebx,1 ;DWORD 步长变为WORD步长
add ebx,[esi].AddressOfNameOrdinals ;AddressOfNameOrdinals指向的是单字RVA数组
add ebx,_hModule
movzx eax,word ptr [ebx] ;无符号扩展Mov 从ebx指向地址取出索引号
shl eax,2 ;索引号*4变为AddressOfFunctions偏移量
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
.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 ;ecx 指向该导出表的文件名字字符串的首地址
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 ;scasW 在edi向高地址增长方向找aX字符,找到设置ZF=1
.if ZERO? ;找到函数名称
sub edi,@lpAddressOfNameOrdinals
sub edi,2 ;lpAddressOfNameOrdinals指向是单字索引数组,减2是从零编号
shl edi,1 ;AddressOfNameOrdinals单字步长变为AddressOfNames双字步长
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
.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
;到此为止,该文件的验证已经完成。为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 ;以下三个菜单是7岁的儿子完成的!!
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