/-----------------------------\ | Xine - issue #4 - Phile 106 | \-----------------------------/ Win32 Anti-Debugging tricks a.k.a. protect your own ass Here i will list some tricks that could be used for the purpose of self- protect your viruses and/or your programs againist debuggers (of all levels, application and system). I hope you will like it. [ Win98/NT: Detecting Application level debuggers with IsDebuggerPresent ] This API is not present in Win95, so you will have to test for its presence, and works with application level debuggers only (such as TD32). And it works fine. Let's see what it's written about it in the Win32 API reference list. -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś The IsDebuggerPresent function indicates whether the calling process is running under the context of a debugger. This function is exported from KERNEL32.DLL. BOOL IsDebuggerPresent(VOID) - Parameters This function has no parameters. - Return Value ž If the current process is running in the context of a debugger, the return value is nonzero. ž If the current process is not running in the context of a debugger, the return value is zero. -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś So, an example for demonstrate this is very simple. Here it goes. ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .586p .model flat extrn GetProcAddress:PROC extrn GetModuleHandleA:PROC extrn MessageBoxA:PROC extrn ExitProcess:PROC .data szTitle db "IsDebuggerPresent Demonstration",0 msg1 db "Application Level Debugger Found",0 msg2 db "Application Level Debugger NOT Found",0 msg3 db "Error: Couldn't get IsDebuggerPresent.",10 db "We're probably under Win95",0 @IsDebuggerPresent db "IsDebuggerPresent",0 K32 db "KERNEL32",0 .code antidebug1: push offset K32 ; Obtain KERNEL32 base address call GetModuleHandleA or eax,eax ; Check for fails jz error push offset @IsDebuggerPresent ; Now search for the existence push eax ; of IsDebuggerPresent. If call GetProcAddress ; GetProcAddress returns an or eax,eax ; error, we assume we're in jz error ; Win95 call eax ; Call IsDebuggerPresent or eax,eax ; If it's not 0, we're being jnz debugger_found ; debugged debugger_not_found: push 0 ; Show "Debugger not found" push offset szTitle push offset msg2 push 0 call MessageBoxA jmp exit error: push 00001010h ; Show "Error! We're in Win95" push offset szTitle push offset msg3 push 0 call MessageBoxA jmp exit debugger_found: push 00001010h ; Show "Debugger found!" push offset szTitle push offset msg1 push 0 call MessageBoxA exit: push 00000000h ; Exit program call ExitProcess end antidebug1 ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Ain't it nice? Micro$oft did the job for us :) But, of course, don't expect this method to work with SoftICE, the g0d ;) [ Win32: Another way of know if we're under the context of a debugger ] If you take a look into the article "Win95 Structures and Secrets", that was written by Murkry/iKX, and published in the Xine-3, you'll realize that the- re is a very cool structure in the FS register. Take a look into the field FS:[20h]... It's 'DebugContext'. Just make the following: mov ecx,fs:[20h] jecxz not_being_debugger [...] <--- do whatever, we're being debugged :) So, if FS:[20h] is zero, we're not being debugged. Just enjoy this little and simple method for detect debuggers! Of course, this can't be applied to SoftICE... [ Win32: Stopping Application level debuggers with SEH ] I still don't know why, but the application level debuggers die simply if the program uses SEH. And also the code emulators, if we make faults, die too :) The SEH, as i published in my article in DDT#1 is used for many inte- resting purposes. Well, as i don' t want to tell you all SEH stuff again, i will cut'n'paste my old description :) --[DDT#1.2_6]--------------------------------------------------------------- Well, this is a very simple tutorial about the Structured Exception Handler. When i saw SEH implemented in a virus, i thought "Well, it does a lot. Must be very hard to implement". So i simple skipped its use. But, as my Destiny made General Protection Faults running under NT, as i read in 0BFF70000h, i realized that i had to do something. And SEH was the only way. Well, we can do it very complex to understand, or very easy. Of course, i prefer to do it more easy :) % Setting up the SEH frame % ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Firstly we save it for our own safety with a simple text line. push dword ptr fs:[0] And now it's time to make the thingy to point to our handler (for example, imagine that we used a call for call the setup of the SEH, and our handler is just after that call instructions: we can use the offset of ret for make it point there) push offset SEH_Handler mov fs:[0],esp Well, as easy as it gets. What about restore the original SEH? More easy. Simply do the opposite of the first instruction. pop dword ptr fs:[0] It's surprising that that very simple thing for implement if our Windoze vi- ruses could do for us. For me (as it was the use of SEH i was searching) the most important one is that i can help us to avoid all that goddamn blue screens when we run our Win95 virus under NT enviroments. That goddamn blue screen appears everytime we try to make comparisons over our hardcoded Win95 kernel under NT. % Example of SEH use % ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Well, you can compile this with: tasm32 /m3 /ml sehtest,,; tlink32 /Tpe /aa sehtest,sehtest,,import32.lib ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .386p .model flat ; Good good... 32 bit r0x0r extrn MessageBoxA:PROC ; Defined APIs extrn ExitProcess:PROC .data szTitle db "Structured Exception Handler example",0 szMessage db "Intercepted General Protection Fault!",0 .code start: call setupSEH ; The call pushes the offset ; past it in the stack rigth? ; So we will use that :) exceptionhandler: mov esp,[esp+8] ; Error gives us old ESP ; in [ESP+8] push 00000000h ; Parameters for MessageBoxA push offset szTitle push offset szMessage push 00000000h call MessageBoxA push 00000000h call ExitProcess ; Exit Application setupSEH: push dword ptr fs:[0] ; Push original SEH handler mov fs:[0],esp ; And put the new one (located ; after the first call) mov ebx,0BFF70000h ; Try to write in kernel (will mov eax,012345678h ; generate an exception) xchg eax,[ebx] end start ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ [...] --[DDT#1.2_6]--------------------------------------------------------------- Well, i hope you understood that. If not... Erhm, forget it :) Also, as the other methods presented before, this cannot be applied to SoftICE [ Win9X: Detect SoftICE (I) ] Well, i must greet here Super/29A, because he was the one that told me about this method. I broke this into two parts: in this one we will see how to do it from a Ring-0 virus. I won't put a whole example program because it would fill unnecessary lines, but you must know that this method must be executed in Ring-0, and the VxDCall must be restored because the call-back problem (do you remember?). Well, we are gonna use the Virtual Machine Manager (VMM) service Get_DDB, so the service will be 00010146h (VMM_Get_DDB). Let's see the information about this service on the SDK. -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś mov eax, Device_ID mov edi, Device_Name int 20h ; VMMCall Get_DDB dd 00010146h mov [DDB], ecx - Determines whether or not a VxD is installed for the specified device and returns a DDB for that device if it is installed. - Uses ECX, flags. - Returns a DDB for the specified device if the function succeeds; - otherwise, returns zero. ž Device_ID: The device identifier. This parameter can be zero for name- based devices. ž Device_Name: An eight-character device name that is padded with blank characters. This parameter is only required if Device_ID is zero. The device name is case-sensitive. -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś Well, you are wondering why all this shit. Very simple, the Device_ID field of SoftICE VxD is constant for all programs, as it's registered in Micro$oft so we have a weapon again the marvelous SoftICE. It's Device_ID is 202h al- ways. So we should use code like this: mov eax,00000202h VxDCall VMM_Get_DDB xchg eax,ecx jecxz NotSoftICE jmp DetectedSoftICE Where NotSoftICE should be the continuation of virus code, and the label DetectedSoftICE should handle the action to perform, as we know that our enemy is alive :) I don't suggest anything destructive because, for example, would hurt my computer, as i always have SoftICE active :) [ Win9X: Detect SoftICE (II) ] Well, here goes another method for detect the presence of my beloved SoftICE but based in the same concept of before: the 202h ;) Again i must greet to Super :) Well, in the Ralph Brown Interrupt list we can see a very cool service in the interrupt 2Fh (multiplex), the 1684h -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś Inp.: AX = 1684h BX = virtual device (VxD) ID (see #1921) ES:DI = 0000h:0000h Return:ES:DI -> VxD API entry point, or 0:0 if the VxD does not support an API Note: some Windows enhanced-mode virtual devices provide services that applications can access. For example, the Virtual Display Device (VDD) provides an API used in turn by WINOLDAP. -ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś-ś So, you put in BX a 202h, and execute this function. And you then say... "Hey Billy... How the fuck i can use interrupts?". My answer is... USE THE VxDCALL0!!! [ Win32: Detect SoftICE (III) ] The definitive and wonderful trick that you was waiting... The global solu- tion of finding SoftICE in both Win9x and WinNT enviroments! It's very easy, 100% API based, and without "dirty" tricks that go againist compatibility. And the answer isn't as hidden as you can think... the key is in an API that you've surely used before: CreateFile. Yes, that API... ain't it charming? Well, we have to try to open the following: + SoftICE for Win9x : "\\.\SICE" + SoftICE for WinNT : "\\.\NTICE" If the API returns us something different than -1 (INVALID_HANDLE_VALUE), SoftICE is active! Here follows a demonstration program: ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .586p .model flat extrn CreateFileA:PROC extrn CloseHandle:PROC extrn MessageBoxA:PROC extrn ExitProcess:PROC .data szTitle db "SoftICE detection",0 szMessage db "SoftICE for Win9x : " answ1 db "not found!",10 db "SoftICE for WinNT : " answ2 db "not found!",10 db "(c) 1999 Billy Belcebu/iKX",0 nfnd db "found! ",10 SICE9X db "\\.\SICE",0 SICENT db "\\.\NTICE",0 .code DetectSoftICE: push 00000000h ; Check for the presence of push 00000080h ; SoftICE for Win9x envirome- push 00000003h ; nts... push 00000000h push 00000001h push 0C0000000h push offset SICE9X call CreateFileA inc eax jz NoSICE9X dec eax push eax ; Close opened file call CloseHandle lea edi,answ1 ; SoftICE found! call PutFound NoSICE9X: push 00000000h ; And now try to open SoftICE push 00000080h ; for WinNT... push 00000003h push 00000000h push 00000001h push 0C0000000h push offset SICENT call CreateFileA inc eax jz NoSICENT dec eax push eax ; Close file handle call CloseHandle lea edi,answ2 ; SoftICE for WinNT found! call PutFound NoSICENT: push 00h ; Show a MessageBox with the push offset szTitle ; results push offset szMessage push 00h call MessageBoxA push 00h ; Terminate program call ExitProcess PutFound: mov ecx,0Bh ; Change "not found" by lea esi,nfnd ; "found"; address of where rep movsb ; to do the change is in EDI ret end DetectSoftICE ;ÄÄÄ[ CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This really works, believe me :) The same method can be applied to other "hostile" drivers, just research a bit on it. [ Final Words ] Well, some simple antidebugging tricks. I hope you can use them in your virus without problems. See ya! Breaking the law, Billy Belcebu/iKX.