Saturday, December 5, 2009

Anti-Debugging, Anti-Disassembling, and Other Tricks for Protecting Against Softice and TRW

Overview



Few programmers realize just how important it is to prevent their code from debugging or disassembly. It's essential to do so, because if a program can be debugged, it is easy for a cracker to understand how the protection works. Fortunately, even the simplest anti-debugging tricks can complicate debugging, and we can use anti-disassembling macros to make it harder to understand the debugged code. When well-executed, both tricks will make it much more difficult for a cracker to remove even the simplest protection.



We've had a look at the compression and encoding programs that many programmers rely on to do the dirty work of software protection. Using these programs alone, though, is really not a good solution, because it will only be a matter of time before a new decoder becomes available, and before the better crackers remove the encoding programs themselves, thus leaving the application without protection against debugging.



Anti-debugging and anti-disassembling tricks aren't hard to use, but it is very important to test your protected application carefully.



Note As of this writing, there are only a few anti-debugging tricks for Windows NT,

Windows 2000, and Windows XP because their internal structure differs from that of Windows 9x. You may find it necessary to have your program test to see which operating system is running and then decide which trick to use. Anti-disassembling tricks, on the other hand, are not operating-system-dependent and will work without such problems. It is therefore advisable to use them as much as possible.



All of the example programs discussed in the following pages are written in assembler, and it is a good idea to avoid programming them in a higher-level programming language. While not every programmer knows assembler, most higher-level languages let you insert assembly code. If your language of choice does not, it will be much more difficult for you to work with the code you'll find here. In that case, your best bet will be to insert the anti-debugging tricks into a separate DLL library, and then to call those functions from the protected application. (Of course, this is not ideal, because a cracker will be able to remove such protection quickly and easily.)



Note You only need to use anti-disassembling macros to protect critical points in the program that should remain hidden from an attacker. You do not need to use these macros throughout the program.



Your program should perform a simple test for the presence of a debugger in memory as soon as it starts. If a debugger is found, the program should refuse to run and possibly display a warning that the debugger should be removed (see Figure 7.1). While a cracker will probably be able to get around the first test easily, you should consider having the program, in a later test, check a second time for the presence of a debugger. Then, if it finds that a debugger is still active, the program could "freeze," or do something else to make it difficult or impossible for the cracker to continue. I do not, however, recommend displaying a warning under any circumstances, because such a warning makes it clear that this is an attack.



The examples that follow are tuned for Turbo Assembler v5.0 from Borland. The code isn't completely optimized and should therefore be understandable by less-than-expert assembler programmers. (I hope that the pros will excuse me.) While it wouldn't be hard to optimize the examples, doing so would result in less-readable code.



My advice to those of you who don't know how to work in assembler is to learn it. Even though many people claim that it is a dead language, there are still fields where it is necessary, and programming good software protection is one of them, especially when tuning your application (which can only be done well in assembler). When tuning your code, you will discover the holes that make it easy for crackers to conquer and remove software protection.



Let's begin.





Detecting SoftICE by Calling INT 68h



Here's a way to detect the presence of SoftICE in memory by calling int 6 8h. The AH register must contain the value 43h before calling int 68h. If SoftICE is active in memory, the return value 0F386h will be in the AX register.



This is a well-known method of detecting SoftICE that is safe and commonly used, but only in Windows 9x. You can see it in action, for example, in SafeDisc:

.386

.MODEL FLAT,STDCALL

locals

jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

.data

message1 db "Detection by calling INT 68h",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0 delayESP dd 0 previous dd 0



;the ESP register saves here

;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

;The new address for Structured Exception Handling (SEH) is set here to ensure that in case of an ;error, the program will continue from an error label and will end correctly. This is important ;if, for example, the program calls an interrupt that will be performed correctly only if SoftICE ;is active, but which will cause an error and crash the program if SoftICE is not active. Finally, ;the previous SEH service address is saved.

mov ah,43h ;service number

int 68h ;calls the INT 68h interruption

push eax ;saves the return value

;Sets previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

;Sets the original SEH service address

pop eax ;restores the return value

cmp ax, 0f386h ;tests to see whether the return value is

;a "magic number"

;If SoftICE is active in memory, the return value will be F386h in the AX register.

jz jump ;if yes, the program jumps because SoftICE is

;active in memory

continue:

call MessageBoxA,0, offset message2,\ offset message1,0



call ExitProcess, -1

;if the return value was other than F386h, ;SoftICE was not found, and an error message ;will be displayed.

;ends the program



jump:

call MessageBoxA,0, offset message3,\ offset message1,0



call ExitProcess, -1

error:

;prints a message that SoftICE was found. Any ;code may follow from this point.

;ends the program

;starts a new SEH service in case of an error.

mov esp, [delayESP] push offset continue ret

;if an error occurs in the program, SEH ;ensures that the program will continue from the ;error label.



ends

end Start

;end of program



Detecting SoftICE by Calling INT 3h



This is one of the most well known anti-debugging tricks, and it uses a back door in SoftICE itself. It works in all versions of Windows, and it is based on calling INT 3h with registers containing the following values: EAX=04h and EBP=4243484Bh. This is actually the "BCHK" string. If SoftICE is active in memory, the EAX register will contain a value other than 4.



This trick has often been used in the code of various compression and encoding programs, and it is well known because of its wide use. When used well, it may cause trouble even for the more experienced crackers.



.386

.MODEL FLAT,STDCALL

locals jumps UNICODE=0 include w32.inc

Extrn SetUnhandledExceptionFilter : PROC .data

message1 db "Detection by calling INT 3h",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0

delayESP dd 0 ;the ESP register is saved 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

;The new address for Structured Exception Handling (SEH) is set here to ensure that in case of an ;error, the program will continue from an error label and will end correctly. This is important ;if, for example, the program calls an interrupt that will be performed correctly only if SoftICE ;is active, but which will cause an error and crash the program if SoftICE is not active. ;Finally, the previous SEH service address is saved.

