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