Skip navigation.

Wandering Through Trojan.NtRootKit.47 Driver


Wandering Through Trojan.NtRootKit.47 Driver
Author: ocean


I didn’t have the dropper at the moment of writing this, only the driver. Without the dropper we can only get a generic idea of what the driver is used for. The driver has been reverse engineered by deadlist, a really irritating thing to do actually, but it can be useful to see the generic structure of a typical driver.

It’s a driver with dll functionality. Erssd shows us that the driver is produced by ErrorSafe, a fake-av (scareware) company. Seems like there are no rootkit functionality in this driver, while only a few zw* functions are exposed to the dropper, through the use of IOCTLS, though we can’t know how this is used without access to the dropper.

Driver entry point:
driver entry point graph
Simple start structure, a Device is created with name “erssdd” and linked with a Dosdevice with the same name, next every PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1] will be written to point to a general IRP_dispatch procedure. Also a driver unload routine is set.

.text:000113EA push 1Ch ; IRP_MJ_MAXIMUM_FUNCTION+1
.text:000113EC lea edi, [ebx+38h]
.text:000113EF pop ecx
.text:000113F0 mov eax, offset irp_dispatch
.text:000113F5 rep stosd

.text:000113F7 mov dword ptr [ebx+34h], offset unload

unload procedure is pretty simple too

.text:0001133A unload:
.text:0001133A cmp Handle, 0
.text:00011341 jz short loc_1134A
.text:00011343 push 0
.text:00011345 call close_handle
.text:0001134A loc_1134A:
.text:0001134A push offset DestinationString
.text:0001134F call ds:IoDeleteSymbolicLink
.text:00011355 push DeviceObject
.text:0001135B call ds:IoDeleteDevice
.text:00011361 retn 4

it will just check if there’s and object handle open and close it (inside function close_handle there’s a call to

now the irp dispatcher procedure :)

IRP Dispatcher:
irp dispatcher graph
the first part of it is simply an initialization that also makes use (as obvious) of the IoGetCurrentIrpStackLocation:

.text:0001128E push ebp
.text:0001128F mov ebp, esp
.text:00011291 sub esp, 0Ch
.text:00011294 push ebx
.text:00011295 push esi
.text:00011296 push edi
.text:00011297 mov edi, [ebp+Irp]
.text:0001129A mov esi, [edi+60h] ; Irp->Tail.Overlay.CurrentStackLocation
.text:0001129D mov eax,[edi+_IRP.AssociatedIrp.SystemBuffer];Irp->AssociatedIrp.SystemBuffer
.text:000112A0 xor edx, edx
.text:000112A2 mov [edi+1Ch], edx ; Irp->IoStatus.Information
.text:000112A5 lea ebx, [edi+_IRP.IoStatus] ; Irp->IoStatus
.text:000112A8 mov [ebx], edx ; clear IoStatus.Status
.text:000112AA mov ecx, [esi+_IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength]
.text:000112AD mov [ebp+InputBufferLenght], ecx
.text:000112B0 mov ecx, [esi+_IO_STACK_LOCATION.Parameters.DeviceIoControl.OutputBufferLength]
.text:000112B3 mov [ebp+OutputBufferLenght], ecx
.text:000112B6 mov ecx, [esi+_IO_STACK_LOCATION.Parameters.DeviceIoControl.IoControlCode]
.text:000112B9 mov [ebp+Irp], ecx
.text:000112BC movzx ecx, byte ptr [esi] ; IO_STACK_LOCATION.MajorFunction
.text:000112BF dec ecx
.text:000112C0 dec ecx
.text:000112C1 mov [ebp+SystemBuffer], eax
.text:000112C4 jz short IRP_MJ_CLOSE
.text:000112C6 sub ecx, 0Ch
.text:000112C9 jnz short complete_irp

what happens on IRP_MJ_CLOSE is really simple, closes an open handle (if there’s one) and the complete the irp. Only other accepted request is IRP_MJ_DEVICE_CONTROL.

.text:000112CB mov ecx, [edi+4] ; Irp->MdlAddress
.text:000112CE cmp ecx, edx
.text:000112D0 jz short loc_112E5
.text:000112D2 test byte ptr [ecx+6], 5 ;Mdl->MdlFlags==(MDL_SOURCE_IS_NON_PAGED_POOL|MDL_MAPPED_TO_SYSTEM_VA)
.text:000112D6 jz short loc_112DD
.text:000112D8 mov eax, [ecx+0Ch]
.text:000112DB jmp short loc_112E5

if mdl flags are right map the page!

.text:000112DD loc_112DD:
.text:000112DD push edx ; AccessMode
.text:000112DE push ecx ; MemoryDescriptorList
.text:000112DF call ds:MmMapLockedPages

For IRP_MJ_DEVICE_CONTROL driver must have a DeviceDispatch routine, let’s give a look a it.

device dispatcher graph
Dispatch routine is done in a similar way to this one from IRP Cheat Sheet in MSDN:

DispatchRoutine_5( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){
// IoStatus.Status = STATUS_XXX;
Irp->IoStatus.Information = YYY;
IoCompletRequest(Irp, IO_NO_INCREMENT);
return STATUS_XXX;

As we can notice there’s a switch/nested if that checks for the function in ioctl code. For every function a check is performed on the minimum lenght for the buffer, if condition is not met, the status returned will be:

mov dword ptr [esi], STATUS_BUFFER_TOO_SMALL

As we can easily see from the code some global variables are used, a variable contains a Handle that will be used by every function. Every function exposes a Zw* function in a way they can be used from the usermode dropper. Exposed functions are ZwWriteFile, ZwReadFile, ZwOpenFile, ZwClose,

For the symbolic link desired access is:

push READ_CONTROL ; DesiredAccess

and the handle attributes make sure that the handle is actually accessed only from kernel mode:

mov [ebp+DestinationString.MaximumLength], 800h
mov [ebp+ObjectAttributes.Length], 18h
mov [ebp+ObjectAttributes.RootDirectory], ebx
mov [ebp+ObjectAttributes.Attributes], OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE
mov [ebp+ObjectAttributes.SecurityDescriptor], ebx
mov [ebp+ObjectAttributes.SecurityQualityOfService], ebx
call ds:ZwOpenSymbolicLinkObject

which opens a symbolic link to “\DosDevices\”%c”:” where “%c” is the drive.

ZwQuerySymbolicLink is obviously used to get \DosDevices\Harddisk* string to build a string that can be used to open subsequently a file with ZwOpen.

Inside the call that uses ZwOpen is used also ZwQueryVolumeInformationFile, that’s used to retrieve some information about files.

push edi ; VolumeInformationClass
push 8 ; VolumeInformationLength
lea eax, [ebp+var_28]
push eax ; VolumeInformation
lea eax, [ebp+IoStatusBlock]
push eax ; IoStatusBlock
push Handle ; FileHandle
call esi ; ZwQueryVolumeInformationFile

Thanks to…

Thanks to evilcry for proof-read and correction of this paper and for being a great friend.
Thanks to emdel, alby, nex for being great friends.

…Thanks to EvilFingers :)

this entry has been published also on my blog and on evilfingers rootkit analytics