Ring0 注入 Ring3 的一種新方法

/***************************************************************************************
*
*    做者: Fypher [nmn714@163.com]
*           http://hi.baidu.com/nmn714
*
*    時間: 2009/12/29
*
*    模塊: InjectRing3.c [sys module]
*
*    平臺: Windows XP SP2
* 
*    描述:
*           Ring0 注入 Ring3 的一種新方法。
*           掛起ring3線程後,修改其TrapFrame裏的eip,再恢復其執行。
*
*    注意: 
*           本程序主要起示例做用,懶得麻煩,因此有些地方使用了一些不太穩定的作法,
*           好比在枚舉進程和模塊時直接讀鏈表。改爲ZwQueryXXXX會比較好           
*
****************************************************************************************/
#include <ntifs.h>

typedef struct _X86_KTRAP_FRAME {
	ULONG   DbgEbp;
	ULONG   DbgEip;
	ULONG   DbgArgMark;
	ULONG   DbgArgPointer;
	ULONG   TempSegCs;
	ULONG   TempEsp;
	ULONG   Dr0;
	ULONG   Dr1;
	ULONG   Dr2;
	ULONG   Dr3;
	ULONG   Dr6;
	ULONG   Dr7;
	ULONG   SegGs;
	ULONG   SegEs;
	ULONG   SegDs;
	ULONG   Edx;
	ULONG   Ecx;
	ULONG   Eax;
	ULONG   PreviousPreviousMode;
	ULONG   ExceptionList;
	ULONG   SegFs;
	ULONG   Edi;
	ULONG   Esi;
	ULONG   Ebx;
	ULONG   Ebp;
	ULONG   ErrCode;
	ULONG   Eip;
	ULONG   SegCs;
	ULONG   EFlags;
	ULONG   HardwareEsp;
	ULONG   HardwareSegSs;
	ULONG   V86Es;
	ULONG   V86Ds;
	ULONG   V86Fs;
	ULONG   V86Gs;
} X86_KTRAP_FRAME, *PX86_KTRAP_FRAME;

typedef struct _MODULE_ENTRY {
	LIST_ENTRY le_mod;
	ULONG  unknown[4];
	ULONG  base;
	ULONG  driver_start;
	ULONG  unk1;
	UNICODE_STRING driver_Path;
	UNICODE_STRING driver_Name;
	//.......
} MODULE_ENTRY, *PMODULE_ENTRY;

typedef ULONG (*FuncType)(PETHREAD Thread);

FuncType KeSuspendThread = NULL;
FuncType KeResumeThread = NULL;


////////////////////////////////////////////////
//
//  被注入到ring3進程的代碼
//
////////////////////////////////////////////////
_declspec (naked) void ShellCode() {
	_asm {
		push eax
		// 彈個MessageBox爲例
		push 0
		push 0
		push 0
		push 0
		mov eax, 0x77D66484		// MessageBoxW 的地址,XP SP2
		call eax
		pop eax
		// jmp ds:12345678H, 絕對地址跳轉
		_emit 0xEA
		_emit 0x78
		_emit 0x56
		_emit 0x34
		_emit 0x12
		_emit 0x1B
		_emit 0x00
	}
}


