domingo, 19 de octubre de 2008

CheckRemoteDebuggerPresent Bypass by Wiccaan

CheckRemoteDebuggerPresent

What is it?
CheckRemoteDebuggerPresent is a Win32 API found on Windows machines. This API requires you to be on a machine that is either Windows XP Home or above. This API will not work on machines lower then this. Just like IsDebuggerPresent, this is another commonly used method to check if a process is being debugged. However, unlike IsDebuggerPresent, CheckRemoteDebuggerPresent determines if a process is being debugged based on the debug port of the process.

A debug port is just like a port for the internet. It allows a debugger to connect to it and be attached while debugging. When a debug port is filled, no other debugger can connect. However, a application can have more then one debug port setup. This allows for remote debugging and such. You can have a debugger attach to a computer on a specified port and it will connect to the given application with that port open at the current moment.

Also, this uses an offset in the _EPROCESS struct of the current process. This memory space is handled by the kernel therefore cannot be simply overwritten like IsDebuggerPresent's flag was. I personally don't know how to overwrite the debugport, I know it is possible in usermode, as two things I have can do it. I cannot find any example though myself :(


Syntax?
The syntax for CheckRemoteDebuggerPresent is as follows:
Code:
BOOL WINAPI CheckRemoteDebuggerPresent(
__in HANDLE hProcess,
__inout PBOOL pbDebuggerPresent
);



How to use it?
A developer would make a call on their current process to determine if a remote debugger has connected to the application to debug it. A debug port does not have to be established for a debugger to attach to the process. When a debugger attaches to a process, the debug port is overwritten with -1 meaning it is being debugged. Along with this the BeingDebugged flag is set in the _PEB struct. The first parameter to this API is a handle to the process you wish to determine if it is being debugged or not. Typically you will either see -1 or GetCurrentProcess() here.

The second parameter is a pointer to a BOOL variable. This variable is overwritten by the API with a TRUE/FALSE value if the process is being debugged or not. The return value of the API is also TRUE if succeeded and FALSE if it fails.

A quick example app calling this API to determine if it is being debugged or not would be:

Code:
#define _WIN32_WINNT 0x0501
#include
#include
int main()
{
BOOL bDebugged = FALSE;
for( ; ; )
{
CheckRemoteDebuggerPresent( GetCurrentProcess(), &bDebugged );
if( bDebugged )
std::cout << "Being debugged!" << std::endl;
else
std::cout << "Not being debugged!" << std::endl;
Sleep( 1000 );
}
return 0;
}



A little more detail.
Unlike IsDebuggerPresent, this API does not directly call things from the data segments of the process. Instead it makes a call to an Nt function. The function called is NtQueryInformationProcess. This API tells the system to obtain a piece of info about the process from the kernel. To see how this works lets take a look at the full code:

Code:
7C859B1E > 8BFF MOV EDI,EDI
7C859B20 55 PUSH EBP
7C859B21 8BEC MOV EBP,ESP
7C859B23 837D 08 00 CMP DWORD PTR SS:[EBP+8],0
7C859B27 56 PUSH ESI
7C859B28 74 35 JE SHORT kernel32.7C859B5F
7C859B2A 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
7C859B2D 85F6 TEST ESI,ESI
7C859B2F 74 2E JE SHORT kernel32.7C859B5F
7C859B31 6A 00 PUSH 0
7C859B33 6A 04 PUSH 4
7C859B35 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8]
7C859B38 50 PUSH EAX
7C859B39 6A 07 PUSH 7
7C859B3B FF75 08 PUSH DWORD PTR SS:[EBP+8]
7C859B3E FF15 AC10807C CALL DWORD PTR DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
7C859B44 85C0 TEST EAX,EAX
7C859B46 7D 08 JGE SHORT kernel32.7C859B50
7C859B48 50 PUSH EAX
7C859B49 E8 1DF8FAFF CALL kernel32.7C80936B
7C859B4E EB 16 JMP SHORT kernel32.7C859B66
7C859B50 33C0 XOR EAX,EAX
7C859B52 3945 08 CMP DWORD PTR SS:[EBP+8],EAX
7C859B55 0F95C0 SETNE AL
7C859B58 8906 MOV DWORD PTR DS:[ESI],EAX
7C859B5A 33C0 XOR EAX,EAX
7C859B5C 40 INC EAX
7C859B5D EB 09 JMP SHORT kernel32.7C859B68
7C859B5F 6A 57 PUSH 57
7C859B61 E8 4AF7FAFF CALL kernel32.7C8092B0
7C859B66 33C0 XOR EAX,EAX
7C859B68 5E POP ESI
7C859B69 5D POP EBP
7C859B6A C2 0800 RET 8


