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