SOFTICE INTERNALS 

                               revision 2, 02/01
                              
                                 by +Spath(spath@iname.com)



INTRODUCTION
This document contains various informations about SoftICE, NuMega's system 
debugger for Microsoft operating systems ; unless specified, these informations 
refer to Windows 95/98/ME SoftICE versions, not the NT/2K ones. My goal here 
is both to explain how this debugger works and to document some of its hidden 
features. The ideal target audience is made of advanced SoftICE users and/or 
curious system coders, thus I will assume you have a good knowledge of x86 
processors and Microsoft operating systems. Contributions and corrections 
are welcome, interesting questions will be considered (but no video/mouse 
complaint, please!), silly requests will be ignored.


I] HOW SOFTICE WORKS
I.1 DOS startup
I.2 Protected mode setup
I.3 Interrupts
I.4 Breakpoints

II] UNDOCUMENTED COMMANDS AND INTERFACES
II.1 Undocumented commands
II.2 Internal variables
II.3 Ver interface
II.4 Dot commands
II.5 INT41h interface
II.6 INT3 - FGJM interface 
II.7 INT3 - BCHK interface 


I] HOW SOFTICE WORKS
=====================

I.1 DOS startup
----------------
Winice.exe is a MZ/LE file, it therefore contains parts of code to be 
executed in real mode (DOS) and parts to be executed in protected mode (VxD). 
When executed from DOS (on command line, or from autoexec.bat), winice.exe 
performs the following steps :

1. change video mode to mode 3 (80x25, text mode, 16 colors) and display
   SoftICE intro message (SoftICE version, OS, ...)
2. test if Windows is already running in enhanced mode, and quit if so.
3. test if an XMS driver is installed, and quit if not. If installed,
   SoftICE stores the XMS driver entry point.
4. test if CPU is 386+ by trying to write higher part of eflags, quit if not.
5. parse command line ; documented switches are :

   /?                display help text about these switches
   /load filename    load symbol informations from file
   /sym number       amount of symbol table memory in decimal K
   /hst number       amount of aditional display memory in decimal K
   /tra number       amount of backtrace history memory in decimal K

   some more undocumented switches are supported :

   /x                break on SoftICE startup
   /m                monochrome video
   /nmi [on|off]     trap Non-Maskable Interrupt
   /nol [on|off]     no capslock/numlock programming
   /vdd              use virtual display device
   /kbd [on|off]     patch keyboard driver [caps/numlock]
   /pen [on|off]     enable pentium support
   /com[123]         use COM port 1-3 for serial debugging
   /exp filename     load exports from file name
   /l filename       load symbol information from file name
   /l% filename      load symbol information from file name
   /load filename    load symbol information from file name
   /load% filename   load symbol information from file name
   /loadx filename   load symbol information from file name
   /loadx% filename  load symbol information from file name
   /load32 filename  load symbol information from file name
   /load32% filename load symbol information from file name

6. hook the following vectors in the IVT :

   2Fh - Microsoft Windows 
   SoftICE handles service 1605h (Windows Init Broadcast) and returns 
   its own ES:BX values for startup infos structure pointer (chained 
   to the previous structure). Previous vector is chained.

   68h - Real Mode Debugger services     
   SoftICE handles the following services (previous vector is chained):
      43h: D386_Identify, SoftICE returns AX=0F386h.
      44h: D386_Prepare_PMode, SoftICE returns a pointer to its protected 
           mode initialisation callback function (PMINIT). This function 
           implements services PMINIT_INIT_IDT, PMINIT_INIT_PAGING, 
           PMINIT_INIT_SPARE_PTE and PMINIT_GET_SIZE_PHYS.
      5080h and 5081h: D386_Load_Segment (device driver code/data segment).
      9000h: SoftICE specific (SIWVID).

   23h - Ctrl-C handler 
   This keyboard sequence can be used to cancel SoftICE loading ; when 
   called SoftICE restores INT 2Fh and INT 68h original handlers, then 
   INT 0Bh or INT 0Ch original handler (the COM ports interrupt, 
   depending on what has been previously hooked according to the /COM 
   command line value). Then SoftICE calls XMS driver services 0Dh and 
   0Ah and exits. Previous vector is ignored.

