bookmark_borderCreate Process Context Injection

思路:

1.CreateProcess 创建进程 ,进程信息放在 PROCESS_INFORMATION pi 中。

2. GetSystemInfo 获取内存中的所有数据,放在 SYSTEM_INFO sysinfo; 中。

3.从 sysinfo.lpMinimumApplicationAddress 开始, 每一次的大小为 sysinfo.dwPageSize 到 sysinfo.lpMaximumApplicationAddress结束,遍历内存。

4.每次都 VirtualQueryEx 一次, 把当前页的内存信息放到 MEMORY_BASIC_INFORMATION mbi = {0}中。

5.当 VirtualQueryEx 出来的类型是 MEM_IMAGE 时,内存中的地址又是指定文件名的映射,就保存并返回这个地址。

6.用 CImage 的 AttachToProcess方法读取刚刚地址的内存头,保存在 PBYTE ImageBase中。

7.用 VirtualQueryEx 出来的内存地址 加 CImage结构体中的m_dwEntryPoint长度,就是程序内存中的入口点。

8.找到打开程序的程序入口点的地址,就可以准备要注入的code了。

typedef struct _INJECT_DATA64 
{
 BYTE ShellCode[0xC0];
 /*Off=0x0C0*/HANDLE ModuleHandle; //Dll句柄
 /*Off=0x0C8*/PUNICODE_STRING pDllPath;//PUNICODE_STRING DllPath
 /*Off=0x0D0*/ULONG DllCharacteristics;
 /*Off=0x0D8*/ULONG_PTR AddrOfLdrLoadDll;//LdrLoadDll地址
 /*Off=0x0E0*/ULONG_PTR ProtectBase; //用于VirtualMemory
 /*Off=0x0E8*/ULONG OldProtect; //用于VirtualMemory
 /*Off=0x0F0*/SIZE_T ProtectSize;s
 /*Off=0x0F8*/ULONG_PTR ExeEntry;//Exe入口点的地址
 /*Off=0x100*/ULONG_PTR AddrOfZwProtectVirtualMemory;
 /*Off=0x108*/BYTE SavedEntryCode[16];//保存exe入口点的前16字节
 /*Off=0x118*/UNICODE_STRING usDllPath;//Dll路径
 /*Off=0x128*/WCHAR wDllPath[256];//Dll路径,也就是usDllPath中的Buffer
}INJECT_DATA64;

9.用 VirtualAllocEx 分配出一块 0x1000 大小的内存,分给 之前打开的进程。

10.ReadProcessMemory 读取目标进程的前8个字节,并存到变量里。

11.用 VirtualProtectEx 修改目标进程的内存属性。

12.找到目标进程中32位 ntdll 的地址。

13.通过计算自己内存里ntdll中ZwProtectVirtualMemory和LdrLoadDll的偏移,来推测目标进程的相关函数的偏移。

14.获取 shellcode的内容,开始到偏移的内容。把内容放到 INJECT_DATA64 结构体的 ShellCode中。

15.填充 INJECT_DATA64 结构体的 其他内容。

16.用 WriteProcessMemory 把 INJECT_DATA64 结构体写道目标进程中,位置是之前用 VirtualAllocEx 分配的内存 。

17.编写Jmp xxxx指令,用 WriteProcessMemory 让程序在开头跳转到 shellcode 执行。

18.ResumeThread 让暂停的线程继续执行。

代码参考 加密解密 随书光盘中的 : chap12\10.CreateProcContextInject\CreateProcContextInject.cpp

bookmark_border理解PE格式——手工分析pe文件找出Pe中dll和函数的位置

VIrtual Size是加载到内存的大小
Raw Size是文件中偏移的大小

以XP上的notepad.exe为例(加密解密第十一章)

https://73007300.xyz/notepad.exe

本文使用010Editor做辅助分析。

找PE文件里的dll函数和地址:
文件从0h 开始+3ch的值,指向了NtHeader


notepad.exe

NtHeader的+04h处可以判断PE文件时运行在32位平台还是64位平台,长度2h:

其中数值对应的解释:

pe文件里 数值都是从后往前读的,4C 01代表的值就是:014C

NtHeader的+18h的位置是OptionalHeader的位置

32位的PE文件中:
OptionalHeader的IMAGE_DATA_DIRECTORY在NtHeader的+78h偏移处。
数据结构:

