[ Team LiB ] Previous Section Next Section

12.15 Detecting SoftICE

12.15.1 Problem

SoftICE is a ring0 debugger that cannot be detected using standard debugger detection techniques.

12.15.2 Solution

Numega's SoftICE debugger is a kernel-mode debugger intended for debugging device drivers and Windows itself. It is favored by software protection crackers because of its power. Four well-known methods for detecting the presence of SoftICE exist, which are detailed in Section 12.15.3.

12.15.3 Discussion

The "Meltice" technique is one of the oldest methods for detecting SoftICE. It attempts to open virtual devices created by SoftICE; if any of these devices exist, the debugger is present.

#include <windows.h>
   
BOOL spc_softice_meltice(void) {
  HANDLE hFile;
   
  hFile = CreateFile(TEXT("\\.\\SICE"), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if (hFile =  = INVALID_HANDLE_VALUE)
    hFile = CreateFile(TEXT("\\.\\NTICE"), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if (hFile =  = INVALID_HANDLE_VALUE)
    hFile = CreateFile(TEXT("\\.\\SIWDEBUG"), GENERIC_READ, 0, 0, 
                                           OPEN_EXISTING, 0, 0);
  if (hFile =  = INVALID_HANDLE_VALUE)
    hFile = CreateFile(TEXT("\\.\\SIWVID"), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if (hFile =  = INVALID_HANDLE_VALUE) return FALSE;
  CloseHandle(hFile);
  return TRUE;
}

SoftICE provides an interface via the debug breakpoint (int3) instruction that allows a process to communicate with the debugger. By loading a magic value ("BCHK") into the ebp register and executing an int3, the Boundschecker (originally the Numega Boundschecker utility) interface can be accessed. The function to be called is loaded into the eax register; function 4 will set the al register to 0 if SoftICE is present.

#include <windows.h>
   
_ _declspec(naked) BOOL spc_softice_boundschecker(void) {
  _ _asm {
      push ebp
      mov  ebp, 0x4243484B       ; "BCHK"
      mov  eax, 4                ; function 4: boundschecker interface
      int 3
      test al, al                ; test for zero
      jnz  debugger_not_present
      mov  eax, 1                ; set the return value to 1
      pop  ebp
      ret
    debugger_not_present:
      xor  eax, eax              ; set the return value to 0
      pop  ebp
      ret
  }
}

The int3 interface can also be used to issue commands to SoftICE by setting the esi and edi registers to magic values, then invoking function 0x911:

#include <windows.h>
   
char *sice_cmd = "hboot";
   
BOOL spc_softice_command(char *cmd) {
  _ _asm {
    push esi
    mov  esi, 0x4647     ; "FG"
    push edi
    mov  edi, 0x4A4D     ; "JM"
    push edx
    mov  edx, [cmd]      ; command (string) to execute
    mov  ax, 0x0911      ; function 911: execute SOFTICE command
    int 3
    pop  edx
    pop  edi
    pop  esi
  }
}

Finally, the presence of SoftICE can be detected by invoking function 0x43 of interrupt 0x68:

#include <windows.h>
   
_ _declspec(naked) BOOL spc_softice_ispresent(void) {
  _ _asm {
    mov ah, 0x43
    int 0x68
    cmp ax, 0xF386
    jnz debugger_not_present
    mov eax, 1
    ret
  debugger_not_present:
    xor eax, eax
    ret
  }
}

SoftICE detection and counterdetection is a continuously evolving field. Different versions of SoftICE have different memory footprints and runtime behavior that can be used to detect them; however, because most software protection crackers have modified their versions of SoftICE to foil known detection methods, it is advisable not to rely entirely on SoftICE detections for protection.

12.15.4 See Also

    [ Team LiB ] Previous Section Next Section