7. open video driver (default SIWVID.386) and read parameters.
8. read and parse WINICE.DAT (max size is 16K).
9. read and parse WINICE.BRK (breakpoint history, see 18). This file
   can contain at most 32 breakpoints, each one described in plaintext
   (e.g. "BPX GetProcAddress").
10. read WINICE.VID for video configuration.
11. check for a VGA driver and get ROM Font pointer 8x14 character,
    8x16 VGA or 8x8 double dot (see the FONT internal variable).
12. get DOS version and pointer to DOS List of Lists.
13. check that it can find WIN.COM.
14. open KRNL386.EXE, USER.EXE, GDI.EXE, WIN386.EXE, DOS386.EXE.
15. allocate extended memory through XMS driver for symbols, backtrace,
    exports and history and display the corresponding informations.
16. if required, display more infos and "Press any key to continue".
17. load and execute WIN.COM

Here Windows is loading and takes control...
 
25. If BootGUI=0 in msdos.sys, SoftICE DOS part takes control back 
    when Windows is shut down. Then, it saves a new WINICE.BRK, free 
    allocated memory, restores hooked interrupts 2Fh, 68h, 0Bh/0Ch 
    and exits to DOS.


I.2 Protected mode setup
-------------------------
Unlike other debuggers, SoftICE is active before any process starts
or before any (non-system) static driver is loaded, and this unique 
feature requires a quite complex protected mode setup. Since usual
WIN.COM is executed at step 17, SoftICE can only get control back 
through hooks (INT2Fh and INT68h in the IVT) and OS callbacks (PMINIT). 
So at step 17 we are still in real mode, WINICE.EXE executes WIN.COM :

18. To get informations about the real mode situation, Windows issues 
    an Init Broadcast call ; this service (INT2Fh/1605h) is hooked by 
    SoftICE (see step 6), which returns its own Startup Infos structure, 
    where it declares itself as a DOS device driver.
19. Since SoftICE is now registered as a DOS device driver, Windows will
    allocate new selectors for it and call INT68h/5080h and 5081h for 
    symbolic debugging support. When SoftICE detects that these calls 
    are used for itself, it will use the selector values to calculate 
    the linear address of the start of its protected mode code (which
    actually is its PMINIT function).
20. Just before switching to protected mode, Windows will issue a 
    D386_Prepare_PMode call, where SoftICE returns the previously 
    calculated PMINIT address.
21. Windows switched to protected mode. During its initialisation, VMM 
    calls the PMINIT routines. Here are the ones SoftICE will react on 
    (in that order) :

    - PMINIT_INIT_IDT: here SoftICE completes its code for BPINTs and
      hooks IDT vectors 1,2,3,6,0Bh,0Ch,0Dh,0Eh,41h.

    - PMINIT_GET_SIZE_PHYS: here SoftICE ignores the service (returns 
      debugger address = debugger size = 0) to force VMM to call the 
      two following ones :

    - PMINIT_INIT_PAGING: there happens most of SoftICE initialization,
      which includes PICs setup, TSC calibration, WINICE.DAT parsing
      (options, macros), video setup, printer setup, keyboard patching, 
      symbols loading, etc. When this service is completed, SoftICE is
      active and can be used.

    - PMINIT_INIT_SPARE_PTE: SoftICE maps the complete physical memory 
      (as indicated in WINICE.DAT) into linear address space.


Since SoftICE is registered as driver, winice.exe will receive all 
VxD system messages (SoftICE only handles SYS_CRITICAL_INIT, DEVICE_INIT,
INIT_COMPLETE, SYS_CRITICAL_EXIT, W32_DEVICEIOCONTROL, SET_DEVICE_FOCUS, 
DESTROY_VM). During Windows startup (when static VxDs are loaded), 
SoftICE will receive the three following messages :