notepad.exe:

64位的PE文件中
OptionalHeader的IMAGE_DATA_DIRECTORY在NtHeader的+88h偏移处:
数据结构:

在 IMAGE_DATA_DIRECTORY 中,每一项的都是如下数据结构的展示出来:

在PEview里看到的就是这种结构:

然后找到Import Table的位置:

RVA:7604h
Size:C8
一直到NtHeader结束,文件偏移的大小(Raw Offset)和映射到内存中的大小(Virtual Offset)都已一样长的,也就是偏移量一一对应。
从这里开始,接下来的区块表部分使得Raw Offset和Virtual Offset不相等了。

有两个值特别值得关注:
NtHeader+38h的值指定了内存中的对齐大小。不足按这个大小补足。
NtHeader+3Ch的值指定了文件中的对齐大小。
我们安装系统分区时常说的4K对齐,就是这两个值对齐。

NtHeader+54h的值指定了所有头的总大小:

也就是Section data部分从400h的位置开始。

NtHeader后面紧跟Section Header,根据 数据目录表 推算,notepad.exe的Section Header是从NtHeader +F8h处开始,也就是 E0 + F8h = 1D8。
Section Header是由多个Section Header组成的。1D8h处即使第一个section header的位置。每个section header的大小是28h
从1D8开始的前8个字节对应的是这部分section的Name的ascii码。
notepad.exe中是.text
微软一般用这个名字的section来存代码。之后再偏移1个DWORD对应的是VirtualSize,在notepad.exe中位于1D8h + 8h + 4h的位置,其大小是1000h

再偏移1个DWORD是Size Of Raw Data,其指定了这部分section的大小,大小是7800h:

再偏移1个DWORD就是PointerToRawData,值是400h,他代表的是在文件偏移中的位置,也就是图中左侧那一列的地址:

.text的section header:

VirtualSize1000h
PointerToRawData400h
Size Of Raw Data7800h

Import Table:

RVA7604h
SizeC8

由于.text的大小时1000h开始,偏移7800h的大小,7604h在这部分大小中,所以import table就在.text里。
参考《加密解密》第十一章的介绍,此时 内存偏移 – 文件偏移 的差值△k的值就是△k= 1000h – 400h = C00h

此时RVA – △k= 7604h – C00h = 6A04 ,6A04指向的是一个输入表结构,一个链状的结构,也就是这个输入表结构在文件上的位置。

输入表结构:

notepad.exe的6A04位置:

梳理一个红圈里的这个IID(Image_Import_Descriptor)结构

OriginalFirstThunkTimeDateStampForwarderChainNameFirstThunk
7990FFFFFFFFFFFFFFFF7AAC12C4

然后看看第四部分指向的Name是不是一个dll名称。
第四部分的值是7AAC。这是个内存中的位置。File Offset = Virtual Offset – △k = 7AACh – C00h = 6EACh。
查看notepad.exe中的6EACh中的内容:

结果和CFF的一致:

最后一个IID结构用0填充:

OriginalFirstThunk 指向的是INT(Import Name Table),也就是dll中的函数名称表
FirstThunk指向的是IAT(Import Address Table) ,也就是dll中函数的地址表

下面来看看OriginalFirstThunk 指向的函数名称对不对。
OriginalFirstThunk指向的是一个Image_Thunk_Data结构类型的元素。
Image_Thunk_Data = OriginalFirstThunk – △k = 7990 – C00 = 6D90

红框中每个框对应一个函数名的RVA。
7A7A – C00 = 6E7A (7)
7A5E – C00 = 6E5E (5)‬
7A9E – C00 = 6E9E (9)‬
7A50 – C00 = 6E50‬ (4)
7A40 – C00 = 6E40 (3)
7A8A – C00 = 6E8A (8)
7A6A – C00 = 6E6A‬ (6)
7A14 – C00 = 6E14 (1)
7A2C – C00 = 6E2C (2)

这些虚地址指向的是一个 IMAGE_IMPORT_BY_NAME结构的数据

以00结束。

再来看FirstThunk指向的位置。
12C4 – △k = 12C4 – C00 = 6C4‬

这里记录了9个虚地址。00结束。
和Peview显示的一致:

如果想引用的dll不被列在pe头的Import Table中,那就要动态加载dll,常用的函数是“LoadLibrary”和“GetProcAddress”