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.


You are deep inside fravia's page of reverse engineering, choose your way out:

papers
Back to the HCU Papers



redhomepage redlinks redsearch_forms red+ORC redstudents' essays redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_fravia+
redIs reverse engineering legal?