22. SYS_CRITICAL_INIT, where SoftICE 
   - calls Win386_Alive service (INT22h/AX=0) to check that WIN386 
     is loaded.
   - gets system VMM version and according to the value, enable (or not)
     some commands (DEVICE,DRIVER,FOBJ,IRP,OBJDIR).
   - gets machine infos (MSDOS version numbers, processor type, etc).
   - hooks PM  faults 06, 0Ch, 0Dh, 0Eh
           VMM faults 06, 0Dh, 0Eh
           V86 faults 06, 0Ch, 0Dh, 0Eh
   - hooks the following VMM services (replacing previous ones):
     Out_Debug_String, In_Debug_Chr, Out_Debug_Chr, Get_Profile_Hex_Int
     and if VMM version is at least 400h, Trace_Out_Service and 
     Debug_Printf_Service.
   - hooks port 84h, redirects it to a retn, and completely disable 
     Windows handling of this port.
   - hooks the following VMM services (chained to previous ones):
     Enable_Local_Trapping, Disable_Local_Trapping, Enable_Global_Trapping,
     Disable_Global_Trapping.
   - installs its Task Switch Callback function (called at each task switch).
   - hooks the following VMM services (chained to previous ones):
     _AllocateThreadDataSlot, _FreeThreadDataSlot.
   - hooks the folowing VMM services (chained to previous ones):
     _ContextDestroy, _Debug_Flags_Service.
   - completes the SIWDEBUG DDB and adds it to the device list.
   - hooks VXDLDR _PELDR_AddExportTable (chained to previous one).
   - creates 3 new descriptors in GDT, one to access SoftICE's own code,
     one for INT 41h hooking and one to access first Meg of memory.

23. DEVICE_INIT, where SoftICE 
   - calls services 0 and 1 of SIWVID.
   - sets the hot key.
   - installs its page fault handler.

24. INIT_COMPLETE, where SoftICE just clears the carry flag. 

At this point SoftICE setup is complete and Windows continues loading
freely.


I.3 Interrupts
---------------
Once SoftICE is loaded, Windows and its applications are running freely
and SoftICE only reacts on specific events. To do so, it has hooked 16 
interrupt vectors in the system IDT (including 5 IRQs, relocated by 
Windows in range 50h-5Fh):

01h : debug exception, used for BPM, BPX, BPIO, T command (and many more).
02h : NMI interrupt.
03h : breakpoint exception, used for BPX.
06h : invalid opcode exception, for logging purposes only.
09h : coprocessor segment overrun.
0Bh : segment not present.
0Ch : stack segment fault.
0Dh : general protection fault, used for BPIO.
0Eh : page fault, used for BPR.
0Fh : no operation.
41h : Microsoft Kernel Debug Interface, interface with SoftICE.
51h : IRQ1: keyboard.
53h : IRQ3: COM2.
54h : IRQ4: COM1.
57h : IRQ7: LPT1.
5Ch : IRQ12: mouse.

The 'IDT' command does not reveal this, but instead shows these interrupt 
descriptors pointing to their original locations (usually VMM). This is 
because SoftICE saved the original IDT before setting its hooks, and when 
the user types the command, it reloads IDTR to use the saved IDT, executes 
a "real" 'IDT' command, and restores IDTR to the system IDT. Thanks to 
this nice implementation, one can quite easily patch his winice.exe to 
always see the real IDT.


I.4 Breakpoints
----------------
SoftICE can handle 256 general breakpoints at a time (breakpoints using
debug registers are however still limited to 4). Each breakpoint 
definition is stored in a 183h bytes large structure (which makes a 
100k large static array). Each time a new breakpoint is set, SoftICE 
performs a test on this breakpoint table to check that memory has 
not been altered : if this is the case, a "Breakpoint Table Corrupted" 
message is displayed.

On top of this, breakpoints numbers are also stored in 'per type' arrays 
(BPX,BPM,BPR,..) which are used to speed up breakpoint recognition, 
deactivation and reactivation. For instance when a INT3 occur, SoftICE
get the breakpoints numbers from the BPX array, then for each number it 
directly fetches all the informations concerning this breakpoint in 
the global breakpoint array.

Setting a new breakpoint in SoftICE is performed in two steps. First, when 
the user types his command, a new breakpoint structure is filled in the 
breakpoint table, but the actual resources (PTE, DRx, ...) are still not 
used. Then, when the user restarts the application (with G,P,T or HERE), 
the 'per type' arrays are parsed, the resources corresponding to all 
active breakpoints are allocated, and the application is resumed.

