打造DLL内存加载引擎学习笔记

本文介绍了DLL内存加载引擎的工作流程,包括申请内存、移动区段数据、修复引入表、处理重定位和调用DllMain。详细讲解了PE结构中的ImageSize如何用于内存分配,以及如何修复导入表和处理重定位项,以确保正确执行DLL。
摘要由CSDN通过智能技术生成

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题


首先看下我们内存加载引擎的流程。

  1. 申请一段大小为dll映射内存后的映像大小的内存空间。

  2. 移动各个区段的数据到申请的内存。

  2. 修复引入表结构的地址表。

  4. 通过重定位结构修复需要重定位的地址。

  5. 调用DllMain入口点

流程解析:

  1. pe结构中nt header结构当中的ImageSize存放的是我们整个文件映射到内存后的映像大小,这个大小是经过对齐的, 读取这个成员值,来申请内存空间,内存空间的属性为“可读可写可执行”,VirtualAlloc来申请。
  2. 读取各个节表结构的各区段的物理偏移和物理大小,然后移动各区段数据到节表结构对应的的内存空间中(注意:实际上就是将节表结构的VirtualAddress + 申请的内存空间地址),移动的大小则为我们物理大小.
  3. 修复导入表结构的地址表。其实就是修复导入表结构FirstThunk指向的地址表,因为我们的程序api调用的也是FirstThunk所指向的地址表成员。程序在未加载之前FirstThunk指向的地址表成员都指向的是一个IMAGE_IMPORT_BY_NAME结构,当加载后这个地址表的成员都被替换成相应的函数地址。所以处理导入表过程函数可以直接读取FirstThunk来取得相应的引入函数名称及序号等,没必要读取OriginalFirstThunk来读取.
  4. 通过重定位结构修复需要重定位的地址,连接器在链接可执行程序的时候会将需要重定位的偏移存放到一个结构中,这样pe loader读取重定位结构就可以定位到需要重定位的地址。
  5. 压入参数, 取得入口点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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值