zoukankan      html  css  js  c++  java
  • 【读书笔记】《windows PE 权威指南》重定位

      1 ;--------------------------------
      2 ;动态加载功能实现
      3 ;moriarty
      4 ;2012/04/13
      5 ;--------------------------------
      6 .386
      7 .model flat,stdcall
      8 option casemap:none
      9 
     10 include windows.inc
     11 
     12 ;声明函数
     13 _QLGetProcAddress    typedef proto     :dword, :dword
     14 
     15 ;声明函数引用
     16 _ApiGetProcAddress     typedef     ptr     _QLGetProcAddress
     17 
     18 _QLLoadLib     typedef        proto    :dword
     19 _ApiLoadLib     typedef        ptr    _QLLoadLib
     20 
     21 _QLMessageBoxA    typedef        proto    :dword, :dword, :dword
     22 _ApiMessageBoxA    typedef        ptr    _QLMessageBoxA
     23 
     24 
     25 ;代码段
     26 .code
     27 szText        db    'HelloWorldPE',0
     28 szGetProcAddr    db    'GetProcAddress',0
     29 szLoadLib    db    'LoadLibraryA',0
     30 szMessageBox    db    'MessageBoxA',0
     31 
     32 user32_DLL    db    'user32.dll',0,0
     33 
     34 ;定义函数
     35 _getProcAddress        _ApiGetProcAddress    ?
     36 _loadLibrary        _ApiLoadLib        ?
     37 _messageBox        _ApiMessageBoxA        ?
     38 
     39 hKernel32Base    dd    ?
     40 hUser32Base    dd    ?
     41 lpGetProcAddr    dd    ?
     42 lpLoadLib    dd    ?
     43 
     44 ;------------------------------------------
     45 ;根据kernel32.dll中的一个地址获取它的基址
     46 ;------------------------------------------
     47 _getKernelBase    proc    _dwKernelRetAddress
     48     
     49     LOCAL    @dwRet
     50     pushad
     51     mov     @dwRet, 0
     52     
     53     mov    edi, _dwKernelRetAddress
     54     and    edi, 0ffff0000h
     55     
     56     .repeat
     57         ;找到kernel32.dll的dos头
     58         .if word ptr [edi] == IMAGE_DOS_SIGNATURE
     59             mov     esi, edi
     60             add    esi, [esi+003ch]
     61             
     62             ;找到kernel32.dll的pe头标示
     63             .if word ptr [esi] == IMAGE_NT_SIGNATURE
     64                 mov    @dwRet, edi
     65                 .break
     66             .endif
     67         .endif
     68         
     69         sub    edi, 010000h
     70         .break    .if edi < 070000000h
     71     .until FALSE
     72     
     73     popad
     74     mov     eax, @dwRet
     75     
     76     ret
     77 
     78 _getKernelBase endp
     79 
     80 
     81 ;--------------------------------------------------------
     82 ;获取指定字符串的api函数的调用地址
     83 ;入口参数:    _hModule 为动态链接库的基址
     84 ;        _lpApi 为api函数名的首地址
     85 ;出口参数:eax为函数在虚拟地址空间中的真实地址
     86 ;--------------------------------------------------------
     87 _getApi    proc    _hModule, _lpApi
     88     
     89     LOCAL    @ret
     90     LOCAL    @dwLen
     91     
     92     pushad
     93     mov    @ret, 0
     94     
     95     ;计算api字符串的长度 (包括最后的0在内)
     96     mov    edi, _lpApi
     97     mov    ecx, -1
     98     xor    al , al
     99     cld
    100     
    101     repnz    scasb ;比较edi与al  直到内容相同退出(即最后一个 0 时退出)
    102     
    103     mov    ecx, edi ;在repnz  比对过程中 edi 最终指向 字符串末尾的地址
    104     sub    ecx, _lpApi ;尾地址-首地址=字符串长度
    105     mov    @dwLen, ecx
    106     
    107     ;从pe文件头的数据目录表 取出导出表首地址
    108     mov    esi, _hModule
    109     add    esi, [esi+3ch] ;[esi+3ch]指向dos头的e_lfaNew字段
    110     
    111     assume    esi :ptr IMAGE_NT_HEADERS ;esi指向IMAGE_NT_HEADERS 的结构
    112     
    113     mov    esi, [esi].OptionalHeader.DataDirectory.VirtualAddress; 指向数据目标表第一项(即DataDirectory[0]导出表)
    114     add    esi, _hModule ;导出表的虚拟地址VA=偏移+基址
    115     
    116     assume    esi :ptr IMAGE_EXPORT_DIRECTORY ;
    117     
    118     ;查找指定名称的导出函数
    119     mov    ebx, [esi].AddressOfNames; 导出函数名地址 数组
    120     add    ebx, _hModule ;得到真实VA
    121     
    122     xor    edx, edx
    123     .repeat
    124         push     esi
    125         mov    edi, [ebx]
    126         add    edi, _hModule ;得到函数名的VA
    127         
    128         mov    esi, _lpApi
    129         mov    ecx, @dwLen
    130         repz    cmpsb    ;比较[edi]、[esi]的内容,将比对结果写入标志位,直到不相同或者ecx为0
    131         
    132         .if    ZERO? ;标志位为0 表示上面的的字符串比对 是匹配的
    133             pop    esi
    134             jmp    @F
    135             
    136         .endif
    137         
    138         pop    esi
    139         add    ebx, 4 ;比对 函数名地址数组的 下一项函数名
    140         inc    edx
    141     .until    edx >=  [esi].NumberOfNames
    142     jmp    _ret
    143     
    144 @@:
    145     ;通过上面步骤得到的AddressOfNames的数组索引  获取函数调用的VA
    146     sub    ebx, [esi].AddressOfNames
    147     sub    ebx, _hModule    ;获得 指定函数名地址 到函数名数组首地址的偏移 
    148     
    149     shr    ebx, 1    ;偏移值 移位操作 =ebx/2 
    150     
    151     add    ebx, [esi].AddressOfNameOrdinals ;首地址+索引值   即 为指定 AddressOfNameOrdinals 的RVA
    152     ;指定的AddressOfNameOrdinals 的VA  
    153     ;注:AddressOfNameOrdinals里面存储的是AddressOfFunctions的索引
    154     ;AddressOfFunctions里面存储的是函数调用的地址
    155     add    ebx, _hModule    
    156     
    157     movzx    eax, word ptr [ebx]
    158     shl    eax, 2    ;根据AddressOfNameOrdinals里面的索引值*2 = AddressOfFunctions首地址的偏移量
    159     
    160     add    eax, [esi].AddressOfFunctions ;函数调用数组首地址+偏移量= 所需求的那个 函数调用地址
    161     add    eax, _hModule ;得到 指定函数调用的 VA
    162     
    163     mov    eax, [eax]
    164     add    eax, _hModule
    165     mov    @ret, eax
    166     
    167 _ret:
    168     assume    esi :nothing
    169     popad
    170     mov    eax, @ret
    171     
    172     ret
    173 _getApi endp
    174 
    175 ;------------------------------------------
    176 ;函数真正开始执行的地方
    177 ;------------------------------------------
    178 start:
    179     ;取当前函数的栈顶数据
    180     ;函数刚开始的时候栈顶地址 一定存在于kernel32.dll地址空间内
    181     ;正合适以此寻找kernel32.dll的基址
    182     mov    eax, dword ptr [esp]
    183     push    eax
    184     call    @F
    185 @@:
    186     pop    ebx
    187     sub    ebx, @B ;重定位功能:ebx存储基址
    188     
    189     pop    eax
    190     
    191     ;获取kernel32.dll的基址
    192     invoke    _getKernelBase,eax
    193     mov    [ebx + offset hKernel32Base], eax
    194     
    195     ;从基地址出发搜索GetProcAddress函数的地址
    196     mov    eax, offset szGetProcAddr
    197     add    eax, ebx ;eax存储的是 加了基址之后的szGetProcAddr的绝对地址VA
    198     
    199     mov    edi, offset hKernel32Base
    200     mov    ecx, [edi + ebx]
    201     
    202     ;调用函数  获取GetProcAddress的调用地址
    203     invoke    _getApi, ecx, eax
    204     
    205     mov    [ebx + offset lpGetProcAddr], eax
    206     
    207     ;将GetProcAddress的真实调用地址 存入_getProcAddress变量中
    208     mov    [ebx + offset _getProcAddress], eax
    209     
    210     ;下面使用GetProcAddress函数的基址 调用该函数的功能
    211     ;参数1:获取  LoadLibraryA 函数名字符串的地址
    212     mov    eax, offset szLoadLib
    213     add    eax, ebx
    214     
    215     ;参数2:获取 Kernel32.dll的基址
    216     mov    edi, offset hKernel32Base
    217     mov    ecx, [edi + ebx] 
    218     
    219     ;获取GetProcAddress的函数调用地址
    220     mov    edx, offset _getProcAddress
    221     add    edx, ebx
    222     
    223     ;调用函数GetProcAddress  获取LoadLibraryA的调用地址
    224     ;GetProcAddress hKernel32Base,LoadLibraryA
    225     push    eax
    226     push    ecx
    227     call    dword ptr [edx]
    228     
    229     ;将函数LoadLibraryA的调用地址存入_loadLibrary变量
    230     mov    [ebx + offset _loadLibrary], eax
    231     
    232     ;使用LoadLibrary载入user32.dll
    233     ;参数1:获取user32.dll字符串的地址
    234     mov    eax, offset user32_DLL
    235     add    eax, ebx
    236     
    237     ;函数LoadLibrary的调用地址
    238     mov    edi, offset _loadLibrary
    239     mov    edx, [ebx + edi]
    240     
    241     ;调用函数LoadLibraryA
    242     ;LoadLibraryA "user32.dll"
    243     push    eax
    244     call    edx
    245     
    246     ;保存user32.dll的基址
    247     mov    [ebx + offset hUser32Base], eax
    248     
    249     ;获取MessageBoxA的函数调用地址
    250     ;参数1:MessageBoxA函数名的地址
    251     mov    eax, offset szMessageBox
    252     add    eax, ebx
    253     
    254     ;参数2:user32.dll的基址
    255     mov     edi, offset hUser32Base
    256     mov    ecx, [edi + ebx]
    257     
    258     ;参数3:GetProcAddress地址
    259     mov    edx, offset _getProcAddress
    260     add    edx, ebx
    261     
    262     ;模仿调用:GetProcAddress hUser32Base,MessageBoxA
    263     push    eax
    264     push    ecx
    265     call    dword ptr [edx]
    266     
    267     ;保存MessageBoxA的函数调用地址
    268     mov    [ebx + offset _messageBox], eax
    269     
    270     ;调用MessageBoxA这个方法
    271     ;参数1:字符串szText
    272     mov    eax, offset szText
    273     add    eax, ebx
    274     
    275     ;参数2:函数MessageBoxA的地址
    276     mov    edx, offset _messageBox
    277     add    edx, ebx
    278     
    279     ;模仿调用MessageBoxA NULL, addr szText,Null,MB_OK
    280     push     MB_OK
    281     push     NULL
    282     push     eax
    283     push     NULL
    284     call    dword ptr [edx]
    285      
    286     ret
    287     
    288     end start
    289     
    290     
    291         
  • 相关阅读:
    深入Apache NiFi 之源码学习
    Apache NiFi 核心概念和关键特性
    运营商手机视频流量包业务日志ETL及统计分析
    HDP Hive性能调优
    redis 实现登陆次数限制
    Hadoop和Spark的Shuffer过程对比解析
    Scala-基础知识
    Python基础知识问答
    Python基础知识+计算器练习
    Sqoop架构原理及常用命令参数
  • 原文地址:https://www.cnblogs.com/moriarty/p/2451806.html
Copyright © 2011-2022 走看看