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 jmp 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 '?' 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
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 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 : 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