papers
Back to the HCU Papers


             	Extending NuMega's SoftIce for Windows 95 using Protected Mode

				   Debugger services API	

			           ---------------------

							by Iceman	

As you all know very well Soft-Ice is the best debugger available on the market. A powerful tool which combined with IDA 3.7+ give you the power to reverse just about anything in this wrold.The idea of extending Soft-Ice is not new , I've seen many implementations of new commands (not so many , in fact) but no one used the mechanism of dot commands which I describe below.

This paper is accompanied by a sample skeleton VxD which implements an extension to Soft-Ice for Windows95.It provides no real functionality at this time , it is designed to illustrate how .dot commands are implememted.The source code is available , too.

You will need at least Windows95 DDK and MASM 6 to build the example VxD , Soft-Ice to see for your own eyes that it's works , IDA 3.7 comes handy to follow chapter 7.No more tools required , a basic knowledge of VxD programming still required.Chapter 6 and 7 are not directly related to dot commands , it is more an introduction to basic reverse engineering techniques for VxD's.Still, it is linked with the general subject , it show you how you can talk to SoftIce from ring3 user code.


	The document is structured as below:



	Chapter 1: VxD introduction.

	Chapter 2: What are dot command's?

	Chapter 3: INT 41h - protected mode debugger interface.

	Chapter 4: Extending Soft-Ice 

	Chapter 5: The sample: icecmd.vxd

	Chapter 6: Moving to ring 3

	Chapter 7: How Numega's Loader works?

	Chapter 8: Notes for Windows NT users.



        Appendix A: Some useful equates for VxD reverse engineering

	Appendix B: INT 22h - Win32 Protected mode interface requests API

		

		Chapter 1: VxD introduction

		---------------------------

Virtual device drivers , referred further as VxD , are basically 32 bit executables that run at the highest privilege level (on ring0).They are used to manage critical system resources.The executable type is not PE but the older type LE (linear executable).Their importance becomes higher than Microsoft added to Windows95 the ability to dynamically load VxD's.In the past , VxD where used almost only for virtualizing hardware devices or to control hardware periphereals.In this days you can see lot of code who heavily relays on VxD to improve execution speed , or to gain access to critical system resources.
Now , since the purpose of this material is not to be a VxD programming introductory material I will jump directly to Chapter2.If you want me to write a introductory material to VxD programming mail me directly and if I find the number of requests high enough i will write one.(over 30 requests will do the job)




		Chapter 2: What are dot commands?

		----------------------------------



	 The protected mode debugger interface API under Windows 95 class OS provides a very 

convenient way for 32 bit system DLL's and VxD to talk to a system debugger.In Windows95 this

interface is accessed via INT 41h.Between other things , INT 41h interface allow a VxD to 

provide debug specific routines which can be called from the  system level debugger's console.

	In theory , any system level debugger can be extended in this way.Care must be taken

because not all the functions provided by INT 41h API are necessary implemented by your debugger.

SoftIce , as well as Microsoft's Wdeb386 supports them.Issueing a dot command is as simple as

breathing.In debugger's console type:



				.Command 



where command is the command that you want to be executed.By a more technical point of view

two types of dot commands are available.Let's see them:



	A.Debug Query dot commands



	Now fire up SoftIce and type: 

		

				.vmm



	Instantly the command window shows you a menu with several debug options what are not

part of SoftIce but implemented through vmm.vxd (Virtual machine manager).What happened behind

our back?When you issue a .VxDname command a Debug_Query message will be sent to the specified

virtual device.If the ControlDispatch procedure of target VxD supports a handler for this 

message control will be passed to it.The handler procedure  for Debug_Query message must reside

in a looked code segment.If it is in pageable code-segment your system may hang if the handler

procedure is paged to disk.If the target VxD does not handle Debug_Query message nothing bad

