小弟我原是C程序员,最近迷上汇编,先拿PE文件做练手,写了一段程序,用于显示PE文件导入的DLL以及相关函数信息,有不足之处请指教。
对
代
码
的解
释请
参考代
码
中的相
关
注
释
。(不要骂我懒,其实我不忙。)
;
小弟用的是
RadASM2.2.1.1+
masm32
进行编辑和编译
;
感谢
Iczelion
提供如此详尽的
PE
文件讲解。
.386
.model flat,stdcall
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
include /masm32/include/comdlg32.inc
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/comdlg32.lib
ERROR_INFO_LEN equ 1024
MyMain proto :DWORD, :DWORD
RVAToOffset proto dwMapAddr:DWORD, dwRVA:DWORD
.data
dwRet DWORD 0
hFile HANDLE INVALID_HANDLE_VALUE
hFileMap HANDLE 0
lpMapAddr LPVOID 0
szErrorInfo db "This file is a not PE file", 0
szSuccessInfo db "This file is a PE file", 0
szHInfo db "PE info", 0
wVirtualAddress
DWORD 0
ofn OPENFILENAME
<>
FilterString db "Executable Files (*.exe, *.dll)",0,"*.exe;*.dll",0
db "All Files",0,"*.*",0,0
szFormatStr db "%d(0x%08X)",0
szStrFormatStr db "%s", 0DH, 0AH, 0
szNameBuf db ERROR_INFO_LEN dup (0)
szSectionFormatInfo
db "SectionName = %s V.Size = %d V.Address = 0x%08X Raw Size = %d Raw Offset = 0x%08X Characteristics = 0x%08X", 0
.data?
hModelIns HINSTANCE ?
szCommLine LPSTR ?
buffer db 512 dup(?)
szOutInfo BYTE ERROR_INFO_LEN dup(0)
.code
My_Start_Lable:
invoke GetModuleHandle,NULL
mov hModelIns, eax
invoke GetCommandLine
mov szCommLine, eax
invoke MyMain, hModelIns, szCommLine
mov dwRet, eax
invoke ExitProcess, 0
;
自定义入口函数
MyMain proc hInst:HINSTANCE, CmdLine:LPSTR
;
定义临时变量
LOCAL bRet[1]:BYTE
LOCAL wSecCon:WORD
LOCAL wSecConTem:WORD
LOCAL szAddr:DWORD
LOCAL dwFunCon:DWORD
mov bRet, 0
mov wSecCon, 0
mov wSecConTem, 0
mov szAddr, 0
mov dwFunCon, 0
mov ofn.lStructSize,SIZEOF ofn
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,512
mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
;
使用打开对话框打开一个
PE
文件
invoke GetOpenFileName, ADDR ofn
;
打开指定的
PE
文件,为之后的内存映射做准备
invoke CreateFile, ADDR buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
;
返回值判断(是不是只要是
WindowsAPI
,函数的返回值都使用
eax
?请大虾指教)
mov hFile, eax
.if hFile == INVALID_HANDLE_VALUE
jmp END_MyMain
.endif
;
创建内存映射
invoke CreateFileMapping, hFile, NULL, PAGE_READONLY, 0, 0, NULL
mov hFileMap, eax
.if hFileMap == 0
jmp END_MyMain
.endif
invoke MapViewOfFile, hFileMap, FILE_MAP_READ, 0, 0, 0
mov lpMapAddr, eax
mov edi, lpMapAddr
assume edi:ptr IMAGE_DOS_HEADER
;
判断
DOS
头的合法性
.if [edi].e_magic != IMAGE_DOS_SIGNATURE
jmp END_MyMain
.endif
add edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
;
判断
PE
文件的合法性
.if [edi].Signature != IMAGE_NT_SIGNATURE
jmp END_MyMain
.endif
mov dx, [edi].FileHeader.NumberOfSections
mov wSecCon, dx
invoke wsprintf, addr szOutInfo, addr szFormatStr, wSecCon, wSecCon
;
定位到节表
add edi, sizeof IMAGE_NT_HEADERS
mov esi, edi
assume esi:ptr IMAGE_SECTION_HEADER
mov dx, wSecCon
mov wSecConTem, dx
;
取得节表信息
get_section_info:
invoke RtlZeroMemory, addr szOutInfo, sizeof szOutInfo
invoke wsprintf, addr szOutInfo, addr szSectionFormatInfo, addr [esi].Name1, [esi].Misc.VirtualSize, [esi].VirtualAddress, [esi].SizeOfRawData, [esi].PointerToRawData, [esi].Characteristics
add esi, sizeof IMAGE_SECTION_HEADER
assume esi:ptr IMAGE_SECTION_HEADER
DEC wSecConTem
.if wSecConTem > 0
jmp get_section_info
.endif
;
取得导入的
DLL
信息
mov edi, lpMapAddr
assume edi:ptr IMAGE_DOS_HEADER
add edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
add edi, sizeof IMAGE_NT_HEADERS
sub edi, sizeof IMAGE_DATA_DIRECTORY * 15
assume edi:ptr IMAGE_DATA_DIRECTORY
invoke RVAToOffset, lpMapAddr, [edi].VirtualAddress
.if eax == 0
jmp END_MyMain
.endif
mov edi, eax
add edi, lpMapAddr
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
OUTPUT_IMPORT_INFO_BEGIN:
mov esi, [edi].OriginalFirstThunk
.if [edi].OriginalFirstThunk == 0
.if [edi].FirstThunk == 0
jmp OUTPUT_IMPORT_INFO_END
.endif
mov esi, [edi].FirstThunk
.endif
;
取得当前导入的
DLL
名称
invoke RtlZeroMemory, addr szOutInfo, sizeof szOutInfo
invoke RVAToOffset, lpMapAddr, [edi].Name1
add eax, lpMapAddr
invoke wsprintf, addr szOutInfo, addr szStrFormatStr, eax
invoke RVAToOffset, lpMapAddr, esi
mov esi, eax
add esi, lpMapAddr
;
取得当前
DLL
所有导入的函数名称
GET_FUNCTIOM_BEGIN:
.if dword ptr [esi] == NULL
jmp GET_FUNCTIOM_END
.endif
invoke RVAToOffset, lpMapAddr, dword ptr[esi]
add eax, lpMapAddr
mov szAddr, eax
invoke RtlZeroMemory, addr szNameBuf, sizeof szNameBuf
mov edx, szAddr
assume edx:ptr IMAGE_IMPORT_BY_NAME
invoke wsprintf, addr szNameBuf, addr szStrFormatStr, addr [edx].Name1
invoke lstrcat, addr szOutInfo, addr szNameBuf
add esi, sizeof IMAGE_THUNK_DATA
jmp GET_FUNCTIOM_BEGIN
GET_FUNCTIOM_END:
invoke MessageBox, NULL, addr szOutInfo, addr szHInfo, MB_OK
add edi, sizeof IMAGE_IMPORT_DESCRIPTOR
jmp OUTPUT_IMPORT_INFO_BEGIN
OUTPUT_IMPORT_INFO_END:
;
显示当前
DLL
和函数名称
invoke MessageBox, NULL, addr szOutInfo, addr szHInfo, MB_OK
mov bRet, TRUE
invoke MessageBox, NULL, addr szSuccessInfo, addr szHInfo, MB_OK
END_MyMain:
.if bRet != TRUE
invoke GetLastError
invoke MessageBox, NULL, addr szErrorInfo, addr szHInfo, MB_OK
.endif
.if hFile != INVALID_HANDLE_VALUE
invoke CloseHandle, hFile
mov hFile, INVALID_HANDLE_VALUE
.endif
.if hFileMap != 0
.if lpMapAddr != 0
invoke UnmapViewOfFile, lpMapAddr
mov lpMapAddr, 0
.endif
invoke CloseHandle, hFileMap
mov hFileMap, 0
.endif
ret
MyMain endp
;
此函数将相对虚拟地址(
RVA
)转换为文件偏移
RVAToOffset PROC dwMapAddr:DWORD, dwRVA:DWORD
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov ebx, dwMapAddr
assume ebx:ptr IMAGE_DOS_HEADER
add ebx, [ebx].e_lfanew
assume ebx:ptr IMAGE_NT_HEADERS
mov cx, [ebx].FileHeader.NumberOfSections
add ebx, sizeof IMAGE_NT_HEADERS
mov edx, dwRVA
SET_RVA:
assume ebx:ptr IMAGE_SECTION_HEADER
.if edx >= [ebx].VirtualAddress
mov eax, [ebx].VirtualAddress
add eax, [ebx].SizeOfRawData
.if edx < eax
mov eax, [ebx].VirtualAddress
sub edx, eax
add edx, [ebx].PointerToRawData
mov eax, edx
ret
.endif
.endif
add ebx, sizeof IMAGE_SECTION_HEADER
loop SET_RVA
mov eax, 0
ret
RVAToOffset endp
end My_Start_Lable