Now there's one major drawback in SoftICE's breakpoint implementation :
SoftICE breakpoints are context sensitive in the lower 2GB, but quite
strangely context checking is performed independantly of the OS. Indeed, 
whenever a breakpoint occurs, the current context is compared to the 
context where it has been set (this original context is saved in the 
breakpoint structure), and if they match SoftICE pops up. Unfortunately, 
this weird method cause both feature limitations and processing overhead, 
which could have been avoided by using the OS' own context mechanism. 
For instance, it would have been possible to set 4 BPMs per task by 
modifying DRx values in the task's _CONTEXT structure, since the OS 
itself would have taken care of keeping the correct DRx values with the
correct threads. In the current implementation, SoftICE only supports 
4 BPMs for the complete system.


I.4.a BPX

BPX is SoftICE's basic breakpoint on execution : when the breakpoint is 
active, the byte at target address is exchanged with CCh, the special 
INT3 opcode. When the INT3 is executed, SoftICE's INT 3 handler is called, 
and the original byte is restored. When exiting SoftICE, the CCh byte is 
set back in place and the breakpoint can trigger again. It should be noted
that the single byte INT3 opcode is slightly different from the two bytes 
'INT 3' opcode, because it offers special features which guarantee more 
robust breakpoint triggering (see intel manuals). 

There's a problem with SoftICE's BPX implementation : sometimes after 
a debugging session, one will find in his executable file some CCh bytes 
where he previously had set BPXs. This apparently comes from the OS virtual 
memory system, which at some point decided to page out to disk a piece 
of memory where a BPX had been set, and therefore has written back to 
the file this CCh byte (this is an old problem, which however has never
been fixed by NuMega). 


I.4.b BPM 

BPM is used to set a breakpoint on memory access or code execution, based 
on debug registers. The breakpoint type is stored in the breakpoint 
structure, and is translated into the R/Wn fields of DR7 :
code execution  = 00
data write only = 01
data read/write = 11
data read only  = 11 (read and write sorted in the handler)

A BPM for data access can be set on byte, word or dword ; when setting 
such breakpoint, SoftICE checks for correct address alignment, according 
to the chosen size (which actually is not necessary, since the processor 
performs automatic alignment). Following debug registers' behaviour, 
the breakpoint is triggered if any of the bytes accessed is in the 
range defined for the breakpoint.

A BPM on execution can be set on any piece of code with any size (as 
long as alignment is correct). However, you should be very careful here
because of the lack of command line checking : to have a chance to be 
triggered, the breakpoint must always be set on the address of the 
first byte of the instruction, and must also have a byte size. Any 
other address/size combination, even accepted by SoftICE command line
parser, will never trigger.


I.4.c BPR

BPR is SoftICE's breakpoint on memory range, which can detect read 
and write accesses to a (hardcoded) maximum range of 400000h bytes.
This is done by marking the corresponding page (or pages, up to 1024)
as non present : when an access to a non present 4k page is made, a 
page fault occur, SoftICE's page fault handler is called and checks
if the faulting address is in the required range.

