Saturday, December 5, 2009

about the PE file's structure should help you better understand this example.

.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.

No comments:

Post a Comment