mov eax,4 ;"magic" values to be found

mov ebp,"BCHK" ;whether SoftICE is active

int 3h ;calls the INT 3h interruption

push eax ;saves the return value

;Sets previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

;Sets the original SEH service address

pop eax ;restores the return value

cmp eax,4 ;tests to see whether eax was changed

jnz jump ;if it was changed, SoftICE is active

;in memory

continue:

call MessageBoxA,0, offset message2,\ offset message1,0

;If the return value is 4 SoftICE wasn't found and the program prints out an error message.

call ExitProcess, -1 ;ends program jump:

call MessageBoxA,0, offset message3,\ offset message1,0

;Displays a message that SoftICE was found; any code may follow this point. call ExitProcess, -1 ;ends program error:

;starts a new SEH service in case of an error.

mov esp, [delayESP] push offset continue ret

;If an error occurs in the program, SEH will ensure that the program will continue from the ;error label.

ends

end Start ;end of program

Detecting SoftICE by Searching Memory



This detection searches the memory in the V86 mode for the WINICE.BR string. Because this method is infrequently used, it's worth considering, though it can only be used in Windows 9x.



This routine can be easily hidden because it doesn't use calls (neither API nor INT). This will make it impossible to detect, and, if you use it well, it may discover a debugging attempt—for an attacker to make the program continue, he will have to change its code or the register's contents.



To discover the debugging attempt, all you need to do is check after this trick to see if the registers really contain the values that they should contain, and you'll need to perform a CRC test to see if the program code has been changed in memory. If SoftICE isn't active in memory, your checking routine will run without problems.



This method's one disadvantage is that it works well only with older versions of SoftICE, and an error will occur if one of SoftICE's newer versions is active in memory.

.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC .data

message1 db "Detection by memory search",0 message2 db "SoftICE not found",0 message3 db "SoftICE found",0 delayESP dd 0 previous dd 0



;the ESP register saves here

;the ESP register will save the address of the ;previous SEH service here.

.code

Start:

;Sets SEH in case of an error



more:

found1:

mov push

call

mov mov

mov mov

[delayESP],esp offset error

SetUnhandledExceptionFilter

[previous], eax

al, "W"

10000h

edi, 10000h ecx, 400000h

cmp

jz jmp

add

cmp

jnz push

repnz SCASB jecxz notfound

dword ptr [edi], "INIC"

found1 more

edi, 4

dword ptr [edi], "RB.E" more

word ptr 1 ;searches for the WINICE.BR string in ;V86 memory

;begins the search here

;specifies the number of bytes to search

;searches for a "W" string in memory

;if the string is not found, the memory search

;ends because SoftICE isn't active in memory.

;when a "W" string is found, this tests to see

;whether the "INIC" string follows.

;ends when "INIC" is found

;otherwise it searches all memory

;move by 4 characters (bytes)

;when "WINIC" is found it checks to see if the ;"E.RB" string follows

;if it does not, the memory search ends ;go here if SoftICE is active in memory and ;save 1 into the stack to show that SoftICE ;was found.



jmp

short found

notfound:

push word ptr 0

;Go here if SoftICE is not found in memory.

found:

;Sets previous SEH service

pop ax test ax,ax jnz jump

;restores the return value

;tests to see if the return value is 1

;if it is, the program jumps because SoftICE is

;active.

error:

;starts a new SEH service in case of an error

mov esp, [delayESP] push offset continue ret

ends

end Start

Detecting SoftICE by Opening Its Drivers and Calling the API Function CreateFileA (SICE, NTICE)



This is the most frequently used detection for SoftICE. It will also find other active VxD (Virtual Device Driver) and Sys drivers.



The principle of this detection is simple: You try to open a file with the same name as the active VxD or Sys file. If the driver is active in memory, and you specify the type of file opening as OPEN_EXISTING (i.e., open the existing file), it will be possible to open it. Once you have called CreateFileA, the EAX register will contain a return value other than ' (OFFFFFFFFh) if it has been opened successfully.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

.data

message1 db "Detection by means of CreateFileA",0

message3 db "SoftICE found",0

message2 db "SoftICE not found",0

delayESP dd 0

previous dd 0

SOFT9x db "\\.\SICE",0

SOFTNT db "\\.\NTICE",0







;the ESP register is saved here

;the ESP register will save the address of the ;previous SEH service here

;the name of the SoftICE driver for Windows 9x ;the name of the SoftICE driver for Windows NT ;and 2000

.code

Start:

;Sets SEH in case of 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 a file

;\\.\SICE

cmp eax, -1 jz noSOFT9x

push word ptr 1 jmp short found

;tests for success

;if not, the program jumps to the test for

;SoftICE NT and 2000

;saves the value 1 into the stack to ;show that SoftICE is active in memory

noSOFT9x:

call CreateFileA, OFFSET SOFTNT,\

FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\ NULL



cmp eax, -1 push dword ptr 1

jnz short found

pop eax

push dword ptr 0

;tries to open a file

;\\.\NTICE

;tests for success

;saves value 1 into the stack to show that

;SoftICE is active in memory

;if SoftICE for Windows NT and 2000 is active,

;the program ends

;when SoftICE isn't active.

;calls the value 1 from the stack

;and saves 0 here to show that SoftICE isn't

;active

found:

;Sets previous SEH service

push dword ptr [previous] call SetUnhandledExceptionFilter

pop eax ;restores the return value

test eax,eax ;tests the return value

jnz jump ;if eax is 1, the program jumps because SoftICE

;is active in memory

continue:

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

;starts a new SEH service in case of an error

ends

end Start



This means of SoftICE detection is used frequently (probably because it is easy to apply with higher-level programming languages), and is very well known. Since you will need to call the API CreateFileA, you should test to see whether a breakpoint was set on this call. It is best to use this detection as the first test for active SoftICE and to then warn the user to remove it.



