高级内存代码注入技术
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
高级内存代码注入技术
1、概述
1.1 PE注入
我们知道进程注入的方法分类主要有以下几种:
DLL注入利用注册表注入利用Windows Hooks注入利用远程线程注入利用特洛伊DLL注入不带DLL的注入直接将代码写入目标进程,并启动远程线程
本文中的技术不同于以往的DLL注入和shellcode注入。通过此方法可将一个进程的完整镜像完全注入到另外一个进程的内存空间中,从而在一个进程空间中包含了两套不同的代码。与DLL注入相比,PE注入的主要优势是不需要很多文件,只需要MAIN.EXE注入到其他进程并唤起自身代码即可。
1.2 影响
利用该方法可以达到一下多个效果:
创建Socket及网络访问访问文件系统创建线程访问系统库访问普通运行时库远控
键盘记录
2、技术原理
2.1 进程代码注入
将代码写入进程的方法较简单,Windows提供了进程内存读取和写入的系统API。首先需要获取某个进程的PID号,然后打开该进程,可以利用Kernel32链接库中的OpenProcess函数实现。
注:远程打开进程是受限的,从Vista起,就存在了类似UAC之类的防护措施。其中主要的进程内存保护机制是Mandatory Integrity Control(MIC)。MIC是基于“完整性级别”的对象访问控制保护方法。完整性级别包括如下四个级别:
低级别:被限制访问大部分系统资源(如IE);中级别:开启UAC时非特权用户和管理员组用户启动的进程;高级别:以管理员权限启动的进程系统级别:SYSTEM用户启动的进程,如系统服务。
根据以上完整性级别要求,我们的进程注入将只允许注入低级别或同等级别的进程。
本文将利用explorer.exe来举例,远程打开进程后利用VirtualAllocEx函数申请一段可保存本进程镜像的内存段,为了计算需要的内存大小我们可以通过读取PE头信息来获取。
?
1 2 3 4 5 6 7 8 9
10
11 /*获取进程模块镜像*/
module = GetModuleHandle(NULL);
/*获取PE头信息*/
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)((LPBYTE)module +((PIMAGE_DOS_HEA /*获取代码长度*/
DWORD moduleSize = headers->OptionalHeader.SizeOfImage;
2.2 获取二进制地址
代码注入中遇到的一个常见问题是模块的基地址会不断变化的问题。通常情况下,当进程启动时,MAIN函数的基地址是0X00400000。当我们将代码注入到其他进程中时,新的基地址将产生在虚拟地址中不可预测的位置。
在一个EXE文件中,编译和链接后,所有的代码和数据地址都是固定的,并建立在虚拟内存基址。对于PE注入,需要使用完整地址指针来修改数据的基地址,并且使用进程的重定位节。
当系统正常加载一个文件时,如果其基地址不可用,系统将为其重新设置一个基地址,同时系统加载器会修改代码中的重定位节。于是,在该PE注入过程中,我们可以模仿系统的加载方式,我们定义变量delta来计算新地址,于是可以访问到代码中的完整地址甚至修改它们。
?
1 2 3 4 5 6 7 /*在目标进程中新申请内存的偏移*/
delta = (DWORD_PTR)((LPBYTE)distantModuleMemorySpace – headers->OptionalHeader.I /* 当前进程的镜像偏移量 */
olddelta = (DWORD_PTR)((LPBYTE)module – headers->OptionalHeader.ImageBase);
下一步,理解重定位数据是如何组织的非常重要。重定位数据被保存在数据目录中,该目录可通过函数IMAGE_DERECTORY_ENTRY_BASERELOC来访问。重定位数据目录是一个类似于IMAGE_BASE_RELOCATION数据结构的数据。以下是该数据结构的定义:
?
1 2 3 4 5 6 7 typedef struct _IMAGE_BASE_RELOCATION {
ULONG VirtualAddress;
ULONG SizeOfBlock;
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
2.3 执行代码
代码被注入后,可以尝试执行其中的函数。首先需要解决的是要计算函数在远程进程中的的地址。
?
1 2 3 4 5 LPTHREAD_START_ROUTINE remoteThread = (LPTHREAD_START_ROUTINE)((LPBYTE)injectedMo (DWORD_PTR)((LPBYTE)callRoutine – (LPBYTE)module));
thread = CreateRemoteThread(proc, NULL, 0, remoteThread, NULL, 0, NULL);
执行自身函数有很多种方法。如下是一些可行的方法:CreateRemoteThread NtCreateThreadEx 挂起、注入、恢复例子代码
?
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16 #include
#include
#include
#include
#include
#pragma comment (lib, “winmm.lib”)
#pragma comment (lib, “kernel32.lib”)