Upload
kent-huang
View
32
Download
1
Embed Size (px)
Citation preview
Reverse EngineeringThe Windows Kernel (2)
Kent Huang
Asynchronous and Ad-Hoc Execution
Introduce the Component inside Windows Kernel
1. Usage
2. Introduce the data structure
3. How it work inside Windows Kernel
4. What’s the usage in root-kit
5. Exercises
System Threads • Driver may create multiple threads handling
different requests from kernel or user.
• Call API PsCreateSystemThreadNTSTATUS PsCreateSystemThread( _Out_ PHANDLE ThreadHandle, _In_ ULONG DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ HANDLE ProcessHandle, _Out_opt_ PCLIENT_ID ClientId, _In_ PKSTART_ROUTINE StartRoutine, _In_opt_ PVOID StartContext );
System Threads
• If process handle is not NULL, thread will be created under that process.
• Someone say, if call PsCreateSystemThread in an IOCTL handler, the new thread will be in the user-mode application ???
Exercises
• Determine whether any of them pass a non-NULL ProcessHandle parameter. Explain the purpose of these routines. Repeat the exercise for as many functions as possible.
Work Items• Similar to system threads
• Except that no physical thread object
• Common driver programming pattern to queue work items inside a DPC
PIO_WORKITEM IoAllocateWorkItem( _In_ PDEVICE_OBJECT DeviceObject ); VOID IoQueueWorkItem( _In_ PIO_WORKITEM IoWorkItem, _In_ PIO_WORKITEM_ROUTINE WorkerRoutine, _In_ WORK_QUEUE_TYPE QueueType, _In_opt_ PVOID Context );
Structure1: kd> dt _IO_WORKITEM nt!_IO_WORKITEM +0x000 WorkItem : _WORK_QUEUE_ITEM +0x010 Routine : Ptr32 void +0x014 IoObject : Ptr32 Void +0x018 Context : Ptr32 Void +0x01c Type : Uint4B +0x020 ActivityId : _GUID 1: kd> dt _WORK_QUEUE_ITEM nt!_WORK_QUEUE_ITEM +0x000 List : _LIST_ENTRY +0x008 WorkerRoutine : Ptr32 void +0x00c Parameter : Ptr32 Void 1: kd> dt _WORK_QUEUE_TYPE TmXPFlt!_WORK_QUEUE_TYPE CriticalWorkQueue = 0n0 DelayedWorkQueue = 0n1 HyperCriticalWorkQueue = 0n2 MaximumWorkQueue = 0n3
1: kd> dt _KPRCB ParentNode nt!_KPRCB +0x338 ParentNode : Ptr32 _KNODE 1: kd> dt _KNODE nt!_KNODE +0x000 DeepIdleSet : Uint4B +0x004 SharedReadyQueueLeaders : Uint4B +0x040 ProximityId : Uint4B +0x044 NodeNumber : Uint2B … 1: kd> dt _ENODE nt!_ENODE +0x000 Ncb : _KNODE +0x0c0 ExWorkQueue : [2] _EX_WORK_QUEUE … 1: kd> dt _EX_WORK_QUEUE nt!_EX_WORK_QUEUE +0x000 WorkPriQueue : _KPRIQUEUE +0x19c WorkItemsProcessed : Uint4B +0x1a0 WorkItemsProcessedLastPass : Uint4B +0x1a4 ThreadCount : Int4B +0x1a8 TryFailed : UChar
kd> !thread THREAD 865b2da8 Cid 0004.003c Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0 Not impersonating DeviceMap e1004438 Owning Process 0 Image: <Unknown> Attached Process 865b5490 Image: System Wait Start TickCount 19549 Ticks: 0 Context Switch Count 901 IdealProcessor: 0 UserTime 00:00:00.000 KernelTime 00:00:01.062 Start Address nt!ExpWorkerThread (0x80534b02) Stack Init f78eb000 Current f78ead1c Base f78eb000 Limit f78e8000 Call 0 Priority 13 BasePriority 12 PriorityDecrement 0 DecrementCount 16 ChildEBP RetAddr Args to Child f78ead60 8056bcc5 86124338 00000000 8055b0fc NotYourFault!TesterWorkerItemRoutine+0x52 (FPO: [Non-Fpo]) (CONV: stdcall) [c:\users\kent_huang\perforce\pd_kent_huang\core\vsapi\pd\kent_huang\vsapi\tools\notyourfault\notyourfault\reverseengineering\workeritem.c @ 19] f78ead74 80534c02 86121548 00000000 865b2da8 nt!IopProcessWorkItem+0x13 (FPO: [Non-Fpo]) f78eadac 805c6160 86121548 00000000 00000000 nt!ExpWorkerThread+0x100 (FPO: [Non-Fpo]) f78eaddc 80541dd2 80534b02 00000001 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo]) 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
Reverse follow API (Win8)
• IoAllocateWorkItem
• IoInitializeWorkItem
• IoQueueWorkItem
• IopQueueWorkItemProlog
• ExQueueWorkItem
Asynchronous Procedure Calls
• Asynchronous I/O completion, thread suspension, and process shutdown
• Undocumented API
• Kernel Mode (PASSIVE_LEVEL , APC_LEVEL)
• User Mode (PASSIVE_LEVEL)
• Rootkits achieve this by queueing a user-mode APC to a thread in the process in which they want to inject code.
Structure1: kd> dt _KAPC nt!_KAPC +0x000 Type : UChar +0x001 SpareByte0 : UChar +0x002 Size : UChar +0x003 SpareByte1 : UChar +0x004 SpareLong0 : Uint4B +0x008 Thread : Ptr32 _KTHREAD +0x00c ApcListEntry : _LIST_ENTRY +0x014 KernelRoutine : Ptr32 void +0x018 RundownRoutine : Ptr32 void +0x01c NormalRoutine : Ptr32 void +0x014 Reserved : [3] Ptr32 Void +0x020 NormalContext : Ptr32 Void +0x024 SystemArgument1 : Ptr32 Void +0x028 SystemArgument2 : Ptr32 Void +0x02c ApcStateIndex : Char +0x02d ApcMode : Char +0x02e Inserted : UChar
1: kd> dt _KTHREAD ApcState nt!_KTHREAD +0x070 ApcState : _KAPC_STATE 1: kd> dt _KAPC_STATE nt!_KAPC_STATE +0x000 ApcListHead : [2] _LIST_ENTRY +0x010 Process : Ptr32 _KPROCESS +0x014 InProgressFlags : UChar +0x014 KernelApcInProgress : Pos 0, 1 Bit +0x014 SpecialApcInProgress : Pos 1, 1 Bit +0x015 KernelApcPending : UChar +0x016 UserApcPending : UChar
Deferred Procedure Calls
• Routines executed at DISPATCH_LEVEL
• Hardware drivers use them to process interrupts coming from the device
• Some rootkits use DPCs to synchronize access to global linked lists
IRQL
Structure1: kd> dt _KDPC nt!_KDPC +0x000 TargetInfoAsUlong : Uint4B +0x000 Type : UChar +0x001 Importance : UChar +0x002 Number : Uint2B +0x004 DpcListEntry : _SINGLE_LIST_ENTRY +0x008 ProcessorHistory : Uint4B +0x00c DeferredRoutine : Ptr32 void +0x010 DeferredContext : Ptr32 Void +0x014 SystemArgument1 : Ptr32 Void +0x018 SystemArgument2 : Ptr32 Void +0x01c DpcData : Ptr32 Void
1: kd> dt _KPRCB DpcData nt!_KPRCB +0x21e0 DpcData : [2] _KDPC_DATA 1: kd> dt _KDPC_DATA nt!_KDPC_DATA +0x000 DpcList : _KDPC_LIST +0x008 DpcLock : Uint4B +0x00c DpcQueueDepth : Int4B +0x010 DpcCount : Uint4B +0x014 ActiveDpc : Ptr32 _KDPC
138 Chapter 3 ■ The Windows Kernel
c03.indd 11:7:49:AM 01/21/2014 Page 138
KPRCB KDPC KDPC KDPC
Type Type Type
DpcData[0]
DpcData[1] DpcListEntry DpcListEntry DpcListEntry
DeferredRoutine DeferredRoutine DeferredRoutine
…
… … …
…… …
…
Figure 3-7
DpcStack is a pointer to a block of memory to be used as the DPC routine’s stack.
Windows has several mechanisms to process the DPC queue. The fi rst mecha-nism is through KiIdleLoop. While “idling,” it checks the PRCB to determine if DPCs are waiting and if so to call KiRetireDpcList to process all DPCs. This is why sometimes these two functions appear on the stack while executing a DPC. For example:
0: kd> kn # Child-SP RetAddr Call Site00 fffff800`00b9cc88 fffff800`028db5dc USBPORT!USBPORT_IsrDpc01 fffff800`00b9cc90 fffff800`028d86fa nt!KiRetireDpcList+0x1bc02 fffff800`00b9cd40 00000000`00000000 nt!KiIdleLoop+0x5a
The second mechanism occurs when the CPU is at DISPATCH_LEVEL. Consider the following stack:
0: kd> kn# Child-SP RetAddr Call Site00 fffff800`00ba2ef8 fffff800`028db5dc USBPORT!USBPORT_IsrDpc01 fffff800`00ba2f00 fffff800`028d6065 nt!KiRetireDpcList+0x1bc02 fffff800`00ba2fb0 fffff800`028d5e7c nt!KyRetireDpcList+0x503 fffff880`04ac67a0 fffff800`0291b793 nt!KiDispatchInterruptContinue04 fffff880`04ac67d0 fffff800`028cbda2 nt!KiDpcInterruptBypass+0x1305 fffff880`04ac67e0 fffff960`0002992c nt!KiInterruptDispatch+0x21206 fffff880`04ac6978 fffff960`000363b3 win32k!vAlphaPerPixelOnly+0x7c07 fffff880`04ac6980 fffff960`00035fa4 win32k!AlphaScanLineBlend+0x30308 fffff880`04ac6a40 fffff960`001fd4f9 win32k!EngAlphaBlend+0x4f409 fffff880`04ac6cf0 fffff960`001fdbaa win32k!NtGdiUpdateTransform+0x112d0a fffff880`04ac6db0 fffff960`001fdd19 win32k!NtGdiUpdateTransform+0x17de0b fffff880`04ac6ed0 fffff960`001fded8 win32k!EngNineGrid+0xb10c fffff880`04ac6f70 fffff960`001fe395 win32k!EngDrawStream+0x1a0
kd> !thread THREAD 8649d020 Cid 0134.032c Teb: 7ffdf000 Win32Thread: e1634008 RUNNING on processor 0 IRP List: 8692cf68: (0006,0094) Flags: 40000000 Mdl: 00000000 Not impersonating DeviceMap e21e88b0 Owning Process 0 Image: <Unknown> Attached Process 864a07f0 Image: ReverseEngineer Wait Start TickCount 20134 Ticks: 0 Context Switch Count 23 IdealProcessor: 0 LargeStack UserTime 00:00:00.000 KernelTime 00:00:00.015 Win32 Start Address ReverseEngineeringTester!ILT+1240(_wmainCRTStartup) (0x0042e4dd) Start Address kernel32!BaseProcessStartThunk (0x7c8106f5) Stack Init f7517000 Current f7516b8c Base f7517000 Limit f7513000 Call 0 Priority 8 BasePriority 8 PriorityDecrement 0 DecrementCount 0 ChildEBP RetAddr Args to Child f78b2fd0 80541b8d f7516bc8 00000000 00000000 NotYourFault!TestDpcRoutine+0x52 (FPO: [Non-Fpo]) (CONV: stdcall) [c:\users\kent_huang\perforce\pd_kent_huang\core\vsapi\pd\kent_huang\vsapi\tools\notyourfault\notyourfault\reverseengineering\workeritem.c @ 57] f78b2ff4 8054185a f7516b20 00000000 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0]) f78b2ff8 f7516b20 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2a (FPO: [Uses EBP] [0,0,1]) WARNING: Frame IP not in any known module. Following frames may be wrong. 8054185a 00000000 00000009 bb835675 00000128 0xf7516b20
Timer• Signal the expiration of a certain amount of time
• Periodically or at some time in the future
VOID KeInitializeTimer( _Out_ PKTIMER Timer );
BOOLEAN KeSetTimer( _Inout_ PKTIMER Timer, _In_ LARGE_INTEGER DueTime, _In_opt_ PKDPC Dpc );
BOOLEAN KeSetTimerEx( _Inout_ PKTIMER Timer, _In_ LARGE_INTEGER DueTime, _In_ LONG Period, _In_opt_ PKDPC Dpc );
Structure1: kd> dt _KPRCB TimerTable nt!_KPRCB +0x2260 TimerTable : _KTIMER_TABLE 1: kd> dt _KTIMER_TABLE nt!_KTIMER_TABLE +0x000 TimerExpiry : [16] Ptr32 _KTIMER +0x040 TimerEntries : [256] _KTIMER_TABLE_ENTRY 1: kd> dt _KTIMER nt!_KTIMER +0x000 Header : _DISPATCHER_HEADER +0x010 DueTime : _ULARGE_INTEGER +0x018 TimerListEntry : _LIST_ENTRY +0x020 Dpc : Ptr32 _KDPC +0x024 Period : Uint4B 1: kd> dt _KTIMER_TABLE_ENTRY nt!_KTIMER_TABLE_ENTRY +0x000 Lock : Uint4B +0x004 Entry : _LIST_ENTRY +0x010 Time : _ULARGE_INTEGER
Process and Thread Callbacks
• Callback function when create or terminate process or thread
• PsSetCreateProcessNotifyRoutine
• PsSetCreateThreadNotifyRoutine
• PsSetLoadImageNotifyRoutine
• Many anti-virus software products register these callbacks to monitor system behavior.
• Kernel-mode root-kits sometimes use them in conjunction with APCs to inject code into new processes
Completion Routines
• Completion routines are used to notify drivers that their I/O request has been completed
• Use when low-level driver complete a IRP
• IoCompleteRequest, IoSetCompletionRoutine
Structure
1: kd> dt _IO_STACK_LOCATION nt!_IO_STACK_LOCATION +0x000 MajorFunction : UChar +0x001 MinorFunction : UChar +0x002 Flags : UChar +0x003 Control : UChar +0x004 Parameters : <unnamed-tag> +0x014 DeviceObject : Ptr32 _DEVICE_OBJECT +0x018 FileObject : Ptr32 _FILE_OBJECT +0x01c CompletionRoutine : Ptr32 long +0x020 Context : Ptr32 Void
I/O Request Packets
• Windows uses I/O request packets (IRPs) to describe I/O requests to kernel- mode components (like drivers)
• IRP can be divided into two areas
• static, dynamic
• Ex. IRP_MJ_CREATE, IRP_MJ_READ, etc…
146 Chapter 3 ■ The Windows Kernel
c03.indd 11:7:49:AM 01/21/2014 Page 146
IRPStatic Port
Dynamic Port
StackCount
Tail.Overlay.CurrentStackLocation
IO_STACK_LOCATION IO_STACK_LOCATION
“next” IRP stack location
“current” IRP stack location
IO_STACK_LOCATION
IO_STACK_LOCATION
IO_STACK_LOCATION
IO_STACK_LOCATION
an IO request packet relationship between IRP and IO_STACK_LOCATIONin an IO request packet
…
…
…
…
…
Figure 3-8
Note that the “next” stack location is the element immediately above the “cur-rent” one (not after it). This is important to know because stack location routines such as IoGetCurrentIrpStackLocation, IoSkipCurrentIrpStackLocation, IoGetNextIrpStackLocation, and others are simply returning pointers to these array elements using pointer arithmetic.
Although IRPs are typically generated by the I/O manager in response to requests from users or other devices, they may also be created from scratch and sent to other devices for processing. A driver can allocate an IRP with IoAllocateIrp, associate it with a thread, fi ll out the IRP major and minor code, set up IO_STACK_LOCATION count/size, fi ll in parameters, and send it to the destination device for processing with IoCallDriver. Some rootkits use this mechanism to directly send requests to the fi le system driver in order to bypass system call hooking. You will analyze one such rootkit in the exercise.
Structure of a Driver
A driver is a piece of software that interacts with the kernel and/or controls hardware resources. While there are many different types of drivers, we are primarily concerned with the following types of kernel-mode drivers:
■ Legacy software driver—Software that runs in ring 0 and interacts with the kernel through documented and undocumented interfaces. Most rootkits and security drivers are of this type.
kd> dt _IRP ntdll!_IRP +0x000 Type : Int2B +0x002 Size : Uint2B +0x004 MdlAddress : Ptr32 _MDL +0x008 Flags : Uint4B +0x00c AssociatedIrp : __unnamed +0x010 ThreadListEntry : _LIST_ENTRY +0x018 IoStatus : _IO_STATUS_BLOCK +0x020 RequestorMode : Char +0x021 PendingReturned : UChar +0x022 StackCount : Char +0x023 CurrentLocation : Char +0x024 Cancel : UChar +0x025 CancelIrql : UChar +0x026 ApcEnvironment : Char +0x027 AllocationFlags : UChar +0x028 UserIosb : Ptr32 _IO_STATUS_BLOCK +0x02c UserEvent : Ptr32 _KEVENT +0x030 Overlay : __unnamed +0x038 CancelRoutine : Ptr32 void +0x03c UserBuffer : Ptr32 Void +0x040 Tail : __unnamed
kd> dt _IO_STACK_LOCATION ntdll!_IO_STACK_LOCATION +0x000 MajorFunction : UChar +0x001 MinorFunction : UChar +0x002 Flags : UChar +0x003 Control : UChar +0x004 Parameters : __unnamed +0x014 DeviceObject : Ptr32 _DEVICE_OBJECT +0x018 FileObject : Ptr32 _FILE_OBJECT +0x01c CompletionRoutine : Ptr32 long +0x020 Context : Ptr32 Void
Structure of a Driver
Kernel Driver
• Type of Kernel Driver
• Legacy software driver
• Legacy filter driver
• File system minifilter driver
How to Write Kernel Driver• WDM ( Windows Driver Model )
• Defined since Windows 2000 and all drivers you analyze are based on it
• KMDF ( kernel-mode driver framework )
• WDF is basically a set of libraries built on top of WDM that simplifies driver development
Entry Points
• The primary responsibility of DriverEntry
• Initialize driver-specific settings
• Register IRP dispatch routines DriverEntry: DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateCloseHandler; DriverObject->MajorFunction[IRP_MJ_CLOSE] = CreateCloseHandler; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControlHandler;
• If you do not initialize the MajorFunction table, default handler is “IopInvalidDeviceRequest”
• If a driver supports dynamic unloading, it must also fill out the DriverUnload field
STATUS_INVALID_DEVICE_REQUEST
Driver and Device Objects
typedef struct _DEVICE_OBJECT { CSHORT Type; USHORT Size; LONG ReferenceCount; struct _DRIVER_OBJECT *DriverObject; struct _DEVICE_OBJECT *NextDevice; struct _DEVICE_OBJECT *AttachedDevice; struct _IRP *CurrentIrp; ... PVOID DeviceExtension; DEVICE_TYPE DeviceType; CCHAR StackSize; ... ULONG ActiveThreadCount; PSECURITY_DESCRIPTOR SecurityDescriptor; ... PVOID Reserved; } DEVICE_OBJECT, *PDEVICE_OBJECT;
IRP Handling • The prototype for these dispatch routines
• If the dispatch routine successfully completes:
• Calls IoCompleteRequest and returns
• If it cannot complete:
• Return an error, pass the IRP to another driver, or pend the IRP
NTSTATUS XXX_Dispatch ( PDEVICE_OBJECT *DeviceObject, PIRP *Irp );
A Common Mechanism for User-Kernel Communication
• Shared memory region double-mapped in user and kernel space
• Create an event that a user-mode thread can wait on; the event state can be used as a trigger for further action
• Interrupt handling
• IRP_MJ_DEVICE_CONTROL operation and commonly referred to as device I/O control or simply IOCTL
I/O Control Code• User-mode code can request these IOCTL
operations through the DeviceIoControl API. User Mode: bResult = DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, &Overlapped);
Kernel Mode: NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath) { … DriverObj->MajorFunction[IRP_MJ_CLEANUP] = DispatchFilter; DriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchFilter; … }
NTSTATUS DispatchFilter(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); switch(IrpStack->MajorFunction) { … case IRP_MJ_DEVICE_CONTROL: // Handle IOCTL break;
… }
}
• Buffering Methods
• Buffered I/O
• Direct I/O
• Neither
• I/O Control Code
Miscellaneous System Mechanisms
• System Control Registers
• Root-kit developers resort to hooking func- tions in the kernel. But Kernel code is mapped as Read-Only.
• Protect by hardware level special control register: CR0
• CPU can write to read-only pages (WP bit)
System Control Registers
KeServiceDescriptorTable
• Many root-kits resort to hooking system calls
• But the system call table (KiServiceTable) is not exported
• How to access KiServiceTable?
• Ex. Sample G
Sections