1. 程式人生 > >ndl1302732的專欄

ndl1302732的專欄

高手請飄過~~~~~~~~~~~~~~~~~

RIP --- x64體系 EIP --- x86體系

RIP/EIP注入原理:     1 掛起目標執行緒,需要用到 SuspendThread 函式     2 掛起之後獲取目標執行緒的上下文,需要用到 GetThreadContext函式       這個函式可以獲得一個CONTEXT結構體封裝的資料。這個結構體的定義在winnt.h標頭檔案中       結構體裡面儲存了當前執行緒的上下文資訊,比如當前執行緒的RIP/EIP在哪裡,通用暫存器的值是       多少等等。     3 RIP/EIP注入關鍵就是修改Context中的RIP/EIP暫存器。使得要執行的程式碼強制跳轉到我們       指定的程式碼。最後將上下文設定回去,這用到 SetThreadContext 函式,最後執行ResumeThread       函式,將掛起執行緒恢復執行。        難點:     1 位置無關程式碼編寫,為什麼要位置無關這裡不再多說了。     2 要注意函式棧對齊問題,其實我這個問題也沒太搞明白,       總之就是棧沒對齊,我的例子程式碼在呼叫MessageBox時會報錯,詳細情況看shellcode的註釋       有懂得希望留言,指教一二。

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>


#include "D:\\VS\\DllToInject\\DllToInject\\Utils.h"

#pragma comment(lib, "D:\\VS\\DllToInject\\x64\\Release\\UtilDll.lib")

CHAR shellcode[] = {
	0x68, 0x78, 0x56, 0x34, 0x12,		//push org rip (這裡需要修復成原來的rip,暫時用0x12345678佔位 見FixShellCode) 

	0x41, 0x51,							//push r9
	0x41, 0x50,							//push r8
	0x52,								//push rdx
	0x51,								//push rcx
	0x50,								//push rax

	0x48, 0x83, 0xEC, 0x28,				//sub rsp, 28  這個程式碼不加呼叫MessageBox會莫名其妙出錯,好像是x64需要棧 mod 16對齊

	0x41, 0xB9, 0x00, 0x00, 0x00, 0x00, //mov r9d,0
	0x41, 0xB8, 0x00, 0x00, 0x00, 0x00, //mov r8d,0
	0xBA, 0x00, 0x00, 0x00, 0x00,		//mov edx,0
	0xB9, 0x00, 0x00, 0x00, 0x00,		//mov ecx,0
	0xB8, 0x78, 0x56, 0x34, 0x12,		//mov eax, messagebox (這裡需要修復成Messagebox的地址,暫時用0x12345678佔位 見FixShellCode)
	0xFF, 0xD0,							//call rax

	0x48, 0x83, 0xC4, 0x28,				//add rsp, 28 平衡棧,很簡單

	0x58,								//pop rax
	0x59,								//pop rcx
	0x5A,								//pop rdx
	0x41, 0x58,							//pop r8
	0x41, 0x59,							//pop r9

	0xC3								//ret 返回到原來的Rip, 這裡很關鍵
};

//修復shellcode中的兩處絕對地址
//oldRip 執行緒掛起前的RIP
//pfn Messagebox的地址
//偷懶起見, 這裡利用for迴圈找0x12345678,找到了就修復,否則需要去數數比較麻煩
void FixShellCode(DWORD oldRip, DWORD pfn){
	for (int i = 0; i < sizeof(shellcode); ++i){
		if (*(DWORD *)(shellcode + i) == 0x12345678){
			*(DWORD *)(shellcode + i) = oldRip;//修復RIP
			break;
		}
	}

	for (int i = 0; i < sizeof(shellcode); ++i){
		if (*(DWORD *)(shellcode + i) == 0x12345678){
			*(DWORD *)(shellcode + i) = pfn;//修復messagebox的地址
			break;
		}
	}

}



int _tmain(int argc, _TCHAR* argv[])
{
	if (!AdjustProcessTokenPrivilege()){
		printf("提權失敗\n");
	}

	DWORD dwPid = GetPidByName("calc.exe");
	if (dwPid == 0){
		printf("GetPidByName failed\n");
		return -1;
	}
	printf("calc.exe pid=%d\n", dwPid);

	DWORD dwThreadId = GetMainThread(dwPid);
	printf("calc main thread id = %d\n", dwThreadId);

	HANDLE hProcess;
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	if (hProcess == NULL)
	{
		MyOutputDebugString("開啟程序失敗!!!!");
		return -1;
	}

	//開啟目標主執行緒
	HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadId);

	if (!OpenThread)
	{
		printf("OpenThread 失敗");
		goto SAFE_EXIT;
	}

	//掛起目標主執行緒
	DWORD bRet = SuspendThread(hThread);
	if (bRet == -1)
	{
		printf("SuspendThread 失敗");
		goto SAFE_EXIT;
	}

	CONTEXT oldContext = { 0 };
	CONTEXT newContext = { 0 };
	DWORD dwOldEip = 0;

	oldContext.ContextFlags = CONTEXT_FULL;
	bRet = GetThreadContext(hThread, &oldContext);
	if (!bRet)
	{
		printf("GetThreadContext 失敗");
		goto SAFE_EXIT;
	}
	newContext = oldContext;

	//兩個函式地址的差值就是shellcode程式碼的大小,可能會有一些多的對齊資料,但是不影響
	DWORD funcSize = sizeof(shellcode);
	printf("shellcode size =%d\n", funcSize);
	FixShellCode(oldContext.Rip, (DWORD)MessageBox);

	//1.在遠端程序中分配記憶體,可讀可寫可執行
	LPVOID pszRemoteBuffer = (char *)VirtualAllocEx(hProcess, NULL, USN_PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	
	if (pszRemoteBuffer == NULL)
	{
		printf("申請遠端空間失敗");
		CloseHandle(hProcess);
		return -1;
	}

	printf("buffer = %p\n", pszRemoteBuffer);

	//2.在遠端申請的記憶體空間中寫入shellcode
	SIZE_T dwWriten = 0;
	if (!WriteProcessMemory(hProcess, pszRemoteBuffer, shellcode, funcSize, &dwWriten))
	{
		printf("寫入shellcode失敗");
		CloseHandle(hProcess);
		VirtualFreeEx(hProcess, pszRemoteBuffer, USN_PAGE_SIZE, MEM_DECOMMIT);
		return -1;
	}

	printf("shellcode write bytes:%d\n", dwWriten);

#ifdef _WIN64
	newContext.Rip = (DWORD)pszRemoteBuffer;
	//dwOldEip = newContext.Rip;
#else 
	newContext.Eip = (DWORD)g_lpBuffer;
	//dwOldEip = newContext.Eip;
#endif

	bRet = SetThreadContext(hThread, &newContext);
	if (!bRet)
	{
		printf("SetThreadContext 失敗");
		goto SAFE_EXIT;
	}


	//然後把主執行緒跑起來
	bRet = ResumeThread(hThread);
	if (bRet == -1)
	{
		printf("ResumeThread 失敗");
		goto SAFE_EXIT;
	}

	//等shellcode執行完畢,然後再釋放記憶體 VirtualFreeEx
	Sleep(3000);

SAFE_EXIT:

	if (hThread){
		CloseHandle(hThread);
	}

	
	VirtualFreeEx(hProcess, pszRemoteBuffer, USN_PAGE_SIZE, MEM_DECOMMIT);
	CloseHandle(hProcess);

	return 0;
}