原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题
首先看下我们内存加载引擎的流程。
1. 申请一段大小为dll映射内存后的映像大小的内存空间。
2. 移动各个区段的数据到申请的内存。
2. 修复引入表结构的地址表。
4. 通过重定位结构修复需要重定位的地址。
5. 调用DllMain入口点
流程解析:
- pe结构中nt header结构当中的ImageSize存放的是我们整个文件映射到内存后的映像大小,这个大小是经过对齐的, 读取这个成员值,来申请内存空间,内存空间的属性为“可读可写可执行”,VirtualAlloc来申请。
- 读取各个节表结构的各区段的物理偏移和物理大小,然后移动各区段数据到节表结构对应的的内存空间中(注意:实际上就是将节表结构的VirtualAddress + 申请的内存空间地址),移动的大小则为我们物理大小.
- 修复导入表结构的地址表。其实就是修复导入表结构FirstThunk指向的地址表,因为我们的程序api调用的也是FirstThunk所指向的地址表成员。程序在未加载之前FirstThunk指向的地址表成员都指向的是一个IMAGE_IMPORT_BY_NAME结构,当加载后这个地址表的成员都被替换成相应的函数地址。所以处理导入表过程函数可以直接读取FirstThunk来取得相应的引入函数名称及序号等,没必要读取OriginalFirstThunk来读取.
- 通过重定位结构修复需要重定位的地址,连接器在链接可执行程序的时候会将需要重定位的偏移存放到一个结构中,这样pe loader读取重定位结构就可以定位到需要重定位的地址。
- 压入参数, 取得入口点rva+申请目标空间地址然后call调用即可
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
.386 02. .model flat, stdcall 03. option casemap:none 04. 05.include windows.inc 06.include user32.inc 07.include kernel32.inc 08.includelib user32.lib 09.includelib kernel32.lib 10. 11.pushad_eax equ 1ch 12. 13. .data 14. 15.szFileName db 'test.dll', 0 16.hFile dd 0 17.hMap dd 0 18. 19. .code 20. 21.Entry: 22. invoke CreateFile, addr szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, \ 23. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL 24. cmp eax, INVALID_HANDLE_VALUE 25. je _Ret 26. mov hFile, eax 27. invoke CreateFileMapping, eax, NULL, PAGE_READONLY, 0, 0, NULL 28. .if eax == NULL 29. invoke CloseHandle, hFile 30. jmp _Ret 31. .endif 32. mov hMap, eax 33. invoke MapViewOfFile, eax, FILE_MAP_READ, 0, 0, 0 34. .if eax == NULL 35. invoke CloseHandle, hMap 36. invoke CloseHandle, hFile 37. jmp _Ret 38. .endif 39. push eax 40. call DllMemLoad 41. 42._Ret: 43. ret 44.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 45.; dll内存加载函数 46.; Arguments: 47.; [esp] - return address 48.; [esp + 4 * 1] - pDllMemory 49.; Return Value: 50.; eax = 51.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 52.DllMemLoad: 53. pushad 54. mov ebx, [esp + 4 * 8 + 4] ; ebx = pDllMemory 55. cmp word ptr [ebx], 'ZM' 56. jnz _RetFalse 57. 58. push ebx 59. 60. mov edi, ebx ; edi = Dos Header 61. add edi, [edi + 3ch] ; edi = NT Header 62. cmp word ptr [edi], 'EP' 63. jnz _RetFalse 64. 65. invoke VirtualAlloc, 0, [edi + 50h], MEM_COMMIT, PAGE_EXECUTE_READWRITE 66. test eax, eax 67. je _RetFalse 68. xchg eax, ebp ; ebp = lpMemroy 69. 70. lea esi, [edi + 14h] 71. xor eax, eax 72. lodsw ; eax = IMAGE_OPTIONAL_HEADER size 73. lea esi, [esi + 2 + eax] ; esi -> section table 74.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 75.; 移动节到分配的内存 76.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 77. movzx ecx, word ptr [edi + 06h] ; ecx = section num 78. 79._MoveSection: 80. push ecx 81. mov edx, [esi + 14h] ; edx = physical offset 82. add edx, [esp + 4] ; [esp + 4] = pDllMemory, edx = source section va 83. mov eax, [esi + 0ch] ; eax = VirtualAddress 84. add eax, ebp ; ebp = lpMemory, eax = dest section va 85. invoke RtlMoveMemory, eax, edx, dword ptr [esi + 10h] 86. add esi, 28h ; esi -> next section 87. pop ecx 88. loop _MoveSection 89.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 90.; 处理导入段 91.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 92. mov edx, ebp ; edx = lpMemory 93. mov eax, edi ; eax = NT Header 94. call InitImport 95.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 96.; 处理重定位 97.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 98. mov edx, ebp ; edx = lpMemory 99. mov eax, edi ; eax = NT Header 100. call InitFixups 101. 102. mov edx, [edi + 28h] ; edx = EntryPoint RVA 103. add edx, ebp ; ebp = lpMemory, edx = EntryPoint VA 104.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 105.; DllMain 106.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 107. push 0 108. push 1 109. push ebp 110. call edx ; call DllMain 111. 112._RetTrue: 113. pop ecx 114. push ebp 115. pop dword ptr [esp + pushad_eax] ; eax = lpMemory 116. popad 117. ret 4 118. 119._RetFalse: 120. popad 121. xor eax, eax 122. ret 4 123.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 124.; 初始化导入表函数 125.; Arguments: 126.; [esp] - return address 127.; eax - NT Header 128.; edx - Alloc mem base 129.; Return Value: 130.; eax = true or eax = false 131.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 132.InitImport: 133. pushad 134. push edx ; [esp] = alloc mem base 135. mov edi, [eax + 80h] ; edi = Import RVA 136. add edi, edx ; edi = Import VA 137. 138._NextImport: 139. cmp dword ptr [edi + 0ch], 0 ; [edi + 0ch] = Name 140. jz _II_RetTrue 141. mov edx, [edi + 0ch] ; edx = Name 142. add edx, [esp] ; edx = import dll name string 143. invoke LoadLibrary, edx 144. test eax, eax 145. jz _II_RetFalse 146. xchg eax, ebx ; ebx = load dll base 147. 148. mov esi, [edi + 10h] 149. add esi, [esp] ; esi = FirstThunk 150. cld 151. 152._NextIat: 153. lodsd 154. test eax, eax 155. je _Next 156. 157. bt eax, 31 ; eax 31-bit copy to CF 158. jnc _IatName 159.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 160.; Iat ordinal 161.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 162. movzx edx, ax ; edx = ordinal 163. invoke GetProcAddress, ebx, edx ; ebx = load dll base 164. mov [esi - 4], eax 165. jmp _NextIat 166.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 167.; Iat Name 168.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 169._IatName: 170. add eax, [esp] ; eax = IMAGE_IMPORT_BY_NAME 171. lea edx, [eax + 2] ; edx -> api name 172. invoke GetProcAddress, ebx, edx 173. mov [esi - 4], eax 174. jmp _NextIat 175. 176._Next: 177. add edi, 14h ; edi -> next Iat struct 178. jmp _NextImport 179. 180._II_RetTrue: 181. pop edx 182. popad 183. xor eax, eax 184. inc eax 185. ret 186. 187._II_RetFalse: 188. pop edx 189. popad 190. xor eax, eax 191. ret 192.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 193.; 初始化并修订重定位地址 194.; Arguments: 195.; [esp] - return address 196.; eax - NT Header 197.; edx - alloc mem base 198.; Return Value: 199.; eax - true or eax - false; 200.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 201.InitFixups: 202. pushad 203. 204. mov esi, [eax + 0a0h] ; esi = reloc rva 205. test esi, esi 206. je _IF_RetFalse ; No reloc 207. cld 208. add esi, edx ; esi = 重定位表的地址 209. 210. push [eax + 0a4h] ; [esp] = 重定位表长度 211. add [esp], esi ; [esp] = 重定位表结束地址 212. 213. mov edi, edx ; edi = alloc mem base 214. mov ebp, edx ; ebp = alloc mem base 215. sub ebp, [eax + 34h] ; ebp等于分配的地址与建议装载地址的差 216. 217._NextFixups: 218. cmp [esp], esi ; 比较是否到了重定位表结束位置 219. je _IF_RetTrue 220. lodsd 221. xchg eax, ebx ; ebx = 当前页起始地址RVA 222. lodsd 223. xchg eax, ecx ; ecx = 当前重定位块的大小 224. sub ecx, 8 225. shr ecx, 1 ; ecx = 重定位项的数量 226. 227._NextOffet: 228. xor eax, eax 229. lodsw ; eax = 读取重定位项 230. bt eax, 13 ; 将eax的第13位复制到CF 231. jnc _NextLoop 232. 233. and ax, 0fffh ; 保留低12位 234. add eax, ebx 235. add dword ptr [edi + eax], ebp ; 修正,加上差值 236. 237._NextLoop: 238. loop _NextOffet 239. jmp _NextFixups 240. 241._IF_RetTrue: 242. pop edx 243. popad 244. xor eax, eax 245. inc eax 246. ret 247. 248._IF_RetFalse: 249. popad 250. xor eax, eax 251. ret 252. 253. end Entry