zoukankan      html  css  js  c++  java
  • 【读书笔记】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
  • 相关阅读:
    吴裕雄 python深度学习与实践(1)
    吴裕雄 python 机器学习-Logistic(1)
    吴裕雄 python 熵权法确定特征权重
    【Uva 1252】Twenty Questions
    【玲珑杯 round#18 B】图论你先敲完模板
    【Uva 10817】Headmaster's Headache
    【玲珑杯 round#18 A】计算几何你瞎暴力
    【Uva 12128】Perfect Service
    【UVa 12186】Another Crisis
    【Uva 10003】Cutting Sticks
  • 原文地址:https://www.cnblogs.com/moriarty/p/2468433.html
Copyright © 2011-2022 走看看