///////////////////////////////////////////////////////
//
//  特徵碼搜索,查找KeSuspendThread和KeResumeThread
//
///////////////////////////////////////////////////////
ULONG FindFunc(PDRIVER_OBJECT DriverObject){
	UNICODE_STRING uniModuleName;
	PMODULE_ENTRY PsLoadedModuleList,pmcurrent;
	ULONG ModuleStart = 0, ModuleEnd = 0;
	ULONG i;
	ULONG suspend1 = 0x8b55ff8b, suspend2 = 0x0cec83ec, suspend3 = 0x758b5653, suspend4 = 0x8e8d5708;		//SP2
	ULONG resume1 = 0x8b55ff8b, resume2 = 0x335651ec , resume3 = 0x8815ffc9 , resume4 = 0x8b804d90;			//SP2

	// 先找ntoskrnl.exe模塊,經過ZwQuerySystemInformation來查找更穩定一些
	// 不過本人很討厭那個繁瑣的函數……
	PsLoadedModuleList = pmcurrent = *((PMODULE_ENTRY*)((ULONG)DriverObject + 0x14));

	if (PsLoadedModuleList == NULL){
		return FALSE;
	}

	RtlInitUnicodeString(&uniModuleName,L"ntoskrnl.exe");

	do {
		if ((pmcurrent->unk1 != 0x00000000) && (pmcurrent->driver_Path.Length != 0)){
			if (!RtlCompareUnicodeString(&uniModuleName, &(pmcurrent->driver_Name), FALSE)){
				ModuleStart = pmcurrent->base;
				ModuleEnd = ModuleStart + pmcurrent->unk1;
				break;
			}
		}
		pmcurrent =  (MODULE_ENTRY*)pmcurrent->le_mod.Flink;
	} while((PMODULE_ENTRY)pmcurrent != PsLoadedModuleList);


	if(!ModuleStart || !ModuleEnd) {
		return FALSE;
	}
	
	// 在ntoskrnl.exe中搜索特徵碼找到KeSuspendThread和KeResumeThread
	for( i = ModuleStart; i <= ModuleEnd; i++) {
		if( MmIsAddressValid((PVOID)i) && MmIsAddressValid((PVOID)(i+12)) ){
			if( (*(PULONG)i == suspend1) &&
				(*(PULONG)(i+4) == suspend2) &&
				(*(PULONG)(i+8) == suspend3) &&
				(*(PULONG)(i+12) == suspend4) )
			{
				KeSuspendThread = (FuncType)i;
				if ( KeResumeThread != NULL )
					return TRUE;
			}
			else if( (*(PULONG)i == resume1) &&
				(*(PULONG)(i+4) == resume2) &&
				(*(PULONG)(i+8) == resume3) &&
				(*(PULONG)(i+12) == resume4) )
			{
				KeResumeThread = (FuncType)i;
				if ( KeSuspendThread != NULL )
					return TRUE;
			}
		}
	}
	return FALSE;
}



/////////////////////////////////////////////////////////////////
////
////  注入ShellCode到線程,將ShellCode拷貝到「飛地」中,
////  比較方便,避免是分配內存和AttachProcess,可是在非
////  Debug的系統模式下,會引起DEP的強烈不滿
////
/////////////////////////////////////////////////////////////////
//VOID InjectShellCode(PETHREAD pThread) {
//	ULONG i;
//	PX86_KTRAP_FRAME pTrapFrame;
//	DbgPrint("Inject Start\n");
//	
//	// 在try塊中掛起線程,看WRK發現SuspendThread失敗時會拋異常
//	__try {
//		KeSuspendThread(pThread);
//	}
//	__except(1) {
//		return;
//	}
//	
//	// PTrapFrame中就是該線程的各個寄存器的值
//	pTrapFrame = *(PX86_KTRAP_FRAME*)((ULONG)pThread + 0x134);
//
//	// 將ShellCode中的0x12345678改爲eip,爲了ShellCode執行完後自動跳回
//	for( i = (ULONG)ShellCode; i <= (ULONG)ShellCode + 0x20; ++i ) {
//		if( MmIsAddressValid((PVOID)i) && MmIsAddressValid((PVOID)(i+3)) ){
//			if ( *(PULONG)i == 0x12345678 ) {
//				DbgPrint("find modify point\n");
//				*(PULONG)i = pTrapFrame->Eip;
//				break;
//			}
//		}
//	}
//
//	// 拷貝ShellCode到「飛地」(使用內核地址)
//	RtlCopyMemory( (PVOID)0xffdf0800, ShellCode, 0x20 );
//
//	// pTrapFrame->EIP指向「飛地」(使用用戶態地址)
//	pTrapFrame->Eip = 0x7ffe0800;
//
//	// 恢復線程執行
//	KeResumeThread(pThread);
//	DbgPrint("Inject End\n");
//}



///////////////////////////////////////////////////////////////
//
//  注入ShellCode到線程,分配內存來拷貝ShellCode
//
///////////////////////////////////////////////////////////////
VOID InjectShellCode(PETHREAD pThread,PEPROCESS pProcess) {
	ULONG i;
	PX86_KTRAP_FRAME pTrapFrame;
	PCLIENT_ID  pCid;
	OBJECT_ATTRIBUTES oa;
	HANDLE hProcess;
	NTSTATUS ntstatus;
	DbgPrint("Inject Start\n");
	
	// 在try塊中掛起線程,看WRK發現SuspendThread失敗時會拋異常
	__try {
		KeSuspendThread(pThread);
	}
	__except(1) {
		return;
	}
	
	// PTrapFrame中就是該線程的各個寄存器的值
	pTrapFrame = *(PX86_KTRAP_FRAME*)((ULONG)pThread + 0x134);

	// 將ShellCode中的0x12345678改爲eip,爲了ShellCode執行完後自動跳回
	for( i = (ULONG)ShellCode; i <= (ULONG)ShellCode + 0x20; ++i ) {
		if( MmIsAddressValid((PVOID)i) && MmIsAddressValid((PVOID)(i+3)) ){
			if ( *(PULONG)i == 0x12345678 ) {
				DbgPrint("find modify point\n");
				*(PULONG)i = pTrapFrame->Eip;
				break;
			}
		}
	}

	// 下面的代碼是分配空間來放置ShellCode
	// 調用一些相應函數來實現更好,我比較懶,就硬編碼了
	InitializeObjectAttributes(&oa,0,0,0,0);
	pCid = (CLIENT_ID*)((ULONG)pThread + 0x1ec);		// Cid 	XP SP2
	ntstatus = ZwOpenProcess( 
		&hProcess, 
		PROCESS_ALL_ACCESS, 
		&oa, 
		pCid 
		);
	if ( NT_SUCCESS(ntstatus) ) {
		PVOID pBuff = NULL;
		SIZE_T size = 0x20;
		ntstatus = NtAllocateVirtualMemory(
			hProcess, 
			&pBuff, 
			0, 
			&size, 
			MEM_RESERVE | MEM_COMMIT,
			PAGE_EXECUTE_READWRITE
			);
		if( NT_SUCCESS(ntstatus) ) {
			KAPC_STATE kapc;
			// 拷貝ShellCode到目標進程中去
			KeStackAttachProcess(pProcess,&kapc);
			RtlCopyMemory(pBuff,ShellCode,size);
			KeUnstackDetachProcess (&kapc);
			// pTrapFrame->Eip指向ShellCode
			pTrapFrame->Eip = (ULONG)pBuff;
		}
		ZwClose(hProcess);
	}
	// 恢復線程執行
	KeResumeThread(pThread);
	DbgPrint("Inject End\n");
}


////////////////////////////////////////////////
//
//  注入ShellCode到進程
//
////////////////////////////////////////////////
VOID Inject(char* strProc, int len) {
	PEPROCESS pProcess;
	PETHREAD pThread;
	PLIST_ENTRY pListHead, pNextEntry;
	PLIST_ENTRY pThListHead, pThNextEntry;


	pProcess = PsGetCurrentProcess();
	pListHead = (PLIST_ENTRY)((ULONG)pProcess + 0x88);		//ActiveProcessLinks
	pNextEntry = pListHead;
	
	// 先找到要注入的進程,經過ZwQuerySystemInformation來查找更穩定一些
	// 不過本人很討厭那個繁瑣的函數……
	do {
		pProcess = (PEPROCESS)((ULONG)pNextEntry - 0x88);
		if ( !_strnicmp((char*)pProcess + 0x174, strProc, len) ) {
			DbgPrint("find process\n");
			pThListHead = (PLIST_ENTRY)((ULONG)pProcess + 0x190);		// ThreadListHead, XP SP2
			pThNextEntry = pThListHead->Flink;
			while ( pThNextEntry != pThListHead) {
				// 接着查找符合條件的線程
				UCHAR SuspendCount;
				ULONG CrossThreadFlags;
								
				pThread = (PETHREAD)((ULONG)pThNextEntry - 0x22c);		// ThreadListEntry, XP SP2
				
				SuspendCount = *(PUCHAR)((ULONG)pThread + 0x1b9);
				CrossThreadFlags = *(PULONG)((ULONG)pThread + 0x248);
								
				if( !SuspendCount && !(CrossThreadFlags & 0x13) ) {		// 非Suspend,非退出態,非內核線程
					DbgPrint("find thread\n");
					
					// 注入找到的線程
					InjectShellCode(pThread,pProcess); 
					break;
				}
				pThNextEntry = pThNextEntry->Flink;
			}
			break;
		}
		pNextEntry = pNextEntry->Flink;
	} while(pNextEntry != pListHead);
}



////////////////////////////////////////////////
//
//       驅動卸載歷程
//
////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT o){
	DbgPrint("Fypher's ring3injector end!\n");
	return;
}



////////////////////////////////////////////////
//
//       驅動加載歷程
//
////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath){
	char* strProc = "winmine.exe";

	DriverObject->DriverUnload=OnUnload;
	DbgPrint("Fypher's ring3injector start!\n");

	if ( !FindFunc(DriverObject) ) {
		DbgPrint("Find KexxxThread failed!\n");
		return STATUS_UNSUCCESSFUL;
	}
	
	Inject(strProc, strlen(strProc));

	return STATUS_SUCCESS;
}
相關文章
相關標籤/搜索