【读书笔记】-PE导入表读取遇到的问题

1、  关于PE加载器加载PE文件与内存映射文件的区别;

Pe加载器加载pe文件到内存,如用户点击一个应用程序时,pe加载器就开始进行例行工作,加载pe文件及所需的相关资源引用到内存;

内存映射是将磁盘文件一模一样的映射到虚拟地址空间中;

所以两者有个很重要的区别:就是对齐单位的不同;pe加载器加载pe文件到内存,使用的内存对齐4k大小;而内存映射文件则还是文件对齐的单位200k【当然是指默认的情况下】。

因为上述情况,解释了我的一个疑惑,为什么很多读取导入表、导出表的程序中,会有一个很频繁的调用 _RvaToFileOffset 就是讲内存RVA转换为文件偏移;

就是因为我们通常读取pe文件的一些信息都是通过内存映射的方式,这种方式就是把硬盘的文件格式放到内存中而已;但是我们读取到的pe文件格式字段中有大量的rva值得字段,所以涉及到这些字段,都必须转换为文件偏移值,才能读取到正确的地址。

 

2、  编写程序过程中需要trace一些寄存器或变量的方法;

Masm32中有很多已经定义好了的宏,我们只需包含以下头文件就可以利用这些宏来trace一些数据,以验证程序的正确性;

           PrintString 变量 ;输出变量的值

           PrintStringByAddr 变量/寄存器  ;输出变量或寄存器所保存地址的字符串

           PrintDec 寄存器  ;输出寄存器的十进制数值

           PrintHex 寄存器  ;输出寄存器的十六进制数值

         PrintLine                     ;输出一条线

这就是几个常用的用来trace的宏,当然也可以用MessageBox函数

记住:要引用相关的头文件

include                comdlg32.inc

includelib  comdlg32.lib

include                debug.inc

includelib  debug.lib

include                masm32.inc

includelib  masm32.lib

 

3、  对于导入表的相关结构加深印象与理解;

IMAGE_IMPORT_DESCRIPTOR 这个结构 对应着程序里面引用的DLL个数;程序每引用一个DLL动态链接库,就会有一个这样的结构;这么一系列结构 以一个所有字段为0的IMAGE_IMPORT_DESCRIPTOR 结束;

其中需要关注的字段是OriginalFirstThunk与FirstThunk 这两个字段 可以推演出程序的导入函数信息;只不过前者是可以推出INT表,后者可推出IAT表,

IMAGE_IMPORT_DESCRIPTOR.Name1 这个字段 是一个rva值,指向dll名称字符串。

 

IMAGE_THUNK_DATA 一个指针结构,4个字节RVA;以全0的IMAGE_THUNK_DATA表示结束。这个RVA值,指向IMAGE_IMPORT_BY_NAME 【导入函数信息】

IMAGE_IMPORT_BY_NAME结构 就2个字段,一个是Hint 意义不大;另外一个是IMAGE_IMPORT_BY_NAME.Name1 这个是导入函数的ascii 字符串函数名称【这是内存映射模式下,pe加载器模式下还没有研究】。

 

4、  附源码:

;===============================================
;读取pe文件的导入表信息
;james.Moriarty
;2012/04/24
;===============================================
.386
.model    flat,stdcall
option    casemap:none

include        windows.inc
include        kernel32.inc
includelib    kernel32.lib
include        user32.inc
includelib    user32.lib

;下面的头文件主要是为了输出调试信息而用
include        comdlg32.inc
includelib    comdlg32.lib
include        debug.inc
includelib    debug.lib
include        masm32.inc
includelib    masm32.lib

.data
    szFileName    db    'D:\Source\macro\macro.exe',0
    szErrorMsg    db    '文件操作错误!',0
    
    hFile        dd    ?
    hMapFile    dd    ?
    lpMemory    dd    ?
    
    

