小弟我原是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(0xX)",0 szStrFormatStr db "%s", 0DH, 0AH, 0 szNameBuf db ERROR_INFO_LEN dup (0) szSectionFormatInfo db "SectionName = %s V.Size = %d V.Address = 0xX Raw Size = %d Raw Offset = 0xX Characteristics = 0xX", 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