--------------------------------------------------- Antidebugging for (m)asses - protecting the env. --------------------------------------------------- written by Piotr Bania [___ http://pb.specialised.info ___] ----------------- 0. DISCLAIMER ----------------- Author takes no responsibility for any actions with provided information or codes. The copyright for any material created by the author is reserved. Any duplication of codes or texts provided here in electronic or printed publications (including compiled code) is not permitted without the author's agreement. ----------------- I. INTRODUCTION ----------------- The number of computer hackers/crackers has reached a very high level recently. Currently it is very hard to develop product which will be secure against those type of people, to be const-stricto it is surely impossible. However, why not make their dirty work harder and give them some more, so lets take a look of few new antidebugging methods/techniques. On the other hand it can help malware/virus researchers understand and analyse latest bad stuff. Enjoy! NOTE: Following examples were created/researched/tested on windows xp box it is possible that they will not work on other windows systems. ----------------- II. EXAMPLES ----------------- ---------------------- [+] Example 01h ---------------------- Affects (works on): All so-called SEH debuggers (tested on Ollydbg and Windbg). Note: Following example requires administrator privileges for well known reasons. In fact it doesn't affects scale of its severity much, because administrator privileges are required to use some debugger features, so if one wants to use all debug stuff it needs to run as admin. Generally after researching few things inside windows kernel, I found that it is possible to open CSRSS.EXE (client server runtime process - system process), while application is being debugged. Have a look at this code (I believe it will make it hell easier to understand): ----// SNIP SNIP //--------------------------------------------------------- push ; pid push 0 ; Inheritable = FALSE push 0C3Ah ; access flags=CREATE_THREAD|VM_OPERATION|VM_READ|VM_WRITE ; \ |QUERY_INFORMATION|800 @callx OpenProcess test eax,eax jz @we_are_not_debugged @evil_me: ; <- execution flows here when debugger is attached int 3 @we_are_not_debugged: ; well no SEH debugger detected or not enough privileges ----// SNIP SNIP //--------------------------------------------------------- Like I said before when application is debugged it is able to open CSRSS.EXE and it this case OpenProcess will not fail (of course I guess I don't have to remind you about privileges). What's more, look at the rights which we have used to open target process, *yuck* - debugged application has full control of CSRSS.EXE! The last thing I have figured out that a special native API exists exported by ntdll used to grab pid of CSRSS named as CsrGetProcessId. (microsoft windows xp sp1 output) E:\asm>find ntdll.dll CsrGetProcessId ------------------------------------------------------------ Little GetProcAddress Utility coded by Piotr Bania ------------------------------------------------------------ * ntdll.dll base addr at: 0x77F50000 + CsrGetProcessId has address: 0x77F5EF76 ------------------------------------------------------------ If you don't want to execute CsrGetProcessId you can emulate it (address hard coded for microsoft windows xp sp1), like this example shows: ----// SNIP SNIP //--------------------------------------------------------- mov eax,077FC46A4h ; ntdll variable where CSRSS pid is stored xchg eax,[eax] ; EAX=now CSRSS pid ----// SNIP SNIP //--------------------------------------------------------- Following example (when debugged) causes BSOD because its creating a thread inside of client server runtime process at not existing location: ----// SNIP SNIP //--------------------------------------------------------- mov eax,077F5EF76h call eax ; execute CsrGetProcessId, returns CSRSS pid push eax push 0 push 0C3Ah @callx OpenProcess ; open CSRSS process test eax,eax jz exit ; opening failed call a dd 0 a: push 0 push 0 push 0 push 1234567h ; yep, it doesn't exist... push 0 push 0 push eax @callx CreateRemoteThread ; creates a thread inside of process, *BSOD* exit: push 0 @callx ExitProcess ----// SNIP SNIP //--------------------------------------------------------- Well I hope you enjoyed this one, so lets think about another example. ---------------------- [+] Example 02h ---------------------- The XP series provides magic API called CheckRemoteDebuggerPresent (well generally it provides a lot of new debug features), and like the name suggests it is used to check if debugger is present. E:\asm>find kernel32.dll CheckRemoteDebuggerPresent ------------------------------------------------------------ Little GetProcAddress Utility coded by Piotr Bania ------------------------------------------------------------ * kernel32.dll base addr at: 0x77E60000 + CheckRemoteDebuggerPresent has address: 0x77EB582B ------------------------------------------------------------ Few words from MSDN about this function: (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/ \ debugactiveprocessstop.asp) ---- CheckRemoteDebuggerPresent The CheckRemoteDebuggerPresent function determines whether the specified process is being debugged. BOOL CheckRemoteDebuggerPresent( HANDLE hProcess, PBOOL pbDebuggerPresent ); Parameters hProcess - [in] Handle to the process. pbDebuggerPresent - [in, out] Pointer to a variable that the function sets to - TRUE if the specified process is being debugged, or FALSE otherwise. Return Values If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. ---- And here is a little code for this example: ----// SNIP SNIP //--------------------------------------------------------- push offset is_present ; our variable push -1 ; process handle mov eax,077EB582Bh call eax ; execute CheckRemoteDebuggerPresent mov eax,dword ptr [is_present] test eax,eax jz @we_are_not_debugged ; no debugger found @we_are_debugged: ; execution flows here when debugger is attached int 3 is_present dd 0 ----// SNIP SNIP //--------------------------------------------------------- Or if you prefer emulated way (by using NtQueryInformationProcess): E:\asm>find ntdll.dll NtQueryInformationProcess ------------------------------------------------------------ Little GetProcAddress Utility coded by Piotr Bania ------------------------------------------------------------ * ntdll.dll base addr at: 0x77F50000 + NtQueryInformationProcess has address: 0x77F5BDD8 ------------------------------------------------------------ And here comes a little emulation example: ----// SNIP SNIP //--------------------------------------------------------- lea eax,our_process_handle push eax mov ebx,esp push 0 push 4 push ebx push 7 push dword ptr [eax] mov eax,077F5BDD8h call eax ; execute NtQueryInformationProcess pop ecx test eax,eax jl exit cmp ecx,0 jge @we_are_not_debugged int 3 ; yes we are debugged! @we_are_not_debugged: ; no debugger detected exit: push 0 @callx ExitProcess our_process_handle dd -1 ----// SNIP SNIP //--------------------------------------------------------- Now lets turn the page to another 3rd example. ---------------------- [+] Example 03h ---------------------- Many times Softice/D* users terminates debugged program by using "r eip ExitProcess" or by assembling direct jump/call to ExitProcess api in this example I will demonstrate how to detect debugger when such action/actions occurs. Well this thing can be done in many ways like patching ExitProcess with jmp to our procedure and so on, however it can be defeated easily and it is not so stealthy. I'm going to show you an example for this technique. Protecting region of ExitProcess by VirtualProtect (for known reasons dirty and not perfect), what is even more funny it causes Break-on-access exception in Olly in current version there is no option to bypass it :)) (of course I believe you know how to rewrite it for catching olly) Schema how this example works: I. STAGE - Setup SEH frame which will catch PAGE_GUARD exception II. STAGE - Change access protection of >ExitProcess to PAGE_GUARD III. STAGE - If debugger action will be found we will end in SEH frame with marker variable set to zero, otherwise it will be set to 1 (good flag). IV. STAGE - display message box NOTES: After STATUS_PAGE_GUARD exception the PAGE_GUARD access protection of ExitProcess is turned off. Better thing then variable marker? Yes you can try random keys stored in registers and so on then you can compare it in SEH frame (context structure) ----// SNIP SNIP //--------------------------------------------------------- mov ebx,077E79863h ; ExitProcess addr push offset seh_handler ;setup SEH frame push dword ptr fs:[0] mov dword ptr fs:[0],esp push offset old_protect push PAGE_EXECUTE_READ OR PAGE_GUARD push 1 push ebx @callx VirtualProtect ; give it PAGE_GUARD protection push 0 push offset m1 push offset m1 push 0 @callx MessageBoxA ; attach debugger and give "r eip ExitProcess" ; of course it must be done after protecting ; ExitProcess exit: mov dword ptr [marker],1 ; marker set to 1 push 0 @callx ExitProcess seh_handler: pop dword ptr fs:[0] ; remove SEH frame pop eax cmp byte ptr [marker],1 ; is this our call? je exit push 0 push offset m2 push offset m2 push 0 @callx MessageBoxA ; we are debugged... jmp exit m2 db "Ups im being debugged :)",0 m1 db "Attach debugger now and change eip to ExitProcess!",0 marker db 0 old_protect dd 0 ----// SNIP SNIP //--------------------------------------------------------- --------------------- III. OUTRO(DUCTION) --------------------- This is seems to be the end of this short article, I have plenty more ideas but not enough time to write them all here. Anyway I hope you enjoyed the stuff I have provided here, if you have any questions drop me a mail.