1. PE头部总体组成
2. DOS MZ头
3. PE头
PE头由3部分组成:
下面分别:
OptionalHeader比较大:
然后是节表, 节表有多个:
PE文件头部就结束了, 最后就是节区了, 来看几段代码:
; main.asm
.586
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
strPause BYTE "pause", 0
strFilePath BYTE "winmine.exe", 0
.code
include PE.asm
include IO.asm
RVA2FOA PROTO _pFileHdr:PTR BYTE, _dwRVA:DWORD
_vReadFile PROTO _pFileName:PTR BYTE
main PROC
LOCAL @pDosHdr:PTR BYTE
push OFFSET strFilePath
call _vReadFile
test eax, eax
jz Ending
mov @pDosHdr, eax
push 5000h
push @pDosHdr
call FindSectionName
push eax
call crt_printf
Ending:
push OFFSET strPause
call crt_system
invoke ExitProcess, 0
main ENDP
end main
; PE.asm
IFNDEF _PEOPA_ASM
_PEOPA_ASM MACRO
ENDM
; 作用: 用于判定是否是PE文件
; 参数: _pFileHdr 指向读到内存中文件的基址指针
; 返回: eax == 1是PE文件, eax == 0则不是PE文件
CheckPE PROC PROC _pFileHdr:PTR BYTE
xor eax, eax
mov esi, _pFileHdr
assume esi:PTR IMAGE_DOS_HEADER
cmp WORD PTR [esi].e_magic, 5A4Dh
jne Ending
add esi, [esi].e_lfanew
assume esi:PTR IMAGE_NT_HEADERS32
cmp WORD PTR [esi].Signature, 4550h
jnz Ending
mov eax, 1
Ending:
ret
CheckPE ENDP
; 作用: 查找RVA地址所在节的名称地址
; 参数: _pFileHdr 指向读到内存中文件的基址指针
; _dwRVA 目标RVA地址
; 返回: 指向RVA所在节区的名称指针
FindSectionName PROC _pFileHdr:PTR BYTE, _dwRVA:DWORD
mov esi, _pFileHdr
assume esi:PTR IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:PTR IMAGE_NT_HEADERS32
; 获取节数
movzx ecx, [esi].FileHeader.NumberOfSections
; 获取节表指针
add esi, SIZEOF IMAGE_NT_HEADERS32
assume esi:PTR IMAGE_SECTION_HEADER
L0:
; 对比是否在当节
mov edx, _dwRVA
mov edi, [esi].VirtualAddress
cmp edx, edi
jb @F
add edi, [esi].SizeOfRawData
cmp edx, edi
jae @F
; 在当节则获取节名, RVA+ImageBase
lea eax, [esi].Name1
mov ecx, 1
@@:
add esi, SIZEOF IMAGE_SECTION_HEADER
loop L0
ret
FindSectionName ENDP
; 作用: 将RVA地址转成FOA即文件偏移
; 参数: _pFileHdr 指向读到内存中文件的基址指针
; _dwRVA 目标RVA地址
; 返回: 目标RVA转成文件偏移的值
RVA2FOA PROC _pFileHdr:PTR BYTE, _dwRVA:DWORD
pushad
mov esi, _pFileHdr
assume esi:ptr IMAGE_DOS_HEADER
; 获取PE头
mov edi, [esi].e_lfanew
assume esi:nothing
add edi, esi
assume edi:ptr IMAGE_NT_HEADERS32
; 获取节数
movzx ecx, [edi].FileHeader.NumberOfSections
assume edi:nothing
; 获取节表地址
add edi, SIZEOF IMAGE_NT_HEADERS32
assume edi:ptr IMAGE_SECTION_HEADER
L0:
mov edx, _dwRVA
cmp edx, [edi].VirtualAddress
jb @F
mov eax, [edi].VirtualAddress
add eax, [edi].SizeOfRawData
cmp edx, eax
jae @F
sub edx, [edi].VirtualAddress
add edx, [edi].PointerToRawData
mov eax, edx
jmp Ending
@@:
add edi, SIZEOF IMAGE_SECTION_HEADER
loop L0
xor eax, eax
Ending:
popad
ret
RVA2FOA ENDP
ENDIF
; IO.asm
IFNDEF _IO_ASM
_IO_ASM MACRO
ENDM
; 作用: 读取文件到内存, 会分配堆空间存储
; 参数: _pFileName 要读取的文件名
; 返回: eax中存储着指向文件内容的指针, 失败则是NULL
_vReadFile PROC _pFileName:PTR BYTE
LOCAL @hFile:HANDLE,
@liFileSize:LARGE_INTEGER,
@pFileAddr:PTR BYTE,
@dwReaded:DWORD
pushad
; 打开文件
invoke CreateFile,
_pFileName,
FILE_ALL_ACCESS,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
cmp eax, INVALID_HANDLE_VALUE
je Ending
mov @hFile, eax
; 获取文件大小
invoke GetFileSizeEx, @hFile, ADDR @liFileSize
; 分配堆空间
invoke VirtualAlloc, NULL, [@liFileSize].LowPart, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE
test eax, eax
jz Ending
mov @pFileAddr, eax
; 清空缓存
invoke RtlZeroMemory, @pFileAddr, @liFileSize.LowPart
; 读取内容
invoke ReadFile, @hFile, @pFileAddr, @liFileSize.LowPart, ADDR @dwReaded, NULL
test eax, eax
jz Ending
jmp @F
Ending:
cmp @pFileAddr, 0
jz @F
invoke VirtualFree, @pFileAddr, 0, MEM_RELEASE
mov @pFileAddr, 0
@@:
cmp @hFile, INVALID_HANDLE_VALUE
jz @F
invoke CloseHandle, @hFile
mov @hFile, INVALID_HANDLE_VALUE
@@:
popad
mov eax, @pFileAddr
ret
_vReadFile ENDP
ENDIF
(完)