happens , so you can experimentate this freely.



	B.Registered dot commands



	Fire up SoftIce again and type:



				.my



	(This command should be available even if you don't have the debug version of W95).You

are in reverse engineering business and still using a retail build of your OS? Belief me , you

miss a LOT of cool things.Get a debug build for at least vmm.vxd , vwin386.vxd , vxdldr.vxd ,

and ring3 kernel components.

	What you see now on your screen is the valid ranges of physical memory.At this point

is important for us to know how the debugger called this code and not how to get valid 

boundaries of physical memory.Things are a little more complicated now.Implementing a dot 

command of this type impose a certain architecture to the server VxD.Be careful.Here the dot 

command is .M and not .MY. The Y following .M is merely a parameter passed to handler for this

command.When the server VxD is initializing it's debug interface , usually but not necessary

inside the procedure that handles Device_Init message broadcasted by virtual machine manager,

it must call a function called RegisterDotCommand (INT 41h , AX=70h) to register the command in

question to the system debugger (see chapter 3 for details).Basically , registering a dot 

command  provides the system debugger with an entry point which will be called when that

command will be issued.To see which dot commands are registered in your system simply type



				.?

	This will show you all the dot commands available , in the order in which they where

registered.				 

	

 	

	Chapter 3: INT 41h - protected mode debugger interface.

	-------------------------------------------------------





	The Protected Mode Debugger Interface API is implemented via INT 41h.This interrupt

calls into the system debugger through a 32 bit interrupt gate , directing it to perform

various actions.The called function is identified by the value contained in AX register.

This interface is partial undocumented , you can find some useful things by reading the

debugsys.inc file that comes with Windows 95 DDK.In this file the API is documented at the

bare minimum , but it's better than nothing.Anyway , for those of you who don't posses the 

DDK I will list below some of the most useful functions.





	AX=00h --  Display character on debug terminal

		  entry : 

			AL = character to display

		     

	AX=01h -- Read character from debug terminal 

		  returns:  

		         AL = readed char



	AX=02h -- Displays a string on debug terminal

       		   entry:  

			 DS:ESI pointer to null terminated string to display



	AX=12h -- Displays a string on debug terminal (called by 16 bit code )

		  entry:  

		         DS:SI pointer to null terminated string to display

	

	AX=40h -- Run debugee until specified  CS:IP  is reached

		  entry : 

			  CX = desired CS

			  BX = desires IP



	AX=70h -- Register dot command (32 bit code )

		  entry:  

			  BL = dot command to register

			  ESI = linear address of the handler routine

			  EDI = linear address of the help text 	

		  returns:

			  AX == 0 if successful

		          AX != 0 if registration failed



	AX=71h -- Register dot command (called by 16 bit code )

		   entry:  

			  BL = dot command to register

			  CX:SI = linear address of the handler routine

			  DX:DI = linear address of the help text 	

		  returns:

			  AX == 0 if successful

		          AX != 0 if registration failed

	    	  		

        AX=72h -- Unregister dot command (unregister dot commands registered by both 70h & 71h)

		  entry:	

	  	          BL = dot command to de-register





	AX=73h -- Debug prinf ( C like printf function >> output on debugger terminal ) 32 bit

		  entry:

			 DS:ESI = address of format string

			 DS:EDI = address of first parameter passed ( all parameter are DWORD's )

		  returns:

			 EAX = nr. of characters printed on debug terminal



	AX=74h -- Debug printf (C like printf function >> out on debugger terminal) 16 bit

		  entry:

			DS:SI = address of format string

			ES:DI = address of the start of the word or dword arguments

		  returns:

			AX = nr of chars outputed



	AX=75h -- Get Register Set 

		   entry :

			DS:ESI = address of a SaveRegs_Struc type  structure



	AX=76h -- Set Alternate Register Set

		    entry:

			 CX = thread ID (0 for current thread)

			 DS:ESI =  address of a SaveRegs_Struc type structure



	

	AX=77h -- Get Command Line Chararacter

		    entry:

			BL = 0 -> get char ,  text pointer not incremented , leading space not ignored

			   = 1 -> get char , increment text pointer , leading blank is skipped

			   = 2 -? get char , text pointer not incremented ,leading blank is skipped

		    exit:

			AL = command line character retrieved

		        AH = 0 if EOL encountered , !0 if more characters await parsing



	AX=78h -- Evaluate Expression

		  entry:

			ds:esi expression to evaluate



		  returns:

			AX: -> 0, returns a data value

			    -> !0 returns a linear address

		 CX = TID 

		 EBX = evaluated value



	AX=79h -- Verify Memory

		  entry:

			ECX = length of memory region

			DS:ESI = starting address of memory to verify



		  returns:

			 AX: -> 0 OK

			     -> !0 memory range is invalid



	AX=7A -- Directs debugger to dump current registers



	AX=7b -- Directs debugger to perform a stack dump

		 entry:

		      BX:  -> 01h - verbose stack dump

			   -> 02h - 16 bit stack dump

			   -> 04h - 32 bit stack dump

	

			

	AX=7dh -- Execute Debugger Command

  		  entry:

			DS:ESI = pointer to the command script

			CX = 	 size in bytes of script



Some structures:



	SaveRegs_Struc	struc

	Debug_EAX		dd	?

	Debug_EBX		dd	?

	Debug_ECX		dd	?

	Debug_EDX		dd	?

	Debug_ESP		dd	?

	Debug_EBP		dd	?

	Debug_ESI		dd	?

	Debug_EDI		dd	?

	Debug_ES		dw	?

	Debug_SS		dw	?

	Debug_DS		dw	?

	Debug_FS		dw	?

	Debug_GS		dw	?

	Debug_EIP		dd	?

	Debug_CS		dw	?

				dd	?

	Debug_EFlags		dd	?

	Debug_CR0		dd	?

	Debug_GDT		dq	?

	Debug_IDT		dq	?

	Debug_LDT		dw	?

	Debug_TR		dw	?

	Debug_CR2		dd	?

	Debug_CR3		dd	?

	Debug_DR0		dd	?

	Debug_DR1		dd	?

	Debug_DR2		dd	?

	Debug_DR3		dd	?

	Debug_DR6		dd	?

	Debug_DR7		dd	?

	Debug_DR7_2		dd	?

	Debug_TR6		dd	?

	Debug_TR7		dd	?

	Debug_TrapNumber	dw	-1	

	Debug_ErrorCode		dw	0	

SaveRegs_Struc ends



	There are more functions implemented through INT 41h.There is no point in list them

here because those are advanced things and are beyond the purpose of this paper who is

wanted to be a introductory and didactic material.If someone really wants them I suggest

browsing the DDK or directly e-mail me and I will try to help.





		Chapter 4: Extending Soft-Ice 

		-----------------------------



	In this chapter you will find some guidelines for writing helper VxD's for extending

Soft-Ice, or other system debuggers (Soft-Ice is the best tool , so don't bother with 

other debuggers , does not worth the effort).You can develop those VxD's in assembly language

or in C/C++.If you choose to write them in C I recommend you buying Vtools from Vireo Software,

this is a great tool.It is not very hard to write them totally in assembly , so this is also

a good choice.

	In the first place we will need a unique device id.This is not mandatory but in 

the sample VxD who accompanies this article I've used this device ID to prevent loading the

VxD if it's already loaded (duplicate init strings in system.ini or registry).

	The  loading order is not very important as long as you not relay on third-party

VxD's to carry out part of your routines.The only VxD's  required  to be loaded before our 

device are the VMM and the Debug Device.No special precautions required since vmm.vxd and debug

device have Init_Order equal to 0. This number means that the VMM will start loading VxD's 

starting with lower values first and finishing with UNDEFINED_INIT_ORDER values last.So simply

specifying UNDEFINED_INIT_ORDER will be OK.

	The next thing very important is the Virtual Device's control procedure.You should

always put this procedure in a locked code segment.This routine is a callback who responds

at messages broadcasted by Vmm and directs our VxD to take appropriate measures (generally this

routine merely pass control to user code procedures responsible with managing those messages.

Remember that returning with Carry Flag set from your handlers means that an error occurred ,

 and a cleared Carry Flag means that everything went OK.A debug helper VxD should manage at 

least two messages.They are Device_Init and Debug_Query.If your plan to write your VxD as a

Dynamic loadable device driver the control procedure is REQUIRED to manage more two messages.

Those are Sys_Dynamic_Device_Init and Sys_Dynamic_Device_Exit.Talking from ring3 user code

to your device is also possible (see Chapter7 for details).In this case I recommend you to use

the DEVICE_IO control mechanism.For this to be possible you must also process W32_DeviceIocontrol

message and write an appropriate handler to dispatch control to your procedures.Please note

that even if your VxD does not handle any messages , a control procedure is still required.

	We already said that the Control Procedure must be in a looked code segment.Also

the procedures that handles Debug_Query events and .dot commands are required to be in looked

code segments.This is due to the way in which system debuggers pass control to those handlers.

Also when you build the VxD the debug level passed to MASM must be at least one.This is because

if you do a retail build on a VxD a lots of Debug Macros defined in Vmm.inc becomes unavailable.

Your code will compile and link without any problem , but you will notice that your Trace_Out

messages and other debug macros there stripped out.

	If your device provide a handler for Debug_Query message , issuing a type one dot

command ( a dot followed by your VxD name ) will be automatic processed.You can strip this out

if you want , processing Debug_Query is not required.

	To use type two dot commands you are required to inform the debugger about their 

existence.The best place where you can do this is inside the handler for Device_Init message.

This handler can be safely put in a discardable code segment , so after completely initializing

the device and registering dots this code will be discarded (You never have enough RAM , so

even if you think that several Kb  are not much ...).You always must try to keep pagelocked

code and data at the very minimum.

	Before you try to register a dot command you should always check if a system debugger 

is present.Issueing INT 41h without a system debugger installed cause protection faults.

If such a protection fault occurs inside a Device_Init message handler of a statically loaded

device , not only the device will not complete initialization but also Windows95 itself will

fail initialization.The simplest way to check for a debugger is to use  Test_Debug_Installed

provided by vmm (VxdCall Test_Debug_Installed).This will return with zero flag clear if

a debugger is present.If ZF if set the a system level debugger is not present.

	If the debugger is not present then you should not load your device.You simply

do this by returning with CF set from the Device_Init message , informing the virtual machine 

manager that the initialization procedure failed . The VMM will not load the device.

	Once you are sure that a debugger is present you can proceed with registering dot

commands . This is accomplished by INT 41h with Ax set to 70h.The other registers must be set

as following: 

		BL = dot command to register.Example mov bl , 'z'.Used to inform the debugger

that it should respond at a .Z command transferring control as below.

		ESI must contain the linear address of the dot command handler routine.

		EDI must contain the linear address of the help string.The help string must

		     end with a CR 

	The following special restriction apply to the handler:

	1.The handler must stay on the same stack used when called Int41h

	2.The handler must not change provided flat CS , DS , and try to access invalid 

	selectors or  memory.

	3.The handler is runned on ring0.



	After you register the dot command you will can access it from within your debugger.

	When you don't wont the command anymore you should de-register it.

If your VxD is statically loaded there is no point in de-registering dot command , since the

code will be there until Windows shuts down. But if your VxD is designed to be a dynamically 

loaded VxD care must be take.If you unload the VxD and a de-register of the dot command is

not performed , the dot command will be considerated as available by debugger , and you can

call it.The only problem is that since the VxD who provide the handler is no longer in memory,

control will be passed to garbage instructions,...I think you get my point.So is a very good 

idea to de-register dot commands in a dynamic VxD. De-registering is performed calling INT 41h

whit AX set to 72h and BL containing the command you want to de-register.Your best choice is

to this thing inside the handler for Sys_Dynamic_Device_Exit.

	Also , while processing a dot or a Debug_Query message , it is not a good idea to

try to leave Soft-Ice by pressing the assigned hot-key.This will hang your machine imediatly.

	Now several things about the dot command handler routine.When the debugger calls the

handler it will set DS and CS to a flat selector. Usually the flat CS will be 28h.It also 

load the first character of the command line in the AL register.The handler routine must

perform a far return (you must return to the debugger which resides in another code segment).

Note that this is not sensed by assembler so it's your duty to explicitly use RETF.The value

returned in AX register can be 0 , indicating that all went OK , any other value means that

an error occurred (usualy a command line error).The first thing you should do after entering

your handler is to parse the command line. This can be accomplished by using INT 41h , AX=77h

(Get command line character) . A pointer to the current character in command line is 

maintained by the debugger.The value specified in BL register choose how this pointer 

is managed.Also the flag specified in BL sets the way in which one or more blank spaces are 

treated.They can be ignored , the pointer value will be updated to the next valid character

in the command line or you can choose to parse them as well.The GetCommandLine function will

return with AH set to 0 if end of line was encountered , and a value !0 otherwise.The 

command line character is retrieved in AL.

	Not much things left to say.It's up to you in which way you will used the facilities

presented here.Your code runs on ring 0 , so you can do just about anything you imagine.The

only thing I want to say is that you should take care about what you are doing.Programming

in ring0 requires a very serious understanding of protection mechanism , page-mode memory

management , x86 internal architecture and the way in which Windows'95 virtual memory manager

manages those facilities.Remember , you have absolute power but using it in a wrong way can

cause your machine to hang , and you can cause loose of data from your hard drives.Fair warning!



  	



		Chapter 5: The sample: icecmd.vxd

		---------------------------------





	Accompanying this article there is a sample VxD who employees most of the techniques 

described in the pervious chapter.The VxD does not provide any real functionality , it is

intended only for didactic purposes.It implements a new dot command .Z and a DebugQuery

handler routine.The sample dot command .Zs will dump the stack trace on the debug console.

You can accomplish same thing issuing STACK command in Soft-Ice.It also show you very

basic command line parsing.The VxD is implemented as a static VxD.It also present a mechanism

in which a VxD can detect if it was previously loaded , preventing multiple instances.Since

the code is commented I will not insist in presenting it once again inside the article.Read

the source code ,I tryed to keep things easy to understand.

	The only thing unexplained is the code what prevents the VxD being loaded twice.

This code resides in VxD_REAL_INIT_SEG code segment.This code is auto called at the time when

the system is still in real mode and the Virtual Device Drivers are loaded.When the system

reach this entrypoint the BX register contain a value that indicates if the device is already

loaded or not.The method requires a unique device Id.To allow loading you must exit with

AX set to a certain value , as shown below.



		Abort_Device_Load -- tells to VMM that this VxD should not be loaded

		Abort_Win386_Load -- tells to VMM to end Windows loading (WIN 95 itself will 

				     fail loading if this value is passed to AX

		Device_Load_Ok    -- tells to VMM that al is OK , device can be loaded

		No_Fail_Message   -- use with Abort_Device_Load and Abort_Device_Load.Instructs

				     the VMM to not display an error message.If not specified

				     the VMM will print an error message on screen.



Also , you must return with BX , EDX , SI registers set to the folowing values:



		BX  --- must contain a pointer to a null terminated page array table

			containing the physical pages reserved for VxD own use.Valid adress

			ranges are 0 to 100h. (That's it the table must reside in very low

			memory.

			MUST be set to 0 if no pages are reserved



		SI --  must contain a pointer to a null terminated area of data items

		 	MUST be set to 0 if there are no specific instance objects.



		EDX -- contain a value who it is passed to the protected mode procedure

		       what is the handler for  Sys_Critical_Init message broadcasted 

		       by VMM.This value is re-loaded in EDX just before the protected mode

		       init procedure is called

		       Usualy set to 0 , but it will not harm anything if contain other

		       values. (This is because it's up to you if the handler for 

		       Sys_Critical_Init will use it or no)			

				

This is not my creation , ive learned this from W. Oney's code.



	Download icecmd.zip & source code



	      

               	

	

		Chapter 6: Moving to ring 3

		---------------------------





	Part of the Protected Mode Debug API is available also on ring3.In fact , some 

functions from WIN32 API like OutputDebugString relays on INT 41h to carry out the job.

This opens more interesting possibilities , since you can write ring3 programs that 

interacts with a system debugger , retrieving info from it and instructing it to carry

out various actions.

	Note that not entire API is disponible on ring3.Generally, experimenting won't

harm as long as you are sure that a system debugger is installed.





	      Chapter 7: How Numega's Loader works?

	      -------------------------------------



	Although this chapter is not directly related to extending system debuggers under

Windows95 ive decided to present it inside this document because it treats a very important

problem: Talking to VxD's from ring3.The basic problem is the question: How can I call the 

code from a VxD and how do I retrieve data from it?Well , there are more than one method

to to this, but I will present only one.This method uses the DEVICE_IO_CONTROL mechanism

to talk to a VxD.It also provides a very convenient way to manipulate complex data structures

between ring3 and ring0.The only downside is that a certain architecture is imposed to the

VxD which will be called using this method.The device driver is required to process the

W32_DeviceIoControl message and to use a table of offsets to dispatch control to desired

procedures.

	NuMega's loader uses this method to instruct SoftIce VxD to perform various actions.

The mechanism is implemented within nmtrans.dll. The executable is a simple shell for

an easy user interaction.Now let's see the API used to communicate in this way with a VxD

and then let's look how NuMega's loader use them.Three WIN32 API functions are used:

CreateFile , CloseHandle and DeviceIoControl.

	I'm sure that all of you know very well CreateFile & CloseHandle so let's see

DeviceIoControl. The DeviceIoControl function sends a control code directly to a specified 

device driver, causing the device to perform the specified operation. 



BOOL DeviceIoControl(



    	    HANDLE  hDevice,		// handle of the device

 	    DWORD  dwIoControlCode,	// control code of operation to perform

	    LPVOID  lpvInBuffer,	// address of buffer for input data

	    DWORD  cbInBuffer,		// size of input buffer

	    LPVOID  lpvOutBuffer,	// address of output buffer

	    DWORD  cbOutBuffer,		// size of output buffer

	    LPDWORD  lpcbBytesReturned,	// address of actual bytes of output

	    LPOVERLAPPED  lpoOverlapped // address of overlapped structure

   );





DIOCParams	STRUC

Internal1	DD	?

VMHandle	DD	?

Internal2	DD	?

dwIoControlCode	DD	?

lpvInBuffer	DD	?

cbInBuffer	DD	?

lpvOutBuffer	DD	?

cbOutBuffer	DD	?

lpcbBytesReturned	DD	?

lpoOverlapped	DD	?

hDevice	DD	?

tagProcess	DD	?

DIOCParams	ENDS



Hope the structure is self-explanatory , tired of typing.Anyway it is used to pass and retrieve

data to the called VxD service,as well as passing desired code of operation.

How Numega use this functions? First CreateFile is called with  LPCSTR lpszName parameter

set to " \\\\.\\SICE".If Softice VxD is loaded this call will return a handle to the virtual

device driver.After that DeviceIoControl is called with HANDLE  hDevice set to the value

returned by previously called CreateFile and DWORD  dwIoControlCode set to a integer value

which unique identifies what routine we want to be called from VxD.If the function succeeds

the return value is TRUE.

	"\\\\.\\SICE" passed to CreateFile is the name of the virtual device we want to

open.You can figure out very easy this name , IDA gives it to you for any VxD.

	When DeviceIoControl is called the system wraps to called VxD routine through a 

standard Windows95 VxD called vwin32.vxd.A DEVICE_IO_CONTROL message is broadcasted to the

target VxD.In this moment esi register points to a DIOCParams type structure.The VxD code

is responsible to annalize the requested service code ( DWORD  dwIoControlCode) and see if

it's a valid one. If it's valid , control is passed to the service routine witch must 

return with carry flag cleared  to indicate success. If the service code does not exist,

or an error occurs (insufficient nr. of parameters ...and so on ) we must return with

carry flag set to indicate an error.Finally , Close Handle is called to destroy the 

handle.

Let's annalize , for example , how the command history is saved to a file (the first listing

is from nmtrans.dll , the second from winice.exe ver 3.22).

A simple quick view in nmtrans.dll shows some interesting names.Two of them are interesting

in our problem. They are DevIO_ConnectToSoftIce and DevIO_CopyLogInfo.Very suggestive names,

isn't it?When we want to save log info control is passed to DevIO_CopyLogInfo.



...............................!!!!SEVERAL LINES STRIPED OUT!!!................................



disasembly listing of nmtrans.dll

DevIO_CopyLogInfo

-------------------



10019999		 call	 DevIO_ConnectToSoftICE        // In fact this calls CreateFile

							       // to open a handle to VxD	

								

1001999E		 mov	 [ebp+var_1C], eax

100199A1		 cmp	 eax, 0FFFFFFFFh	       // If we don't have a valid handle	

100199A4		 jz	 short loc_100199EA	       // jump out of here	

100199A6		 push	 0

100199A8		 lea	 eax, [ebp+var_24]

100199AB		 push	 eax

100199AC		 push	 1Ch

100199AE		 lea	 eax, [ebp+var_40]

100199B1		 push	 eax

100199B2		 push	 0

100199B4		 push	 0

100199B6		 push	 9C406018h		      //Push control code of operation

							      //Remember this , it is a critical

							      //value.It decides what service 

							      // will eventualy execute winice.

							      // See below!		 			

100199BB		 mov	 eax, [ebp+var_1C]

100199BE		 push	 eax			      //Push the handle to winice VxD on stack	

100199BF		 call	 ds:DeviceIoControl	      //Call DeviceIoControl

							      //At this point control will be passed to

							      //Soft-Ice VxD.

							      //Remember that the OS have to execute

							      // several auxiliary operations , you

							      // will not land directly in Winice!					

100199C5		 mov	 [ebp+var_20], eax

100199C8		 lea	 esi, [ebp+var_40]

100199CB		 mov	 edi, ebx

100199CD		 mov	 ecx, 7

100199D2		 repe movsd

100199D4		 jmp	 short loc_100199EA





	So we learned that the dll's call into SoftIce VxD through DeviceIoControl Win32 API

function.To figure out exactly what service will be called from target VxD we have to remember

several things about VxD architecture.First of all we must know that every VxD have a 

ControlDispatch routine that receives mesages broadcasted by system.This control block identifies

the messages and call coresponding routines.When this proc. is entered eax contain message code.

Ida Pro 3.7 identifies very well this procedure,giving us a very good starting point.





00000096 Control_0	 proc near		 ; DATA	XREF: LCOD:0000055Ao

00000096					 ; LCOD:0000407Co

00000096		 call	 sub_7C8BB

0000009B		 cmp	 eax, 0			 // case SYS_CRITICAL_INIT	

0000009E		 jz	 loc_93E

000000A4		 cmp	 eax, 1			 // case DEVICE_INIT	

000000A7		 jz	 short loc_D8

000000A9		 cmp	 eax, 2			 // case INIT_COMPLETE		

000000AC		 jz	 loc_BDC

000000B2		 cmp	 eax, 6			 // case SYS_CRITICAL_EXIT

000000B5 

000000B5 loc_B5:				 ; DATA	XREF: LCOD:000B4640o

000000B5		 jz	 loc_BDE

000000BB		 cmp	 eax, 23h ; '#'		 // case W32_DEVICEIOCONTROL

							 // 

							 // Using DeviceIoControl for talking

							 // to VxD requires that this message

							 // is processed!

000000BE		 jz	 loc_5FD		 // than jump to this location



000000C4		 cmp	 eax, 0Fh		 //  case SET_DEVICE_FOCUS

000000C7		 jz	 loc_15F

000000CD		 cmp	 eax, 0Ch		 // case DESTROY_VM

000000D0		 jz	 loc_19B

000000D6		 clc	 

000000D7		 retn	 



The control is passed to the handler for  W32_DeviceIocontrol message (loc_5FD in this case).



000005FD loc_5FD:				 ; CODE	XREF: Control_0+28

000005FD		 push	 ebx			//

000005FE		 push	 esi		 	//Save those registers	

000005FF		 push	 edi			//



00000600		 push	 10h			// EnterMutex param2 

00000602		 push	 ds:dword_40BF		// EnterMutex param1

00000608		 VMMcall _EnterMutex		// Create a mutex to ensure that only

							// one thread will acces this code at 

							// a given time



0000060E		 add	 esp, 8			// restore stack as at 5ff

00000611		 push	 esi			// save esi

00000612		 mov	 esi, offset dword_40C3	    // pointer to an Exception_Handler_Struc

							    // in this struct are defined the

							    // type of handler and memory range

							    // what is guarded

		

00000617		 VMMcall Install_Exception_Handler  // install a ring0 exception handler

							    // if something goes wrong we can

							    // trap the problem	

0000061D		 pop	 esi			// restore the esi 

							// esi contain a pointer to a DIOCparams 

							// struct (All info you pass to ring3 

							// DeviceIOControl + some not very 

							// important things for us	

0000061E		 call	 sub_4B4F5		// ??? unexplored by me

00000623		 mov	 ds:dword_40B7,	1

0000062D		 push	 1

0000062F		 call	 sub_8595C		// ??? unexplored by me

00000634		 push	 eax

00000635		 mov	 ds:dword_40BB,	esp

0000063B		 mov	 dword ptr [esi+20h], 0  // set number of bytes returned

							 // may be modified by the requested

							 // service

00000642		 mov	 ecx, [esi+0Ch]		 //load in eax dwIoControlCode 

							 //from DIOCParams.dwIoControlCode

00000645		 mov	 edx, ecx

00000647		 shr	 edx, 10h

0000064A		 cmp	 edx, 9C40h		 // check for validity and reduction	

00000650		 jnz	 short loc_672

00000652		 mov	 edx, ecx

00000654		 shr	 edx, 2

00000657		 and	 edx, 0FFFh

0000065D		 cmp	 edx, 800h

00000663		 jl	 loc_8BD

00000669		 sub	 edx, 800h

0000066F		 inc	 edx

00000670		 mov	 ecx, edx		// end check for validity and reduction

							// load basic service code in ecx

						        // maximum value for a valid request

							// will be 0xB	

00000672 

00000672 loc_672:				 ; CODE	XREF: LCOD:00000650



00000672		 inc	 ecx		 // services are 0 based so increment

00000673		 cmp	 ecx, 0Ch	 // is service available?

00000679		 jnb	 loc_8BD	 // if no , Get Out of here!	

0000067F		 jmp	 ds:off_7584[ecx*4]  //else jump to the requested routine

						     // it's address is stored in a DWORD

						     // array begining in this case at

						     // off_7584  			

						     



// The mutex object is removed elsewhere in the code as well as the exception handler

// Not listed here because it's unimportant for our problem

						     



Remember the control code of operation passed to DeviceIoControl?.This is the moment 

then it becomes crucial.As already told , at this moment the ESI register points to

a DIOCParams type structure.This structure contains all info passed to DeviceIoControl.

Now if you watch the code above you will notice that from 0x635 to 0x672 a reduction

of this value to a much smaller one  is performed.In fact , the maximum value contained

in ecx at this step is OxB , which BTW is the number of services that SoftIce expose

through this interface. ( in fact , the maximum number of services is 0xC ,but this

contains the default DIOC_GETVERSION service).

At location 673 the code looks if this service is in range (in SICE are implemented 0x0c 

services what can be called using this method).If it determines that it's a valid service , 

the code jumps to the start adress of requested service. This adress is stored in a table , 

in our case at ds:off_7584.



00007584 off_7584	 dd offset off_686	 // if ecx = 0

00007588		 dd offset off_68B	 // if ecx = 1

0000758C		 dd offset loc_690	 // if ecx = 2

00007590		 dd offset loc_6E8	 // if ecx = 3

00007594		 dd offset loc_71C	 // if ecx = 4

00007598		 dd offset loc_7F6	 // if ecx = 5

0000759C		 dd offset loc_825	 // if ecx = 6	

000075A0		 dd offset loc_75C       // if ecx = 7

000075A4		 dd offset loc_78B       // if ecx = 8  

000075A8		 dd offset loc_7BA       // if ecx = 9

000075AC		 dd offset loc_8B6       // if ecx = A

000075B0		 dd offset loc_85D       // if ecx = B



ECX == 0 coresponds to a default DIOC_GETVERSION service .All others values between 0x1 and 0xb

at VxD services called by one  of the folowing functions:



			   DevIO_ConnectToSoftICE  (00019490)

            		   DevIO_CopyLogInfo  (00019940)

	                   DevIO_LoadExports  (00019760)

                           DevIO_LoadNm32SymbolTable  (00019630)

                           DevIO_Nm32QueryTableInfoFirst  (00019C50)

                           DevIO_Nm32QueryTableInfoNext  (00019D60)

                           DevIO_Nm32QueryTables  (00019B40)

                           DevIO_Nm32RemoveSymbolTable  (00019E40)

                           DevIO_Nm32TranslateError  (000194F0)

                           DevIO_Nm32VersionInfo  (00019840)

                           DevIO_SetWLDRBreak  (00019A30)



	All this functions exported by nmtrans.dll use the same mechanism.Of course , operation

codes ar different , as well as the data passed and retrieved.

	Looking at this table we have a clear picture of the starting adress of the VxD routines 

what are called from ring3.Pretty interesting , isn't it? And we can call them any time

now , once we figured what code of operation coresponds to it.

Note : al C++ style comments in the listing there are NOT auto generated by IDA.







	Chapter 8: Notes for Windows NT users

	-------------------------------------





Windows NT does not support the dot commands interface.Anyway , there is a potential method to extend kernel mode debuggers under NT through so-called bang commands.At least Microsoft's Windeb can be extended in this way , I know nothing about NTICE at this time.
The method involves building some strange dynamic link libraries and them register them from debugger's console.If NTICE supports this interface than we have a gold mine because programing this kind of dll's is not so restrictive as programming VxD's and we have much more "high level" exposed by Windows NT native API and ntoskernel.exe
Worth a investigation !

Appendix A: Some useful equates for VxD reverse engineering ----------------------------------------------------------- SYS_CRITICAL_INIT 0000H DEVICE_INIT 0001H INIT_COMPLETE 0002H SYS_VM_INIT 0003H SYS_VM_TERMINATE 0004H SYSTEM_EXIT 0005H SYS_CRITICAL_EXIT 0006H CREATE_VM 0007H VM_CRITICAL_INIT 0008H VM_INIT EQU 0009H VM_TERMINATE 000AH VM_NOT_EXECUTEABLE 000BH DESTROY_VM 000CH VM_SUSPEND 000DH VM_RESUME 000EH SET_DEVICE_FOCUS 000FH BEGIN_MESSAGE_MODE 0010H END_MESSAGE_MODE 0011H REBOOT_PROCESSOR 0012H QUERY_DESTROY 0013H DEBUG_QUERY 0014H BEGIN_PM_APP 0015H END_PM_APP 0016H DEVICE_REBOOT_NOTIFY 0017H CRIT_REBOOT_NOTIFY 0018H CLOSE_VM_NOTIFY 0019H POWER_EVENT 001AH SYS_DYNAMIC_DEVICE_INIT 001BH SYS_DYNAMIC_DEVICE_EXIT 001CH CREATE_THREAD 001DH THREAD_INIT 001EH TERMINATE_THREAD 001FH THREAD_Not_Executeable 0020H DESTROY_THREAD 0021H PNP_NEW_DEVNODE 0022H W32_DEVICEIOCONTROL 0023H Appendix B: INT 22h - Win32 Protected mode interface requests API -----------------------------------------------------------------

Int this appendix you will find some useful functions that Windows expose through INT 22h.You can use this to gather data about diferent kernel objects, or to perform Win32 specific debug operations.

AX=02h -- Converts a physical address to a linear address in curent context entry: ECX=phisycal address returns: ESI=linear address AX= 1 if success , otherwise 0 AX=07h -- Check to see if an address is within a VxD object entry: DS:ESI = buffer to receive object name BX = thread number EDX = linear address to query returns: If EAX == 0, EDX = base address of object If EAX != 0, error AX=08h -- Get PDE for a specific context entry: BX = thread number EDX = linear address returns: if EAX == 0, ECX = PDE if EAX != 0, error AX=0Ah -- Get LDT base entry: BX = thread number returns: if EAX == 0 EDI = pointer to LDT ECX = LDT limit if EAX != 0, error Credits (in alphabetic order) ----------------------------- This time credits go to: fravia+, (aot) for hosting my documents :-) +Mammon, for being a main +HCU backbone Stone, United Cracking Force 98, (aot) for knowledge +Undertaker, for AfterDeath To all others of you who asked my challenging questions , or gave useful idees, directly or in wonderful electronic disscussions. Final notes -----------

This document is providing " as is " whithout any warranties.It expose some potentialy dangerous techniques.Incorect use of the interfaces presented may harm the OS integrity , causing loose of data.Nor I , or fravia+, or any other Web_masters who may host this document and attached source code can be held responsable for anything this info or code do to you or your's machine.
The present document may not be modified whithout my express permission.Slightly editing for correcting typos may be done in place , whithout permission from me.
Note that:
Masm 6 and Windows 95 DDK are trademarks of Microsoft Corporation
IDA is a trademark of Data Rescue company
SoftIce is a trademark of NuMega Technologies

You can contact me at ice_man81@hotmail.com".Feedback is always apreciated.