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.