Wandering Through Trojan.NtRootKit.47 Driver
Author: Davide “ocean” Quarta
Introduction
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.
Analysis
Driver entry point:
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
.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
ZwClose).
now the irp dispatcher procedure
IRP Dispatcher:
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.
DeviceDispatcher:
Dispatch routine is done in a similar way to this one from IRP Cheat Sheet in MSDN:
NTSTATUS
DispatchRoutine_5(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// <– Process the IRP here.
//
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,
ZwOpenSymbolicLinkObject/ZwQuerySymbolicLinkObject.
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 was posted on Wednesday, November 25th, 2009 at 11:23 am and is filed under Rootkit Analysis, Rootkit Research. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.





November 27th, 2009 at 11:27 am
Can you introduce code block highlighting? Something along the lines of this
http://marijn.haverbeke.nl/codemirror/contrib/python/index.html