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