SoftICE and KDExtensions

Rating: 2 votes, 2.00 average.
Well I was writing one extension for softice, and I faced one serious problem which in turn might not be that big problem if softice authors decided to write softice code properly at some points. SoftICE manual doesn't provide us with concept how to write KDExtensions, but in turn it gives us tools which we might use to convert existing windbg extensions into softice extension. One of rules is that we may not use Exception Handling in KDExtension (taken from SoftICE manual), and silently it refuses usage of many exports from ntoskrnl.exe...

KD2SYS.exe works simply by adding extra code to your dll, and changing it's entrypoint to code which looks like this:

.1000147F: B800000000                     mov         eax,0
.10001484: C20800                         retn        8
.10001487: 0010                           add         [eax],dl
.10001489: 0000                           add         [eax],al
when extension is loaded, it MUST have Debug symbols so softice will know that it should check EntryPoint for mov eax, 0/retn 8 using INT 2D (during driver loading ntoskrnl.exe will call -> DbgLoadImageSymbols which in turns will call int 2D, hooked by SoftICE which will examine entrypoint of driver and substitute mov eax, 0 with jmp __softice_code which will in turn call DllEntryPoint.

PAGE:004D7D27                 push    dword ptr [edi] ; ImageBase
PAGE:004D7D29                 call    _CacheImageSymbols@4 ; CacheImageSymbols(x)   
PAGE:004D7D2E                 test    eax, eax
PAGE:004D7D30                 jz      __no_debug_symbols
Upper code shows part of ntos which checks if Debug directory is used, and after that it will call DbgLoadImageSymbols.

If you take a look at upper Disassm code, you may see that right after retn 08 is stored : 1000h which is RVA of DllEntryPoint... You may examine a little bit hook of int2D and you will see how loading of KD takes place in SoftICE, not a nuclear physics as you may trace Int2D hook in SoftICE without a problem, as it will be running at PASSIVE_LEVEL (level at which drivers are being loaded).

Next step is to create such driver that will have similar if not the same code which will be handld by SoftICE. My walkaround was to define DriverEntry in asm code like this:

extern                  DllEntryPoint@12:dword 
public  C               DriverEntry@8

DriverEntry@8:          mov     eax, 0
                        ret     8
                        dd      0FFFFFFFFh
                        dd      offset DllEntryPoint@12
Also make sure that TARGETTYPE=MINIPORT to link directly with DriverEntry@8 as your entrypoint, as DRIVER type will link using GsDriverEntry:

INIT:00011185                 public GsDriverEntry
INIT:00011185 GsDriverEntry   proc near
INIT:00011185                 mov     edi, edi
INIT:00011187                 push    ebp
INIT:00011188                 mov     ebp, esp
INIT:0001118A                 mov     eax, __security_cookie
INIT:0001118F                 test    eax, eax
INIT:000111B8                 mov     __security_cookie_complement, eax
INIT:000111BD                 pop     ebp
INIT:000111BE                 jmp     DriverEntry
Which is not what I want...

Next step is to write convert.c/asm code which will:

1. open your file
2. locate entry point
3. calculate relative offset of DllEntryPoint
4. store it in placess of 0FFFFFFFF
5. update checksum
6. save changes

Now you may have neet extension (at least that's how I write them). Kayaker probably has better solution

Now comes funn part which I figgured after making dump of whole memory in VMWare, as minidump wasn't enough for me.

I tried to call some procedures which require dropping of IRQL like ExAllocatePool, which will eventually endup in ExAcquireQueuedSpinLock, which will drop IRQL to DISPATCH_LEVEL. I've started receiving numerous BSODs, and I tought that IRQL was an issue... and those BSODs occured only, and only when I was breaking in softice from ring3 applications, so I figured something had to be wrong, but in my wildest dreams I wouldn't suspect that solution was that stupid...

Let's have a look at code responsible for calling KDExtension in softice:

.text:A7AB9D3A si_callExtension proc near             
.text:A7AB9D3A ExtensionApi    = dword ptr  8
.text:A7AB9D3A hCurrentProcess = dword ptr  0Ch
.text:A7AB9D3A hCurrentThread  = dword ptr  10h
.text:A7AB9D3A dwCurrentPc     = dword ptr  14h
.text:A7AB9D3A dwProcessor     = dword ptr  18h
.text:A7AB9D3A args            = dword ptr  1Ch
.text:A7AB9D3A                 push    ebp
.text:A7AB9D3B                 mov     ebp, esp
.text:A7AB9D3D                 push    ds
.text:A7AB9D3E                 push    es
.text:A7AB9D3F                 push    fs
.text:A7AB9D41                 push    gs
.text:A7AB9D43                 pusha
.text:A7AB9D44                 pushf
.text:A7AB9D45                 mov     edi, kd_extension_esp_start
.text:A7AB9D4B                 mov     ecx, kd_extension_stack_size
.text:A7AB9D51                 shr     ecx, 2
.text:A7AB9D54                 xor     eax, eax
.text:A7AB9D56                 cld
.text:A7AB9D57                 rep stosd
.text:A7AB9D59                 cli
.text:A7AB9D5A                 mov     save_sice_esp, esp
.text:A7AB9D60                 mov     save_sice_ebp, ebp
.text:A7AB9D66                 mov     ErrorString_to_display, 0
.text:A7AB9D70                 mov     si_extension_aborted_pagefault, 0
.text:A7AB9D77                 mov     b_extension_executing, 1
.text:A7AB9D7E                 mov     dl, 1
.text:A7AB9D80                 call    Install_Reinsall_DivideOverflowHandler
.text:A7AB9D85                 mov     esp, kd_extension_esp
.text:A7AB9D8B                 sti
.text:A7AB9D8C                 mov     fs, word ptr kd_extension_fs 
.text:A7AB9D92                 call    sub_A7AB9C86    
.text:A7AB9D97                 push    [ebp+args]
.text:A7AB9D9A                 push    [ebp+dwProcessor]
.text:A7AB9D9D                 push    [ebp+dwCurrentPc]
.text:A7AB9DA0                 push    [ebp+hCurrentThread]
.text:A7AB9DA3                 push    [ebp+hCurrentProcess]
.text:A7AB9DA6                 call    [ebp+ExtensionApi]
.text:A7AB9DA9 loc_A7AB9DA9:                           
.text:A7AB9DA9                 cli
.text:A7AB9DAA                 mov     esp, save_sice_esp
.text:A7AB9DB0                 mov     ebp, save_sice_ebp
.text:A7AB9DB6                 mov     b_extension_executing, 0
.text:A7AB9DBD                 call    restore_SEH
.text:A7AB9DC2                 xor     dl, dl
.text:A7AB9DC4                 call    Install_Reinsall_DivideOverflowHandler
.text:A7AB9DC9                 sti
.text:A7AB9DCA                 mov     edi, kd_extension_esp_start
.text:A7AB9DD0                 mov     ecx, kd_extension_stack_size
.text:A7AB9DD6                 shr     ecx, 2
.text:A7AB9DD9                 xor     eax, eax
.text:A7AB9DDB                 cld
.text:A7AB9DDC                 repe scasd
.text:A7AB9DDE                 mov     eax, ecx
.text:A7AB9DE0                 inc     eax
.text:A7AB9DE1                 shl     eax, 2
.text:A7AB9DE4                 popf
.text:A7AB9DE5                 popa
.text:A7AB9DE6                 pop     gs
.text:A7AB9DE8                 pop     fs
.text:A7AB9DEA                 pop     es
.text:A7AB9DEB                 pop     ds
.text:A7AB9DEC                 pop     ebp
.text:A7AB9DED                 retn    18h
.text:A7AB9DED si_callExtension endp
Now comes funny part, really funny part!!!!

.text:A7AB9D8C                 mov     fs, word ptr kd_extension_fs
This is not kd_extension_fs, this is FS of interupted TASK!!!!!!!!!! So if you are debugging ring3 code, KDExtension will be called with FS = 0x3B which points to TEB instead of KPCR, what most exports from ntoskrnl.exe will expect it to be!!! Of course, this is not the problem when you interupt TASK which is running in ring0, but I want my extension to work the same way no matter if interupted task is in ring0 or ring3.

That's the reason why KeSetEvent, ExAllocatePool, KeInsertQueueDpc and many, many others will fail, as those at some point expect FS to point to KPCR instead of TEB!

My solution was to create 2 functions, and call them, one at the beginning of exported function, and one at the end:

ULONG   old_fs;
void    set_fs()
                xor     eax, eax
                mov     ax, fs
                mov     old_fs, eax
                mov     eax, 30h
                mov     fs, ax

void    restore_fs()
                mov     eax, old_fs
                mov     fs, ax
Although those seem like not safe functions, remember that softice uses NMI to suspend all other CPUs while it works, so this code is absolutely safe, as all other CPUs are stoped while SoftICE code is executing (at least it seems so), and current CPU is executing at HIGH_IRQL so no synchronization is required with global varaibla, as softice ensurs that only one thread can touch it

Does anyone remember this exception in SoftICE window when dumping memory from ring3 process using IceExt?

A page fault at CS:EIP 0008:12345678 occurred when address 12345678 was referenced SS:EBP 0010:12345678
Well here is the answer why it occurs FS is wrongly set by SoftICE

Submit "SoftICE and KDExtensions" to Digg Submit "SoftICE and KDExtensions" to Submit "SoftICE and KDExtensions" to StumbleUpon Submit "SoftICE and KDExtensions" to Google