It should be noted that since the non-present bit of the PTE is used,
this can detect accesses from any privilege level, yet SoftICE will 
only trigger for accesses from ring3. This means that for some reason
SoftICE page fault handler filters accesses from ring0 (and therefore 
limits this breakpoint's capabilities).

This breakpoint requires a little implementation subtlety : since a 
page fault occur before the faulting instruction is executed, SoftICE 
must in some way deactivate the BPR, let the instruction execute, and 
reactivate the BPR (so that the breakpoint can be triggered again). 
This is done in the page fault handler by marking the page as present, 
then setting setting the single-step flag (T) of the current thread ; 
when the instruction has been executed an INT 1 is triggered, SoftICE's 
INT 1 handler clears the trap flag and mark the page not present again.

A BPR has some other hardcoded limitations, it cannot be set on memory 
pages used by :
- SoftICE (check is performed using SoftICE's specific descriptor in GDT).
- current GDT.
- saved IDT (2FFh bytes range).
- saved LDT.
- page directories (and not tables, as said in the doc). 

Note also that SoftICE's page fault handler is post-chained to original 
VMM's one, which explains the nice 'virtual breakpoint' feature (you can 
set BPRs on pages not yet present in memory).


I.4.d BPIO

This is a breakpoint on IO port access, and uses two methods :

- TSS' IO Port bitmap (on Win9x only, 386+ processors) can only 
  catch accesses from Ring3 in pmode, and all accesses from V86 mode.
  This is the default method on Win9x versions.

- Debug registers (with '-h' option, Win9x/NT, Pentium+ processors)
  can catch accesses from Ring3 and Ring0, but not in V86 mode.

In both cases, the exception (GPF for IO Bitmap, Debug exception 
for DRx) is triggered whichever condition has been defined (R, RW or W). 
Then, SoftICE disassembles the IO opcode and checks if it matches 
the breakpoint R/W condition.


I.4.e BPINT

This breakpoint allows triggering on interrupt event. Whenever a BPINT 
is asked on an interrupt vector which is not already owned by SoftICE,
the debugger patches the corresponding IDT descriptor, and makes the 
new handler address point to this code :

push <INT_number>
jmp  <BPINTHandler>

All BPINT breakpoints finally end in the same handler, where 
informations are checked and reported. For interrupts that are already
owned by SoftICE, the IDT descriptor is not modified and the BPINT
condition is checked in the current handler. In both cases, SoftICE 
will pop up in the first instruction that is not part of its own 
code (usually in VMM handler).

BPINT command parsing allows only interrupts up to 5Fh because Windows 
use only IDT interrupts up to 5Fh (upper interrupts are dispatched 
through GPF to PMode or RealMode handlers). Therefore you cannot use
BPINT to set a breakpoint on, for instance, INT68h real mode interrupt, 
you should instead use a BPX style breakpoint, e.g. "BPX *($0:(68*4))"


II] UNDOCUMENTED COMMANDS AND INTERFACES
==========================================
Please keep in mind that the following chapter describes the features 
found in SoftICE 3.23, some have been removed (and some other added) in 
later versions, and so none is guaranteed to work on your system.

II.1 Built-in commands
-----------------------
Most of SoftICE's built-in commands are described in SoftICE's users 
guide, but there are still a few more which give interesting system
variables (type '?<command>' to test them).

- UTID/UPID: these are the Win9x counterparts of NT's TID (thread ID) 
             and PID (process ID), which are very useful for setting
             conditional breakpoints.

- UTCB/UPDB: return pointers to the current Thread Control Block/
             Process DataBase.

- VMMTID: returns VMM thread ID (always 1).

- CONTEXT: returns handle (ie pointer) to the current _CONTEXT structure.

- K32XOR: the famous "Obsfucator" value, which when xored with the values
          returned from GetCurrentProcessID() and GetCurrentThreadID()
          give the addresses of the actual corresponding structures.

A special command :

- GENINT BCW: for BoundsChecker activation.


II.2 Internal Variables
------------------------
SoftICE uses several internal variables to retain settings informations. 
These variables may be changed dynamically using the "set variable parameter" 
command, current settings can be viewed by typing "set variable" with no 
parameter. These variables are : 

ALTSCR [on|off]         : moves display to a second, monochrome monitor.
CASESENSITIVE [on|off]  : make global/local symbol names case-sensitive.
CODE [on|off]           : display hexadecimal bytecodes.
EXCLUDE [on|off]        : enable/disable range address checking.
FAULTS [on|off]         : trap General Protection and Page Faults.
I1HERE [on|off]         : make any embedded INT1 invoke SoftICE.
I3HERE [on|off]         : make any embedded INT3 invoke SoftICE.
LOWERCASE [on|off]      : display code in lowercase.
MOUSE [on|off 1|2|3]    : turn mouse on/off, set movement speed
                          from 1 (slow) to 3 (fast).
PAUSE [on|off]          : pause after each screenful of info in the
                          Command Window.
REFERENCE [on|off]      : toggle memory display of referenced operands
                          (top right corner).
SYMBOLS [on|off]        : use symbol information.
TABS [on|off 1|2|3|4|5|6|7|8] : set tabstops every 1-8 characters.
THREADP [on|off]        : enable/disable thread-specific stepping.
VERBOSE [on|off]        : display diagnostic messages.


Some variables can also be set in winice.dat without using the "init" line :

