PE教程2: 檢驗PE檔案的有效性
PE教程2: 檢驗PE檔案的有效性
本教程中我們將學習如何檢測給定檔案是一有效PE檔案。
下載 範例
理論:
如何才能校驗指定檔案是否為一有效PE檔案呢? 這個問題很難回答,完全取決於想要的精準程度。您可以檢驗PE檔案格式裡的各個資料結構,或者僅校驗一些關鍵資料結構。大多數情況下,沒有必要校驗檔案裡的每一個資料結構,只要一些關鍵資料結構有效,我們就認為是有效的PE檔案了。下面我們就來實現前面的假設。
我們要驗證的重要資料結構就是 PE header。從程式設計角度看,PE header 實際就是一個 IMAGE_NT_HEADERS 結構。定義如下:
IMAGE_NT_HEADERS
STRUCT
Signature dd ?
FileHeader
IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32
<>
IMAGE_NT_HEADERS ENDS
Signature
一dword型別,值為50h,
45h, 00h, 00h(PE\0\0)。
本域為PE標記,我們可以此識別給定檔案是否為有效PE檔案。
FileHeader 該結構域包含了關於PE檔案物理分佈的資訊,
比如節數目、檔案執行機器等。
OptionalHeader 該結構域包含了關於PE檔案邏輯分佈的資訊,雖然域名有"可選"字樣,但實際上本結構總是存在的。
我們目的很明確。如果IMAGE_NT_HEADERS的signature域值等於"PE\0\0",那麼就是有效的PE檔案。實際上,為了比較方便,Microsoft已定義了常量IMAGE_NT_SIGNATURE供我們使用。
IMAGE_DOS_SIGNATURE
equ 5A4Dh
IMAGE_OS2_SIGNATURE equ 454Eh
IMAGE_OS2_SIGNATURE_LE equ 454Ch
IMAGE_VXD_SIGNATURE equ 454Ch
IMAGE_NT_SIGNATURE equ 4550h
接下來的問題是: 如何定位 PE header? 答案很簡單: DOS MZ header 已經包含了指向 PE header 的檔案偏移量。DOS MZ header 又定義成結構 IMAGE_DOS_HEADER 。查詢windows.inc,我們知道 IMAGE_DOS_HEADER 結構的e_lfanew成員就是指向 PE header 的檔案偏移量。
現在將所有步驟總結如下:
- 首先檢驗檔案頭部第一個字的值是否等於 IMAGE_DOS_SIGNATURE,是則 DOS MZ header 有效。
- 一旦證明檔案的 DOS header 有效後,就可用e_lfanew來定位 PE header 了。
- 比較 PE header 的第一個字的值是否等於 IMAGE_NT_HEADER。如果前後兩個值都匹配,那我們就認為該檔案是一個有效的PE檔案。
Example:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
SEH struct
PrevLink dd ? ; the address of the
previous seh structure
CurrentHandler dd ? ; the address
of the exception handler
SafeOffset dd ? ; The offset where
it's safe to continue execution
PrevEsp dd ? ;
the old value in esp
PrevEbp dd ? ; The old value
in ebp
SEH ends
.data
AppName db "PE tutorial no.2",0
ofn OPENFILENAME <>
FilterString db "Executable Files (*.exe, *.dll)",0,"*.exe;*.dll",0
db "All Files",0,"*.*",0,0
FileOpenError db "Cannot open the file for reading",0
FileOpenMappingError db "Cannot open the file for memory mapping",0
FileMappingError db "Cannot map the file into memory",0
FileValidPE db "This
file is a valid PE",0
FileInValidPE db "This file is not a valid PE",0
.data?
buffer db 512 dup(?)
hFile dd ?
hMapping dd ?
pMapping dd ?
ValidPE dd ?
.code
start proc
LOCAL seh:SEH
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
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile, addr buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL
.if eax!=INVALID_HANDLE_VALUE
mov hFile, eax
invoke CreateFileMapping, hFile, NULL, PAGE_READONLY,0,0,0
.if eax!=NULL
mov
hMapping, eax
invoke
MapViewOfFile,hMapping,FILE_MAP_READ,0,0,0
.if eax!=NULL
mov pMapping,eax
assume fs:nothing
push fs:[0]
pop seh.PrevLink
mov seh.CurrentHandler,offset SEHHandler
mov seh.SafeOffset,offset FinalExit
lea eax,seh
mov fs:[0], eax
mov seh.PrevEsp,esp
mov seh.PrevEbp,ebp
mov edi, pMapping
assume edi:ptr IMAGE_DOS_HEADER
.if [edi].e_magic==IMAGE_DOS_SIGNATURE
add edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
.if [edi].Signature==IMAGE_NT_SIGNATURE
mov ValidPE, TRUE
.else
mov ValidPE, FALSE
.endif
.else
mov ValidPE,FALSE
.endif
FinalExit:
.if ValidPE==TRUE
invoke MessageBox, 0, addr FileValidPE, addr AppName, MB_OK+MB_ICONINFORMATION
.else
invoke MessageBox, 0, addr FileInValidPE, addr AppName, MB_OK+MB_ICONINFORMATION
.endif
push seh.PrevLink
pop fs:[0]
invoke UnmapViewOfFile, pMapping
.else
invoke MessageBox, 0, addr FileMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif
invoke CloseHandle,hMapping
.else
invoke MessageBox, 0, addr
FileOpenMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif
invoke CloseHandle, hFile
.else
invoke MessageBox,
0, addr FileOpenError, addr AppName, MB_OK+MB_ICONERROR
.endif
.endif
invoke ExitProcess, 0
start endp
SEHHandler
proc uses edx pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD
mov edx,pFrame
assume edx:ptr SEH
mov eax,pContext
assume eax:ptr CONTEXT
push [edx].SafeOffset
pop [eax].regEip
push [edx].PrevEsp
pop [eax].regEsp
push [edx].PrevEbp
pop [eax].regEbp
mov ValidPE, FALSE
mov eax,ExceptionContinueExecution
ret
SEHHandler endp
end start
分析:
本例程開啟一檔案,先檢驗DOS header是否有效,有效就接著檢驗PE header的有效性,ok就認為是有效的PE檔案了。這裡,我們還運用了結構異常處理(SEH),這樣就不必檢查每個可能的錯誤: 如果有錯誤出現,就認為PE檢測失效所致,於是給出我們的報錯資訊。其實Windows內部普遍使用SEH來檢驗引數傳遞的有效性。若對SEH感興趣的話,可閱讀Jeremy Gordon的 文章。
程式呼叫開啟檔案通用對話方塊,使用者選定執行檔案後,程式便開啟檔案並對映到記憶體。並在有效性檢驗前建立一 SEH:
assume fs:nothing
push fs:[0]
pop seh.PrevLink
mov seh.CurrentHandler,offset SEHHandler
mov
seh.SafeOffset,offset FinalExit
lea eax,seh
mov
fs:[0], eax
mov seh.PrevEsp,esp
mov
seh.PrevEbp,ebp
一開始就假設暫存器 fs為空(assume fs:nothing)。 記住這一步不能省卻,因為MASM假設fs暫存器為ERROR。接下來儲存 Windows使用的舊SEH處理函式地址到我們自己定義的結構中,同時儲存我們的SEH處理函式地址和異常處理時的執行恢復地址,這樣一旦錯誤發生就能由異常處理函式安全地恢復執行了。同時還儲存當前esp及ebp的值,以便我們的SEH處理函式將堆疊恢復到正常狀態。
mov
edi, pMapping
assume edi:ptr IMAGE_DOS_HEADER
.if
[edi].e_magic==IMAGE_DOS_SIGNATURE
成功建立SEH後繼續校驗工作。置目標檔案的首位元組地址給edi,使其指向DOS header的首位元組。為便於比較,我們告訴編譯器可以假定edi正指向IMAGE_DOS_HEADER結構(事實亦是如此)。然後比較DOS header的首字是否等於字串"MZ",這裡利用了windows.inc中定義的IMAGE_DOS_SIGNATURE常量。若比較成功,繼續轉到PE header,否則設ValidPE 值為FALSE,意味著檔案不是有效PE檔案。
add
edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
.if [edi].Signature==IMAGE_NT_SIGNATURE
mov ValidPE, TRUE
.else
mov
ValidPE, FALSE
.endif
要定位到PE
header,需要讀取DOS
header中的e_lfanew域值。該域含有PE
header在檔案中相對檔案首部的偏移量。edi加上該值正好定位到PE
header的首位元組。這兒可能會出錯,如果檔案不是PE檔案,e_lfanew值就不正確,加上該值作為指標就可能導致異常。若不用SEH,我們必須校驗e_lfanew值是否超出檔案尺寸,這不是一個好辦法。如果一切OK,我們就比較PE
header的首字是否是字串"PE"。這裡在此用到了常量IMAGE_NT_SIGNATURE,相等則認為是有效的PE檔案。
如果e_lfanew的值不正確導致異常,我們的SEH處理函式就得到執行控制權,簡單恢復堆疊指標和基棧指標後,就根據safeoffset的值恢復執行到FinalExit標籤處。
FinalExit:
.if ValidPE==TRUE
invoke
MessageBox, 0, addr FileValidPE, addr AppName, MB_OK+MB_ICONINFORMATION
.else
invoke MessageBox, 0, addr FileInValidPE,
addr AppName, MB_OK+MB_ICONINFORMATION
.endif
上述程式碼簡單明確,根據ValidPE的值顯示相應資訊。
push
seh.PrevLink
pop fs:[0]
一旦SEH不再使用,必須從SEH鏈上斷開。
翻譯:iamgufeng [Iczelion's Win32 Assembly Homepage][LuoYunBin's Win32 ASM Page]
.
相關文章
- PE檔案結構解析22022-05-23
- PE檔案格式2015-11-15
- PE檔案檢測DOS頭\NT頭2017-09-10
- PE教程3: File Header (檔案頭)2015-11-15Header
- 深入剖析PE檔案2014-09-25
- PE檔案格式的RVA概念2008-04-14
- PE 檔案結構圖2023-09-05
- windows載入PE檔案的流程2024-09-08Windows
- PE檔案結構複習2020-11-11
- PE檔案結構解析32022-05-30
- PE檔案結構解析12022-05-20
- C++ 獲取PE檔案自校驗值的程式碼2019-01-05C++
- PE檔案格式詳細解析(一)2020-03-08
- 初步瞭解PE檔案格式(上)2017-07-03
- win32 PE 檔案格式 (轉)2007-12-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----PE檔案頭2015-11-15Win32
- PE檔案格式詳細解析(二)--IAT2020-03-08
- 惡意軟體PE檔案重建指南2020-08-19
- 21.1 Python 使用PEfile分析PE檔案2023-10-19Python
- PE檔案結構(五)基址重定位2014-10-07
- PE檔案結構(四) 輸出表2014-10-06
- PE教程4: Optional Header2015-11-15Header
- 再探.NET的PE檔案結構(安全篇)2019-05-11
- PE檔案新增節顯示啟動資訊...2002-04-01
- PE教程6: Import Table(引入表2015-11-15Import
- PE教程5: Section Table(節表)2015-11-15
- PE教程7: Export Table(引出表)2015-11-15Export
- 手工構造一個超微型的 PE 檔案 (轉)2007-08-17
- 第二部分 PE檔案格式2015-03-23
- PE檔案結構(二) 區塊,檔案偏移與RVA轉換2014-10-02
- ResHacker 用命令列方式修改 windows PE檔案版本號2020-11-30命令列Windows
- C/C++ 實現PE檔案特徵碼識別2020-09-14C++特徵
- iso檔案怎麼用u盤安裝系統 u盤pe安裝iso檔案方法教程2022-09-24
- SigFlip如何篡改身份認證碼簽名的PE檔案2021-10-14
- C語言程式設計獲取PE檔案DOS頭2016-02-02C語言程式設計
- [轉載]淺析.NET
Framework對PE檔案格式的擴充套件2004-08-26Framework套件
- diy pe教學2 (11千字)2002-08-09
- 體驗 Orbeon form PE 版本提供的 JavaScript Embedding API2021-12-19ORBORMJavaScriptAPI