#include

#define WIN32_LEAN_AND_MEAN

#include BOOL IsSoftIce95Loaded( ) {

HANDLE hFile;

// "\\.\SICE" for Windows 9x hFile = CreateFile( "\\\\.\\SICE",

GENERIC_READ | GENERIC_WRITE,

FILE_SHARE_READ | FILE_SHARE_WRITE.

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL);

if( hFile != INVALID_HANDLE_VALUE )

{

CloseHandle(hFile); return TRUE;

}

}

BOOL IsSoftIceNTLoaded()

{

HANDLE hFile;

// "\\.\NTICE" for Windows NT

hFile = CreateFile( "\\\\.\\NTICE",

GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL);

if( hFile != INVALID_HANDLE_VALUE )

{

CloseHandle(hFile); return TRUE;

}

return FALSE;

}

int main(void)

{

if( IsSoftIce95Loaded() )

printf("SoftICE for Windows 9x is active in memory.\n");

else if( IsSoftIceNTLoaded() )

printf("SoftICE for Windows NT or 2000 is active in memory.\n");

else

printf("SoftICE wasn't found.\n"); return 0;

}



Detecting SoftICE by Measuring the Distance Between INT 1h and INT 3h Services



This detection routine is one of the best ways to detect SoftICE when it is active in memory, and the most difficult to discover. It's based on the fact that when SoftICE is active, the distance between the INT 1h and INT 3h services will always be 1Eh.



This method is great to use when you don't have to call any interrupts, API, or VxD calls, and discovery is difficult but not impossible. Unfortunately it can be used only in Windows 9x.

.386p

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC



.data










message1


db


"Detection of service distance


INT 1h and INT 3h",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










;previous SEH service here

pIDT


db


6 dup (0)


;IDT is saved here

.code

Start:

;Sets SEH in case of an error



mov


[delayESP], esp




push


offset error




call


SetUnhandledExceptionFilter




mov


[previous], eax




sidt


fword ptr pIDT


;saves IDT

mov


eax,dword ptr [pIDT + 2]


;puts a pointer to the interrupt table into eax

add


eax,8


;inserts the INT 1h vector address into eax

mov


ebx, [eax]


;inserts the INT 1h service address into ebx

add


eax,16


;inserts the INT 3h vector address into eax

mov


eax, [eax]


;inserts the INT 3h service address into eax

and


eax, 0ffffh


;selector will not be used

and


ebx, 0ffffh


;even with INT 1h

sub


eax,ebx


;calculates the distance between interrupt







;services

push


eax


;saves the result

;Sets previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax ;restores the result

cmp eax, 01eh ;when 01Eh is the value in eax, SoftICE is

;active in memory
jz jump ;and the program jumps

continue:

call MessageBoxA,0, offset message2, offsetmessage1,0

call ExitProcess, -1

jump:

error:

call MessageBoxA,0, offset message3,

offsetmessage1,0

call ExitProcess, -1

mov esp, [delayESP] push offset continue ret

;starts a new SEH service in case of an error

ends

end Start



Detecting SoftICE by Opening Its Drivers and Calling the API Function CreateFileA (SIWVID)



This means of detection is like the detection we used in the "Detecting SoftICE by Opening Its Drivers and Calling the CreateFileA API function (SICE, NTICE)" section, and it is based on the fact that SoftICE uses its own graphic driver, VxD siwvid, in Windows 9x. When SoftICE is active in memory, this graphic driver will run, and you can detect it, just as we did the SICE or NTICE drivers, by using an API call at CreateFileA.



This particular method is slightly less used than the one in that earlier section, and as such may be more useful. This trick is valid only in Windows 9x.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC .data

message1 db "Detection2 by means of CreateFileA",0

db "SoftICE found",0

db "SoftICE not found",0 dd 0 dd 0

Db "\\.\SIWVID",0

;the ESP register saves here

;the ESP register will save the address of the ;previous SEH service here ;the name of the SoftICE graphic driver

message3 message2 delayESP previous

SOFTVIDEO

.code

Start:

;Sets SEH in case of an error

mov [delayESP], esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

call CreateFileA, OFFSET SOFTVIDEO,\ FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\ NULL

;tries to open a file \\.\SIWVID
push eax ;saves the return value

;Sets previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax

cmp eax, -1

jnz jump

;restores the return value ;tests for success

;if found, the program ends because SoftICE is ;active in memory

continue:

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

;starts a new SEH service in case of an error

ends

end Start



Detecting SoftICE by Calling the NmSymIsSoftICELoaded DLL Function from the nmtrans.dll Library



The SoftICE DLL library nmtrans.dll contains the NmSymIsSoftICELoaded function, which we can use to see whether SoftICE is active in memory. This trick can be used in all Windows versions, and it is not used very often.



To use this trick, first load the nmtrans.dll library into memory by API- calling LoadLibraryA. Next, find its address by API-calling GetProcAddress, and then calls it. If the return value is other than 0 then SoftICE is active in memory.



The nmtrans.dll library uses an API call to CreateFileA for SoftICE detection, as shown in the section above titled "Detecting SoftICE by Opening Its Drivers and Calling the CreateFileA API Function (SICE, NTICE)." It is important to test breakpoints at API calls to the LoadLibraryA, GetProcAddress, CreateFileA, and possibly even to NmSymIsSoftICELoaded.

Because paths for Windows 9x and Windows NT are firmly set, this isn't an ideal tool, because SoftICE could be installed anywhere. In the "Using the Windows Registry to Find the Directory Where SoftICE Is Installed" section later in this chapter, I will show you how to use the Windows registers to determine where

SoftICE has been installed.

.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC
Extrn GetProcAddress : PROC

Extrn LoadLibraryA : PROC

.data

message1 db "Detection using nmtrans.dll",0 message3 db "SoftICE found",0 message2 db "SoftICE not found",0 delayESP dd 0 previous dd 0

SOFTsym

.code

SOFT95sym db "C:\program\sice\SoftICE\nmtrans.dll",0 SOFTNTsym db "C:\program\NTICE\nmtrans.dll",0

db "NmSymIsSoftICELoaded",0 ;the ESP register saves here

;the ESP register will save the address of

;the previous SEH service here

;adjust this path according to the SoftICE

;installation

;this may be performed by reading from ;registers

;the name of the function in nmtrans.dll

Start:

;Sets SEH in case of an error

mov [delayESP],esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

no95:

exit:

call LoadLibraryA, offset SOFT95sym

test eax,eax
jz no95

call GetProcAddress, eax, offset SOFTsym

test eax,eax

jz no95

call eax

test eax,eax

jnz exit

call LoadLibraryA, offset SOFTNTsym

test eax,eax

jz exit

call GetProcAddress, eax, offset SOFTsym

test eax,eax

jz exit

call eax



push eax

push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax

test eax,eax

jnz jump

call MessageBoxA,0, offset message2,\ offset message 1, 0 call ExitProcess, -1

;loads DLL (nmtrans.dll) for SoftICE ;Windows 9x

;jump on error ;finds the address of the ;NmSymIsSoftICELoaded function ;jump on error

;calls the function NmSymIsSoftICELoaded ;whose address is in EAX

;jump if eax is not 0, because SoftICE is ;active in memory



;loads DLL (nmtrans.dll) for SoftICE ;Windows NT and 2000

;jump on error

;detects the address of the

;NmSymIsSoftICELoaded function

;jump on error

;calls NmSymIsSoftICELoaded function whose ;address is in EAX

;saves the return value ;Sets previous SEH service



;restores the return value

;if EAX isn't 0, SoftICE is active

;in memory

;and the program ends



jump:



error:

call MessageBoxA,0, offset message3,\ offset message 1, 0 call ExitProcess, -1

;starts a new SEH service in case of ;an error

mov esp, [delayESP] push offset continue ret

ends

end Start

Detecting SoftICE by Identifying Its INT 68h Service



This rarely used means of detection tests the first bytes of the INT 68h service to see whether it is a SoftICE service. This detection routine is hard to discover, but the test may only be launched from a DOS program running under Windows, because the INT 68h service is for DOS only.



Because it cannot be used directly in a 32-bit Windows program, this routine is almost useless. Its other disadvantage is that the beginning of the service is different for various SoftICE versions. The compared values shown in this code are from SoftICE v4.05 for Windows 9x.



.MODEL TINY .386P

.DATA

message db "SoftICE detection by means of its INT 68h service identification", 0dh, 0ah, 24h

found db "SoftICE active", 24h

notfound db "SoftICE not found", 24h

Detecting SoftICE by Detecting a Change in the INT 41h Service



This detection is based on the fact that your program will change the INT 41h vector to your new service. If SoftICE is active in memory, this will not be possible; if SoftICE is not in memory, your new service will be performed.



As with the previous example, this technique only runs in a DOS box under Windows 9x. It cannot be used in 32-bit Windows applications.



.MODEL TINY .386P

.DATA

message found notfound

db "SoftICE detection by means of its INT 41h", 0dh, 0ah,24h service identification db "SoftICE active",24h

db "SoftICE not found",24h

.CODE

.STARTUP

lea dx, message mov ah,9

int 21h

xor ax,ax mov es,ax mov bx, cs lea dx, new_int41

xchg dx, es:[41h*4]



xchg bx, es:[41h*4+2]



in al, 40h xor cx,cx

int 41h

xchg dx, es:[41h*4] xchg bx, es:[41h*4+2] cmp cl,al

;function number -- > Show string ;INT 21h call shows label

;nulls ax

;puts 0 into es (sets interrupt vector segment) ;puts the program segment into bx ;puts the offset part of the new int 41h ;service into dx

;sets an offset part of the new int 41h service ;address and puts the offset part of the old ;service address into dx

;sets the segment part of the new int 41h ;service address and puts the segment part of ;the old service address into bx ;reads a value into al ;nulls cx

;calls INT 41h. If SoftICE isn't active in

;memory, the program will perform the new ;service

;sets the offset part of the original INT 41h ;service address

;sets the segment part of the original int 41h ;service address

;compares cl and al and if the result is



jump:

jnz short jump lea dx, notfound jmp short farther

lea dx, found

;negative SoftICE is active in memory ;jumps if SoftICE is active in memory

farther:

mov ah,9

int 21h

mov ax,4c00h

int 21h
new_int41 PROC

mov cl,al

iret new_int41 ENDP END

;function number -- > Show string

;INT 21h call

;function number -- > Ends program

;INT 21h call this is your new INT 41h service

;moves the value from al into cl to show that ;SoftICE isn't active in memory ;returns from the service



Detecting SoftICE by Opening Its Driver and Calling the API Function CreateFileA (SIWDEBUG)



This trick is also based on searching for a SoftICE driver in memory. The API function CreateFileA is used because it can find out whether the driver is active in memory, and it is also necessary to use the API function GetLastError once it has been called. CreateFileA will return the value ' in the EAX register, which means that it didn't succeed in opening the file. Once you call GetLastError, you will be able to find out whether SoftICE is active in memory. If it is, the EAX will be 02; if it is not, this value will be 32h.



This is a less widely known anti-debugging trick than the detection method described in the "Detecting SoftICE by Opening Its Drivers and Calling the CreateFileA API Function (SICE, NTICE)" section. It works only in Windows 9x.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

.DATA

message1 message3 message2 delayESP previous

SIWDEBUG

db "Detection by means of CreateFileA and SIWDEBUG driver",0

db "SoftICE found",0

db "SoftICE not found",0

dd 0 ;the ESP register saves here

dd 0 ;the ESP register will save the address of the

;previous SEH service here
db "\\.\SIWDEBUG",0 ; the name of the SoftICE driver

.CODE Start:

;Sets SEH in case of an error

mov [delayESP],esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

call CreateFileA, OFFSET SIWDEBUG, FALSE,\ FALSE, NULL, FALSE, OPEN_EXISTING, NULL

;tries to open the \\.\SIWDEBUG file

call GetLastError ;returns an error number (if SoftICE is

;active EAX=32h; if it is not, EAX=02)

push eax ;saves the return value

;Sets previous SEH service push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax ;restores the return value

cmp eax, 32h ;tests to see if SoftICE is active in memory

jz jump ;jumps if it is active

continue:

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



;starts a new SEH service in case of an error

ends

end Start

Detecting SoftICE by Calling Int 2Fh and Its Function GET DEVICE API ENTRY POINT for VxD SICE



This infrequently used trick calls the INT 2Fh interrupt and its function, GET DEVICE API ENTRY POINT. The service must be called with ES and DI equal to 0. It's not hard to use this trick in a DOS box under Windows.



BX contains the value 0202h, which is a VxD ID for SoftICE. If VxD is active, a non-zero value will return in es and di, which is the address for the DEVICE API ENTRY POINT. This means that SoftICE is active in memory.



.MODEL TINY

.386P

.DATA message

found notfound

db 'SoftICE detection through calling Int 2fh and its function GET DEVICE API ENTRY

POINT for VXD SICE', 0dh, 0ah,24h db 'SoftICE active',24h

db 'SoftICE not found',24h

.CODE

.STARTUP

lea dx, message mov ah,9

int 21h

xor di,di mov es,di mov ax, 1684h

mov bx, 202h int 2fh

mov ax, es add ax, di test ax,ax jnz short jump

lea dx, notfound jmp short farther

;function number -- > show string ;INT 21h call shows label ;nulls di ;nulls es

;the number of GET DEVICE API ENTRY POINT

;function

;VxD ID for SICE VxD

;calls GET DEVICE API ENTRY POINT

;puts the value from es into axf ;adds di to ax ;tests whether ax=0

;if not, it jumps since SoftICE is active in ; ;memory

mov ah,9

int 21h

mov ax,4c00h

int 21h

;function number -- > show string

;INT 21h call shows label

;function number -- > ends the program

;INT 21h call

As you can see, it's very easy to use this trick in a DOS program. There is a problem, though, with using it in a 32-bit Windows application, because the simple INT 2Fh can't be used without the program ending with an error. After considering this problem for some time, I figured out a way to solve it, as follows.



The program first finds the address of the INT 5h service, sets your new service instead of it, and then calls the new INT 5h service. In this way, the program is switched into ring0, the high privilege state. Once in ring0, you can call a VxD and then set a new INT 68h service. At this point, the INT 5h service will end, and the program will switch back into ring3, the low privilege state.



The program will call INT 68h with the AH register set to 43h, which is the same means of detection as that used in the preceding DOS example. The AH register doesn't have to be set, because this is just a trick to fool an attacker into thinking that you are trying to detect the debugger in this way, though you are not.



You call your new service for this interrupt by calling INT 68h, using the same INT 2Fh call as in a DOS program running under Windows. If SoftICE is found, it sets a mark and returns to the main program.



At this point, the INT 5h running in the ring0 service is called again, and the original INT 68h service is set there. Later, the program switches back into ring3 and sets the original INT 5h service. Finally, it tests to see whether the mark indicating that SoftICE had been found was set.



This is a somewhat complicated solution, but it's the best I've been able to come up with. There may well be a better one.



.386p

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

Interrupt equ 5 ;interrupt 3 will make debugging more difficult

.DATA

message1 message2 message3 delayESP previous

db "Detection by means of the Int 2Fh_GET DEVICE API ENTRY POINT for VxD SICE",0

db "Debugger not found",0 db "Debugger found",0

dd 0 ;the ESP register will be saved here

dd 0 ;the ESP register will save the address of the

mark

;previous SEH service here
db 0 ;a value of 1 will be set here if SoftICE is

;active in memory

.CODE

Start:



;Sets SEH in case of an error

mov [delay, ESP] push offset error

call SetUnhandledExceptionFilter mov [previous], eax

push edx

sidt [esp-2] ;reads IDT into the stack

pop edx

add edx,(Interrupt*8)+4 ;reads a vector of the required interrupt

;(INT 5h)

mov ebx,[edx]

mov bx,word ptr [edx-4] ;reads an address of the old service of the

;required interrupt (INT 5h)

lea edi,InterruptHandler mov [edx-4],di

ror edi,16 ;sets a new service for the interrupt (INT 5h)

mov [edx+2],di

push ds ;saves registers for

;security

;jump into Ring0 (the newly defined service

;INT 5h)

;restores registers

;saves registers for ;security

;calls INT 68h and function 43h (which will call ;your new service INT 68h)

;sets a mark that your service INT 68h may be ;cancelled

;jumps into Ring0 (your new service INT 5h) ;restores registers

push es

int Interrupt

pop es pop ds push ds push es mov ah,43h

int 68h

stc

int Interrupt pop es pop ds

;sets the original service ;of the INT 5h interrupt

mov [edx-4],bx ror ebx,16 mov [edx+2],bx

;Sets the previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

cmp byte ptr mark,1 ;tests to see whether a mark has been set that

;SoftICE is active in memory
jz jump ;if yes the program will end

continue:

call MessageBoxA,0, offset message2,

offsetmessage1,0

call ExitProcess, -1

jump:

call MessageBox, 0, offset message3, offsetmessage1,0 call ExitProcess, -1

error:

mov esp, [delayESP] push offset continue ret

;A new INT 5h service (runs in Ring0) InterruptHandler:

;sets a new SEH service in case of an error

pushad

jc uninstall

mov eax, 68h

mov esi, offset HookInt68

db 0cdh,20h dd 000010041H VMMCall Hook_V86_Int_Chain

mov eax, 68h

mov esi, OFFSET HookInt68 db 0cdh,20h dd 000010080H VMMCall Hook_PM_Fault

popad iretd

uninstall:

mov eax, 68h

mov esi, OFFSET HookInt68 db 0cdh,20h dd 000010118H VMMCall UnHook_V86_Int_Chain

mov eax, 68h

mov esi, OFFSET HookInt68 db 0cdh,20h dd 00001011AH VMMCall UnHook_PM_Fault

popad iretd ;saves registers

;tests your mark and if it is set it will jump ;to uninstall your INT 68h service ;interrupt number where it will set your new ;service

;address of your new service ;calls the VxD call

;Hook_V86_Int_Chain

;interrupt number where it will set your new ;service

;address of your new service ;calls the VxD call

;Hook_PM_Fault

;restores registers ;jump back to Ring3

;the program will jump here if it is to ;uninstall your INT 68h service ;interrupt number where your new service has ;been set

;the address of your new service ;calls the VxD call

;UnHook_V86_Int_Chain

;the interrupt number where your new service ;has been set

;the address of your new service ;calls the VxD call

;UnHook_PM_Fault

;restores registers ;jump back to ring3

;The new service INT 68h

;saves registers ;nulls es:di

;VxD ID for SICE VxD

;GET DEVICE API ENTRY POINT function number ;calls the GET DEVICE API ENTRY POINT

;moves a value from es into ax

HookInt68:

pushfd ; pushad xor di, di mov es, di mov bx, 202h mov ax, 1684h

int 2Fh

;mov ax, es ;add ax, di

;The preceding two lines must always be left out because the ES is always set to a value other than ;0 when calling the INT 2Fh. This is the main difference between the DOS version and the 32-bit ;Windows version of this detection.

mov ax,di ;saves only the DI register

test ax,ax ;tests if ax=0

jz short none ;if yes, it will jump because SoftICE isn't

;active in memory

mov byte ptr mark, 1 ;sets a mark that SoftICE is active

none:

popad popfd ret

;restores registers

;jumps back from the service

ends

end Start



One advantage of this method of detection is that some detectors of anti-debugging tricks have problems with it. For example, if FrogsICE were running, Windows would "freeze." (This method only works in Windows 9x, though.)





Detecting SoftICE by Calling INT 2Fh and Its Function GET DEVICE API ENTRY POINT for VxD SIWVID



This detection is based on the same principle as the previous one, except that instead of searching for the VxD of the SICE driver, it searches for the VxD SIWVID whose VxD ID is 7A5Fh.



Here's how it looks as a DOS program running under Windows.



.MODEL TINY .386P

.DATA

message db 'Detecting SoftICE by calling Int 2fh and its function GET DEVICE API ENTRY

POINT for VXD SIWVID', 0dh, 0ah,24h
found db 'SoftICE active',24h

notfound db 'SoftICE not found',24h .CODE

.STARTUP



lea


dx, message




mov


ah,9


;a number of the function ' display the string

int


21h


;the INT 21h call displays the label

xor


di,di


;nulls di

mov


es,di


;nulls es

mov


ax, 1684h


;GET DEVICE API ENTRY POINT function number

mov


bx, 7A5Fh


;VxD ID for SIWVID VxD

int


2fh


;calls the GET DEVICE API ENTRY POINT

mov


ax, es


;puts the value from es into ax

add


ax, di


;adds di to ax

test


ax,ax


;tests if ax=0

jnz


short jump


;if not, it will jump since SoftICE is active ;in memory

lea


dx, notfound




jmp


shortfarther






farther:

lea dx, found

mov ah,9

int 21h

mov ax,4c00h

int 21h



;a number of the function ' display the string

;INT 21h call

;a number of the function ' ends the program

;INT 21h call

END



No great changes had to be made to make this method run as a 32-bit Windows application.



.386p

.MODEL FLAT,STDCALL

locals

jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;interrupt number 3 makes debugging more

;difficult

.DATA

messagel db "Detection by Int 2Fh_GET DEVICE API ENTRY POINT for VXD SIWVID",0 message2 db "Debugger notfound",0 message3 db "Debugger 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

mark db 0 ;a value of 1 will be set here if SoftICE is

;active in memory

.CODE

Start:



;Sets SEH in case of an error

mov [delay, ESP] push offset error

call SetUnhandledExceptionFilter mov [previous], eax

push edx

sidt [esp-2] pop edx

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

int Interrupt

pop es pop ds push ds push es mov ah,43h

int 68h

;reads IDT into the stack

;reads the vector of the required interrupt

;(INT 5h)

;reads the address of the old service of the ;required interrupt (INT 5h)



;sets the new service of the interrupt (INT 5h)

;saves registers ;for security

;jump into the Ring0 (newly defined INT 5h ;service)

;restores registers

;saves registers ;for security

;calls INT 68h and the 43h function (your new ;INT 68h service will be called)

stc

int Interrupt pop es pop ds

mov [edx-4],bx ;sets the original service ror ebx,16 mov [edx+2],bx

;Sets the previous SEH service ;jump into Ring0 (your INT 5h service) ;restores registers



;of the INT 5h interrupt

push dword ptr [previous]

call SetUnhandledExceptionFilter

cmp byte ptr mark, 1 ;tests to see if a mark was set indicating that

;SoftICE is active in memory

jz jump

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

;The new INT 5h service (runs in Ring0) InterruptHandler:

pushad

jc uninstall mov eax, 68h

mov esi, offset HookInt68

db 0cdh,20h dd 000010041H VMMCall Hook_V86_Int_Chain

mov eax, 68h

mov esi, OFFSET HookInt68 db 0cdh,20h dd 000010080H VMMCall Hook_PM_Fault

popad iretd ;saves registers

;tests our mark when it is set; jumps uninstall ;our service INT 68 h

;the interrupt number where your new service has ;been set.

;the address of your new service ;calls VxD ;Hook_V86_Int_Chain

;the interrupt number where your new service has ;been set.

;calls VxD ;Hook_PM_Fault

;restores registers

;jump back to ring3; jumps here when it has to ;uninstall our service INT 68 h

uninstall:

mov eax, 68h

mov esi, OFFSET HookInt68

db 0cdh,20h dd 000010118H VMMCall UnHook_V86_Int_Chain

mov eax, 68h

mov esi, OFFSET HookInt68 db 0cdh,20h dd 00001011AH VMMCall UnHook_PM_Fault

popad iretd ;the interrupt number where your new service has ;been set.

;calls VxD

;UnHook_V86_Int_Chain

;the interrupt number where your new service has ;been set.

;the address of your new service

;calls VxD

;UnHook_PM_Fault

;restores registers ;jumps back into ring3

;The new INT 68h service HookInt68:

pushfd pushad

xor di, di

mov es, di

mov bx, 7A5Fh

mov ax, 1684h

int 2Fh

mov ax, es

add ax, di

;saves registers ;nulls es:di

;VxD ID for SIWVID VxD

;number of the GET DEVICE API ENTRY POINT

;function

;calls the GET DEVICE API ENTRY POINT

;puts the value from es into ax ;adds di to ax

;The preceding two lines must always be left out because the ES is always set to a value different ;from 0 when calling the INT 2Fh. This is the major difference between this and the DOS version of ;this detection.

mov ax, di ;saves only the DI register

test ax, ax

jz short none

mov byte ptr mark, 1

;tests to see if ax=0

;if yes it ends because SoftICE isn't active ;in memory

;sets a mark that SoftICE is active in memory

none:

popad popfd ret

;restores registers

;jump back from the service

ends

end Start

As with the previous example, this detection works only in Windows 9x.



Using the CMPXCHG8B Instruction with the LOCK Prefix



This slightly different trick uses the LOCK CMPXCHG8B instruction to make SoftICE detect an error and stop. The correct usage is LOCK CMPXCHG8B [EAX].



The instruction CMPXCHG8B is used for 64-bit values. It compares a value in the EDX:EAX registers with the 64-bit value saved in the address determined by the pointer in the EAX register. If the values are the same, it saves the value from the ECX:EBX registers here, and sets the ZF flag in the flag register. If they do not match, it reads the 64-bit value from the address saved in the EAX register into the EDX:EAX registers, and nulls the ZF flag.



The register that determines the location of the 64-bit value may also be used incorrectly. It is a special instruction that will stop as an error if SoftICE is active, because it doesn't handle this instruction correctly (some Pentium processors had this problem too).



There are several possibilities for the instruction LOCK CMPXCHG8B:



Prefix Instruction



F0


0F


C7


C8


-


LOCK


CMPXCHG8B


EAX

F0


0F


C7


C9


-


LOCK


CMPXCHG8B


ECX

F0


0F


C7


CA


-


LOCK


CMPXCHG8B


EDX

F0


0F


C7


CB


-


LOCK


CMPXCHG8B


EBX

F0


0F


C7


CC


-


LOCK


CMPXCHG8B


ESP

F0


0F


C7


CD


-


LOCK


CMPXCHG8B


EBP

F0


0F


C7


CE


-


LOCK


CMPXCHG8B


ESI

F0


0F


C7


CF


-


LOCK


CMPXCHG8B


EDI

If a program running without SoftICE encounters this incorrect instruction, it will continue to run by virtue of its SEH service. However, if that same program is run together with SoftICE, it will stop at the incorrect function.



Most attackers will be content with only removing the instruction, and they will let the program run on. You can take advantage of this fact because the code that follows LOCK CMPXCHG8B EAX will only run if the abovementioned instruction was removed or jumped over, which would mean that someone had tried to work with the program code.



.386p

.MODEL FLAT,STDCALL

locals

jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC .data

message1 db "CMPXCHG8B instruction usage with the LOCK prefix",0

message3 db "Program doesn't run properly, it has been changed while running!",0 message2 db "SEH service was called (OK)",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

;LOCK CMPXCHG8B EAX

db 0F0h, 0F0h, 0C7h, 0C8h ;jumps to your SEH service, a label error.

;Normally the program should never get this far.

jmp jump

;Sets the previous SEH service

push dword ptr [previous] call SetUnhandledExceptionFilter

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 ;the program will jump here in case of an error

;in the LOCK CMPXCHG8B EAX instruction



This trick can be used in all Windows versions. Nevertheless, I don't recommend using it often because it might cause problems with some processors.





Detecting SoftICE with the VxDCall



A VxD is really nothing more than a DLL running at the processor's highest privilege level (ringO). Since VxD runs at ringO, there's essentially nothing they can't do. In Windows 9x, it is possible to call VxDCall, but only from ringO (not from ring3). (Though as I'll demonstrate later, this isn't completely true.)



In this example, we'll use the VxD calls from ringO without having to program a VxD driver. We'll call the VMMCall Get_DDB, which will find the Device Description Block. You must define the driver name or its VxD ID before the call.



Your goal is to find the two drivers, SICE and SIWVID. The VxD ID of the first one is 202h and the second one is 7A5Fh. If a VxD driver hasn't been installed, the ECX register value will be 0, meaning that SoftICE isn't active in memory. If ECX contains any other value, the VxD is active, which tells us that SoftICE is active in memory.



.386p

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;interrupt numbers 1 or 3 will make debugging ;more difficult

db "Detection by means of VxDCALL - VMMCall Get_DDB",0

db "Debugger not found",0 db "Debugger found",0

dd 0 ;the ESP register saves here

dd 0 ;the ESP register will save the address of the

;previous SEH service here

dd 202h ;the VxD ID for SICE

dd 7a5Fh ;the VxD ID for SIWVID

;the VxD name will not be needed

.CODE Start:



;Sets SEH if there is an error

mov [delayESP],esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

push edx

sidt [esp-2] pop edx

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

int Interrupt

pop es

pop ds

mov [edx-4],bx

ror ebx,16 mov [edx+2],bx push eax

;reads IDT into the stack

;reads the vector of the required interrupt

;(INT 5h)

;reads the address of the required interrupt's ;old service

;sets a new interrupt service ;saves registers for security

;jumps into ring0 (newly defined service INT 5h) ;restores registers

;sets the original service of the interrupt

;(INT 5h)



;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 the return value is 1. If so,

;SoftICE is active in memory
jz jump ;and the program jumps

continue:

call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1

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:

mov eax, Device_ID mov edi, Device_Name

;VMMCall Get_DDB db 0cdh, 20h dd 000010146H

test ecx, ecx

jnz Debugger_detect

mov eax, Device_ID2

mov edi, Device_Name

;VMMCall Get_DDB

db 0cdh, 20h

dd 000010146H

test ecx,ecx

jnz Debugger_detect

xor eax,eax

iretd Debugger_detect:

xor eax,eax inc al

iretd

;202h is for SICE VxD ID

;This will be set only if Device_ID is unknown,

;otherwise it will be 0 (as is the case here)

;calls the service

;this is actually INT 20h

;0001h is a type of call=VMM 0146h Get_DDB

;function is called

;ecx=DDB or 0 if VxD is not installed

;if SoftICE wasn't found by the first method,

;the program will try the next one

;7a5Fh for SIWVID VxD ID

;this will be set only if Device_ID is unknown,

;otherwise it is 0 (as is the case here)

;service call

;this is actually INT 20h

;0001h is a type of call=VMM 0146h Get_DDB

;function is called

;ecx=DDB or 0 if VxD is not installed

;nulls EAX to show that SoftICE isn't active

;in memory

;jump back to ring3



;sets value 1 into EAX to show that SoftICE is ;active in memory ;jump back to ring3

ends

end Start



This is a good method of detection, and one that's hard to detect. Its only disadvantage, as with all detection programs that use ringO, is that it works only in Windows 9x.

Finding an Active Debugger Through the DR7 Debug Register



You can also use the debug registers with x86 processors to determine whether a debugger is active. Debug register 7 (DR7) is the most important one for you in this trick. If there is no debugger in memory, DR7 will be default to 400h. If there is a debugger, it will have a different value.



The detection routine searches in the following way:



mov eax,dr7 ;reads a value from DR7

cmp eax, 400h ;tests to see if it is 400h value

jnz Debugger_detect ;if not, a debugger is active in memory



While it is possible to detect a debugger with only three instructions, Windows just cannot make it that simple. The problem is that you can work with the debug registers only in ring0. This changes the situation substantially, because the trick is then usable only with the VxD or Sys drivers, or by using it to switch into ring0 for Windows 9x.

.386p

.MODEL FLAT,STDCALL

locals

jumps

UNICODE=0

include w32. inc

Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;the interrupt numbers 1 or 3 will make /debugging more difficult

.DATA message1 message2 message3 delayESP previous

db "Detection by means of the DR7 debug register",0 db "Debugger not found",0 db "Debugger found",0

dd 0 ;the ESP register saves here

dd 0 ;the ESP register saves 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

push edx

sidt [esp-2] pop edx

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

int Interrupt

pop es pop ds

mov [edx-4],bx ror ebx,16 mov [edx+2],bx push eax

;reads IDT into the stack

;reads a vector of the required interrupt ;reads the address of the required interrupt

;sets a new interrupt service ;saves register for security

;jumps into ring0 (the newly defined service

;INT 5h)

;restores registers

;sets the original interrupt service (INT 5h) ;saves the return value

;Sets the previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax ;restores the return value

cmp eax, 400h ;tests to see if the return value is 400h, the

/correct value of the DR7 register. If it is, a /debugger isn't active.

jnz jump ;if the return value is other than 400h, a

/debugger 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:

mov esp, [delayESP] push offset continue ret

;Your new service INT 5h (runs in Ring0) ;sets a new SEH service in case of an error

InterruptHandler:

mov eax,dr7 iretd ;reads a value from DR7 ;jump back to ring3

ends

end Start



This is a rarely used trick that you can see, for example, in an older version of SafeDisc. It is one of the best detection methods because it is very hard to discover, particularly because it doesn't use any API calls or interrupts.



However, you can discover this trick if you set the GD flag in the DR7 register to switch on a breakpoint (int 01h) and then read the value of the DR6 flag BD register to see if the next instruction is read/write DRx. The need to switch into ring0 might give this method away, though. It works only in Windows 9x.

Detecting SoftICE by Calling VxDCall Through Kernel32!ORD_0001

This method of detection, which is very similar to the example in the "Detecting SoftICE by Detecting a Change in the INT 41h Service" section, calls the VWIN32_Int41Dispatch function 4fh. If SoftICE is active in the memory, the AX register will contain the value 0F386h. This trick can only be used in Windows 9x.



While VxDCall cannot be called directly from ring3, we're fortunate that the developers of Windows 9x "forgot" to remove a way to do it in the system. KERNEL32.DLL contains many exported functions called API calls, and these API calls are listed normally in the file's export table, so it's simple to import them into a program. Some functions are hidden from the programmer's eyes, and they are exported in another way (ordinal). Although it isn't possible to import them normally into a program and then call them, there is another way to call them.



This is a good detection method but it is somewhat tricky because it is necessary to find the export address of the Kernel32!ORD_0001. My friend EliCZ has found a way to use the hidden exports of the KERNEL32.DLL library without finding its address in the program code. The problem is that its direct importing into the machine code during program compilation isn't functional in the TASM compiler, for which the examples have been optimized.

No comments:

Post a Comment