NOPIC2=[on|off]     : disable/enable second PIC handling.
NOLEDS=[on|off]     : disable/enable capslock and numlock programming.
NOPAGE=[on|off]     : disable/enable mapping of non-present pages.
NOCONTEXT=[on|off]  : enable/disable context sensitivity for breakpoints.
MACROS=number       : maxmimum number of macros.
NMI=[on|off]        : trap Non-Maskable Interrupt.
VERBOSE=[on|off]    : enable/disable diagnostic messages.
KBD=[on|off]        : patch/do not patch keyboard driver.
LOWERCASE=[on|off]  : enable/disable lowercase assembly.
MOUSE=[on|off]      : enable/disable mouse support.
THREADP=[on|off]    : enable/disable thread-specific stepping.
EXCLUDE=range       : exclude physical memory range for SoftICE. 
SIWVIDRANGE=[on|off]: exclude SoftICE video physical memory range.
TRA=size            : trace buffer size.
HST=size            : command history buffer size.
LOAD=filename       : load symbols from file.
LOAD%=filename      :   "     "     "    "
LOADX=filename      :   "     "     "    "
LOADX%=filename     :   "     "     "    "
LOAD32=filename     :   "     "     "    "
LOAD32%=filename    :   "     "     "    "
PHYSMB=size         : amount of system RAM.
PHONE=number        : phone number for serial debugging.
COM1=[on|off]       : enable COM1 for serial debugging.
DINIT=string        : DIAL initialiaztion string for serial debugging.
ANSWER=string       : ANSWER initialization string for serial debugging.
EXP=filename        : load exports.
VDD=[on|off]        : use Virtual Display Device.
DDRAW=[on|off]      : enable/disable direct video monitor access.
DRAWSIZE=size       : video memory size in K.
MONITOR=value       : defines number of display driver to use.
WDMEXPORTS=[on|off] : get exports loaded by PELDR driver.
NAME=string         : your name (returned by VER command).
COMPANY=string      : your company (returned by VER command)
SERIAL=string       : your serial number (returned by VER command)
DISPLAY=[VIPE|S3|MACH32|MACH86|0|VD] : video display type.
SRC=                : no longer supported 
FAULTS=[on|off]     : trap GPFs and PFs.
SYM=size            : mem alloc for symbols.
LBRECORD=[on|off]   : enable/disable last-branch message collection.


II.3 VER interface
--------------------
According to Numega's documentations, the 'ver' command just display
SoftICE version number ; actually, it does much more : 

  :ver?
  query [address [count]]  Display PageQuery information
  mutex                    Display mutex list
  pager                    Display pager list
  exception                Display VxD exception handler list
  vmsg [msg-number]        Display VxD control messages
  vxd [address]            Display VxD location list or VxD name
  phys <address>           Display phys address for NP linear address
  addr                     Display Winice module32/context structures
  export                   Display Winice export32 structures
  sym                      Display Winice symbol table structure
  break <break-number>     Display Winice breakpoint structure
  context [context-handle] Change address context in symbol table
  debug                    Toggle Winice verbose display

In SoftICE 3.0/3.01 'ver ice' display a hello message from SoftICE team.

Actually, there are 6 more commands, not described in the ver help ;
note that each ver commands is identified with only its first 3 
letters, therefore I can only guess their full name :

  dot           : display infos about registered dot commands.
  sio           : greetings from the SoftICE team ; for some unknown reason,
                  Numega people allow users to call this only once.
  lin (offset)  : dump memory at the first address in the current data window
                  + offset (very handy to navigate through a linked list).
  asm <vxdname> : disassemble a vxd. 
  int           : enable/disable INT41h handling.
  tes           : does nothing.


II.4 Dot commands
------------------
There are 3 types of dot commands, none of them is SoftICE specific,
yet they are so useful that I decided to include them in this document. 

- VxD debug interface : when issuing a ".VxDname" command into
  SoftICE, a Debug_Query message is sent to the corresponding VxD.
  If this VxD handles this message, it can send back informations
  to the debugger, or even start an interactive session. Here are
  some sample Win95 VxD dot commands, and what they are useful for 
  (interactive commands are marked with a (I)) :

  .vmm      Virtual Machine Manager, plenty of system informations (I).
  .vdmad    Virtual DMA Device, DMA channels. 
  .vxdldr   VxD Loader, informations about loaded VxDs.
  .vtd      Virtual Timer Device, gives VTD functions addresses.
  .vpicd    Virtual PIC Device, IRQs and IRQ handlers (I)
  .dosmgr   DOS virtual machine infos (I)
  .vmpoll   Virtual Machine Polling device.
  .vtdapi   Virtual Timer Device API.

