原文: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调用即可
代码:
.386
.model flat, stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
pushad_eax equ 1ch
.data
szFileName db 'test.dll', 0
hFile dd 0
hMap dd 0
.code
Entry:
invoke CreateFile, addr szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, \
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
cmp eax, INVALID_HANDLE_VALUE
je _Ret
mov hFile, eax
invoke CreateFileMapping, eax, NULL, PAGE_READONLY, 0, 0, NULL
.if eax == NULL
invoke CloseHandle, hFile
jmp _Ret
.endif
mov hMap, eax
invoke MapViewOfFile, eax, FILE_MAP_READ, 0, 0, 0
.if eax == NULL
invoke CloseHandle, hMap
invoke CloseHandle, hFile
jmp _Ret
.endif
push eax
call DllMemLoad
_Ret:
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; dll内存加载函数
; Arguments:
; [esp] - return address
; [esp + 4 * 1] - pDllMemory
; Return Value:
; eax =
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DllMemLoad:
pushad
mov ebx, [esp + 4 * 8 + 4] ; ebx = pDllMemory
cmp word ptr [ebx], 'ZM'
jnz _RetFalse
push ebx
mov edi, ebx ; edi = Dos Header
add edi, [edi + 3ch] ; edi = NT Header
cmp word ptr [edi], 'EP'
jnz _RetFalse
invoke VirtualAlloc, 0, [edi + 50h], MEM_COMMIT, PAGE_EXECUTE_READWRITE
test eax, eax
je _RetFalse
xchg eax, ebp ; ebp = lpMemroy
lea esi, [edi + 14h]
xor eax, eax
lodsw ; eax = IMAGE_OPTIONAL_HEADER size
lea esi, [esi + 2 + eax] ; esi -> section table
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 移动节到分配的内存
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
movzx ecx, word ptr [edi + 06h] ; ecx = section num
_MoveSection:
push ecx
mov edx, [esi + 14h] ; edx = physical offset
add edx, [esp + 4] ; [esp + 4] = pDllMemory, edx = source section va
mov eax, [esi + 0ch] ; eax = VirtualAddress
add eax, ebp ; ebp = lpMemory, eax = dest section va
invoke RtlMoveMemory, eax, edx, dword ptr [esi + 10h]
add esi, 28h ; esi -> next section
pop ecx
loop _MoveSection
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 处理导入段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
mov edx, ebp ; edx = lpMemory
mov eax, edi ; eax = NT Header
call InitImport
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 处理重定位
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
mov edx, ebp ; edx = lpMemory
mov eax, edi ; eax = NT Header
call InitFixups
mov edx, [edi + 28h] ; edx = EntryPoint RVA
add edx, ebp ; ebp = lpMemory, edx = EntryPoint VA
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; DllMain
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
push 0
push 1
push ebp
call edx ; call DllMain
_RetTrue:
pop ecx
push ebp
pop dword ptr [esp + pushad_eax] ; eax = lpMemory
popad
ret 4
_RetFalse:
popad
xor eax, eax
ret 4
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 初始化导入表函数
; Arguments:
; [esp] - return address
; eax - NT Header
; edx - Alloc mem base
; Return Value:
; eax = true or eax = false
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
InitImport:
pushad
push edx ; [esp] = alloc mem base
mov edi, [eax + 80h] ; edi = Import RVA
add edi, edx ; edi = Import VA
_NextImport:
cmp dword ptr [edi + 0ch], 0 ; [edi + 0ch] = Name
jz _II_RetTrue
mov edx, [edi + 0ch] ; edx = Name
add edx, [esp] ; edx = import dll name string
invoke LoadLibrary, edx
test eax, eax
jz _II_RetFalse
xchg eax, ebx ; ebx = load dll base
mov esi, [edi + 10h]
add esi, [esp] ; esi = FirstThunk
cld
_NextIat:
lodsd
test eax, eax
je _Next
bt eax, 31 ; eax 31-bit copy to CF
jnc _IatName
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Iat ordinal
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
movzx edx, ax ; edx = ordinal
invoke GetProcAddress, ebx, edx ; ebx = load dll base
mov [esi - 4], eax
jmp _NextIat
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Iat Name
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_IatName:
add eax, [esp] ; eax = IMAGE_IMPORT_BY_NAME
lea edx, [eax + 2] ; edx -> api name
invoke GetProcAddress, ebx, edx
mov [esi - 4], eax
jmp _NextIat
_Next:
add edi, 14h ; edi -> next Iat struct
jmp _NextImport
_II_RetTrue:
pop edx
popad
xor eax, eax
inc eax
ret
_II_RetFalse:
pop edx
popad
xor eax, eax
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 初始化并修订重定位地址
; Arguments:
; [esp] - return address
; eax - NT Header
; edx - alloc mem base
; Return Value:
; eax - true or eax - false;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
InitFixups:
pushad
mov esi, [eax + 0a0h] ; esi = reloc rva
test esi, esi
je _IF_RetFalse ; No reloc
cld
add esi, edx ; esi = 重定位表的地址
push [eax + 0a4h] ; [esp] = 重定位表长度
add [esp], esi ; [esp] = 重定位表结束地址
mov edi, edx ; edi = alloc mem base
mov ebp, edx ; ebp = alloc mem base
sub ebp, [eax + 34h] ; ebp等于分配的地址与建议装载地址的差
_NextFixups:
cmp [esp], esi ; 比较是否到了重定位表结束位置
je _IF_RetTrue
lodsd
xchg eax, ebx ; ebx = 当前页起始地址RVA
lodsd
xchg eax, ecx ; ecx = 当前重定位块的大小
sub ecx, 8
shr ecx, 1 ; ecx = 重定位项的数量
_NextOffet:
xor eax, eax
lodsw ; eax = 读取重定位项
bt eax, 13 ; 将eax的第13位复制到CF
jnc _NextLoop
and ax, 0fffh ; 保留低12位
add eax, ebx
add dword ptr [edi + eax], ebp ; 修正,加上差值
_NextLoop:
loop _NextOffet
jmp _NextFixups
_IF_RetTrue:
pop edx
popad
xor eax, eax
inc eax
ret
_IF_RetFalse:
popad
xor eax, eax
ret
end Entry