Page 1 of 2 12 LastLast
Results 1 to 15 of 24

Thread: Guide to creating a Softice Kernel Debugger Extension (KDExtension)

  1. #1
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries

    Guide to creating a Softice Kernel Debugger Extension (KDExtension)

    Hi All,

    This is an, er extension, to an earlier thread and part of the discussion relates to it

    I had always planned on providing a skeleton project for developing Softice extensions, both in MASM and C++, in the hopes of allowing people to develop and share their own Softice extension plugins. Perhaps I can outline a brief recipe here. Softice extension drivers are based on the same WINDBG_EXTENSION_APIS interface used by WinDbg. One critical difference from a regular driver is how DriverEntry is handled.

    Normally ntoskrnl creates a mostly blank DRIVER_OBJECT structure for the driver being loaded, then calls its DriverEntry routine as an indirect call. You can trace back from a regular DriverEntry routine and find where this is done, it's a fixed address in ntoskrnl that is used for all normal drivers. The prototype for a regular DriverEntry is:

    DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegistryPath)

    With a Softice extension driver the stack parameters are different on driver entry. As a result of the dependancy to NTICE you declared on installation in the CreateService call, ntoskrnl passes control to Softice for final loading of the driver. The prototype on DriverEntry is more like a regular DLL main entry routine:

    DriverEntry(long pBaseAddress, long fdwReason, long reserved)

    If you trace back from a Sice extension DriverEntry you will land in ntice.sys code rather than ntoskrnl and find that it was called like this:
    ; 6A 00         push    0
    ; 6A 01         push    1                    ; DLL_PROCESS_ATTACH
    ; FF 76 17      push    dword ptr [esi+17h]  ; Base Address (MZ header)
    ; FF 56 1B      call    dword ptr [esi+1Bh]  ; DriverEntry_fdwReason_LoadKDExtension
    Also, DriverEntry is called on unloading as well
    ; 6A 00        push    0
    ; 6A 00        push    0                     ; DLL_PROCESS_DETACH
    ; FF 76 17     push    dword ptr [esi+17h]   ; Base Address (MZ header)
    ; FF 56 1B     call    dword ptr [esi+1Bh]   ; DriverEntry_fdwReason_UnloadKDExtension
    What is important here is that you have no direct reference to PDRIVER_OBJECT. So you can't even use IoCreateDevice or IoCreateSymbolicLink without first finding a pointer to the Driver Object. A pointer to DRIVER_OBJECT does sit on the stack but varies with Softice versions
    ; [ebp+24h] ; pDRIVER_OBJECT for DS3.1
    ; [ebp+28h] ; pDRIVER_OBJECT for DS2.7

    What I do is to find an absolute pointer to DRIVER_OBJECT using ObReferenceObjectByName, then just proceed as with a normal DriverEntry routine.

    Now, what I've described is a method for a "normal" Softice KDExtension. i.e. install/register the driver with a dependancy on NTICE, incorporate a WinDbgExtensionDllInit interface routine, create individual extension command modules, and use some form of modified DriverEntry to allow for the parameter differences. None of this is documented per se, but is the method I use.

    Sten does things a little different. While he's changed his method a bit over time, I see he's now able to avoid the problem of the missing PDRIVER_OBJECT entirely. In recent versions IceExt was registered as a KDExtension (KDExt) by directly modifying Softice code rather than creating an entry in the Registry. If you follow the Softice code I posted above (continue from DriverEntry_fdwReason_LoadKDExtension), you see where Sice then parses the EXPORT_DIRECTORY of your KDExt and copies it into some global buffer. I *believe* that IceExt modifies this directly, "fooling" Sice into having already registered this KDExt, please correct me if I'm wrong.

    In addition, in tracing the loading of IceExt v.67 I now see that the entire sequence of Softice having to do the final loading of the KDExt driver has been eliminated. It's loaded as a normal driver from the usual ntoskrnl path. This must be the use of TARGETTYPE MINIPORT you mentioned. I hadn't seen that yet, IceExt is being loaded as a miniport driver it seems. One benefit of doing it this way is being able to change the name of the installed driver and hide the fact that IceExt even exists. Nice trick Sten

    Back to the rest. No, you only require a WinDbgExtensionDllInit routine. ExtensionApiVersion or CheckVersion are not used by Softice (I don't think WinDbg even uses the latter).

    I'll continue in the next post.

  2. #2
    Very interesting...
    I'm waiting the next post

  3. #3
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries

    Guide to creating a Softice Kernel Debugger Extension - II

    Here is a rough guide to creating a Softice Extension.
    Have in place a standard skeleton driver + driver install routine.

    The critical parts of the install routine:
    aNTICE                   db "NTICE",0,0           ; double null terminated
         ; Create the service with a dependancy on NTICE
         invoke CreateService, hScManager, offset ServiceName, offset ServiceName,\
              ADDR RootFilePath, 0,0,offset aNTICE,0,0
    Use regular registry writing routines to register the name of your driver under
    KDExtensionName          db "KDExtname.sys;",0
    note the ";" at the end, concatenate strings for multiple extension drivers
    A DriverEntry routine modified to get a pointer to its own Driver Object:
    //				Driver Entry
    // Changes in standard driver DriverEntry routine for Softice KDExtensions
    DriverEntry(long pBaseAddress, long fdwReason, long reserved)
    	NTSTATUS			status;
    	PDRIVER_OBJECT		pDriverObject = 0;
    	PDEVICE_OBJECT		pDeviceObject = 0;
        UNICODE_STRING		usDeviceName;
        UNICODE_STRING		usSymbolicName;        
        switch ( fdwReason )
            case DLL_PROCESS_ATTACH:
    	pDriverObject = (PDRIVER_OBJECT) (GetDriverObject(DriverName));
    	RtlInitUnicodeString(&usDeviceName,	DeviceNameBuffer);
    	RtlInitUnicodeString(&usSymbolicName, SymbolicNameBuffer);
    	status = IoCreateDevice(
    		pDriverObject,			// IN PDRIVER_OBJECT 
    		sizeof(DEVICE_EXTENSION),	// IN ULONG DeviceExtensionSize
    		&usDeviceName,			// IN PUNICODE_STRING 
    		0,				// IN ULONG DeviceCharacteristics
    		FALSE,				// IN BOOLEAN Exclusive 
    		&pDeviceObject);		// OUT PDEVICE_OBJECT
    	if (!NT_SUCCESS(status))
    		return status;
    	status = IoCreateSymbolicLink(
    		&usSymbolicName,		// IN PUNICODE_STRING
    		&usDeviceName);		// IN PUNICODE_STRING
    	if (!NT_SUCCESS(status))
    		IoDeleteDevice (pDriverObject->DeviceObject);
    		return status;
    	// Initialize the Device Extension
    //                                        DeviceObject->DeviceExtension; 	
    //    RtlZeroMemory(pDevExt, sizeof(DEVICE_EXTENSION));
    	// Set up dispatch routine entry points for IRP_MJ_Xxx requests
    	// IRP's sent by GUI app when opening and closing a handle to the driver.
    	pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchControl;
    	pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchControl;
    	// Dispatch routine for DeviceIoControl calls from the GUI app
    	pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;
    	// Define the DriverUnload procedure
    	pDriverObject->DriverUnload = DriverUnload;
    //      case DLL_PROCESS_DETACH:	
    //		break;	
    	return STATUS_SUCCESS;
    }  // end DriverEntry
    //			External Declarations
    EXTERN_C PVOID IoDriverObjectType;
    NTSTATUS NTAPI ObReferenceObjectByName( 
    	IN PUNICODE_STRING ObjectPath, 
    	IN ULONG Attributes, 
    	IN PACCESS_STATE PassedAccessState OPTIONAL, 
    	IN ACCESS_MASK DesiredAccess OPTIONAL, 
    	IN POBJECT_TYPE ObjectType, 
    	IN KPROCESSOR_MODE AccessMode, 
    	IN OUT PVOID ParseContext OPTIONAL, 
    	OUT PVOID *ObjectPtr 
    //				GetDriverObject
    // Returns DriverObject pointer
    NTSTATUS GetDriverObject(PWSTR pwszDriverName)
    NTSTATUS status;
    UNICODE_STRING DeviceName;
    PDRIVER_OBJECT pDriverObject = 0;
    	RtlInitUnicodeString(&DeviceName, pwszDriverName);
    	status = ObReferenceObjectByName(&DeviceName, OBJ_CASE_INSENSITIVE,
    		NULL, 0, (POBJECT_TYPE)IoDriverObjectType, 
    		KernelMode, NULL, (PVOID*)&pDriverObject);
    	if (!pDriverObject) {
    		return 0;
    	return (long) pDriverObject;
    }	// end GetDriverObject

    The required WinDbgExtensionDllInit routine.
    See WinDbg docs for details:
     WINDBG_EXTENSION_APIS   ExtensionApis;
    //                   WinDbgExtensionDllInit
    // WinDbg (or Softice) calls this function when the user loads the
    // extension DLL. Its job is to save the address of the callback table
    // so that other parts of the DLL can use it. This function is required.
    // lpExtensionApis is a pointer to the WINDBG_EXTENSION_APIS structure,
    // a list of internal functions that can be called from WinDbg or Softice
    // extensions.
    VOID WinDbgExtensionDllInit(
        PWINDBG_EXTENSION_APIS lpExtensionApis,
        ULONG MajorVersion, ULONG MinorVersion)
    	// wdbgexts.h expects variable name of "ExtensionApis"
    	// to store the address of WINDBG_EXTENSION_APIS
        ExtensionApis = *lpExtensionApis;
    } // end WinDbgExtensionDllInit

    And finally, a basic KDExtension available as a ! command in Softice:
    They run at the same elevated IRQL level as Softice - do not use INT1/INT3 to debug!
    //  KDExtension simply to echo user input
    #include <windef.h>
    #include "wdbgexts.h"
    DECLARE_API ( _echome )
    	dprintf ("%s\n", args);
    Each ! command is declared as an export in a .DEF file such as:
    LIBRARY KDExtname.sys
         ; User defined Debugger Extension functions
         ; must be lower case
         ; Default Debugger Extension functions
    ;	not required with Softice extensions
    ;       ExtensionApiVersion
    ;       CheckVersion

    I hope this helps as a short guide to creating KDExtensions. Who says Softice is useless?


  4. #4
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Ring -1
    Blog Entries
    You da man Kayaker.

  5. #5
    I thought an "extension" was a "cord" used to plug the fan in closer to the LazyButt chair from which one watched sports on TV.


  6. #6
    That's very interesting stuff, indeed.
    Thanks a lot, Kayaker!

    They run at the same elevated IRQL level as Softice - do not use INT1/INT3 to debug!
    Erm, i guess INT1 includes trapflag single stepping, so could anyone give me some hint on how to (live) debug those routines at all?


  7. #7
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    Hi Pyrae

    I'm glad you found it useful. How do you debug a KDExtension? The same way porcupines make love - very carefully!

    Yeah, it's a little disconcerting at first not to be able to single step, so you resort to other means. Depending on what you're trying to do, much of the code can be test developed in regular driver code (or even in a Windbg dll). Any API that Softice uses can also be used in a KDExtension (KDExt), so in my mind it has already "passed" the test of being able to run at an elevated IRQL safely. I haven't encountered any problems with standard memory allocation functions for example.

    I don't know that Softice *always* runs at IPI_LEVEL, but KeGetCurrentIrql indicates that is the case from within a KDExt:

    #include <ntddk.h>

    dprintf("Current IRQL %08X\n", (KIRQL) KeGetCurrentIrql());
    // IPI_LEVEL 29 (0x1D) // Interprocessor interrupt level

    Once the WinDbgExtensionDllInit proc has run (immediately after DriverEntry), you also have access to the WINDBG_EXTENSION_APIS routines in regular driver code. For example, DbgPrint and dprintf can be used interchangably.

    The critical part is being able to parse the extension arguments properly, i.e. what you type into the Softice command window along with your ! command. Since this can be both text and data you need to separate the string components and handle them as required. The data portions of the string can be converted to values with ExtensionApis.lpGetExpressionRoutine.
        PCSTR lpExpression
    There's a small caution with Softice here. In Windbg, the GetExpression routine recognizes spaces (20h) between arguments as null terminators. The string is of the args:VARARG type. You can pass a pointer to any point in the string and GetExpression will parse the argument until a null or space is reached. In the Softice version however, GetExpression doesn't recognize 'spaces', so you need to deal with that. I found it easiest to parse each argument into an indexed, null terminated string buffer before passing them to GetExpression. Not a big deal and it actually makes it easier to handle the arguments.

    Note that GetExpression recognizes ascii representations of numbers (such as "401000"), as well as terms such as ("eip", "eax+ebx-5", etc.). Such term values will be translated at the time, so "eip" will return the current instruction pointer for example.

    In terms of developing the argument parsing and the rest of your routine in regular driver code, you can simply "fake" the DECLARE_API prototype of a KDExt. In reality all you need is (PCSTR args) just as you intend to type it in.
    VOID TestKDExt (char* args);
    If all goes well, you should be able to paste the finished code into a blank KDExtension proc (cautious use of dprintf is useful here


  8. #8
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    Some other notes. These are the default ExtensionApis routines available for use in Softice (see Windbg docs for details)
    extern WINDBG_EXTENSION_APIS   ExtensionApis;
    #define dprintf          (ExtensionApis.lpOutputRoutine)
    #define GetExpression    (ExtensionApis.lpGetExpressionRoutine)
    #define CheckControlC    (ExtensionApis.lpCheckControlCRoutine)
    #define GetContext       (ExtensionApis.lpGetThreadContextRoutine)
    #define SetContext       (ExtensionApis.lpSetThreadContextRoutine)
    #define Ioctl            (ExtensionApis.lpIoctlRoutine)
    #define Disasm           (ExtensionApis.lpDisasmRoutine)
    #define GetSymbol        (ExtensionApis.lpGetSymbolRoutine)
    #define ReadMemory       (ExtensionApis.lpReadProcessMemoryRoutine)
    #define WriteMemory      (ExtensionApis.lpWriteProcessMemoryRoutine)
    #define StackTrace       (ExtensionApis.lpStackTraceRoutine)
    While you can easily read and write memory directly, it's actually safer to use the lpReadProcessMemoryRoutine and lpWriteProcessMemoryRoutine routines because there is some degree of internal page-fault protection when using them. If you do encounter a memory access violation, Softice will give you a nice error message rather than a nice BSOD as would occur otherwise:

    db 'Extension aborted: A page fault at CS:EIP %04x:%08x occurred'
    db ' when address %08x was referenced SS:EBP %04x:%08x ',0

    The lpIoctlRoutine uses a number of IoctlType values. In Softice only a portion of the available types are implemented. For reference I will list them here.
        USHORT   IoctlType,  // Specifies the type of Ioctl routine call.
        PVOID    lpvData,    // Address of a data structure, depends on the value of IoctlType.
        ULONG    cbSize      // Specifies the size of the structure lpvData points to.
    ; IoctlType values supported by SoftIce
    IG_KD_CONTEXT                      equ 1     ; returns number of and current processor
    IG_READ_CONTROL_SPACE              equ 2     ; reads processor-specific control space 
    IG_WRITE_CONTROL_SPACE             equ 3     ; not implemented
    IG_READ_IO_SPACE                   equ 4     ; reads from system I/O locations using IN instruction 
    IG_WRITE_IO_SPACE                  equ 5     ; writes to system I/O locations using OUT instruction 
    IG_READ_PHYSICAL                   equ 6     ; reads from physical memory
    IG_WRITE_PHYSICAL                  equ 7     ; writes to physical memory
    IG_READ_IO_SPACE_EX                equ 8     ; same as IG_READ_IO_SPACE
    IG_WRITE_IO_SPACE_EX               equ 9     ; same as IG_WRITE_IO_SPACE
    IG_KSTACK_HELP                     equ 10    ; not implemented
    IG_SET_THREAD                      equ 11    ; sets thread to use for next StackTrace call
    IG_READ_MSR                        equ 12    ; reads the contents of a Model-Specific Register (MSR)
    IG_WRITE_MSR                       equ 13    ; writes to a Model-Specific Register (MSR)
    IG_GET_DEBUGGER_DATA               equ 14    ; not implemented
    IG_GET_KERNEL_VERSION              equ 15    ; always returns 1
    IG_RELOAD_SYMBOLS                  equ 16    ; not implemented
    IG_GET_SET_SYMPATH                 equ 17    ; not implemented
    IG_GET_EXCEPTION_RECORD            equ 18    ; not implemented
    ; The rest are supported by WinDbg only
    ;IG_IS_PTR64                        equ 19
    Here's an example of using IG_READ_IO_SPACE and IG_WRITE_IO_SPACE to read the CMOS clock from within Softice:
    //  KDExtension to Output system date and time
    #include <windef.h>
    #include "wdbgexts.h"
    ; Uses the lpIoctlRoutine ExtensionApis function with IoctlTypes
    ; usage: !time
    // wdbgexts.h ExtensionApis.lpIoctlRoutine
    	typedef struct _IOSPACE {
    		ULONG       Address;
    		ULONG       Length;                   // 1, 2, or 4 bytes
    		ULONG       Data;
    ; This is an example of reading the CMOS and system clock using I/O Ports 70-71.  
    ; Offset(dec)       Function 
    ; 0                 RTC seconds.  Contains the seconds value of current time 
    ; 2                 RTC minutes.  Contains the minutes value of the current time 
    ; 4                 RTC hours.  Contains the hours value of the current time 
    ; 7                 RTC date day.  Contains day value of current date 
    ; 8                 RTC date month.  Contains the month value of current date 
    ; 9                 RTC date year. Contains the year value of current date
    ; 50                Century Date BCD - Value for century of current date
    ; To read from CMOS do the following:
    ;        write to port 70 with the address value to read or write.
    ;        write to port 71 with the new value or read the value of interest from port 71.
    ; In a traditional routine in kernel mode (Win2K/XP) or user mode (Win9x),
    ; you would read the Month as follows:
    ;	                mov al, 08h   ; month
    ;	                OUT 070h, al
    ;	                IN al, 071h
    DECLARE_API ( time )
     int UnitIn[7] = { 50, 9, 8, 7, 4, 2, 0 };
     int UnitOut[7];
     int i;
    	pIOSPACE.Length = 1;
    	// load each date/time unit in turn for write/read of CMOS system clock     
    	for (i = 0; i <= 6; ++i)
    		pIOSPACE.Data = UnitIn[i];
    		// write to port 70h
    		pIOSPACE.Address = 0x70;
    		Ioctl (
    			sizeof IOSPACE
    		// read from port 71
    		pIOSPACE.Address = 0x71;
    		Ioctl (
    			sizeof IOSPACE
    		// store the result
    		UnitOut[i] = (int) pIOSPACE.Data;
    	// output date
    	dprintf (" The Date is %02X/%02X/%02X%02X \n", 
    			UnitOut[2], UnitOut[3], UnitOut[0], UnitOut[1]);		
    	// output time
    	dprintf (" The Time is %02X:%02X:%02X \n", 
    			UnitOut[4], UnitOut[5], UnitOut[6]);

  9. #9
    I was trying to write an extension (my first) but i'm not able to compile the project. I get a lot of error messages related with wdbgexts.h file, look at some of them:
    wdbgexts.h(48): error C2144: syntax error : 'void' should be preceded by ';'
    wdbgexts.h(59): error C2059: syntax error : '__cdecl'
    wdbgexts.h(66): error C2059: syntax error : '__stdcall'

    I included the file inside the main .cpp file, I set the right path but maybe I should set some other particular options, can you help me?

  10. #10
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    Whohoo, another convert ;-)


    I don't know what you're missing. The only #include files I use are <windef.h>, wdbgexts.h and <ntddk.h>. By default VC6++ also uses the external dependancies:


    Here are the compiler and linker options I use for a KDextension driver:



    Project Options:

    /nologo /Gz /ML /W3 /Zi /Oy /Gy /D _X86_=1 /D WIN32_LEAN_AND_MEAN=1 /D DBG=1 /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /GF /QIfdiv- /QIf /Oxs /c


    ntoskrnl.lib hal.lib /base:"0x10000" /version:5.0 /stack:0x40000,0x1000 /entry:"DriverEntry" /incremental:no /pdb:"Debug/KDExtName.pdb" /debug /machine:IX86 /nodefaultlib /def:".\KDExtName.def" /out:"..\bin\Debug\KDExtName.sys" /DRIVER /IGNORE:4078 /MERGE:.rdata=.text /ALIGN:32 /SUBSYSTEM:native

    To get a Softice NMS symbol file you can load for debugging, under Custom Build use:

    --Build SoftICE Symbols--

    $(DRIVERWORKS)\bin\nmsym /trans:source,package,always $(TARGETPATH)


    It should compile properly with the latest wdbgexts.h version. Let us know how you get on.


  11. #11
    Thanks Kayaker for this EXCELLENT post !
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  12. #12
    Registered User
    Join Date
    Feb 2004
    Yes excellent post !

    I've got one question for Kayaker (or anyone else who can answer).

    Is it safe to compile drivers with the compiler from VS, since (tell me if I'm wrong) it's not the same as the 'build' command use ?

    From now on i'm using the VS IDE for convenience and some batch build utilities, called from VS (so the compiler from VS is never used).

    If someone is interested by those build batch integration with VS IDE :

    Ddkbuild batch command

    Hollis Technology Solutions

    Open Systems Resources

    Then arise another question :

    Why MS doesn't let driver's programmers use the VS IDE (using a simple wizard to set correct linker /compiler switches) ?

    Thank you very much.
    Omne tulit punctum qui miscuit utile dulci

  13. #13
    Thanks Kayaker for your post but the problem still exist... I tried to compile with both ddk and VS without luck. I don't know

  14. #14
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    Thanks all, I'm happy for the interest. Sorry Neitsa, I don't use VS, sorta for the reasons you described. VC6 seemed to be the most compatible setup for driver development at the time I was getting into it.

    The Ddkbuild utility you mentioned seems to be a good option for VS. Maybe someone else has a better answer.

    These 2 links may also be of some help:

    Windows Driver Build Methods and Tools

    Essentials Of Building Windows Drivers

    And none of this is to discourage anyone from building Softice extension drivers in MASM either! A few simple macros and it's easy to convert the code I posted above into MASM-friendly format. Don't be turned off by the idea of translating all the information in Wdbgexts.h. WINDBG_EXTENSION_APIS and any specific structs you might want to use are all that is necessary for those who prefer working with Four-F's KMDKit.


  15. #15
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    Quote Originally Posted by Foreigner
    Thanks Kayaker for your post but the problem still exist... I tried to compile with both ddk and VS without luck. I don't know
    Foreigner, here's my version of wdbgexts.h. It's worth a shot to see if you have better luck. There *must* be a simple answer!!
    Attached Files Attached Files

Similar Threads

  1. DbgEng based Kernel Debugger
    By blabberer in forum Blogs Forum
    Replies: 0
    Last Post: January 26th, 2013, 13:09
  2. NEW: Syser Kernel Debugger by wuyanfeng
    By Kayaker in forum Advanced Reversing and Programming
    Replies: 15
    Last Post: August 19th, 2005, 23:46
  3. Replies: 6
    Last Post: August 8th, 2005, 03:38
  4. Novell Linux Kernel Debugger available !
    By Zero in forum Linux RCE
    Replies: 2
    Last Post: July 3rd, 2005, 23:51


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts