📄 chap8-1-6.htm.primary
字号:
</tr>
<tr>
<td align="center" bgcolor="#666600"><font size="2" face="MS Sans Serif" color="#FFFFFF">Address
of Function 2</font> </td>
</tr>
<tr>
<td align="center" bgcolor="#666600"><font size="2" face="MS Sans Serif" color="#FFFFFF">Address
of Function 3</font> </td>
</tr>
<tr>
<td align="center" bgcolor="#666600"><font size="2" face="MS Sans Serif" color="#FFFFFF">Address
of Function 4</font> </td>
</tr>
<tr>
<td align="center" bgcolor="#666600"><font size="2" face="MS Sans Serif" color="#FFFFFF">...</font>
</td>
</tr>
<tr>
<td align="center" bgcolor="#666600"><font size="2" face="MS Sans Serif" color="#FFFFFF">Address
of Function n </font></td>
</tr>
</table>
</td>
</tr>
</table>
<p><font size="2" color="#000000">由</font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b>
</font><font size="2" color="#000000">指向的</font><font size="2" face="MS Sans Serif" color="#000000">RVA</font><font size="2" color="#000000">数组始终不会改变,所以若还反过头来查找引入函数名,</font><font size="2" face="MS Sans Serif" color="#000000">PE</font><font size="2" color="#000000">装载器还能找寻到。<br>
</font><font size="2" face="MS Sans Serif" color="#000000">当然再简单的事物都有其复杂的一面。</font><font size="2" color="#000000">有些情况下一些函数仅由序数引出,也就是说您不能用函数名来调用它们</font><font size="2" face="MS Sans Serif" color="#000000">:
</font><font size="2" color="#000000">您只能用它们的位置来调用。此时,调用者模块中就不存在该函数的<b> </b></font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_IMPORT_BY_NAME</b>
</font><font size="2" color="#000000">结构。不同的,对应该函数的 </font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_THUNK_DATA</b>
</font><font size="2" color="#000000">值的低位字指示函数序数,而最高二进位 </font><font size="2" face="MS Sans Serif" color="#000000">(MSB)</font><font size="2" color="#000000">设为</font><font size="2" face="MS Sans Serif" color="#000000">1</font><font size="2" color="#000000">。例如,如果一个函数只由序数引出且其序数是</font><font size="2" face="MS Sans Serif" color="#000000">1234h</font><font size="2" color="#000000">,那么对应该函数的
</font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_THUNK_DATA</b>
</font><font size="2" color="#000000">值是</font><font size="2" face="MS Sans Serif" color="#000000">80001234h</font><font size="2" color="#000000">。</font><font size="2" face="MS Sans Serif" color="#000000">Microsoft</font><font size="2" color="#000000">提供了一个方便的常量来测试</font><font size="2" face="MS Sans Serif" color="#000000">dword</font><font size="2" color="#000000">值的</font><font size="2" face="MS Sans Serif" color="#000000">MSB</font><font size="2" color="#000000">位,就是
</font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_ORDINAL_FLAG32</b></font><font size="2" color="#000000">,其值为</font><font size="2" face="MS Sans Serif" color="#000000">80000000h</font><font size="2" color="#000000">。<br>
假设我们要列出某个</font><font size="2" face="MS Sans Serif" color="#000000">PE</font><font size="2" color="#000000">文件的所有引入函数,可以照着下面步骤走</font><font size="2" face="MS Sans Serif" color="#000000">:</font></p>
<ol>
<li><font size="2" color="#000000">校验文件是否是有效的</font><font size="2" face="MS Sans Serif" color="#000000">PE</font><font size="2" color="#000000">。</font></li>
<li><font size="2" color="#000000">从 </font><font size="2" face="MS Sans Serif" color="#000000">DOS
header </font><font size="2" color="#000000">定位到 </font><font size="2" face="MS Sans Serif" color="#000000">PE
header</font><font size="2" color="#000000">。</font></li>
<li><font size="2" color="#000000">获取位于 </font><font color="#000000" size="2" face="MS Sans Serif"><b>OptionalHeader
</b></font><font size="2" color="#000000">数据目录地址。</font></li>
<li><font size="2" color="#000000">转至数据目录的第二个成员提取其</font><font color="#000000" size="2" face="MS Sans Serif"><b>VirtualAddress</b></font><font size="2" color="#000000">值。</font></li>
<li><font size="2" color="#000000">利用上值定位第一个 </font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_IMPORT_DESCRIPTOR</b>
</font><font size="2" color="#000000">结构。</font></li>
<li><font size="2" color="#000000">检查 </font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b></font><font size="2" color="#000000">值。若不为</font><font size="2" face="MS Sans Serif" color="#000000">0</font><font size="2" color="#000000">,顺着
</font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b>
</font><font size="2" color="#000000">里的</font><font size="2" face="MS Sans Serif" color="#000000">RVA</font><font size="2" color="#000000">值转入那个</font><font size="2" face="MS Sans Serif" color="#000000">RVA</font><font size="2" color="#000000">数组。若
</font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b>
</font><font size="2" color="#000000">为</font><font size="2" face="MS Sans Serif" color="#000000">0</font><font size="2" color="#000000">,就改用</font><font color="#000000" size="2" face="MS Sans Serif"><b>FirstThunk</b></font><font size="2" color="#000000">值。有些连接器生成</font><font size="2" face="MS Sans Serif" color="#000000">PE</font><font size="2" color="#000000">文件时会置</font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b></font><font size="2" color="#000000">值为</font><font size="2" face="MS Sans Serif" color="#000000">0</font><font size="2" color="#000000">,这应该算是个</font><font size="2" face="MS Sans Serif" color="#000000">bug</font><font size="2" color="#000000">。不过为了安全起见,我们还是检查
</font><font color="#000000" size="2" face="MS Sans Serif"><b>OriginalFirstThunk</b></font><font size="2" color="#000000">值先。</font></li>
<li><font size="2" color="#000000">对于每个数组元素,我们比对元素值是否等于</font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_ORDINAL_FLAG32</b></font><font color="#000000" size="2"><b>。</b>如果该元素值的最高二进位为</font><font size="2" face="MS Sans Serif" color="#000000">1</font><font size="2" color="#000000">,
那么函数是由序数引入的,可以从该值的低字节提取序数。</font></li>
<li><font size="2" color="#000000">如果元素值的最高二进位为</font><font size="2" face="MS Sans Serif" color="#000000">0</font><font size="2" color="#000000">,就可将该值作为</font><font size="2" face="MS Sans Serif" color="#000000">RVA</font><font size="2" color="#000000">转入
</font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_IMPORT_BY_NAME</b></font><font size="2" color="#000000">
数组,跳过 </font><font color="#000000" size="2" face="MS Sans Serif"><b>Hint</b>
</font><font size="2" color="#000000">就是函数名字了。</font></li>
<li><font size="2" color="#000000">再跳至下一个数组元素提取函数名一直到数组底部</font><font size="2" face="MS Sans Serif" color="#000000">(</font><font size="2" color="#000000">它以</font><font size="2" face="MS Sans Serif" color="#000000">null</font><font size="2" color="#000000">结尾</font><font size="2" face="MS Sans Serif" color="#000000">)</font><font size="2" color="#000000">。现在我们已遍历完一个</font><font size="2" face="MS Sans Serif" color="#000000">DLL</font><font size="2" color="#000000">的引入函数,接下去处理下一个</font><font size="2" face="MS Sans Serif" color="#000000">DLL</font><font size="2" color="#000000">。</font></li>
<li><font size="2" color="#000000">即跳转到下一个 </font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_IMPORT_DESCRIPTOR</b>
</font><font size="2" color="#000000">并处理之,如此这般循环直到数组见底。</font><font size="2" face="MS Sans Serif" color="#000000">(<b>IMAGE_IMPORT_DESCRIPTOR</b>
</font><font size="2" color="#000000">数组以一个全</font><font size="2" face="MS Sans Serif" color="#000000">0</font><font size="2" color="#000000">域元素结尾</font><font size="2" face="MS Sans Serif" color="#000000">)</font><font size="2" color="#000000">。</font></li>
</ol>
<h3><font color="#000000">示例<font face="Arial, Helvetica, sans-serif">:</font></font></h3>
<p><font size="2" color="#000000">本例程打开一</font><font size="2" face="MS Sans Serif" color="#000000">PE</font><font size="2" color="#000000">文件,将所有引入函数名读入一编辑控件,同时显示
</font><font color="#000000" size="2" face="MS Sans Serif"><b>IMAGE_IMPORT_DESCRIPTOR</b>
</font><font size="2" color="#000000">结构各域值。</font></p>
<p><font face="Fixedsys" color="#000000">.386 <br>
.model flat,stdcall <br>
option casemap:none <br>
include \masm32\include\windows.inc <br>
include \masm32\include\kernel32.inc <br>
include \masm32\include\comdlg32.inc <br>
include \masm32\include\user32.inc <br>
includelib \masm32\lib\user32.lib <br>
includelib \masm32\lib\kernel32.lib <br>
includelib \masm32\lib\comdlg32.lib <br>
<br>
IDD_MAINDLG equ 101 <br>
IDC_EDIT equ 1000 <br>
IDM_OPEN equ 40001 <br>
IDM_EXIT equ 40003 <br>
<br>
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD <br>
ShowImportFunctions proto :DWORD <br>
ShowTheFunctions proto :DWORD,:DWORD <br>
AppendText proto :DWORD,:DWORD <br>
<br>
SEH struct <br>
PrevLink dd ? ; the address of the previous seh structure <br>
CurrentHandler dd ? ; the address of the new exception handler <br>
SafeOffset dd ? ; The offset where it's safe to continue execution <br>
PrevEsp dd ? ; the old value in esp <br>
PrevEbp dd ? ; The old value in ebp <br>
SEH ends <br>
<br>
.data <br>
AppName db "PE tutorial no.6",0 <br>
ofn OPENFILENAME <> <br>
FilterString db "Executable Files (*.exe, *.dll)",0,"*.exe;*.dll",0
<br>
db
"All Files",0,"*.*",0,0 <br>
FileOpenError db "Cannot open the file for reading",0 <br>
FileOpenMappingError db "Cannot open the file for memory mapping",0
<br>
FileMappingError db "Cannot map the file into memory",0 <br>
NotValidPE db "This file is not a valid PE",0 <br>
CRLF db 0Dh,0Ah,0 <br>
ImportDescriptor db 0Dh,0Ah,"================[ IMAGE_IMPORT_DESCRIPTOR
]=============",0 <br>
IDTemplate db "OriginalFirstThunk = %lX",0Dh,0Ah <br>
db "TimeDateStamp
= %lX",0Dh,0Ah <br>
db "ForwarderChain
= %lX",0Dh,0Ah <br>
db "Name =
%s",0Dh,0Ah <br>
db "FirstThunk
= %lX",0 <br>
NameHeader db 0Dh,0Ah,"Hint Function",0Dh,0Ah <br>
db "-----------------------------------------",0
<br>
NameTemplate db "%u %s",0 <br>
OrdinalTemplate db "%u (ord.)",0 <br>
<br>
.data? <br>
buffer db 512 dup(?) <br>
hFile dd ? <br>
hMapping dd ? <br>
pMapping dd ? <br>
ValidPE dd ? <br>
<br>
.code <br>
start: <br>
invoke GetModuleHandle,NULL <br>
invoke DialogBoxParam, eax, IDD_MAINDLG,NULL,addr DlgProc, 0 <br>
invoke ExitProcess, 0 <br>
<br>
DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD <br>
.if uMsg==WM_INITDIALOG <br>
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_SETLIMITTEXT,0,0 <br>
.elseif uMsg==WM_CLOSE <br>
invoke EndDialog,hDlg,0 <br>
.elseif uMsg==WM_COMMAND <br>
.if lParam==0 <br>
mov eax,wParam <br>
.if ax==IDM_OPEN <br>
invoke ShowImportFunctions,hDlg <br>
.else ; IDM_EXIT <br>
invoke SendMessage,hDlg,WM_CLOSE,0,0 <br>
.endif <br>
.endif <br>
.else <br>
mov eax,FALSE <br>
ret <br>
.endif <br>
mov eax,TRUE <br>
ret <br>
DlgProc endp <br>
<br>
SEHHandler proc uses edx pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD
<br>
mov edx,pFrame <br>
assume edx:ptr SEH <br>
mov eax,pContext <br>
assume eax:ptr CONTEXT <br>
push [edx].SafeOffset <br>
pop [eax].regEip <br>
push [edx].PrevEsp <br>
pop [eax].regEsp <br>
push [edx].PrevEbp <br>
pop [eax].regEbp <br>
mov ValidPE, FALSE <br>
mov eax,ExceptionContinueExecution <br>
ret <br>
SEHHandler endp <br>
<br>
ShowImportFunctions proc uses edi hDlg:DWORD <br>
LOCAL seh:SEH <br>
mov ofn.lStructSize,SIZEOF <br>
ofn mov ofn.lpstrFilter, OFFSET FilterString <br>
mov ofn.lpstrFile, OFFSET buffer <br>
mov ofn.nMaxFile,512 <br>
mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES
or OFN_EXPLORER or OFN_HIDEREADONLY <br>
invoke GetOpenFileName, ADDR ofn <br>
.if eax==TRUE <br>
invoke CreateFile, addr buffer, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL <br>
.if eax!=INVALID_HANDLE_VALUE <br>
mov hFile, eax <br>
invoke CreateFileMapping, hFile, NULL, PAGE_READONLY,0,0,0
<br>
.if eax!=NULL <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -