前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Ring0 注入 Ring3 的一种新方法

Ring0 注入 Ring3 的一种新方法

作者头像
战神伽罗
发布2019-11-21 15:14:23
1.2K0
发布2019-11-21 15:14:23
举报
文章被收录于专栏:Eureka的技术时光轴
代码语言:javascript
复制
/***************************************************************************************
*
*    作者: 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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档