Device Driver Coding

  1. Debugging the Debugger - Reversing kldbgdrv.sys and Potential Usages

    After various articles on malware analysis I decided to talk about a little different topic, due to the fact that I'm involved into Kernel Mode Coding the Windows Internals Research become a truly important aspect of my Research. As the title suggests, this time I'm going to Study and Reverse a particular component used by the Windows Debugging System, more precisely a Device Driver involved into Local Kernel Debugging.

    Not many people is aware that is possible to perform Local Kernel Debugging, one of the most used Debugging Configurations is the Remote Debugging. Local Kernel Debugging can offer many important vantages, like valuable informations on the Status of the Kernel and Inspect Kmode Components. LKD (Local Kernel Debugging) can be acheived by booting in Debug Mode, both kd and windbg fully supports LKD.

    The essential question now is, How the Debugging Engine, let's consider for example Windbg, is able to obtain informations about the kernel by running from usermode?

    The reply to this question not only will uncover the problem itself but also will open new interesting questions and possibilities, such as:

    #1 - "It's possible to develop an application that can use the same
    technology ?"

    #2 - "How to access the involved components and what are parameter to
    have access?"

    To begin the study of this problem, we have in first instance to reproduce the environement necessary to start a Local Debugging Session.

    I've used for these tests Windows 7 32-Bits Ultimate Edition and Windbg.

    First step is to enable debug mode by running:

    bcdedit -debug on then reboot.

    At this point we have literally to debug our debugger to be able to reveal the mechanism and component involved in communication between the Debug Engine and Kernel.

    In the past Windows Editions, the function used was NtSystemDebugControl, but from Windows Vista to Higher Versions this function is not immediately available.

    To trace windbg activities I've used a great tool for professional API Tracing, called Blade API Monitor.

    The hypothesis was, if windbg runs at usermode and accesses a kernel component it's obvious that will be used a Device Driver, by assuming true this statement, every application that deals directly with Device Drivers will use:

    * CreateFile -> For Driver Opening
    * ReadFile / WriteFile -> Device Driver Communication
    * DeviceIoControl -> Data exchange between Driver and umode application
    * NtSystemDebugControl


    After setting the proper filter for these functions, let's run a Local Kernel Debugging Session and watch the results from API Monitor.

    When debugger is loaded, we can launch some command, like:

    !drvobj
    !irpfind
    !isr

    From API Log emerges an important result, we have two threads:

    #- First Thread

    DeviceIoControl(...)
    DeviceIoControl(...)
    CreateFileW(wchar_t* lpFileName = C:\Windows\Fonts\staticcache.dat,...)

    #- Second Thread

    CreateFileW( wchar_t* lpFileName = C:\Windows\system32\kldbgdrv.sys )
    return value -> void* return = 0x00000128

    WriteFile(void* hFile = 0x00000128, .., unsigned long nNumberOfBytesToWrite = 0x000031F0 )

    CreateFileW(wchar_t* lpFileName = \\.\kldbgdrv)
    return value -> void* return = 0x00000170

    DeviceIoControl(void* hDevice = 0x00000170)
    IOCTL -> unsigned long dwIoControlCode = 0x0022C007
    As you can see, from the second thread we obtain a valuable amount of informations.

    WinDbg creates a driver called kldbgdrv.sys placed in %\system32\ this file is 0x31F0 long.

    Successively this driver is openened and windbg starts to send IOCTL to this driver.

    The IO Control Code used is 0x0022C007.

    When debugging session finishes, kldbgdrv.sys it's deleted.

    To reverse this driver we have obviously to dump it, so the first operation is to locate where is placed and successively carve out this.

    The most probable location where can be located are the resources of windbg or kd executables, so let's explore their PE.

    Between resources of windbg.exe we can see that the last one called "17476" which contains another subdir called "30583" by opening also this last directory finally appears our wldbgdrv.sys (can be easly detected by watching between strings)

    From the starting address of this resource if we add the len of bytes ( nNumberOfBytesToWrite = 0x000031F0) we can easly into a new file kldbgdrv.sys

    Now let's reverse this driver.

    INIT:00010D1F push offset aKdsystemdebugc ; "KdSystemDebugControl"
    INIT:00010D24 lea eax, [ebp+DestinationString]
    INIT:00010D27 push eax ; DestinationString
    INIT:00010D28 call ds:RtlInitUnicodeString
    INIT:00010D2E lea ecx, [ebp+DestinationString]
    INIT:00010D31 push ecx ; SystemRoutineName
    INIT:00010D32 call ds:MmGetSystemRoutineAddress
    INIT:00010D38 mov [ebp+var_1C], eax
    INIT:00010D3B cmp [ebp+var_1C], 0
    INIT:00010D3F jnz short loc_10D4B
    INIT:00010D41 mov eax, STATUS_PROCEDURE_NOT_FOUND
    INIT:00010D46 jmp loc_10DF5

    this is a really interesting piece of code, here the driver attempts to obtain the Routine Address of the function KdSystemDebugControl()

    INIT:00010D4B mov edx, [ebp+DriverObject]
    INIT:00010D4E mov dword ptr [edx+34h], offset sub_10A10 ;DriverUnload
    INIT:00010D55 mov eax, [ebp+DriverObject]
    INIT:00010D58 mov dword ptr [eax+38h], offset sub_10A50 ;DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50
    INIT:00010D5F mov ecx, [ebp+DriverObject]
    INIT:00010D62 mov dword ptr [ecx+40h], offset sub_10A50 ;DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50
    INIT:00010D69 mov edx, [ebp+DriverObject]
    INIT:00010D6C mov dword ptr [edx+70h], offset sub_10A80 ;DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_10A80
    INIT:00010D73 push offset aDeviceKldbgdrv ; "\\Device\\kldbgdrv"
    INIT:00010D78 lea eax, [ebp+DeviceName]
    INIT:00010D7B push eax ; DestinationString
    INIT:00010D7C call ds:RtlInitUnicodeString
    ..
    INIT:00010D96 call ds:IoCreateDevice

    Here the device it's created \\Device\\kldbgdrv and

    There are also four MajorFunctions associations:

    DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_10A10;
    DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50; // IofCompleteRequest(Irp, 0)
    DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_10A50; // IofCompleteRequest(Irp, 0)
    DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_10A80; // Suddenly Reversed
    Let's check the latest Dispatch Routine:

    PAGE:00010AEE push edx
    PAGE:00010AEF push eax ; PrivilegeValue
    PAGE:00010AF0 call ds:SeSinglePrivilegeCheck
    PAGE:00010AF6 movzx ecx, al
    PAGE:00010AF9 test ecx, ecx
    PAGE:00010AFB jnz short loc_10B09
    PAGE:00010AFD mov [ebp+var_3C], STATUS_ACCESS_DENIED
    PAGE:00010B04 jmp loc_10C5B
    PAGE:00010B09 mov [ebp+var_4], 0
    PAGE:00010B10 mov edx, [ebp+var_30]
    PAGE:00010B13 mov [ebp+var_48], edx
    PAGE:00010B16 cmp [ebp+var_48], 22C007h ; IOCTL = 22C007h
    PAGE:00010B1D jz short loc_10B24
    PAGE:00010B1F jmp loc_10C2B


    When the IOCTL = 22C007h it's sent the first operation is to check if the action has the proper privileges "SeSinglePrivilegeCheck", successively this dispatch routine validates and sanitizes parameters sent with the IOCTL, by using MmUserProbeAddress and ProbeForWrite.

    Finally we can say that kldbgdrv.sys works as wrapper for KdSystemDebugControl.

    This function belongs to NtSystemDebugControl but can be accessed only at kernel mode.

    Here it's prototipe:

    NTSTATUS
    NTAPI
    KdSystemDebugControl(
    SYSDBG_COMMAND Command,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength,
    PULONG ReturnLength,
    KPROCESSOR_MODE PreviousMode
    );
    _SYSDBG_COMMAND it's an enum, let's suppose we want SysDbgReadVirtual we have the corresponding struct:

    typedef struct _SYSDBG_VIRTUAL
    {
    PVOID Address;
    PVOID Buffer;
    ULONG Request;
    } SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL;
    At this point we have all elements to use with success kldbgdrv.sys, that means the possibility to have access to the kernel ...
  2. Device Drivers Vulnerability Research, Avast a real case

    In the past days I worked intensively on Antivirusís Device Drivers bugs, at the actual state of art the major part of well known AVs suffer of basical and more hidden bugs. The totality of AVs that Iíve checked presents defects that could be maliciously used to takeover an Antivirus Infrastructure and in some case the entire Operating System with attacks like DoS and/or Remote/Local Privilege Escalation.

    I want to make a precisation here, exists an important difference between Bug and Vulnerability, simply bugs does not affects the integrity of a system and does not constitute a true danger. Vulnerabilities constitutes an effective risk for systems integrity, included informations contained inside it. When we are dealing with applications specifically devoted to security, every bug could be considered a vulnerability, because an attacker could block/kill overcome checks performed by the application itself and propagate in system and produce damages. Just think to a basical crash that could affect an Antivirus could be implemented into a malicious application that checks the presence of AVs and induces the bug.

    In this little post we are going to see some defects of last device drivers used by Avast, Iím precisely talking of

    Build Number: 4.8.1351.0

    Avast loads the following drivers:

    • Aasvmker4.sys
    • aswMon2.sys
    • aswRdr.sys
    • aswSP.sys



    Avast loads the following Drivers could be tested by fuzzing IOCTLs, for this task could be used IOCTL Fuzzer and Kartoffel. Letís disassemble the first driver, Aavmker4.sys that from DeviceIoControl hook appears to be heavy used. This is the DriverEntry()drivers

    Code:
    00010748 mov eax, [ebp+DriverObject]
    0001074B push offset NotifyRoutine ; NotifyRoutine
    00010750 mov dword ptr [eax+70h], offset sub_1098C ; DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_1098C;
    00010757 call PsSetCreateProcessNotifyRoutine
    sub_1098C contains the switch statement to handle various IOCTL notifications, essentially IOCTL check is structured in form of nested If and Switches.

    Code:
    001098C ; int __stdcall sub_1098C(int, PIRP Irp)
    000109C4 mov ecx, 0B2D6002Ch
    000109C9 cmp eax, ecx
    000109CB ja loc_10D12
    000109D1 jz loc_10CE9
    Checks if IOCTL is less or equal to 0◊0B2D6002C, if condition is true checks if IOCTL is exactly 0◊0B2D6002C a certain task is performed by the device driver and finally ends with a classical
    epilogue:

    Code:
    IofCompleteRequest(X, 0);
    return value;
    By monitoring Aavmker4.sys activity, with a DeviceIoControl hook emerges that the most used IOCTLs are:

    • 0xB2D60030
    • 0xB2D60034



    Now we have two possibilities the first is to fuzz these IOCTLs and check crash dump if happens and after check code for more details, the second possibility is to invert the check order.

    This the xml configuration to test Aavmker4.sys

    Code:
    <allow>
    <drivers>
    <entry>Aavmker4.sys</entry>
    </drivers>
    <devices>
    <entry>\Device\AavmKer4</entry>
    </devices>
    
    <ioctls>
    <entry>0xb2d60030</entry>
    <entry>0xb2d60034</entry>
    </ioctls>
    <processes>
    <entry>ashServ.exe</entry>
    </processes>
    </allow>
    launch fuzzer and Avast Scan, as you can see Driver resists to Fuzzing attempts, its time to see code referred to 0xB2D60030 and 0xB2D60034.

    0xB2D60030

    Code:
    00010D25 cmp eax, 0B2D60030h
    00010D2A jz short loc_10DA8
    00010D2C cmp eax, 0B2D60034h
    00010D31 jz short loc_10D5B
    00010DC5 mov edi, [ebx+0Ch]
    00010DC8 cmp esi, 878h
    00010DCE jz short loc_10DDA ;Check buffer size
    00010DD0 push offset aIoctl_aavm_sta ; ď******* IOCTL_AAVM_START_REQUEST_AND_SEĒÖ
    00010DD5 jmp loc_10ABA ;Jump to Io Completion
    If buffer size differs from 878h Dbg Prints an error message, else supplied buffer is correctly sanitized via MmUserProbeAddress, MmIsAddressValid. We can say that this IOCTL is correctly handled.

    0xB2D60034:

    Code:
    00010D5B cmp esi, 8
    00010D5E jnz loc_10AC0 ;If differs from 8 return STATUS_INVALID_PARAMETER
    00010D64 call PsGetCurrentProcessId
    Now letís test aswSP.sys. In Device Driver vulnerabilty research itís fundamental to have a complete log of all activities of a driver, this can be obtained by correctly planning a battery of test unit. Each test should correspond to a primitive logic operation performed by an application that makes use of driver. I usually build several mini logs for each activity and finally a complete log. Here a little list of monitoring primitives:


    • On Load
    • On apparent Idle
    • On Working
    • On Shutdown
    • Various, like On Surprise Stop



    This will give us a complete report of all activities and involved IOCTL. In the specific case of aswMon2.sys we can isolate the following:


    • * 0xb2c80018
    • * 0xb2c80014
    • * 0xb2c80020
    • * 0xB2c800C0
    • * 0xB2c800C4
    • * 0xB2c800C8


    From IOCTL Logger we can see that 0xB2c800C0 is heavly used, time to locate Ioctl Dispatcher:

    Code:
    0001178B and dword ptr [ebx+34h], 0
    0001178F mov dword ptr [ebx+6Ch], offset sub_11FB6
    00011796 mov dword ptr [ebx+28h], offset off_18988
    C like:
    Code:
    v2->DriverUnload = 0;
    v2->MajorFunction[13] = (PDRIVER_DISPATCH)sub_11FB6;
    v2->FastIoDispatch = (PFAST_IO_DISPATCH)&unk_18988;
    with a bit of research we land to sub_10B82 that contains the switch for Ioctls.

    Code:
    00010BBD mov eax, 0B2C80018h
    00010BC2 cmp ecx, eax
    00010BC4 push edi
    00010BC5 ja loc_11066
    00010BCB jz loc_10F70
    00010BD1 cmp ecx, 0B2C80008h
    00010BD7 jz short loc_10C3C
    00010BD9 cmp ecx, 0B2C8000Ch
    00010BDF jz short loc_10C16
    00010BE1 cmp ecx, 0B2C80010h
    00010BE7 jz short loc_10BFF
    00010BE9 cmp ecx, 0B2C80014h
    00010BEF jnz loc_111AC
    00010BF5 call sub_108BC
    From logs emerged that the most frequently used is 0B2C8000C so itís obvious that we will study this for first:

    0xB2C8000C:

    Code:
    00010C16 cmp [ebp+arg_C], 1448h
    00010C1D jnz loc_111AC ;check len
    00010C23 mov esi, [ebp+SourceString]
    00010C26 mov ecx, 512h
    00010C2B mov edi, offset dword_18A58
    00010C30 rep movsd
    00010C32 call sub_108F0
    00010C37 jmp loc_112C1 ;go out
    In this case user supplied input is correctly sanitized, so 0xB2C8000C can be excluded from fuzz testing. From the log On Shutdown emerged the massive presence of 0xB2c80018, so letís fuzz it. Here the configuration for IOCTL Fuzzer:

    Code:
    <?xml version=Ē1.0″ encoding=Ēwindows-1251″?>
    <cfg>
    <log_file>C:\ioctls.txt</log_file>
    <hex_dump>true</hex_dump>
    <log_requests>true</log_requests>
    <debug_log_requests>true</debug_log_requests>
    <fuze_requests>true</fuze_requests>
    <fuze_size>true</fuze_size>
    <allow>
    <drivers>
    <entry>aswMon2.SYS</entry>
    </drivers>
    <devices>
    <entry>\Device\aswMon</entry>
    </devices>
    <ioctls>
    <entry>0xb2c80018</entry>
    </ioctls>
    <processes>
    <entry>ashServ.exe</entry>
    </processes>
    </allow>
    <deny>
    <drivers>
    
    <entry>aswSP.SYS</entry>
    <entry>Aavmker4.SYS</entry>
    <entry>aswTDI.SYS</entry>
    </drivers>
    <ioctls>
    
    <entry>0xb2c8000c</entry>
    <entry>0xb2c80014</entry>
    <entry>0xb2c80020</entry>
    </ioctls>
    </deny>
    </cfg>
    The config script allows only 0xB2c80018 sent from aswMon, other drivers are locked. Obviously fuzzing need to follow the log unit that evidenced out IOCTL, so run fuzzer and stop all Avast services.

    Bang..a BSOD, discovered an Avast vulnerability into aswMon2.sys

    From crashdump:

    kd> !analyze -v

    UNEXPECTED_KERNEL_MODE_TRAP_M
    Arguments:
    Arg1: 00000008, EXCEPTION_DOUBLE_FAULT
    Arg2: 80042000
    Arg3: 00000000
    Arg4: 00000000_KERNEL_MODE_TRAP_M (1000007f)
    STACK_TEXT:
    WARNING: Stack unwind information not available. Following frames may be wrong.
    f76f3234 8053d251 f76f3250 00000000 f76f32a4 nt+0◊600fa
    f76f32a4 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0◊66251
    f76f3328 8052c793 41414141 00000000 f76f377c nt+0◊55712
    f76f33a4 804fc700 f76f377c f76f3478 05050505 nt+0◊55793
    f76f3760 8053d251 f76f377c 00000000 f76f37d0 nt+0◊25700
    f76f37d0 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0◊66251
    f76f3854 8052c793 41414141 00000000 f76f3ca8 nt+0◊55712
    f76f38d0 804fc700 f76f3ca8 f76f39a4 05050505 nt+0◊55793
    f76f3c8c 8053d251 f76f3ca8 00000000 f76f3cfc nt+0◊25700
    f76f3cfc 8052c712 badb0d00 20a0a0a1 f76f5658 nt+0◊66251
    f76f3d80 8052c793 41414141 00000000 f76f41d4 nt+0◊55712
    f76f3dfc 804fc700 f76f41d4 f76f3ed0 05050505 nt+0◊55793
    f76f41b8 8053d251 f76f41d4 00000000 f76f4228 nt+0◊25700
    f76f4228 8052c712 badb0d00 20a0a0a1
    ...
  3. Something About Firewall Hooking and Packer Filtering #2

    Hi,

    Here the second and last part of my little paper..
    First of all, letís introduce some more specification, to make previous blog entry more clear
    The last struct showed, is the _FIREWALL_CONTEXT_T, and as can be seen there is DIRECTION_E that could be a little obscure, so here is reported:

    Code:
    typedef enum _IP_DIRECTION_E {
      IP_TRANSMIT,
      IP_RECEIVE
    } DIRECTION_E, *PDIRECTION_E;
    Represents easly a packet is Receiver or Transmitted.

    The return values by the filter-routine can be:

    FORWARD = 0
    DROP = 1
    ICMP_ON_DROP = 2


    that are proper of FORWARD_ACTION

    As previously said, to implement IP_SET_FIREWALL_HOOK_INFO, itís necessary to write a filter function for \device\IP, so the pointer (to IP) self can be obtained easly by calling IoGetDeviceObjectPointer( )

    Now can be installed the filter function, by passing througout IPís pointer the address of the filtering function self, with IoBuildDeviceIoControlRequest(IOCTL_IP_SET_FIREWALL_HOOK, IpDeviceObject,Ö..)
    Itís important to say also (according to DDK documentation) that IOCTL_PF_SET_EXTENSION_POINTER registers filter-hook callback to the IP filter driver, to ďmake knownĒ \device\IP to reroute every packet received or transmitted, and finally this same IOCTL clears the filter function from IP device. All these specifications could be made, by filling up the proper structure of this IOCTL, that will go to constitute the InputBuffer of IoBuildDeviceIoControlRequest:

    PF_SET_EXTENSION_HOOK_INFO, that inside have another struct PacketFilterExtensionPtr which specifies the pointer to the filter hook callback, and when ins FALSE clears the filter.

    Code:
    typedef PF_FORWARD_ACTION (*PacketFilterExtensionPtr)(
    
       IN unsigned char *PacketHeader, //Pointer to  Ip header of packet
       IN unsigned char *Packet, //Points a buffer with informations in the packet
    //that filter-hook receives
       IN unsigned int PacketLength , //Length of the packet
       IN unsigned int RecvInterfaceIndex,//Index number for the interface adapter (InGoing)
       IN unsigned int SendInterfaceIndex,//Index number for the interface adapter (OutGoing)
       IN IPAddr RecvLinkNextHop, //IP address for the interface adapter that received the packet
       IN IPAddr SendLinkNextHop //IP address for the interface adapter that will transmit the packet
    );
    See you to the next post
  4. Something About Firewall Hooking and Packet Filtering #1

    Hi,

    This little article is only a more dettailed analysis of an 'old' article from codproject that inspired me.

    Firewall hooking is a task in major part not well documented, MS doesnít provides a clear and exaustive documentation about structures and development, so the only mode to have more knowledge is the RCE method.

    These filter-hooks obviously works only at kernel mode, installing a callback function, and the driver installs a callback into \device\IP (which can be seen with WinObj) but letís also parse \system32\Drivers

    Fortunately, no extreme binary analysis is needed, we can study directly some header file from DDK, and precisely ipfirewall.h, so letís take a deeper look to this file. Immediately we can see two intersing structs, the first is IPPacketFirewallPtr that works as a callout routine, and the most interesting _IP_SET_FIREWALL_HOOK_INFO

    First Struct:

    Code:
    typedef FORWARD_ACTION (*IPPacketFirewallPtr)(
      VOID **pData, //can be pMdl or pRcvBuf
      UINT RecvInterfaceIndex, //Received Data
      UINT *pSendInterfaceIndex, //Index where          data is sent
      UCHAR *pDestinationType, //Can be Local
      Network, Remote, Broadcast, Multicast.
      VOID *pContext, //Points to     _FIREWALL_CONTEXT_T
    UINT ContextLength, //sizeof(FIREWALL_CONTEXT_T)
    struct IPRcvBuf **pRcvBuf
    );
    Second Struct:

    Code:
    _IP_SET_FIREWALL_HOOK_INFO {
      IPPacketFirewallPtr FirewallPtr; // Packet filter   callout.
      UINT Priority; // Priority of the hook
      BOOLEAN Add; // if TRUE then ADD else DELETE
    } IP_SET_FIREWALL_HOOK_INFO, *PIP_SET_FIREWALL_HOOK_INFO;
    This is the heart structure necessary to set-up the filter-hook, which can be done by sending a IOCTL to \device\Ip

    #define IOCTL_IP_SET_FIREWALL_HOOK \
    _IP_CTL_CODE(12, METHOD_BUFFERED, FILE_WRITE_ACCESS)

    IP_SET_FIREWALL_HOOK_INFO will be the Input Structure to be filled for the IOCTL.

    By observing IPPacketFirewallPtr, we can see _FIREWALL_CONTEXT_T which is:

    Code:
    typedef struct _FIREWALL_CONTEXT_T {
      DIRECTION_E Direction;
      void *NTE;
      void *LinkCtxt;
      NDIS_HANDLE LContext1;
      UINT LContext2;
    } FIREWALL_CONTEXT_T, *PFIREWALL_CONTEXT_T;
    After installing the filter-hook, can be powered up a set of rules to FORWARD or DROP a packet.

    Thanks to Jesus O.