.386
.MODEL FLAT,STDCALL
locals
jumps
UNICODE=0
include w32.inc
include pe.inc
Extrn SetUnhandledExceptionFilter : PROC .DATA
message1 db "Detection by means of the VxDCALL called through Kernel32!ORD_0001",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of the
;previous SEH service here
dd 0 ;the address of the beginning (base) of the
;KERNEL32 in memory
KernelAddress
a_VxDCall dd 0 ;the address for calling VxDCall
;(Kernel32!ORD_0001)
.CODE Start:
mov ecx,[esp] ;puts the address from which it was called into
;the ecx. (This is always somewhere in
;KERNEL32.DLL.)
push ecx ;saves the address
;Sets SEH if there is an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
Подпись: ecxpop
edx,edx ecx
dx,[ecx+03ch]
GetKrnlBaseLoop:
xor dec mov
test dx,0f800h
jnz GetKrnlBaseLoop
cmp ecx,[ecx+edx+34h]
;restores the address
;starts searching for the beginning address ;(base) of the KERNEL32.DLL in memory ;nulls the edx register ;searches backward
;tries to read parts of the file header from the ;MZ start of the PE header
;tests to see if the correct value was found ;if not, the program jumps to search farther ;compares the actual address with the address ;where this PE file (KERNEL32.DLL) was supposed ;to be loaded (its image base)
;Basically, this tests to see if it's found the beginning of KERNEL32.DLL
jnz mov
GetKrnlBaseLoop [KernelAdress],ecx
or
jz
mov ebx,ecx
ebx,ebx continue
mov eax,dword ptr [ebx+3ch]
add eax,ebx
;if divided, the program jumps to search farther ;it found the beginning address (base) of the ;KERNEL32.DLL and saves it
;puts the beginning (base) address of the
;KERNEL32.DLL into EBX
;if the beginning of the KERNEL32.DLL wasn't ;found, there has been an error and it will jump
;(if EBX=0)
;reads the file header from the MZ part of the ;PE header
;sets the EAX register at the beginning of the ;PE header
mov edi,dword ptr [eax+NT_OptionalHeader. \ OH_DirectoryEntries. \ DE_Export. \ DD_VirtualAddress]
;This is actually mov edi, dword ptr [eax+78h]
;reads the relative virtual address (RVA) from ;the Export table
add edi,ebx ;sets on Export table in the KERNEL32.DLL
;The program searches for the Kernel32!ORD_0001 call address entrance values: ebx = base address of ;the KERNEL32.DLL, and edi = address of the export table in the KERNEL32.DLL file
mov esi,dword ptr [edi+ED_AddressOfFunctions]
;This is actually mov esi, dword ptr [edi+1Ch]
;reads the RVA address for the list of exported ;functions
add esi,ebx ;sets on the list of the exported functions
xor edx,edx ;nulls the edx register (there will be a
;counter here).
address_loop:
cmp edx,dword ptr [edi+ED_NumberOfFunctions]
;This is actually mov esi, dword ptr [edi+14h]
;tests edx (counter) against the number of ;exported functions to determine if all numbers ;have been tested
jae continue ;if yes the program jumps because VxDCall
;(Kernel32!ORD_0001) wasn't found; this is ;an error
mov ecx,00000008h-01h ;puts 7 into the cx register and, as a result,
;repeats 8 times
function_loop:
inc edx
lodsd
cmp eax,dword ptr[esi]
jne address_loop
loop function_loop
add eax,ebx
;increases the edx register (counter) by 1 ;reads the address of the exported function ;tests to see if this is the beginning of the
;PE header of the KERNEL32.DLL
;if not, the program jumps to search farther ;if yes, it tests 7 more times
;the RVA address for the VxDCall ;(Kernel32!ORD_0001) is in the eax register. By ;adding the value from ebx, the beginning (base) ;address of the KERNEL32.DLL in memory, it sets ;on the real VxDCall address
mov dword ptr [a_VxDCall], eax ;saves the address for the VxDCall
;(Kernel32!ORD_0001)
; SoftICE detection starts here
push 0000004fh
push 002a002ah
call [a_VxDCall]
push eax
;4fh function, the same as in the "Detecting ;SoftICE by Detecting a Change in the INT 41h ;Service" example.
;the upper word determines which type of VxD ;call is called (VWIN32); the lower word ;determines which function is called
;(VWIN32_Int41Dispatch)
;calls VxDCall (KERNEL32!ORD_0001)
;saves the return value
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the return value
cmp ax, 0f386h ;if SoftICE is active in memory, the return
;value will be 0f386h
jz jump ;and the program ends
continue:
call MessageBoxA,0, offset message2,0\ offset message1,0 call ExitProcess, -1
jump:
error:
call MessageBoxA,0, offset message3,0\ offset message1,0 call ExitProcess, -1
mov esp, [delayESP] push offset continue ret
;sets a new SEH service in case of an error
ends
end Start
Using the Windows Registry to Find the Directory Where SoftICE Is Installed
In the section entitled "Detecting SoftICE by Calling the NmSymIsSoftICE- Loaded DLL Function from the nmtrans.dll Library," on page 109 we had to specify the SoftICE installation directory, and here's how to find it. Like most Windows programs, SoftICE saves various bits of information into the registers, such as the version number, registration number, user name, and installation directory. This information can be found in two locations:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\SoftICE
and
HKEY_LOCAL_MACHINE\Software\NuMega\SoftICE
We'll use the second location in the following program. As you'll see, I have used two functions from the ADVAPI32.DLL library to work more easily with the Windows registers. This trick works in all Windows versions.
.386
.MODEL FLAT,STDCALL
locals
jumps
UNICODE=0
includelib advapi32.lib include w32.inc
Extrn SetUnhandledExceptionFilter Extrn RegOpenKeyExA Extrn RegQueryValueExA
: PROC : PROC : PROC
;a function exported from the ADVAPI32.DLL ;a function exported from the ADVAPI32.DLL
.DATA
message1 message3 message2 message4 message5 delayESP previous
result size
size2
subkey db "Software\NuMega\SoftICE\",0
current_ver db "Current Version",0 install_dir db "InstallDir",0 data_buffer db 200 dup (0) value_buffer db 20 dup (0) data_buffer2 db 200 dup (0)
directory by means of registers",0
directory: ",0
;the ESP register saves here
;the ESP register will save the address of the ;previous SEH service here
;the size of the data buffer for the SoftICE ;version
;the size of the data buffer for the SoftICE ;installation directory
;the SoftICE version number will be saved here
;the location where SoftICE is installed will be ;saved here
.CODE Start:
;Sets SEH if there is an error
mov [delayESP], esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
push offset result push 20016h push 0
push offset subkey push 80000002h
call RegOpenKeyExA
test eax,eax
jnz not found
push offset size
push offset data_buffer
push offset value_buffer
push 0
push offset current_ver
;the result will be saved here ;access type
;non-essential (must be 0) ;string with the subkey name
;HKEY_LOCAL_MACHINE = 80000002 where a subkey
;will be opened
;opens the required access in registers and ;saves the result for further handling
;jump if error ;size of the data buffer ;address of the data buffer ;address of the value buffer ;non-essential (must be 0)
;name of the value for comparison (Current ;Version)
;where the result was saved
;reads the installed version of SoftICE into
test eax,eax
jnz not found
push offset size2
push offset data_buffer2
push offset value_buffer
push 0
push offset install_dir
push result
eax,eax not found
al
call RegQueryValueExA
test
jnz inc
;the data_buffer
;if an error occurred, the program jumps
;size of the data buffer
;address of the data buffer
;address of the value buffer
;non-essential (must be 0)
;name of the value for comparison
;where the result was saved
;reads the SoftICE installation directory into ;the data_buffer
;if an error occurred, the program jumps ;increases the al register by 1 (if there was ;no error eax=0) to show that everything is OK
ok:
xor eax,eax
push eax
;nulls the eax register to show that there was ;an error
;saves the result
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax
;restores the result
test eax,eax jnz jump
;jumps if everything was OK
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
;prints out an error message
jump:
error:
call MessageBoxA,0, offset message3,\
offset message1,0
call MessageBoxA,0, offset data_buffer,\
offset message5,0
call MessageBoxA,0, offset data_buffer2,\
offset message4,0
call ExitProcess, -1
mov esp, [delayESP] push offset continue ret
;prints out a message that SoftICE was found
;prints out the SoftICE version number
;prints out location where SoftICE is installed
;sets a new SEH service in case of an error
ends
end Start
TRW Detection Using the Distance Between the Int 1h and the Int 3h
Services
This detection is based on the same principle as the method described in the "Detecting SoftICE by Measuring the Distance Between INT 1h and INT 3h Services" section, except that it will try to detect TRW instead of SoftICE. The distance between the Int 1h and Int 3h services is 2FCh. Because this is the same trick used to detect SoftICE, we can test for the presence of both debuggers in memory. It detects only older types of TRW, not the new TRW2000.
This wonderful detection method doesn't require you to call any interrupts, API functions, or VxD calls. It works only in Windows 9x, but the same is true for TRW.
.386p
.MODEL FLAT,STDCALL locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC
.DATA
message1 db "TRW detection using the distance between Int 1h and Int 3h",0 message3 db "TRW found",0 message2 db "TRW not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of the
;previous SEH service here
pIDT db 6 dup (0) ;IDT will be saved here
.CODE
Start:
;Sets SEH if there is an error
mov [delayESP], esp
push offset error
call SetUnhandledExceptionFilter
mov [previous], eax
sidt
fword ptr pIDT
;saves the IDT
mov
eax,dword ptr [pIDT+2]
;puts pointer to the interrupt table into eax
add
eax, 8
;puts int 1h vector address into eax
mov
ebx, [eax]
;puts int 1h service address into ebx
add
eax, 16
;puts int 3h vector address into eax
mov
eax, [eax]
;puts Int 3h service address into eax
and
eax, 0ffffh
;selector will not be used
and
ebx 0ffffh
;selector will not be used even with int 1h
sub
eax,ebx
;calculates the distance between the interrupt
;services
push
eax
;saves the result
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the result
cmp eax, 2fch ;if eax is 2FCh, TRW is active in memory
jz jump ;and the program jumps
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
Detecting TRW by Opening Its Driver Through Calling the API of the CreateFileA (TRW)
This detection program is based on the same principle as the SoftICE detection. It too tries to open the driver file and, if it succeeds, the driver is active in memory.
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC .data
message1 db "TRW detection by means of CreateFileA",0
message3 db "TRW found",0
message2 db "TRW not found",0
delayESP dd 0
SOFT9x
previous dd 0
db "\\.\TRW,"0
;the ESP register saves here
;the ESP register will save the address of the ;previous SEH service here ;name of the TRW driver
.code
Start:
;Sets SEH if there is an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
call CreateFileA, OFFSET SOFT9x,\ FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\
NULL ;tries to open \\.\TRW file
push eax ;saves the return value
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the return value
cmp eax, -1 ;tests to see if it succeeded
jnz jump ;if yes, it jumps since TRW is active in memory
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
This trick works only for older versions of TRW and is ineffective against TRW2000.
Launching the BCHK Command of the SoftICE Interface
In the "Detecting SoftICE by Calling INT 3h" section, I showed you how to determine whether SoftICE is active in memory using the BCHK interface. This interface also lets you launch various commands for SoftICE that can be used, for example, for setting, switching off, switching on, and deleting breakpoints.
The BCHK interface contains 32 functions for SoftICE in Windows 9x, and 36 functions in Windows NT and 2000. Because there is no documentation, it isn't easy to determine the command usage, so I'll show a few tricks here that my friend EliCZ discovered and demonstrate their practical use with an example. (My thanks to Eli.)
In the following example, I'll show how to work with SoftICE breakpoints; a good trick that complicates program tracing. This example uses a different method to set the SEH service than I have been using so far, and one that is simpler, easier to understand, and doesn't need any API calls. (You'll find comments throughout the example because it is hard to understand at first glance.)
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
;There is no need for any API calls
.data
message1
message3
message2
mark
db "Running commands of the BCHK interface of the SoftICE",0 db "SoftICE commands functional",0
db "SoftICE isn't active, an error occurred, the program was traced or breakpoints have been set",0
db 0 ;a sign that everything went OK
.code
Start:
xor eax,eax ;nulls the eax register
call real_start ;jumps to the real start of the program
;Sets a new SEH service if there is an error ;The program will get here under two circumstances:
;1. If SoftICE isn't active in memory, an error will occur while calling the Int 3h, and the SEH ;service will be called (it will be called more times, always at the Int 3h).
;2. If SoftICE is active, an error will occur after a breakpoint was set on the NOP instruction, ;after which the SEH service will be called, and it will have you jump over the instruction. If ;the program has been traced, SoftICE will stop at the NOP instruction, and will refuse to ;continue tracing, and will not display any messages.
mov ecx, [ecx+0B8h] ;reads the context address
sub eax,eax ;nulls the eax register
inc dword ptr [ecx+0B8h] ;increases the EIP register by 1 and moves 1
;byte farther in the program
;In short, this means that it moves by one instruction, which is only one byte long. The Int 3h ;instruction is one byte long and, therefore, if SoftICE isn't active in memory, it will be jumped ;over. The NOP instruction is also one byte long, and once a breakpoint has been set on it, an ;error will occur, the SEH service will be called, and the program will jump over it.
;sets the mark on value 1 to show that everything went OK. If this is any value other than 1, your ;SEH service has been called repeatedly, which is bad since SoftICE isn't active in memory. If the ;mark is 0, your SEH service wasn't called at all, which means that the program has been traced.
ret ;returns back to the program from the SEH
;service
real_start:
push dword ptr fs:[eax] ;saves the original SEH service
mov dword ptr fs:[eax], esp
;Sets your new SEH service (which you have ended by means of CALL) to be able to save the following ;return address into the stack. This is your new SEH service.
mov
int
mov eax,4
ebp,"BCHK"
cmp
jz
mov mov
int
cmp jnz sub
3h
eax,4 continue
eax,0
ebp,"BCHK" 3h
eax, 0ff01h
continue
edi,edi
int
inc
jz
mov eax,8
3h
eax
not needed
dec edi
dec eax
xchg eax,ebx
mov ecx, cs
lea edx, breakpoint
mov bh, 0+0
mov eax, 0ah
int 3h
;"magic" values for finding out if SoftICE is ;active
;if SoftICE is active,
;calls interrupt 3. (If SoftICE isn't active, an ;error will occur and our new SEH service will ;be called.)
;tests to see if the eax has been changed
;if not, SoftICE isn't active in memory and the
;program jumps
;Get ID function (returns an ID for SoftICE) ;calls the BCHK interface
;calls SoftICE
;tests to see if the return value is 0FF01h ;if not, SoftICE isn't active in memory ;use the edi register as a mark if it is ;necessary to deactivate breakpoints. You will ;set this to 0 to say that it isn't necessary. ;Deactivates the lowest breakpoint function ;deactivates the nearest breakpoint
;calls SoftICE
;returns -1 (0FFFFFFFFh) if an error occurs ;jumps if there was an error; it is not ;necessary to deactivate breakpoints ;says that it is necessary to deactivate ;breakpoints
;returns the error value (-1) into the eax ;exchanges values in the ;unneeded registers ;selector (segment) of the breakpoint ;address where you will set the breakpoint ;0 represents the breakpoint type (BT_EXECUTE); ;+ 0 represents the length of the breakpoint
;(BL_BYTE)
;sets the breakpoint function (which sets a ;breakpoint)
;calls SoftICE
;You set a breakpoint to the label "breakpoint" where the NOP instruction is located. If the ;program encounters the breakpoint, an error will occur, and your SEH service will be called.
mov eax,9
int 3h
nop
;activate the breakpoint function to explicitly ;activate breakpoints.
;calls SoftICE
breakpoint:
nop
mov eax, 0ch
int 3h
push eax
mov eax, 0bh
int 3h
inc edi
je not needed2
mov eax,8
int 3h
;SoftICE will stick here during tracing, or an ;error will occur here and the program will call ;your new SEH service
;get breakpoint status function (saves the DR6 ;register into the eax register)
;calls SoftICE
;saves the status (DR6)
;clears the breakpoint function (removes a ;breakpoint)
;calls SoftICE
;tests to see if it is necessary to deactivate ;the breakpoint
;deactivate the lowest breakpoint function, ;which deactivates the lowest breakpoint
;calls SoftICE
not needed2:
pop ecx
xor eax,eax
pop dword ptr fs:[eax]
pop eax
xchg eax,ecx
;bsf eax,eax
db 0fh, 0bch, 0c0h
cmp byte ptr mark, 1
jnz continue
cmp al,bl
jz jump
;restores the status (DR6)
;sets the original SEH service ;clears the stack ;puts the status into the eax
;because TASM has a problem translating this ;function, you will enter its byte form ;tests if the program was traced. If so, there ;will be a value other than 0 in the eax
;tests for an error
;tests to see if everything was OK ;if so, the program jumps
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
ends
end Start
This is a good method of detecting SoftICE, and it can seriously complicate debugging. It works in all Windows versions.
Detecting TRW by Calling Int 3h
This trick uses the fact that when calling Int 3h while tracing a program, TRW handles the error and continues without calling the SEH service, which lets you discover it.
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC .data
message1 db "Detecting TRW by calling Int 3h",0 message3 db "TRW found",0 message2 db "TRW not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of the
;previous SEH service here
.code
Start:
;Sets SEH in case of an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
int 3h ;calls int 3h. If the program has been traced in
;TRW it will continue past INT 3h without ;calling the SEH service, which is an error
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
jmp jump ;the program will get here only if it has been
;traced in TRW
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error:
mov esp, [delayESP] push offset continue ret
ends
end Start
;sets a new SEH service in case of an error
Surprisingly, this detection doesn't work with older versions of TRW, though it does work with TRW2000. Its only disadvantage is that the program must be traced for the trick to succeed. Fortunately, we can hide the detection program in a routine deeper in the program code, like this:
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC .data
message1 db "TRW detection by calling Int 3h",0
message3 db "TRW found",0
message2 db "TRW not found",0
delayESP dd 0
previous dd 0
;the ESP register saves here
;the ESP register saves the address of the
;previous SEH service here
.code
Start:
;Sets SEH in case of an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
call test ;jumps to the test; if the program continues on
;TRW is active
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
jmp jump ;the program gets here only if it has been
;traced in TRW
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
error:
;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
test:
int 3h ;calls int 3h. If the program has been traced in
;TRW it will continue past INT 3h without ;calling the SEH service, which is an error
ret ;the program will get here only if TRW is active
ends
end Start
This is a slightly less conspicuous method of detection. Unfortunately, the Int 3h call can be hidden only one level deeper in the program; any deeper than that and the SEH service will be called.
On the other hand, it is possible to use the method from the "Launching the BCHK Command of the SoftICE Interface" section, where the SEH service secures a move to the other instruction in case of an Int 3h call, thus preventing the program from failing. It is then possible to insert one Int 3h call into each program branch, which will make debugging in TRW very difficult.
Detecting SoftICE by Opening Its Driver with an API Call to the
CreateFileA (SIWVIDSTART) Function
This SoftICE detection program is based on finding its driver, SIWVIDSTART. I don't think much explanation is needed here because this is only another version of a well-known trick.
This method of detection works only with SoftICE for Windows NT and 2000.
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC .data
message1 db "Detection by means of CreateFileA (SIWVIDSTART)",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of the
;previous SEH service here
SIWVIDSTART db "\\.\SIWVIDSTART, 0" ;name of the SoftICE driver
.code
Start:
;Sets SEH if there is an error
mov [delayESP], esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
call CreateFileA, OFFSET SIWVIDSTART,\ FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\ NULL
;tries to open the SIWVIDSTART file
push eax ;saves the return value
;Sets the previous SEH service
jump:
error:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
mov esp, [delayESP] push offset continue ret
;sets a new SEH service in case of an error
Detecting SoftICE by Opening Its Driver with an API Call to the CreateFileW (NTICE, SIWVIDSTART) Function
The API function CreateFile is the most commonly used method of detecting SoftICE drivers. It has a twin, though, in Windows NT and 2000—CreateFileW. The only difference is that the function works with a Unicode string, which means that each character of the string is a zero value. This is an almost unknown
method of detection.
I don't know why so many people blindly use the API function CreateFileA, the most well-known anti-debugging trick. If you aren't afraid to experiment a bit, you can find other ways.
.386p
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC
Extrn CreateFileW : PROC
.DATA
message1 db "Detection by means of CreateFileW (SIWVIDSTART)",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of the
;previous SEH service here
SIWVIDSTART db "\",0,"\",0,".",0,"\",0,"S",0,"I",0, "W",0,"V",0,"I",0,"D",0,"S",0,"T",0,"A", 0,"R",0,"T",0,0,0
;name of the SoftICE driver
.CODE Start:
;Sets SEH if there is an error
mov [delayESP], esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
call CreateFileW, OFFSET SIWVIDSTART,\ FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\ NULL
;tries to open the SIWVIDSTART file
push eax ;saves the return value
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the return value
cmp eax, -1 ;tests to see if it succeeded
jnz jump ;if yes, the program jumps since SoftICE is
;active in memory
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
I used the SIWVIDSTART driver in this example, but you could also use NTICE. This trick works only in Windows NT and 2000, and it's not possible to search for the SICE and other drivers in Windows 9x.
Detecting SoftICE by Opening Its Driver with an API Call to Function _lcreat (SICE, NTICE, SIWVID, SIWDEBUG, SIWVIDSTART)
This method of detecting the SoftICE driver uses the API function _lcreat, which is almost never used. It is included in Windows only to make it compatible with older 16-bit programs. As such, you can use it right away.
.386
.MODEL FLAT,STDCALL
locals
jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC
Extrn _lcreat : PROC
.DATA
message1 message3 message2
delayESP previous
SOFTVIDEO
db "Detection through _lcreat",0
db "SoftICE found",0 db "SoftICE not found",0 dd 0 dd 0
db "\\.\SIWVID",0 ;ESP register saves here
;the ESP register saves the address of the ;previous SEH service here ;the name of the SoftICE driver
.CODE Start:
;Sets SEH if there is an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
;opening attributes (0=normal) ;address with the driver name ;tries to open the SIWVID file ;saves the return value
push 0
push offset SOFTVIDEO
call _lcreat push eax
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the return value
cmp eax, -1 ;tests to see if it succeeded
jnz jump ;if yes, it will jump since SoftICE is active
;in memory
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
This is definitely a better trick than using the API function CreateFileA. It can also be used for detecting the driver in older versions of TRW (\\ .\TRW). This trick works in all Windows versions.
Detecting SoftICE by Opening Its Driver with an API Call to Function
_lopen (SICE, NTICE, SIWVID, SIWDEBUG, SIWVIDSTART)
If you aren't already fed up with all the tricks for finding SoftICE drivers, I will show you one more. It uses the API function _lopen, which almost isn't used at all. It is in Windows only to make it compatible with older 16-bit programs.
This is the same detection method as in the preceding example, so the description is the same.
.386
.MODEL FLAT,STDCALL
locals jumps
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC
Extrn _lopen : PROC
.DATA
message1 db "Detection by means of _lopen",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register saves the address of the
;previous SEH service here
SOFTVIDEO db "\\.\SIWVID", 0 ;the name of the SoftICE driver
.CODE Start:
;Sets SEH if there is an error
mov [delayESP], esp push offset error
call SetUnhandledExceptionFilter
mov [previous], eax
push 0 ;a type of opening 0 = OF_READ (opens a file
;for reading only)
push offset SOFTVIDEO ;the address with the driver name
call _lopen ;tries to open the SIWVID file
push eax ;saves the return value
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the return value
cmp eax, -1 ;tests to see if it succeeded
jnz jump ;if yes it will jump since SoftICE is active
;in memory
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
Anti-FrogsICE Trick
FrogsICE is an application that crackers use for hiding SoftICE from anti-debugging tricks (see Figure 7.3). Unfortunately, it is currently able to detect most tricks. Fortunately, because its creator chose to hide SoftICE with a VxD driver, you can find errors just like in any other program. Therefore, detecting FrogsICE isn't impossible.
Note One good thing about Windows 9x is that you can find many ways to do what you want in it. This is not possible in Windows NT, 2000, or XP because of their restrictions. FrogsICE is a Windows 9x application and, as such, you can take advantage of the operating system's possibilities.
The following trick cannot detect FrogsICE, but it will cause an error in the program if FrogsICE is active. It is performed by the VxD call VMM_GetDDBList. The VxD call can be used only in ring0, so you must either first switch into it or use your own VxD driver. Because it's easier to switch into ring0, I'll use this method in the following example.
If FrogsICE isn't active, the program will end correctly. If it is active, an error will occur after the VMM_GetDDBList call. Unfortunately, you will also lose control over your program then, because it will end right after the error has occurred.
UNICODE=0
include w32.inc
Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;an interrupt number; numbers 1 or 3 will make ;debugging more difficult
.DATA
message1 message2 delayESP previous
.CODE Start:
db "Anti-FrogsICE trick",0 db "FrogsICE isn't active",0
dd 0 dd 0
;the ESP register saves here
;the ESP register will save the address of the ;previous SEH service here
;Sets SEH if there is an error
mov [delayESP],esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
push edx
sidt [esp-2]
add edx,(Interrupt *8)+4
mov ebx,[edx]
mov bx,word ptr [edx-4]
lea edi,InterruptHandler
mov [edx-4],di
ror edi,16
mov [edx+2],di
push ds
push es
pop pop
mov
ror mov
int Interrupt
es es
[edx-4],bx
ebx,16 [edx+2],bx
;reads the IDT into the stack pop edx ;reads the vector of the required interrupt
;(Int 5h)
;reads the address of the old service of the ;required interrupt (INT 5h)
;sets a new interrupt service (INT 5h) ;saves registers for security
;jump into Ring0 (a new service INT 5h that you ;have defined) ;restores registers
;sets the original service of the INT 5h ;interrupt
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
continue:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
;Your new INT 5h service (runs in Ring0) InterruptHandler:
db 0cdh, 20h dd 00001013FH
;VMMCall
VMM_GetDDBList
iretd
;this call will cause FrogsICE to crash
;if the program gets here FrogsICE isn't active
;in memory
ends
end Start
FrogsICE is a great threat to protection developers. If you cannot prevent debugging, then protecting the
application from crackers' attacks will be more and more difficult. Fortunately, even FrogsICE isn't foolproof, and it is possible to protect against it. The protection program should first focus on the application that FrogsICE is trying to hide, and only then should it detect FrogsICE itself.
NoteThere is one other application like FrogsICE that runs in Windows NT and 2000, a sort of port for these systems, called NTALL. Fortunately, it isn't able to hide SoftICE as well.
Detecting SoftICE by Searching for the Int 3h Instruction in the UnhandledExceptionFilter
If SoftICE is active in memory, the interrupt instruction Int 3h will be set at the API address of the UnhandledExceptionFilter function. The following sample program first searches for the address of this API function, and then it searches for the Int 3h at its beginning.
.386
.MODEL FLAT,STDCALL
locals jumps UNICODE=0 include w32.inc
Extrn SetUnhandledExceptionFilter : PROC Extrn UnhandledExceptionFilter : PROC .DATA
message1 db "Detecting SoftICE by searching for the Int 3h instruction in
UnhandledExceptionFilter",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0
delayESP dd 0 ;the ESP register saves here
previous dd 0 ;the ESP register will save the address of
;the previous SEH service here.
.code
Start:
;Sets SEH if there is an error
mov [delayESP], esp push offset error
call SetUnhandledExceptionFilter mov [previous], eax
mov eax, offset UnhandledExceptionFilter ;puts the address for a jump into
;the UnhandledExceptionFilter into eax
mov eax, [eax+2] ;reads the address of the API function of the
;UnhandledExceptionFilter in the Import table
mov eax, [eax] ;reads the address of the
;UnhandledExceptionFilter function from the
;Import table
push eax ;saves the API address of the
;UnhandledExceptionFilter function
;Sets the previous SEH service
push dword ptr [previous]
call SetUnhandledExceptionFilter
pop eax ;restores the API address of the
;UnhandledExceptionFilter function
cmp byte ptr [eax], 0cch ;tests to see if the Int 3h instruction is
;present
jz jump ;if so, the program will jump because SoftICE
;is active in memory
continue:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
error: ;sets a new SEH service in case of an error
mov esp, [delayESP] push offset continue ret
ends
end Start
This is a fairly good detection method that works in Windows NT, Windows 2000, and Windows XP.
Detecting SoftICE Through Int 1h
This trick for discovering SoftICE utilizes the Int 1h interrupt. A new SEH service is set before the SEH service is called. This service is called once the Int 1h instruction has been performed. The EIP register in the Context field is increased by one, causing a 1-byte move in the program, at which point the SEH service identifies the error.
If it is an exception breakpoint, the program will finish without any changes. Otherwise the EIP register in the Context field is increased by one again, resulting in a one more 1-byte move in the program.
Next, the service tests to see if the Exception Single Step error has occurred. If it has, then SoftICE is active in memory (or the error would have been the Exception Access Violation), and the program sets a mark. The service then finishes and the program continues to run.
Finally, the program tests to see if the mark has been set, and if it has, SoftICE is active in memory.
.386
.MODEL FLAT,STDCALL locals jumps
UNICODE=0
include w32.inc
.data message1 message2 message3 Mark
db "Detecting SoftICE through Int 1h",0 db "SoftICE not found",0 db "SoftICE found",0 db 0
.code
Start:
xor eax,eax
push offset xhandler
push dword ptr fs:[eax]
mov dword ptr fs:[eax], esp
mov eax, cs
test ax, 100bh
jne Win9x
int 1h
nop nop
pop dword ptr fs:[0]
add esp,4
cmp mark, 1
jz jump
;nulls the eax register before setting the ;new SEH service
;address for your new SEH service (xhandler) ;saves the address of the original SEH service ;sets your new SEH service
;tests to see if the operating system is ;Windows 9x
;if so, the program jumps because this trick is only for Windows NT and Windows 2000 ;this causes an error so the program calls ;your new SEH service (xhandler)
;sets the original SEH service ;clears the stack
;tests to see if the mark is set
;if the mark is set, the program jumps because
;SoftICE is active in memory.
Win9x:
call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1
;ends the program
jump:
call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1
;ends the program
;Your new SEH service (xhandler) xhandler:
mov eax, [esp+04]
mov ecx, [esp+0ch]
inc dword ptr [ecx+0b8h
mov eax, [eax]
sub eax, 80000003h
jz end
inc dword ptr [ecx+0b8h]
dec eax
jnz jump
inc mark
;finds the exception number
;reads the address of the beginning of the ;context
;increases EIP by 1
;reads the exception number
;if the exception number is the Exception ;Breakpoint the program will end ;increases EIP by one
;if it wasn't the Exception Access Violation ;then the program will end
;SoftICE is active and the program will set ;the mark
end:
xor ret
eax,eax
;jump back into the program
ends
end Start
This is an excellent and little-known SoftICE detection method. This trick works only in Windows NT, 2000, and XP because the Int 1h instruction is incompatible with Windows 9x.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment