It's clear than implementation of threads differs in different operating systems, as already you might know, threads in a single process share their resources like memory and address space, however in processes the concept is totally different, processes have their own set of resources which are not share together .
As already mentioned conceptually threads are inside processes, here's the illustration :
Threads can scheduled by the operating system, in case of multithreading context switching let the processor to switch between different threads on a process .
The kernel of an operating system is responsible for manipulation of objects like threads for creation, monitoring, termination and etc .
Processes and Threads in combination in most operating systems are divided into two set of implementation :
- User Mode Implementation (User-Land)
- Kernel Mode Implementation (Kernel-Land)
In case of kernel threads, these threads are the lightest threads, within each process there is at-least one kernel thread, however in situations where a process have different kernel threads, they're sharing resources as mentioned above .
other threads are User-Space threads, operating system don't aware of them and these threads are in control and schuduled by userspace and implementation of these threads are carried out by usermode libraries .
With this basic set of information we are going to implement a kernel thread in windows xp service pack 3.
There's an almost an undocumented function in windows kernel "ntoskrnl.exe" named "PsCreateSystemThread", it's also notable that this function is exported by "ntoskrnl.exe" and "ntkrnlpa.exe" (Physical address enabled version of windows kernel) .
Well, all we need to run out is tracing PsCreateSystemThread and see what's going on this thread .
as you can see in loc_4A23FA parameters are pushed into the stack in reverse order, so we conclude that the first parameter is a ThreadHandle that give us the possibility of manipulating threads, needness to going to MSDN we're trying to inspect legal parameters out :
NTSTATUS __stdcall PsCreateSystemThread
(
PHANDLE ThreadHandle,
ULONG DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PCLIENT_ID ClientId,
PKSTART_ROUTINE StartRoutine,
PVOID StartContext
);
Reversing the prototypes out give us the parameters, so as a conclusion to that inspection we're now have 7 parameters there :
- PHANDLE ThreadHandle -> out parameters which gives a handle to us
- ULONG Desired Access -> Requested access type of the thread with ACCESS_MASK
- POBJECT_ATTRIBUTES ObjectAttributes -> filled POBJECT_ATRIBUTES data structure
- HANDLE ProcessHandle -> Specified the process you want to create to
- PCLIENT_ID ClientId -> we set this to 0
- PKSTART_ROUTINE StartRoutine -> our entry in driver
- PVOID StartContext -> ripped from MSDN (Supplies a single argument that is passed to the thread when it begins execution)
Let's have some fun with windbg & inspect what's going on in "PsCreateSystemThread"
Well, if you're be more close, PsCreateSystemThread calls PspCreateThread to accomplish the thread creation task .
What is PsCreateThread is actually is what you is an almost Pseudocode there :
NTSTATUS __stdcall sub_4A12D0(int a1, ACCESS_MASK AccessMask, int a3, HANDLE Handle,
int a5, int a6, unsigned int a7, int a8, char a9, unsigned int a10, int a11)
{
int v74; // [sp+E4h] [bp-54h]@39
int v75; // [sp+E8h] [bp-50h]@5
int v76; // [sp+ECh] [bp-4Ch]@6
int v77; // [sp+F0h] [bp-48h]@6
LARGE_INTEGER CurrentTime; // [sp+F4h] [bp-44h]@23
int v79; // [sp+FCh] [bp-3Ch]@1
NTSTATUS v80; // [sp+100h] [bp-38h]@19
BOOLEAN v81; // [sp+107h] [bp-31h]@25
KPROCESSOR_MODE PreviousMode[4]; // [sp+108h] [bp-30h]@1
LONG AccessStatus; // [sp+10Ch] [bp-2Ch]@25
BOOLEAN MemoryAllocated[4]; // [sp+110h] [bp-28h]@24
PSECURITY_DESCRIPTOR SecurityDescriptor; // [sp+114h] [bp-24h]@24
int BugCheckParameter1; // [sp+118h] [bp-20h]@20
int v87; // [sp+11Ch] [bp-1Ch]@1
CPPEH_RECORD ms_exc; // [sp+120h] [bp-18h]@21
v79 = *MK_FP(__FS__, 292);
JUMPOUT(a10, 0, *(unsigned int *)loc_4A19F0);
PreviousMode[0] = 0;
v87 = 0;
v11 = 0;
v72 = 0;
if ( Handle )
{
result = ObReferenceObjectByHandle(Handle, 2u, PsProcessType, PreviousMode[0], &Object, 0);
v11 = Object;
v72 = Object;
}
else
{
if ( a10 )
{
v11 = (void *)a5;
ObfReferenceObject(a5);
v72 = (void *)a5;
result = 0;
}
else
{
result = -1073741816;
}
}
if ( result >= 0 )
{
JUMPOUT(PreviousMode[0], 0, *(unsigned int *)loc_4A19DF);
v13 = ObCreateObject(PreviousMode[0], (int)PsThreadType, a3, *(int *)PreviousMode, 0, 600, 0, 0, (int)&v75);
if ( v13 < 0 )
{
v35 = v13;
ObfDereferenceObject(v11);
return v35;
}
v14 = v75;
memset((void *)v75, 0, 0x258u);
*(_DWORD *)(v14 + 564) = 0;
*(_DWORD *)(v14 + 544) = v11;
*(_DWORD *)(v14 + 492) = *((_DWORD *)v11 + 33);
v76 = v14;
v77 = 0;
v15 = sub_490D5C(dword_48A4E0, &v76);
*(_DWORD *)(v14 + 496) = v15;
if ( v15 )
{
*(_DWORD *)(v14 + 576) = dword_47A53C;
KeInitializeSemaphore((PRKSEMAPHORE)(v14 + 500), 0, 1);
*(_DWORD *)(v14 + 460) = v14 + 456;
*(_DWORD *)(v14 + 456) = v14 + 456;
*(_DWORD *)(v14 + 532) = v14 + 528;
*(_DWORD *)(v14 + 528) = v14 + 528;
*(_DWORD *)(v14 + 472) = v14 + 468;
*(_DWORD *)(v14 + 468) = v14 + 468;
*(_DWORD *)(v14 + 568) = 0;
KeInitializeSpinLock((PKSPIN_LOCK)(v14 + 480));
*(_DWORD *)(v14 + 488) = v14 + 484;
*(_DWORD *)(v14 + 484) = v14 + 484;
v61 = (char *)v11 + 128;
if ( (unsigned __int8)ExAcquireRundownProtection() )
{
JUMPOUT(a7, 0, *(unsigned int *)loc_4A19FE);
v87 = 0;
_EAX = 16;
_EDX = v14 + 584;
__asm { lock or [edx], eax }
*(_DWORD *)(v14 + 548) = a10;
v16 = sub_4A169D(v14, 0, sub_4A241F, a10, a11, 0, 0, v11);
if ( v16 < 0 )
{
if ( v87 )
{
sub_4A0DB0((ULONG_PTR)v11, v87);
*(_DWORD *)(v14 + 32) = 0;
}
}
else
{
v19 = v79;
--*(_DWORD *)(v19 + 212);
v69 = (char *)v11 + 108;
_ECX = (int)((char *)v11 + 108);
_EDX = 2;
__asm { cmpxchg [ecx], edx }
if ( !(*((_BYTE *)v11 + 584) & 8) )
{
v24 = *((_DWORD *)v11 + 104);
*((_DWORD *)v11 + 104) = v24 + 1;
v25 = *((_DWORD *)v11 + 101);
*(_DWORD *)(v14 + 556) = (char *)v11 + 400;
*(_DWORD *)(v14 + 560) = v25;
*(_DWORD *)v25 = v14 + 556;
*((_DWORD *)v11 + 101) = v14 + 556;
sub_421912(v14);
v64 = (char *)v11 + 108;
_ECX = (int)((char *)v11 + 108);
_EDX = 0;
__asm { cmpxchg [ecx], edx }
v22 = v79;
v27 = *(_DWORD *)(v79 + 212) == -1;
++*(_DWORD *)(v22 + 212);
if ( v27 )
{
if ( *(_DWORD *)(v22 + 52) != v22 + 52 )
{
*(_BYTE *)(v79 + 73) = 1;
LOBYTE(_ECX) = 1;
HalRequestSoftwareInterrupt(_ECX);
}
}
ExReleaseRundownProtection((char *)v11 + 128);
if ( !v24 )
{
LOBYTE(v28) = 1;
sub_4AA64B(v11, v28);
if ( dword_48A580 )
{
v74 = (int)&unk_48A560;
v67 = 8;
do
{
v39 = sub_56F2AA(v74);
v40 = v39;
if ( v39 )
{
v41 = (void (__cdecl *)(_DWORD, _DWORD, _DWORD))sub_56F15E(v39);
v41(*((_DWORD *)v11 + 83), *((_DWORD *)v11 + 33), 1);
sub_56F3DD(v74, v40);
}
v74 += 4;
}
while ( v67-- != 1 );
}
}
v29 = *((_DWORD *)v11 + 77);
if ( v29 )
{
if ( *(_DWORD *)(v29 + 196) )
{
_EAX = (int)((char *)v11 + 580);
if ( !(*((_BYTE *)v11 + 580) & 5) )
{
_ECX = 4;
__asm { lock or [eax], ecx }
--*(_DWORD *)(v79 + 212);
v63 = v29 + 32;
ExAcquireResourceSharedLite((PERESOURCE)(v29 + 32), 1u);
v44 = *(_DWORD *)(v29 + 196);
if ( v44 )
IoSetIoCompletion(v44, *(_DWORD *)(v29 + 200), *((_DWORD *)v11 + 33), 0, 6, 0);
ExReleaseResourceLite(v63);
v45 = v79;
v46 = *(_DWORD *)(v79 + 212) == -1;
++*(_DWORD *)(v45 + 212);
if ( v46 )
{
if ( *(_DWORD *)(v45 + 52) != v45 + 52 )
{
*(_BYTE *)(v45 + 73) = 1;
LOBYTE(v45) = 1;
HalRequestSoftwareInterrupt(v45);
}
}
}
}
}
sub_494D9E(v14, a8, 1);
if ( dword_48A540 )
{
v73 = (int)&unk_48A520;
v65 = 8;
do
{
v47 = sub_56F2AA(v73);
v68 = v47;
if ( v47 )
{
v48 = (void (__cdecl *)(_DWORD, _DWORD, _DWORD))sub_56F15E(v47);
v48(*(_DWORD *)(v14 + 492), *(_DWORD *)(v14 + 496), 1);
sub_56F3DD(v73, v68);
}
v73 += 4;
}
while ( v65-- != 1 );
}
sub_40C5C2(v14, 2);
if ( !a7 )
goto LABEL_16;
P = ExAllocatePoolWithTag(0, 0x30u, 0x61437350u);
if ( P )
{
KeInitializeApc(P, v14, 0, sub_49528E, 0, dword_5B8958, 1, 0);
if ( (unsigned __int8)KeInsertQueueApc(P, dword_5B8954, 0, 0) )
{
LABEL_16:
if ( a9 )
{
ms_exc.disabled = 1;
sub_421DD9(v14);
ms_exc.disabled = -1;
if ( *(_BYTE *)(v14 + 584) & 1 )
sub_4215D2(v14);
}
if ( a7 )
v30 = *(_DWORD *)(v79 + 68);
else
v30 = (int)v11;
v80 = sub_48D1B7(
0,
v30,
(int)&v59,
(int)&v60,
AccessMask,
(PGENERIC_MAPPING)((char *)PsThreadType + 104));
if ( v80 >= 0 )
{
v80 = ObInsertObject(v14, (int)&v59, AccessMask, 0, 0, (int)&BugCheckParameter1);
SeDeleteAccessState(&v59);
if ( v80 < 0 )
{
_EAX = 2;
_ECX = v14 + 584;
__asm { lock or [ecx], eax }
if ( a9 )
sub_421D81(v14);
}
else
{
ms_exc.disabled = 2;
*(_DWORD *)a1 = BugCheckParameter1;
if ( a6 )
{
*(_DWORD *)a6 = *(_DWORD *)(v14 + 492);
*(_DWORD *)(a6 + 4) = *(_DWORD *)(v14 + 496);
}
ms_exc.disabled = -1;
}
KeQuerySystemTime(&CurrentTime);
v32 = (unsigned __int64)(8i64 * *(_QWORD *)&CurrentTime) >> 32;
*(_DWORD *)(v14 + 448) = 8 * CurrentTime.LowPart;
*(_DWORD *)(v14 + 452) = v32;
_EDI = v14 + 584;
if ( *(_BYTE *)(v14 + 584) & 2 )
{
*(_DWORD *)(v14 + 580) = 2032639;
}
else
{
v80 = ObGetObjectSecurity((PVOID)v14, &SecurityDescriptor, MemoryAllocated);
if ( v80 < 0 )
{
_EAX = 2;
__asm { lock or [edi], eax }
if ( a9 )
sub_421D81(v14);
sub_4219A2(v14);
ObfDereferenceObject(v14);
ObCloseHandle(BugCheckParameter1, *(int *)PreviousMode);
return v80;
}
SubjectSecurityContext.ProcessAuditId = v11;
SubjectSecurityContext.PrimaryToken = (PACCESS_TOKEN)PsReferencePrimaryToken(v11);
SubjectSecurityContext.ClientToken = 0;
v33 = v14 + 580;
v81 = SeAccessCheck(
SecurityDescriptor,
&SubjectSecurityContext,
0,
0x2000000u,
0,
0,
(PGENERIC_MAPPING)((char *)PsThreadType + 104),
PreviousMode[0],
(PACCESS_MASK)(v14 + 580),
&AccessStatus);
sub_494D55((char *)v11 + 200, SubjectSecurityContext.PrimaryToken);
ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated[0]);
if ( !v81 )
*(_DWORD *)v33 = 0;
*(_DWORD *)v33 |= 0x61u;
}
sub_4219A2(v14);
ObfDereferenceObject(v14);
return v80;
}
_EAX = 2;
_ECX = v14 + 584;
__asm { lock or [ecx], eax }
if ( a9 )
sub_421D81(v14);
v16 = v80;
goto LABEL_77;
}
_EAX = 2;
_ECX = v14 + 584;
__asm { lock or [ecx], eax }
ExFreePoolWithTag(P, 0);
}
else
{
_EAX = 2;
_ECX = v14 + 584;
__asm { lock or [ecx], eax }
}
v16 = -1073741670;
LABEL_77:
sub_4219A2(v14);
sub_4268A9(v14, 2);
goto LABEL_78;
}
v66 = (char *)v11 + 108;
_ECX = (int)((char *)v11 + 108);
_EDX = 0;
__asm { cmpxchg [ecx], edx }
v38 = *(_DWORD *)(v19 + 212)++ == -1;
if ( v38 )
{
if ( *(_DWORD *)(v19 + 52) != v19 + 52 )
{
*(_BYTE *)(v19 + 73) = 1;
LOBYTE(_ECX) = 1;
HalRequestSoftwareInterrupt(_ECX);
}
}
sub_54BBBB(v14);
if ( v87 )
sub_4A0DB0((ULONG_PTR)v11, v87);
v16 = -1073741558;
}
ExReleaseRundownProtection((char *)v11 + 128);
}
else
{
v16 = -1073741558;
}
}
else
{
v16 = -1073741670;
}
ObfDereferenceObject(v14);
LABEL_78:
result = v16;
}
return result;
}
For creating kernel-mode components in windows kernel knows as kernel modules or virtual device drivers, you know it comes to Windows Driver Kit.
Based on realization we grabbed from the above steps, the final code looks like here :
#include<ntddk.h>
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
VOID StartRoutine(IN PVOID StartContext);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, DriverEntry )
#pragma alloc_text( PAGE, StartRoutine )
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
PDEVICE_OBJECT deviceObject=NULL;
NTSTATUS status ;
PVOID tmpObj;
HANDLE ThreadHandle=0;
OBJECT_ATTRIBUTES threadOb;
UNICODE_STRING usDriverName;
/*
threadOb.Length = 24;
threadOb.RootDirectory = 0;
threadOb.ObjectName = 0;
threadOb.Attributes = 512;
threadOb.SecurityDescriptor = 0;
threadOb.SecurityQualityOfService = 0;
*/
InitializeObjectAttributes(&threadOb, NULL,
OBJ_KERNEL_HANDLE, NULL, NULL);
PsCreateSystemThread(&ThreadHandle,
(ACCESS_MASK) 0L,
&threadOb,
0,
0,
StartRoutine,
0
);
RtlInitUnicodeString(&usDriverName, L"\\Device\\tr1");
status = IoCreateDevice(DriverObject, 0,
&usDriverName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE, &deviceObject);
status = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, NULL,
KernelMode, &tmpObj, NULL);
ZwClose(DriverObject);
return status;
}
VOID StartRoutine(IN PVOID StartContext)
{
LARGE_INTEGER li;
li.QuadPart=1000000;
while(1){
KeDelayExecutionThread(KernelMode , 0, &li);
DbgPrint("Thread 0.1 is running on your system now!");
}
}
Hope it would be helpful .
No comments:
Post a Comment