- Registered commands : a VxD can also register a dot command to
  the kernel debugger, by using Microsoft kernel debugger interface. 
  This interface is based on INT 41h, whose handler is owned by the 
  debugger : to register its dot command, a VxD simply issue an 
  INT 41h/AX=70h and gives to the debugger the addresses of the 
  command's code and helptext (see INT 41h chapter). By default, 
  only the '.m' command is available, which gives you full control 
  on memory management (try '.m?' to see the options). '.?' displays 
  a list of registered dot commands.

- WIN386 commands : these are kernel built-in commands
  ..?       Display a list of kernel built in commands 
  .ds       Display protected mode stack with labels
  .r  [#]   Display the registers of the current thread (or thread #)
  .vm [#]   Display the complete VM status of the current VM (or #)
  .vc [#]   Display the current VM's (or #) control block
  .vr [#]   Display the registers of the current VM (or #)
  .vs [#]   Display the current VM's (or #) virtual stack
  .vl       Display list of all valid VM handles
  .vh [b|w|d] [#]   Display as Byte/Word/Dword the VMM linked list, 
                    given a list handle.

Note that all these commands are available in standard Windows retail
versions, but debug versions provide more dot commands.


II.5 INT 41h Interface
------------------------
This interrupt is defined as Microsoft Kernel debugger interface. 
SoftICE supports the following services :

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=0Fh -- Find closest symbol
       find the symbol nearest to the address in CX:EBX and 
       display the result in the format symbol name <+offset>
       the offset is only included if needed, and no CR&LF 
       is displayed.

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=4Fh -- Check if a protected mode debugger is installed 
       return: AX = F386h, if true

AX=50h -- Define a segment value for the debugger's symbol handling
       entry: SI type (0: code selector, 1: data selector,
                    80h: code segment, 81h: data segment)
              BX segment #
              CX actual segment/selector
              DX data instance
              ES:(E)DI pointer to module name

AX=52h -- Free segment (16 bits)
       notify the debugger that a segment has been freed
       entry: BX segment value

AX=5Ah -- Indicate Kernel Vars 
       Used by the Windows kernel to tell the
       debugger the location of kernel variables
       used in the heap dump commands.
       entry: BX = version number of this data (03a0h)
              DX:CX points to:
                 WORD  hGlobalHeap  ****
                 WORD  pGlobalHeap  ****
                 WORD  hExeHead  ****
                 WORD  hExeSweep
                 WORD  topPDB
                 WORD  headPDB
                 WORD  topsizePDB
                 WORD  headTDB  ****
                 WORD  curTDB  ****
                 WORD  loadTDB
                 WORD  LockTDB
                 WORD  SelTableLen  ****
                 DWORD SelTableStart	****

              (starred fields are used by the
               heap dump commands). 

AX=5Dh -- Get system informations
       DS:SI = pointer to an array of offsets:
       BX = windows version		
       CX = number of words in array
            WORD1 = fDebugUser (1 = DEBUG, 0 = RETAIL)
            WORD2 = 16 bit offset to hHmenuSel
            WORD3 = 16 bit offset to hHwndSel
            WORD4 = 16 bit offset to pclsList
            WORD5 = 16 bit offset to pdceFirst
            WORD6 = 16 bit offset to hwndDesktop
       This array MUST BE COPIED it goes away when we return 
       from this service.

AX=64h -- DLL Loaded
       entry: CX:BX = DLL entry point CS:IP
              SI = module handle

AX=65h -- Module Removed
       entry: ES = module handle

AX=66h -- Print Debug message (SoftICE specific)
       entry: CL = error message to display
              Message is always : "WINICE: LogError ERR_" followed by a
              mnemonic of the faulting function.
       returns: nothing

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 printf (C like printf function >> output on debugger
          terminal ), 32 bits.
       entry: DS:ESI = address of format string
              DS:EDI = address of first parameter passed (all parameters
                       are DWORD's )
       returns: EAX = nr. of characters printed on debug terminal

AX=74h -- Debug printf (C like printf function >> output on debugger 
          terminal), 16 bits.
       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
       returns: 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=7Ah -- Directs debugger to dump current registers

AX=7Bh -- Directs debugger to perform a stack dump
       entry: BX:  -> 01h - verbose stack dump			  
                   -> 02h - 16 bit stack dump
                   -> 04h - 32 bit stack dump

(services 7Ch, 7Dh and 7Eh are not supported)

AX=7Fh -- Check the debugger wants control on the fault.
       entry: BX = fault number
              CX = fault type mask
                     DEBUG_FAULT_TYPE_V86
                     DEBUG_FAULT_TYPE_PM
                     DEBUG_FAULT_TYPE_RING0
                     DEBUG_FAULT_TYPE_FIRST
                     DEBUG_FAULT_TYPE_LAST
       returns: AX = 0, handle fault normally
                AX != 0, handled by debugger

AX=80h -- Set Break
       This service allows an error break or ctrl-c handler to be
       set.  The old value that is returned must be save and set
       back to remove the break handler.
       entry: DS:ESI = pointer to BreakStruc with the CS:EIP 
                       and SS:ESP values to be used when a error 
                       break or ctrl-c happens. The old value is 
                       copied into this buffer.
       returns: AX = 0, no error
                AX != 0, error on BreakStruc address

(services 81h and 82h are not supported)

AX=83h -- Trap Fault : allows ring 3 code to send a fault to the debugger
       entry: BX = fault number
              CX = faulting CS
              EDX = faulting EIP
              ESI = fault error code
              EDI = faulting flags
       returns: CX = replacement CS
                EDX = replacement EIP

AX=84h -- Set stack trace callback : sets the "k" command callback filter 
       used to back trace thru thunks.

       entry: EBX = linear address of call back routine, zero to uninstall
              ECX = linear address of the end of the call back routine
              EDX = EIP to use for for faults in call back routine
       returns: none

       Callback:
       entry: EAX = linear base of SS
              EBX = linear address of SS:EBP
              DS, ES = flat ds
              SS = NOT flat ds !

       returns: EAX = FALSE, no thunk
                      TRUE, is a thunk
                CX:ESI = new SS:EBP
                DX:EDI = new CS:EIP

(services 85h to 8Ah are not supported)

AX=0150h -- Define a 32-bit segment for Windows 32
         entry: SI = type (0: code selector, 1: data selector)
                DX:EBX points to a D386_Device_Params STRUC
                with all the necessaries in it

AX=0152h -- Free segment (32 bits)
       notify the debugger that a segment has been freed
       entry: BX segment number
              DX:EDI pointer to module name

AX=0F003h -- ForceGO
          enter the debugger and perform the equivalent
          of a 'G' command to force a stop at the
          specified CS:EIP
          entry: CX  = desired CS
                 EBX = desired EIP


II.6 INT3 - FGJM interface 
----------------------------
To use this interface, one should issue an INT03 with SI = 'FG', 
DI = 'JM' and AH = service number. About 30 different functions
are implemented, more details to come later.


II.7 INT03 - BCHK Interface  
-----------------------------
This interface provides 32 services, to use it issue and INT 03 with 
EBP = 'BCHK' and AL = service number (00h-1Fh). Here is a short 
overview of what these services are about, I may detail them later :

AL=00h - Get interface version 
AL=08h - Reset debug register  
AL=09h - Set debug register
AL=0Ah - Modify debug register 
AL=0Bh - Disable debug register 
AL=0Ch - Get DR6
AL=0Dh - Desactivate DRx breakpoint condition 
AL=0Eh - Execute a SoftICE command
AL=0Fh - Notify Boundschecker activation
AL=10h - Notify BoundsChecker desactivation
AL=15h - Set BPM breakpoint
AL=16h - Clear breakpoint (BC)
AL=17h - Enable/Disable breakpoint (BE/BD)
AL=1Ch - Table command


DISCLAIMER
This document is distributed in the hope that it will be useful
but without _any_ warranty. You can copy and distribute verbatim
copies of this document, but changing it is not allowed. In no
event will the author be liable for any damage resulting from
the (mis)use of these informations. 

GREETINGS
- First of all, I want to thank The Owl for having shared with 
  me his incredible knowledge of SoftICE. Both his IDB and our 
  interactive discussions have been an invaluable help for 
  me while writing this document. 

- Iceman and mammon_, for having published SoftICE informations 
  in the past, some of which I have reused in this document.

- Most of these informations were obtained by working with IDAPro, 
  the excellent disassembler from Datarescue.
  
EOF