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