.code
;-----------------------------------------------
;将内存rva转换为文件偏移foa
;-----------------------------------------------
_RvaToFileOffset    proc    _lpFile,_dwRva
    LOCAL    @dwRet
    pushad
    
    ;esi -> pe头
    mov    esi,    _lpFile
    assume    esi    :ptr IMAGE_DOS_HEADER
    add    esi,    [esi].e_lfanew
    assume    esi    :ptr IMAGE_NT_HEADERS
    
    mov    edi,    _dwRva
    
    ;edx -> 节表
    ;ecx <= 节的个数
    mov    edx,    esi
    add    edx,    sizeof IMAGE_NT_HEADERS
    assume    edx    :ptr IMAGE_SECTION_HEADER
    movzx    ecx,    [esi].FileHeader.NumberOfSections
    
    ;循环每个节表 以求得参数_dwRva这个rva值是在哪个节内?
    .repeat
        mov    eax,    [edx].VirtualAddress
        ;VirtualSize的值不一定准确,用SizeOfRawData取节区的起止范围也可以
        ;当然也可以取下一个节区的起始值作为本节的结束值
        ;(VirtualAddress+SizeOfRawData) --- 下个节区开始  这个范围都是 0 填充的
        add    eax,    [edx].SizeOfRawData
                
        
        
        ;_dwRva在 本节区范围内
        .if    (edi>=[edx].VirtualAddress) && (edi<eax)
            
            ;计算节内偏移
            mov    eax,    [edx].VirtualAddress
            sub    edi,    eax
            
            ;该节 起始位置的文件偏移
            mov    eax,    [edx].PointerToRawData
            
            ;加上节内偏移   就等于 rva的 文件偏移
            add    eax,    edi
            
            jmp    @f
        .endif
        
        add    edx,    sizeof    IMAGE_SECTION_HEADER
    .untilcxz
    
    assume    edi    :nothing
    assume    esi    :nothing
    mov    eax,    -1
    
    @@:
    mov    @dwRet,    eax
    popad
    mov    eax,    @dwRet
    
    
    ret

_RvaToFileOffset endp

start:    
    ;打开指定文件,并将文件内存映射
    invoke    CreateFile,offset szFileName,FILE_SHARE_READ or FILE_SHARE_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
    .if    eax == INVALID_HANDLE_VALUE
        jmp    error
    .endif
    mov    hFile,    eax
    
    invoke    CreateFileMapping,hFile,NULL,PAGE_READONLY,0,0,NULL
    .if    !eax
        jmp    error
    .endif
    mov    hMapFile,    eax
    
    invoke    MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
    .if    !eax
        jmp    error
    .endif
    mov    lpMemory,    eax
    
    ;esi指向dos头
    mov    esi,    eax
    assume    esi    :ptr IMAGE_DOS_HEADER
    
    ;edi指向pe头
    mov    edi,    [esi].e_lfanew
    add    edi,    esi
    assume    edi    :ptr IMAGE_NT_HEADERS
    
    ;edx 指向DataDirectory的第二项 即导入表
    mov    edx,    [edi].OptionalHeader.DataDirectory[8].VirtualAddress    
    
    invoke    _RvaToFileOffset,esi,edx
    mov    edx,    eax
    add    edx,    esi
    assume    edx    :ptr IMAGE_IMPORT_DESCRIPTOR
    
    
    ;循环遍历每个IMAGE_IMPORT_DESCRIPTOR结构(即每个引用的dll)
    .while     [edx].OriginalFirstThunk || [edx].TimeDateStamp || [edx].ForwarderChain || [edx].Name1 || [edx].FirstThunk
        
        invoke    _RvaToFileOffset,esi,[edx].Name1
        add    eax,    esi
        PrintLine
        PrintStringByAddr eax
        
        .if     [edx].OriginalFirstThunk
            mov    ebx,    [edx].OriginalFirstThunk
        .elseif
            mov    ebx,    [edx].FirstThunk
        .endif
        
        ;PrintHex    ebx
        
        invoke    _RvaToFileOffset,esi,ebx
        add    eax,    esi
        mov    ebx,    eax
        
        ;循环遍历每个dll里面的每个函数
        .while    dword ptr [ebx]
            
            .if    dword ptr [ebx] & IMAGE_ORDINAL_FLAG32
                mov    eax,    dword ptr [ebx]
                and    eax,    0ffffh
                PrintHex    eax
            .else                
                invoke     _RvaToFileOffset,esi,dword ptr [ebx]                
                add    eax,    esi
                assume    eax    :ptr IMAGE_IMPORT_BY_NAME
                
                movzx    ecx,    [eax].Hint
                PrintDec    ecx
                
                ;IMAGE_IMPORT_BY_NAME.Name1是一个ascii字符串 变长数组
                ;所以取地址
                lea    eax,    [eax].Name1
                PrintStringByAddr eax
                
                
            .endif
                                               ;
            add    ebx,    4
        .endw
        add    edx,    sizeof IMAGE_IMPORT_DESCRIPTOR
    .endw
    
    ;释放资源
    invoke    UnmapViewOfFile,lpMemory
    invoke    CloseHandle,hMapFile
    invoke    CloseHandle,hFile
    
    
    jmp    return
error:    
    invoke    MessageBox,NULL,offset szErrorMsg,NULL,MB_OK

return:
    invoke    ExitProcess,NULL
end    start

 

posted on 2012-04-24 17:35  james_moriarty  阅读(2010)  评论(0编辑  收藏  举报

导航