?? pe-tut6.html
字號:
invoke MessageBox, 0,
addr FileMappingError, addr AppName, MB_OK+MB_ICONERROR <br>
.endif <br>
invoke CloseHandle,hMapping <br>
.else <br>
invoke MessageBox, 0, addr FileOpenMappingError,
addr AppName, MB_OK+MB_ICONERROR <br>
.endif <br>
invoke CloseHandle, hFile <br>
.else <br>
invoke MessageBox, 0, addr FileOpenError, addr AppName, MB_OK+MB_ICONERROR
<br>
.endif <br>
.endif <br>
ret <br>
ShowImportFunctions endp <br>
<br>
AppendText proc hDlg:DWORD,pText:DWORD <br>
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_REPLACESEL,0,pText <br>
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_REPLACESEL,0,addr CRLF
<br>
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_SETSEL,-1,0 <br>
ret <br>
AppendText endp <br>
<br>
RVAToOffset PROC uses edi esi edx ecx pFileMap:DWORD,RVA:DWORD <br>
mov esi,pFileMap <br>
assume esi:ptr IMAGE_DOS_HEADER <br>
add esi,[esi].e_lfanew <br>
assume esi:ptr IMAGE_NT_HEADERS <br>
mov edi,RVA ; edi == RVA <br>
mov edx,esi <br>
add edx,sizeof IMAGE_NT_HEADERS <br>
mov cx,[esi].FileHeader.NumberOfSections <br>
movzx ecx,cx <br>
assume edx:ptr IMAGE_SECTION_HEADER <br>
.while ecx>0 ; check all sections <br>
.if edi>=[edx].VirtualAddress <br>
mov eax,[edx].VirtualAddress <br>
add eax,[edx].SizeOfRawData <br>
.if edi<eax ; The address is in this
section <br>
mov eax,[edx].VirtualAddress
<br>
sub edi,eax<br>
mov eax,[edx].PointerToRawData
<br>
add eax,edi ; eax == file offset
<br>
ret <br>
.endif <br>
.endif <br>
add edx,sizeof IMAGE_SECTION_HEADER <br>
dec ecx <br>
.endw <br>
assume edx:nothing <br>
assume esi:nothing <br>
mov eax,edi <br>
ret <br>
RVAToOffset endp <br>
<br>
ShowTheFunctions proc uses esi ecx ebx hDlg:DWORD, pNTHdr:DWORD <br>
LOCAL temp[512]:BYTE <br>
invoke SetDlgItemText,hDlg,IDC_EDIT,0 <br>
invoke AppendText,hDlg,addr buffer <br>
mov edi,pNTHdr <br>
assume edi:ptr IMAGE_NT_HEADERS <br>
mov edi, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
<br>
invoke RVAToOffset,pMapping,edi <br>
mov edi,eax <br>
add edi,pMapping <br>
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR <br>
.while !([edi].OriginalFirstThunk==0 && [edi].TimeDateStamp==0
&& [edi].ForwarderChain==0 && [edi].Name1==0 && [edi].FirstThunk==0) <br>
invoke AppendText,hDlg,addr ImportDescriptor <br>
invoke RVAToOffset,pMapping, [edi].Name1 <br>
mov edx,eax <br>
add edx,pMapping <br>
invoke wsprintf, addr temp, addr IDTemplate, [edi].OriginalFirstThunk,[edi].TimeDateStamp,[edi].ForwarderChain,edx,[edi].FirstThunk
invoke AppendText,hDlg,addr temp <br>
.if [edi].OriginalFirstThunk==0 <br>
mov esi,[edi].FirstThunk <br>
.else <br>
mov esi,[edi].OriginalFirstThunk
<br>
.endif <br>
invoke RVAToOffset,pMapping,esi <br>
add eax,pMapping <br>
mov esi,eax <br>
invoke AppendText,hDlg,addr NameHeader <br>
.while dword ptr [esi]!=0 <br>
test dword ptr [esi],IMAGE_ORDINAL_FLAG32
<br>
jnz ImportByOrdinal <br>
invoke RVAToOffset,pMapping,dword ptr [esi]
<br>
mov edx,eax <br>
add edx,pMapping <br>
assume edx:ptr IMAGE_IMPORT_BY_NAME <br>
mov cx, [edx].Hint <br>
movzx ecx,cx <br>
invoke wsprintf,addr temp,addr NameTemplate,ecx,addr
[edx].Name1 <br>
jmp ShowTheText <br>
ImportByOrdinal: <br>
mov edx,dword ptr [esi] <br>
and edx,0FFFFh <br>
invoke wsprintf,addr temp,addr OrdinalTemplate,edx
<br>
ShowTheText: <br>
invoke AppendText,hDlg,addr temp <br>
add esi,4 <br>
.endw <br>
add edi,sizeof IMAGE_IMPORT_DESCRIPTOR <br>
.endw <br>
ret <br>
ShowTheFunctions endp <br>
end start </font></p>
<h3><font face="Arial, Helvetica, sans-serif">Analysis:</font></h3>
<p><font face="MS Sans Serif" size="-1">The program shows an open file dialog
box when the user clicks Open in the menu. It verifies that the file is a valid
PE and then calls <font color="#CC9900"><b>ShowTheFunctions</b></font>.</font></p>
<p><font face="Fixedsys">ShowTheFunctions proc uses esi ecx ebx hDlg:DWORD, pNTHdr:DWORD
<br>
LOCAL temp[512]:BYTE </font></p>
<p><font face="MS Sans Serif" size="-1">Reserve 512 bytes of stack space for string
operation.</font></p>
<p><font face="Fixedsys"> invoke SetDlgItemText,hDlg,IDC_EDIT,0 </font></p>
<p><font face="MS Sans Serif" size="-1">Clear the text in the edit control</font></p>
<p><font face="Fixedsys"> invoke AppendText,hDlg,addr buffer </font></p>
<p><font face="MS Sans Serif" size="-1">Insert the name of the PE file into the
edit control. <font color="#CC9900"><b>AppendText </b></font>just sends <font color="#CCFFCC"><b>EM_REPLACESEL
</b></font> messages to append the text to the edit control. Note that it sends
<font color="#CCFFCC"> <b>EM_SETSEL</b></font> with wParam=-1 and lParam=0 to
the edit control to move the cursor to the end of the text.</font></p>
<p><font face="Fixedsys"> mov edi,pNTHdr <br>
assume edi:ptr IMAGE_NT_HEADERS <br>
mov edi, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
</font></p>
<p><font face="MS Sans Serif" size="-1">Obtain the RVA of the import symbols.
edi at first points to the PE header. We use it to go to the 2nd member of the
data directory array and obtain the value of VirtualAddress member.</font></p>
<p><font face="Fixedsys"> invoke RVAToOffset,pMapping,edi <br>
mov edi,eax <br>
add edi,pMapping </font></p>
<p><font face="MS Sans Serif" size="-1">Here comes one of the pitfalls for newcomers
to PE programming. Most of the addresses in the PE file are RVAs and <font color="#FF6666"><b>RVAs
are meaningful only when the PE file is loaded into memory by the PE loader</b></font>.
In our case, we do map the file into memory but not the way the PE loader does.
Thus we cannot use those RVAs directly. Somehow we have to convert those RVAs
into file offsets. I write RVAToOffset function just for this purpose. I won't
analyze it in detail here. Suffice to say that it checks the submitted RVA against
the starting-ending RVAs of all sections in the PE file and use the value in
<font color="#FFFFCC"> <b>PointerToRawData</b></font> field in the <font color="#CCFFCC"><b>IMAGE_SECTION_HEADER</b></font>
structure to convert the RVA to file offset.<br>
To use this function, you pass it two parameters: the pointer to the memory
mapped file and the RVA you want to convert. It returns the file offset in eax.
In the above snippet, we must add the pointer to the memory mapped file to the
file offset to convert it to virtual address. Seems complicated, huh? :)</font></p>
<p><font face="Fixedsys"> assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
<br>
.while !([edi].OriginalFirstThunk==0 && [edi].TimeDateStamp==0
&& [edi].ForwarderChain==0 && [edi].Name1==0 && [edi].FirstThunk==0) </font></p>
<p><font face="MS Sans Serif" size="-1">edi now points to the first <font color="#CCFFCC"><b>IMAGE_IMPORT_DESCRIPTOR</b></font>
structure. We will walk the array until we find the structure with zeroes in
all members which denotes the end of the array.</font></p>
<p><font face="Fixedsys"> invoke AppendText,hDlg,addr
ImportDescriptor<br>
</font><font face="Fixedsys"> invoke RVAToOffset,pMapping,
[edi].Name1 <br>
mov edx,eax <br>
add edx,pMapping </font></p>
<p><font face="MS Sans Serif" size="-1">We want to display the values of the current
<font color="#CCFFCC"> <b>IMAGE_IMPORT_DESCRIPTOR</b></font> structure in the
edit control. Name1 is different from the other members since it contains the
RVA to the name of the dll. Thus we must convert it to a virtual address first.</font></p>
<p><font face="Fixedsys"> invoke wsprintf, addr temp,
addr IDTemplate, [edi].OriginalFirstThunk,[edi].TimeDateStamp,[edi].ForwarderChain,edx,[edi].FirstThunk
invoke AppendText,hDlg,addr temp </font></p>
<p><font face="MS Sans Serif" size="-1">Display the values of the current <font color="#CCFFCC"><b>IMAGE_IMPORT_DESCRIPTOR</b></font>.</font></p>
<p><font face="Fixedsys"> .if [edi].OriginalFirstThunk==0
<br>
mov esi,[edi].FirstThunk <br>
.else <br>
mov esi,[edi].OriginalFirstThunk
<br>
.endif </font></p>
<p><font face="MS Sans Serif" size="-1">Next we prepare to walk the <font color="#CCFFCC"><b>IMAGE_THUNK_DATA</b></font>
array. Normally we would choose to use the array pointed to by <font color="#FFFFCC"><b>OriginalFirstThunk</b></font>.
However, some linkers errornously put 0 in <font color="#FFFFCC"><b>OriginalFirstThunk</b></font>
thus we must check first if the value of <font color="#FFFFCC"><b>OriginalFirstThunk</b></font>
is zero. If it is, we use the array pointed to by <font color="#FFFFCC"><b>FirstThunk</b></font>
instead.</font></p>
<p><font face="Fixedsys"> invoke RVAToOffset,pMapping,esi
<br>
add eax,pMapping <br>
mov esi,eax </font></p>
<p><font face="MS Sans Serif" size="-1">Again, the value in <font color="#FFFFCC"><b>OriginalFirstThunk</b></font>/<font color="#FFFFCC"><b>FirstThunk</b></font>
is an RVA. We must convert it to virtual address.</font></p>
<p><font face="Fixedsys"> invoke AppendText,hDlg,addr
NameHeader</font><font face="Fixedsys"><br>
.while dword ptr [esi]!=0 </font></p>
<p><font face="MS Sans Serif" size="-1">Now we are ready to walk the array of<font color="#CCFFCC"><b>
IMAGE_THUNK_DATAs</b></font> to look for the names of the functions imported
from this DLL. We will walk the array until we find an entry which contains
0.</font></p>
<p><font face="Fixedsys"> test dword ptr
[esi],IMAGE_ORDINAL_FLAG32 <br>
jnz ImportByOrdinal </font></p>
<p><font face="MS Sans Serif" size="-1">The first thing we do with the<font color="#CCFFCC"><b>
IMAGE_THUNK_DATA</b></font> is to test it against <font color="#CCFFCC"><b>IMAGE_ORDINAL_FLAG32</b></font>.
This test checks if the most significant bit of the<font color="#CCFFCC"><b>
IMAGE_THUNK_DATA</b></font> is 1. If it is, the function is exported by ordinal
so we have no need to process it further. We can extract its ordinal from the
low word of the <font color="#CCFFCC"><b>IMAGE_THUNK_DATA</b></font> and go
on with the next <font color="#CCFFCC"><b>IMAGE_THUNK_DATA</b></font> dword.</font></p>
<p><font face="Fixedsys"> invoke RVAToOffset,pMapping,dword
ptr [esi] <br>
mov edx,eax <br>
add edx,pMapping <br>
assume edx:ptr IMAGE_IMPORT_BY_NAME </font></p>
<p><font face="MS Sans Serif" size="-1">If the MSB of the IAMGE_THUNK_DATA is
0, it contains the RVA of IMAGE_IMPORT_BY_NAME structure. We need to convert
it to virtual address first.</font></p>
<p><font face="Fixedsys"> mov cx, [edx].Hint
<br>
movzx ecx,cx <br>
invoke wsprintf,addr temp,addr NameTemplate,ecx,addr
[edx].Name1 <br>
jmp ShowTheText </font></p>
<p><font face="MS Sans Serif" size="-1">Hint is a word-sized field. We must convert
it to a dword-sized value before submitting it to wsprintf. And we print both
the hint and the function name in the edit control</font></p>
<p><font face="Fixedsys"> ImportByOrdinal: <br>
mov edx,dword ptr [esi] <br>
and edx,0FFFFh <br>
invoke wsprintf,addr temp,addr OrdinalTemplate,edx
</font></p>
<p><font face="MS Sans Serif" size="-1">In the case the function is exported by
ordinal only, we zero out the high word and display the ordinal.</font></p>
<p><font face="Fixedsys"> ShowTheText: <br>
invoke AppendText,hDlg,addr temp <br>
add esi,4 </font></p>
<p><font face="MS Sans Serif" size="-1">After inserting the function name/ordinal
into the edit control, we skip to the next <font color="#CCFFCC"><b>IMAGE_THUNK_DATA</b></font>.</font></p>
<p><font face="Fixedsys"> .endw <br>
add edi,sizeof IMAGE_IMPORT_DESCRIPTOR </font></p>
<p><font face="MS Sans Serif" size="-1">When all <font color="#CCFFCC"><b>IMAGE_THUNK_DATA</b></font>
dwords in the array are processed, we skip to the next <font color="#CCFFCC"><b>IMAGE_IMPORT_DESCRIPTOR</b></font>
to process the import functions from other DLLs.</font></p>
<h3><font face="MS Sans Serif" size="-1">Appendix:</font></h3>
<p><font face="MS Sans Serif" size="-1">It would be incomplete if I don't mention
something about bound import. In order to explain what it is, I need to digress
a bit. When the PE loader loads a PE file into memory, it examines the import
table and loads the required DLLs into the process address space. Then it walks
the<font color="#CCFFCC"><b> IMAGE_THUNK_DATA</b></font> array much like we
did and replaces the<font color="#CCFFCC"><b> IMAGE_THUNK_DATAs</b></font> with
the real addresses of the import functions. This step takes time. If somehow
the programmer can predict the addresses of the functions correctly, the PE
loader doesn't have to fix the<font color="#CCFFCC"><b> IMAGE_THUNK_DATAs</b></font>
each time the PE file is run. Bound import is the product of that idea.<br>
To put it in simple terms, there is a utility named <font color="#FFFFCC"><b>bind.exe</b></font>
that comes with Microsoft compilers such as Visual Studio that examines the
import table of a PE file and replaces the<font color="#CCFFCC"><b> IMAGE_THUNK_DATA</b></font>
dwords with the addresses of the import functions.When the file is loaded, the
PE loader must check if the addresses are valid. If the DLL versions do not
match the ones in the PE files or if the DLLs need to be relocated, the PE loader
knows that the precomputed addresses are not valid thus it must walk the array
pointed to by <font color="#CCFFCC"><b>OriginalFirstThunk</b></font> to calculate
the new addresses of import functions.<br>
Bound import doesn't have much significance in our example because we use OriginalFirstThunk
by default. For more information about the bound import, I recommmend <a href="files/pe1.zip">LUEVELSMEYER's
pe.txt</a>.</font></p>
<hr>
<p align="center"><font face="MS Sans Serif" size="-1"><b>[<a href="http://win32asm.cjb.net">Iczelion's
Win32 Assembly Homepage</a>]</b></font></p>
<p> </p>
<p> </p>
</body>
</html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -