重定位(搜索KERNEL32.DLL得到API地址)

来源:互联网 发布:乐乎lofter 小乐清水子 编辑:程序博客网 时间:2024/05/19 09:15
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     cld100     101     repnz    scasb ;比较edi与al  直到内容相同退出(即最后一个 0 时退出)102     103     mov    ecx, edi ;在repnz  比对过程中 edi 最终指向 字符串末尾的地址104     sub    ecx, _lpApi ;尾地址-首地址=字符串长度105     mov    @dwLen, ecx106     107     ;从pe文件头的数据目录表 取出导出表首地址108     mov    esi, _hModule109     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 ;得到真实VA121     122     xor    edx, edx123     .repeat124         push     esi125         mov    edi, [ebx]126         add    edi, _hModule ;得到函数名的VA127         128         mov    esi, _lpApi129         mov    ecx, @dwLen130         repz    cmpsb    ;比较[edi]、[esi]的内容,将比对结果写入标志位,直到不相同或者ecx为0131         132         .if    ZERO? ;标志位为0 表示上面的的字符串比对 是匹配的133             pop    esi134             jmp    @F135             136         .endif137         138         pop    esi139         add    ebx, 4 ;比对 函数名地址数组的 下一项函数名140         inc    edx141     .until    edx >=  [esi].NumberOfNames142     jmp    _ret143     144 @@:145     ;通过上面步骤得到的AddressOfNames的数组索引  获取函数调用的VA146     sub    ebx, [esi].AddressOfNames147     sub    ebx, _hModule    ;获得 指定函数名地址 到函数名数组首地址的偏移 148     149     shr    ebx, 1    ;偏移值 移位操作 =ebx/2 150     151     add    ebx, [esi].AddressOfNameOrdinals ;首地址+索引值   即 为指定 AddressOfNameOrdinals 的RVA152     ;指定的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 ;得到 指定函数调用的 VA162     163     mov    eax, [eax]164     add    eax, _hModule165     mov    @ret, eax166     167 _ret:168     assume    esi :nothing169     popad170     mov    eax, @ret171     172     ret173 _getApi endp174 175 ;------------------------------------------176 ;函数真正开始执行的地方177 ;------------------------------------------178 start:179     ;取当前函数的栈顶数据180     ;函数刚开始的时候栈顶地址 一定存在于kernel32.dll地址空间内181     ;正合适以此寻找kernel32.dll的基址182     mov    eax, dword ptr [esp]183     push    eax184     call    @F185 @@:186     pop    ebx187     sub    ebx, @B ;重定位功能:ebx存储基址188     189     pop    eax190     191     ;获取kernel32.dll的基址192     invoke    _getKernelBase,eax193     mov    [ebx + offset hKernel32Base], eax194     195     ;从基地址出发搜索GetProcAddress函数的地址196     mov    eax, offset szGetProcAddr197     add    eax, ebx ;eax存储的是 加了基址之后的szGetProcAddr的绝对地址VA198     199     mov    edi, offset hKernel32Base200     mov    ecx, [edi + ebx]201     202     ;调用函数  获取GetProcAddress的调用地址203     invoke    _getApi, ecx, eax204     205     mov    [ebx + offset lpGetProcAddr], eax206     207     ;将GetProcAddress的真实调用地址 存入_getProcAddress变量中208     mov    [ebx + offset _getProcAddress], eax209     210     ;下面使用GetProcAddress函数的基址 调用该函数的功能211     ;参数1:获取  LoadLibraryA 函数名字符串的地址212     mov    eax, offset szLoadLib213     add    eax, ebx214     215     ;参数2:获取 Kernel32.dll的基址216     mov    edi, offset hKernel32Base217     mov    ecx, [edi + ebx] 218     219     ;获取GetProcAddress的函数调用地址220     mov    edx, offset _getProcAddress221     add    edx, ebx222     223     ;调用函数GetProcAddress  获取LoadLibraryA的调用地址224     ;GetProcAddress hKernel32Base,LoadLibraryA225     push    eax226     push    ecx227     call    dword ptr [edx]228     229     ;将函数LoadLibraryA的调用地址存入_loadLibrary变量230     mov    [ebx + offset _loadLibrary], eax231     232     ;使用LoadLibrary载入user32.dll233     ;参数1:获取user32.dll字符串的地址234     mov    eax, offset user32_DLL235     add    eax, ebx236     237     ;函数LoadLibrary的调用地址238     mov    edi, offset _loadLibrary239     mov    edx, [ebx + edi]240     241     ;调用函数LoadLibraryA242     ;LoadLibraryA "user32.dll"243     push    eax244     call    edx245     246     ;保存user32.dll的基址247     mov    [ebx + offset hUser32Base], eax248     249     ;获取MessageBoxA的函数调用地址250     ;参数1:MessageBoxA函数名的地址251     mov    eax, offset szMessageBox252     add    eax, ebx253     254     ;参数2:user32.dll的基址255     mov     edi, offset hUser32Base256     mov    ecx, [edi + ebx]257     258     ;参数3:GetProcAddress地址259     mov    edx, offset _getProcAddress260     add    edx, ebx261     262     ;模仿调用:GetProcAddress hUser32Base,MessageBoxA263     push    eax264     push    ecx265     call    dword ptr [edx]266     267     ;保存MessageBoxA的函数调用地址268     mov    [ebx + offset _messageBox], eax269     270     ;调用MessageBoxA这个方法271     ;参数1:字符串szText272     mov    eax, offset szText273     add    eax, ebx274     275     ;参数2:函数MessageBoxA的地址276     mov    edx, offset _messageBox277     add    edx, ebx278     279     ;模仿调用MessageBoxA NULL, addr szText,Null,MB_OK280     push     MB_OK281     push     NULL282     push     eax283     push     NULL284     call    dword ptr [edx]285      286     ret287     288     end start289     290     291