Ok so this might look a bit confusing but its not that bad. Our call to NtQueryInformationProcess is:

Code:
7C859B31 6A 00 PUSH 0
7C859B33 6A 04 PUSH 4
7C859B35 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8]
7C859B38 50 PUSH EAX
7C859B39 6A 07 PUSH 7
7C859B3B FF75 08 PUSH DWORD PTR SS:[EBP+8]
7C859B3E FF15 AC10807C CALL DWORD PTR DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess


This function, ZwQueryInformationProcess is as follows:
Code:
NTSTATUS WINAPI ZwQueryInformationProcess(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);


The push 7 is the ProcessInformationClass which is ProcessDebugPort, we are requesting the debugport value from this call. The debugport is a handle, which converts to 4 bytes, hence the push 4 as well. The last push is the return length which is NULL since it doesn't really matter if we have that or not. Mostly used for error checking.

After the call, we check if the function was successful by testing eax, the return value. This will only not jump if the value is less then 0. NT_SUCCESS, or the successful return of an NT function is Value >= 0. So anything 0 and above is good. (In theory that is, it can still fail even if it works.)

So if the function worked, we jump and goto:
Code:
7C859B50 33C0 XOR EAX,EAX
7C859B52 3945 08 CMP DWORD PTR SS:[EBP+8],EAX
7C859B55 0F95C0 SETNE AL
7C859B58 8906 MOV DWORD PTR DS:[ESI],EAX
7C859B5A 33C0 XOR EAX,EAX
7C859B5C 40 INC EAX


Here we xor eax out against itself. This makes EAX 0 then is compared to our return buffer value. If the debug port is set, this value is -1 or FFFFFFFF in hex. So if the port is set we would be doing:

cmp FFFFFFFF, 0

The next line says, set negitive if true. Which sets the low word of EAX to 1 making eax equal 00000001

After this we store EAX back into the buffer for pBool if it is being debugged or not, xor out EAX, pop our stored registers and return.


Bypassing Method :: Hooking
At the moment this is the only fessible method that I know of as I don't know how to write to kernel memory in usermode. There are ways, but I have yet to figure it out myself and there is very little documentation for the Nt functions and stuff like this.

So we will hook the API instead :) In my case, I am using Detours since well.. yea it works and its easy.

You will need Detours 1.5 for this since the new versions have removed some used functions.

We will create a DLL with the following code:


Code:
#define _WIN32_WINNT 0x0501
#include
#include "Detours/detours.h"
#pragma comment(lib, "Detours/detours.lib")

DETOUR_TRAMPOLINE( BOOL WINAPI Real_CheckRemoteDebuggerPresent(HANDLE,PBOOL), CheckRemoteDebuggerPresent );
BOOL Mine_CheckRemoteDebuggerPresent(HANDLE hHandle, PBOOL pBool)
{
_asm mov dword ptr ss:[pBool], 0x0
return TRUE;
}

DWORD DllSetup()
{
DetourFunctionWithTrampoline( (PBYTE)Real_CheckRemoteDebuggerPresent, (PBYTE)Mine_CheckRemoteDebuggerPresent );
return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved )
{
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls( hModule );
CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)DllSetup, NULL, NULL, NULL );
return TRUE;
case DLL_PROCESS_DETACH:
DetourRemove( (PBYTE)Real_CheckRemoteDebuggerPresent, (PBYTE)Mine_CheckRemoteDebuggerPresent );
return TRUE;
}
return FALSE;
}


To explain the code some, we first create a thread to handle the hooking. Why? Well if you inject into a process chances are you are going to need to set it to SUSPEND mode to pause it while injecting. If you do not use a new thread, your injection will fail as the DLL will attempt to be handled in the processes main thread. This will just not work at all since the thread is suspended, nothing will happen.

Instead, you create a new thread and handle the hooking inside that.

Inside our thread, we simply tell Detours to create a callback to CheckRemoteDebuggerPresent and have it trampoline from my function to the original. Meaning we have access to both the hooked and normal functions.

Now inside our callback function which is called EVERYTIME CheckRemoteDebuggerPresent is called, we are going to completely ignore the original function. We do not need to call it at all. Instead, we want to simply overwrite the PBOOL param and then return TRUE.

So we use: _asm mov dword ptr ss:[pBool], 0x0

This says move 0 into the pointer of pBool which sets pBool to FALSE. Then the function returns TRUE as if it was really called.

No hay comentarios: