Log in

View Full Version : Guide to creating a Softice Kernel Debugger Extension (KDExtension)


Kayaker
May 13th, 2005, 20:13
Hi All,

This is an, er extension, to an earlier thread and part of the discussion relates to it
http://www.woodmann.com/forum/showthread.php?t=7069

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:
Code:

; 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.

Opcode
May 13th, 2005, 20:21
Very interesting...
I'm waiting the next post

Kayaker
May 13th, 2005, 20:21
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:
Code:


aNTICE db "NTICE",0,0 ; double null terminated

; Create the service with a dependancy on NTICE
invoke CreateService, hScManager, offset ServiceName, offset ServiceName,\
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,\
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,\
ADDR RootFilePath, 0,0,offset aNTICE,0,0


Use regular registry writing routines to register the name of your driver under
HKLM\SYSTEM\CurrentControlSet\Services\NTICE\KDExtensions

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:
Code:

/////////////////////////////////////////////////////////////////////
// Driver Entry
//
// Changes in standard driver DriverEntry routine for Softice KDExtensions
/////////////////////////////////////////////////////////////////////

EXTERN_C NTSTATUS
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
FILE_DEVICE_UNKNOWN, // IN DEVICE_TYPE
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
// PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDriverObject->
// 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;

//------------------------------------------------
//------------------------------------------------

break;

//////////////////////////////////////////////////
// case DLL_PROCESS_DETACH:
// break;
}

//------------------------------------------------

return STATUS_SUCCESS;

} // end DriverEntry
/////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////
// External Declarations
/////////////////////////////////////////////////////////////////////

EXTERN_C PVOID IoDriverObjectType;

EXTERN_C
NTSYSAPI
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;
}

ObDereferenceObject(pDriverObject);


return (long) pDriverObject;

} // end GetDriverObject

/////////////////////////////////////////////////////////////////////



The required WinDbgExtensionDllInit routine.
See WinDbg docs for details:
Code:


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;

//================================================

return;

} // 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!
Code:

//////////////////////////////////////////////////////////////////
// KDExtension simply to echo user input
//////////////////////////////////////////////////////////////////

#include <windef.h>
#include "wdbgexts.h"


DECLARE_API ( _echome )
{

dprintf ("%s\n", args);
return;
}

Each ! command is declared as an export in a .DEF file such as:
Code:

LIBRARY KDExtname.sys

EXPORTS

;----------------------------------
; User defined Debugger Extension functions
; must be lower case
;----------------------------------

_echome

;----------------------------------
; Default Debugger Extension functions
;----------------------------------

WinDbgExtensionDllInit

; not required with Softice extensions
; ExtensionApiVersion
; CheckVersion



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

Regards,
Kayaker

dELTA
May 14th, 2005, 08:02
You da man Kayaker.

JMI
May 15th, 2005, 13:33
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.

Regards,

Pyrae
May 27th, 2005, 07:55
That's very interesting stuff, indeed.
Thanks a lot, Kayaker!

Quote:

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?


regards,
Pyrae

Kayaker
May 28th, 2005, 03:00
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.
Code:

ULONG_PTR
(WDBGAPI*PWINDBG_GET_EXPRESSION)(
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


Cheers,
Kayaker

Kayaker
May 28th, 2005, 03:03
Some other notes. These are the default ExtensionApis routines available for use in Softice (see Windbg docs for details)
Code:

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.
Code:

lpIoctlRoutine
ULONG
(WDBGAPI*PWINDBG_IOCTL_ROUTINE)(
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:
Code:

//////////////////////////////////////////////////////////////////
// KDExtension to Output system date and time
//////////////////////////////////////////////////////////////////

#include <windef.h>
#include "wdbgexts.h"


/*
;=========================================================================
; Uses the lpIoctlRoutine ExtensionApis function with IoctlTypes
; IG_READ_IO_SPACE and IG_WRITE_IO_SPACE
; usage: !time
;
// wdbgexts.h ExtensionApis.lpIoctlRoutine

typedef struct _IOSPACE {
ULONG Address;
ULONG Length; // 1, 2, or 4 bytes
ULONG Data;
} IOSPACE, *PIOSPACE;



; 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 )
{


IOSPACE pIOSPACE;
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 (
IG_WRITE_IO_SPACE,
&pIOSPACE,
sizeof IOSPACE
);


// read from port 71
pIOSPACE.Address = 0x71;

Ioctl (
IG_READ_IO_SPACE,
&pIOSPACE,
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]);

//===================================================

return;
}


Kayaker

Foreigner
June 9th, 2005, 09:22
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?

Kayaker
June 9th, 2005, 17:32
Whohoo, another convert ;-)

Hi,

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:

basetsd.h
bugcodes.h
guiddef.h
ia64reg.h
ntdef.h
ntiologc.h
ntstatus.h


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

Preprocessor:

_X86_=1,WIN32_LEAN_AND_MEAN=1,DBG=1

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

Linker:

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:

Description:
--Build SoftICE Symbols--

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

Output:
.\Debug\$(TARGETNAME).nms


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

Kayaker

bflash2k
June 10th, 2005, 03:37
Thanks Kayaker for this EXCELLENT post !

Neitsa
June 10th, 2005, 04:12
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
http://www.hollistech.com/

Open Systems Resources
http://www.osronline.com/article.cfm?article=43

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.

Foreigner
June 10th, 2005, 10:35
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

Kayaker
June 10th, 2005, 10:46
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
http://www.wd-3.com/archive/HowBuild.htm

Essentials Of Building Windows Drivers
http://www.wd-3.com/archive/WinBuild.htm


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.

Cheers,
Kayaker

Kayaker
June 10th, 2005, 10:51
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!!

Foreigner
June 10th, 2005, 15:14
No way... Am i the only one unable to compile??

Foreigner
June 12th, 2005, 05:47
Ok, i solved part of the problems. All the errors related to wdbgexts.h are gone; the solution? I changed the order of the included files from:
#include <windef.h>
#include <ntddk.h>
#include "wdbgexts.h"
to:
#include <ntddk.h>
#include <windef.h>
#include "wdbgexts.h"

Now i have only few little problems like:
DLL_PROCESS_ATTACH undeclared identifier
DEVICE_EXTENSION undeclared identifier
I can resolve them with simple define/struct keywords but I have a simple question for you all; seems like they are not include in ntddk... could it be possible?

Kayaker
June 12th, 2005, 11:49
Aah, I should have thought of suggesting that #include order thing... yeah sometimes that can be quite annoying. I actually use a separate defines file just for driver specific stuff. There are many driver examples around written as .c files rather than .cpp, and the declaration for ntddk.h needs to be different also if you try to compile them. I prefer C++ mainly because it allows you to define variables anywhere in code, C doesn't seem to be as flexible that way.

I use this as a default header file for all driver work:
Code:
/*

defines.h - include file for main.cpp

-------------------------------------------------------
To compile a driver in MS Visual C++:

1. Name the main file as *.c, or
2. Name the main file as *.cpp and wrap the include
for ntddk.h around an extern "C" declaration

The following #ifdef takes care of both cases

-------------------------------------------------------
/TP compiler option: specifies a C++ source file
allows variables to be defined outside of proc start

*/

#ifdef __cplusplus // C++ conversion
extern "C" {
#include <ntddk.h>
}
#else
#include <ntddk.h>
#endif


/////////////////////////////////////////////////////////////////////

EXTERN_C NTSTATUS
DriverEntry(long pBaseAddress, long fdwReason, long reserved);
//DriverEntry(long pBaseAddress, long fdwReason);

// Declare DriverEntry as INIT code, paged out after calling
// not really needed with VC compiler
#pragma alloc_text(INIT, DriverEntry)


NTSTATUS DispatchCreateClose (PDEVICE_OBJECT pDeviceObject, PIRP pIrp);
NTSTATUS DispatchControl (PDEVICE_OBJECT pDeviceObject, PIRP pIrp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);

/////////////////////////////////////////////////////////////////////


I suppose I threw a couple of undefined things in there.

taken from winnt.h:
#define DLL_PROCESS_ATTACH 1
#define DLL_PROCESS_DETACH 0


As for DEVICE_EXTENSION, that is a user-defined structure you may or may not use. It's important that driver variables are in non-paged code, declaring them in a DeviceExtension structure is a convenient way of handling them. If you don't use a DeviceExtension, the parameter sizeof(DEVICE_EXTENSION) in the IoCreateDevice declaration should be 0 or NULL instead. (/my bad, I left that in)

I commented out the initialization of PDEVICE_EXTENSION in DriverEntry but left it in to illustrate how a KDExtension can be used as a regular driver in every other way. In fact, you don't even need to declare any !(bang) commands, but can still make use of the WINDBG_EXTENSION_APIS functions in other code with Softice loaded.

For example, I use the ExtensionApis.lpDisasmRoutine to disassemble instructions and queue them to later be written to a file (using a dedicated file output system thread which runs in its own time). This can be done from a KDExtension, but you could also use the function in regular driver code as an inline Disasm routine, or even pass the results back to user mode.

An example of using a Device Extension structure to disassemble a single instruction with lpDisasmRoutine:
Code:

// Our Device Extension
typedef struct _DEVICE_EXTENSION {
ULONG_PTR Address; // address to disassemble
char DisasmText[0x100]; // buffer holding disassembled text
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

///////////////////////////

/*
ULONG
(WDBGAPI*PWINDBG_DISASM)(
ULONG_PTR *lpOffset, // Points to the instruction to be disassembled
PCSTR lpBuffer, // Receives the disassembled instruction
ULONG fShowEffectiveAddress
// specifies whether or not to print the effective address
// use only FALSE for Softice
);

; On return:
; EAX = opcode length
; lpOffset = address of *next* instruction to be disassembled
*/

//////////////////////////////////////////////////


// Here I'm just using a pointer to the start of TestProc as
// a test address for disassembly


VOID TestProc
{
// To get a reference to your DeviceExtension from anywhere in code,
// you only need a pointer to the DriverObject.
// If doing this from within a KDExtension, you can use the method
// I posted above in the DriverEntry routine to get PDRIVER_OBJECT.

// Get DriverObject structure
PDRIVER_OBJECT pDriverObject =
(PDRIVER_OBJECT) (GetDriverObject(DriverName));

// Get DeviceExtension structure
PDEVICE_EXTENSION pDevExt =
(PDEVICE_EXTENSION)pDriverObject->DeviceObject->DeviceExtension;

//////////////////////////////////////////////////

long LengthOpcode;

// fake some address to disassemble
pDevExt->Address = (ULONG_PTR) &TestProc;

// on return, pDevExt->Address holds next address to disasm

LengthOpcode = Disasm(
&pDevExt->Address, // IN OUT ULONG_PTR
pDevExt->DisasmText, // OUT PCSTR
FALSE
);

// print out the disassembly
dprintf ("%s \n", pDevExt->DisasmText);

} // END TestProc



Kayaker

blank
July 19th, 2005, 07:17
Sorry to dig up a kinda old thread, but some of the questions was left unanswered (and I've been a bit busy).

I had to modify the Windbgets.h to get the kdext working; the method was originally described in the mamaich example (which is 'obsolete' now imho) I think. I use the r 3790 of winddk, and for example for the wxp (<winddk-inatall-dir>\3790\inc\wxp\wdbgexts.h) target the first line to modify is at line 1367 - there you have a *commented* #ifdef _WINBASE_ - uncomment this and the corresponding #endif, around line 1603.
I did this after copying the wdbgexts.h to project dir and then just include as "" not <>, to keep the winddk dir clean.

Hope this resolved the problems some have with wdbgexts.h errors.

Kayaker
July 19th, 2005, 23:41
Hi,

It's always nice to add more information on a subject, and to know that something was successful. Funny, I must have had the same problem, my project file is 'patched' but the original has that #ifdef _WINBASE_ commented out as you said.

If you make an interesting use for the kdext, do tell

Cheers,
Kayaker

laola
July 31st, 2005, 01:59
Adding just my $0.05 (well, inflation...) to the issues with ntddk.h: There are QUITE some different versions. Make sure your compiler sees the most recent file first. The original ntddk.h that comes with VC6 is so much outdated that half of the interesting stuff seems to be missing. The most recent one can be found in the appropriate DDK, however if you use VC or VS to write your extension, make sure that the include search paths are set in a way that the DDK path comes BEFORE the default paths, otherwise the compiler will use the outdated file that ships with the compiler.
And BTW, it is always a good idea to include winnt.h and winerror.h as both contain an abundance of defined values

deroko
October 16th, 2006, 05:41
Sorry for bringing this old thread up, but this topic is very interesting.
I'm kinda lost, I add my own service in registry dependent on ntice, load driver via NtLoadDriver. Image is being loaded, but entry is not called as DllEntry, it is called like any normal driver(driver object, reg path), and loading is performed by ntoskrnl, not by softice of course, WinDbgExtensionDllInit is exported, as some other procedures that I need to run from sice.

Also I used kd2sys.exe to add registry keys for my driver, then I copy my driver to sys32\drivers\ but again it is loaded as any normal driver, even, if it is cleanly stated in registry that service depends on ntice and KDExtensions = my_driver.sys.

edit: solved - added /DEBUG to my linker options

Kayaker
October 16th, 2006, 16:14
Hey Deroko,

I was just about to reply to this but see you solved the problem. Not sure why the /DEBUG option made a difference, but as long as it works. I'll post what I wrote anyway just in case it's useful.


Glad to see use of this. I'm not sure the exact problem but I can say for sure you shouldn't use the KD2SYS.exe utility. I forget the exact reason but I know it doesn't work with KDExtensions written this way, or for IceExt. I think the utility was to somehow convert a dll file to a sys one, but it's not relevant here.

One thing you should check is if the NTICE KDExtensions string is written properly. It should end with a semicolon for one. It should also be in small caps if you have an early version of IceExt also registered under KDExtensions (more recent versions don't use that anyway).

You probably already know about increasing KDStackSize and KDHeapSize from the defaults..

"KDExtensions"= REG_EXPAND_SZ iceprobe.sys;
"KDStackSize"=dword:00008000
"KDHeapSize"=dword:00100000


I'll post a bit of random code here which maybe will help in the proper installation, let me know if it doesn't. After using CreateService with a dependancy on NTICE, I register the KDExtension with a separate proc:

Code:

;======
.DATA
;======

; Driver related
;--------------------------------------------
ServiceName db "IceProbe",0
DriverName db "IceProbe.sys",0
DriverPath db "\\.\IceProbe",0

; the following string is defined in small caps due to conflict
; with IceExt install routine (pre v.60)
KDExtensionName db "iceprobe.sys;",0
aNTICE db "NTICE",0,0 ; double null terminated

; Register KDExtension with NTICE
;--------------------------------------------
lpSubKey_NTICE db "SYSTEM\CurrentControlSet\Services\NTICE",0
lpValueName_KDExt db "KDExtensions",0


;======
.CODE
;======

;============InstallDriver proc================================
InstallDriver PROC
LOCAL hScManager:HANDLE
LOCAL ScLockWORD
LOCAL pFilePartWORD
LOCAL FilePath[MAX_PATH]:BYTE
LOCAL RootFilePath[MAX_PATH]:BYTE
CSIDL_SYSTEM equ 25h


mov status, STATUS_UNSUCCESSFUL

;-------------------------------------------------------
; Establish a connection to the service control manager
;-------------------------------------------------------
invoke OpenSCManager, 0, 0, SC_MANAGER_ALL_ACCESS

.IF eax !=0
mov hScManager,eax

;------------------------------------------
; Install to %SYSTEMROOT%\drivers\
;------------------------------------------

; Get full path of %SYSTEMROOT% ("C:\WINNT\System32"
invoke SHGetFolderPath, NULL, CSIDL_SYSTEM, NULL, NULL, ADDR RootFilePath
; Concatenate with "\Drivers\<DriverName>"
INVOKE _lstrcat, ADDR RootFilePath, CTEXT("\Drivers\"
INVOKE _lstrcat, ADDR RootFilePath, offset DriverName
; Get full path of driver in current directory and copy to %SYSTEMROOT%\drivers\
invoke GetFullPathName, offset DriverName, MAX_PATH, ADDR FilePath, ADDR pFilePart
invoke CopyFile, ADDR FilePath, ADDR RootFilePath, FALSE

invoke SendDlgItemMessage, hDlg, IDC_LISTBOX, LB_ADDSTRING, 0,\
CTEXT("Driver copied to root directory"

;------------------------------------------

invoke LockServiceDatabase, hScManager
mov ScLock, eax

; create the service with a dependancy on NTICE
invoke CreateService, hScManager, offset ServiceName, offset ServiceName,\
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,\
ADDR RootFilePath, 0,0,offset aNTICE,0,0

.if eax != 0
invoke CloseServiceHandle, eax

INVOKE RegisterKDExtension

OutputLBText hListBox_Log, CTEXT("Registry Keys Created:"
OutputLBText hListBox_Log, CTEXT("HKLM\SYSTEM\ControlSet\CurrentControlSet\Services\%s", , offset ServiceName
OutputLBText hListBox_Log, CTEXT("HKLM\SYSTEM\ControlSet\Enum\Root\LEGACY_%s", \n, offset ServiceName

mov status, STATUS_SUCCESS

.else ; CreateService fails

invoke GetLastError
; most likely error is ERROR_SERVICE_EXISTS equ 431h
OutputLBText hListBox_Log, CTEXT("ERROR: CreateService failed with System Message:"
OutputSystemErrorMsg hListBox_Log

.endif ; CreateService endif

.ELSE ; OpenSCManager fails

invoke GetLastError
OutputLBText hListBox_Log, CTEXT("ERROR: OpenSCManager failed with System Message:"
OutputSystemErrorMsg hListBox_Log

jmp err_OpenSCManager

.ENDIF ; OpenSCManager endif

;===============================================================

invoke UnlockServiceDatabase, ScLock
invoke CloseServiceHandle, hScManager
err_OpenSCManager:

mov eax, status
ret

InstallDriver endp
;============End InstallDriver===========================



;============RegisterKDExtension proc===========================
RegisterKDExtension PROC uses ebx
LOCAL lpMemWORD
LOCAL lpMem2WORD
LOCAL phkResultWORD
LOCAL lpcbDataWORD
LOCAL lpData[64]:BYTE

; SoftIce Extensions need to be registered in the Registry under
; HKLM\SYSTEM\CurrentControlSet\Services\NTICE\KDExtensions
; Since there may already be existing KDExtensions (i.e. "iceext.sys;",
; we want to concatenate our extension to the end of the string.
;
; RegQueryValueEx:
; lpData - [out] Pointer to a buffer that receives the value's data.
; lpcbData - [in, out] Pointer to a variable that specifies the size of the buffer
; pointed to by the lpData parameter, in bytes. When the function returns,
; this variable contains the size of the data copied to lpData.
;
; We call RegQueryValueEx once with the value of lpData as NULL, the size
; of any existing string in KDExtensions will be returned in lpcbData.
; Then we can allocate a buffer of sufficient size to receive the string in lpData
; and call RegQueryValueEx again. Finally we concatenate *our* KDExtension string
; and write the entire string back to the Registry.

invoke RegOpenKeyEx, HKEY_LOCAL_MACHINE, ADDR lpSubKey_NTICE, 0,\
KEY_ALL_ACCESS, ADDR phkResult

.IF eax == ERROR_SUCCESS ; if HKLM\SYSTEM\CurrentControlSet\Services\NTICE\ exists

; query KDExtensions value with NULL lpData buffer size
invoke RegQueryValueEx, phkResult, ADDR lpValueName_KDExt,\
0, 0, NULL, ADDR lpcbData

.if lpcbData == 0 ; the value name has not been created yet

; add our KDExtensions string
invoke RegSetValueEx, phkResult, ADDR lpValueName_KDExt, 0,\
REG_EXPAND_SZ, offset KDExtensionName, sizeof KDExtensionName

.else ; the subkey is not empty

; create a buffer of the size required (size was returned in lpcbData)
invoke GetProcessHeap
invoke HeapAlloc, eax, HEAP_ZERO_MEMORY, [lpcbData]
mov lpMem, eax

; query the value again with correct buffer, string returned in lpMem
invoke RegQueryValueEx, phkResult, offset lpValueName_KDExt,\
0, 0, lpMem, ADDR lpcbData

; check if "IceProbe.sys;" already exists
invoke InString, 1, lpMem, offset KDExtensionName

.IF eax == 0 || eax == -1 ; no previous occurrence

; create a buffer to hold complete string
mov ebx, [lpcbData] ; size of any existing strings
add ebx, sizeof KDExtensionName ; size of "IceProbe.sys;"
; = size of buffer needed

invoke GetProcessHeap
invoke HeapAlloc, eax, HEAP_ZERO_MEMORY, ebx
mov lpMem2, eax

; copy string of any existing KDExtension to the buffer
INVOKE _lstrcpy, lpMem2, lpMem, [lpcbData]
; concatenate with "IceProbe.sys;"
INVOKE _lstrcat, lpMem2, offset KDExtensionName
; my _lstrcat routine returns length of complete string in eax

; write complete string back to subkey
invoke RegSetValueEx, phkResult, offset lpValueName_KDExt,\
0, REG_EXPAND_SZ, lpMem2, eax

; free the heap buffers
invoke GetProcessHeap
push eax
invoke HeapFree, eax, NULL, lpMem
pop eax
invoke HeapFree, eax, NULL, lpMem2

.ELSE ; "IceProbe.sys" already existed in KDExtension subkey

invoke GetProcessHeap
invoke HeapFree, eax, NULL, lpMem

; invoke GetLastError
; OutputLBText CTEXT("ERROR: OpenSCManager failed with System Message:"
; OutputSystemErrorMsg
; OutputLBText CTEXT("KDExtension already registered", \n

.ENDIF


.endif ; lpcbData ==

invoke RegCloseKey, phkResult
mov status, STATUS_SUCCESS ; driver successfully registered

.ENDIF

ret
RegisterKDExtension endp
;============End RegisterKDExtension===========================


Let me know if this helps any, if not we'll try again.

Cheers,
Kayaker

deroko
October 16th, 2006, 22:34
Hi Kayaker,

tnx for reply, of course, your source is useful

I have spent last 2 days checking all possible link options to make my extension to work. Was comparing my peheader with IceExts to be sure that everything is just fine. and only difference was in debug symbols, so I decided to try /debug option and suddenly it worked At first I tought that omf format didn't contain "some" needed data for link.exe to link it properly.

Anyway here is my make file if anyone also uses tasm32 to compile driver sources:

Code:

C:\tasm32\bin\tasm32 /ml /z /m9 /q %1
C:\winddk\2600\bin\x86\link .\ntice.lib c:\tasm32\lib\iNTOSKRNL.lib c:\tasm32\lib\iHAL.lib /version:5.0 /BASE:0x10000 /ALIGN:32 /IGNORE:4033 /nologo /DRIVER /ENTRY:start /out:%1.sys /subsystem:NATIVE %1.obj /SECTION:.text,RWEX /def:%1.def /DEBUG
@echo off
del %1.obj


Regards, and tnx again for help