What is it?
IsDebuggerPresent is a Win32 API found on Windows machines. This is one of the most common methods of preventing debugging, as well as the most bypassed method probably. This is a simple boolean returned function that checks the PEB stack of the process to determine if the process is currently being debugged or not. This function will only work on user-mode debuggers as kernel mode debuggers do not touch the PEB when attaching.
Syntax?
The syntax for IsDebuggerPresent is as follows:
Code:
BOOL WINAPI IsDebuggerPresent(void);
How Is It Used?
Well, there are many different things a developer can do when making their application use this function. Typically, the program will just close if this API returns true. But, being more creative, a developer can disable options in the program, cause wrong info to be displayed or something on that line, as well as many other things to avoid the debugger from obtaining useful info from the program. But in most cases the program will exit.
Bypassing?
As I said above there are many ways to bypass IsDebuggerPresent. One of the more commonly seen methods in programs such as olly is to simply hook the function and always return false. You can also rewrite the programs code thats calling this, such as completely remove the call, jump over it, or change the compare to always get the return you like. A simple C++ application to demonstrate with:
Code:
#define _WIN32_WINNT 0x0500
#include
#include
int main()
{
if( IsDebuggerPresent() == TRUE )
return 1;
_getch();
return 0;
}
#include
#include
int main()
{
if( IsDebuggerPresent() == TRUE )
return 1;
_getch();
return 0;
}
In OllyDbg we will find this here:
Code:
00401000 /$ FF15 00204000 CALL DWORD PTR DS:[<&KERNEL32.IsDebugger>; [IsDebuggerPresent
00401006 83F8 01 CMP EAX,1
00401009 |. 75 01 JNZ SHORT asdf.0040100C
0040100B |. C3 RET
0040100C |> FF15 9C204000 CALL DWORD PTR DS:[<&MSVCR80._getch>] ; [_getch
00401012 |. 33C0 XOR EAX,EAX
00401014 \. C3 RET
00401006 83F8 01 CMP EAX,1
00401009 |. 75 01 JNZ SHORT asdf.0040100C
0040100B |. C3 RET
0040100C |> FF15 9C204000 CALL DWORD PTR DS:[<&MSVCR80._getch>] ; [_getch
00401012 |. 33C0 XOR EAX,EAX
00401014 \. C3 RET
As you can see, the call is made and then compared to 1. If you set a breakpoint on the compare then run the program with no plugins hiding Olly from this API, EAX should contain 1, meaning the API returning true saying there is a debugger found. So if we find a debugger, eax is 1. Then we have:
CMP 1, 1
Which will return 0 meaning they are the same. (Most if not all functions that are comparing something will return 0 meaning successful or correct.) If EAX were to be 0, we'd be comparing 0 to 1 and CMP would return 1 instead meaning they are not the same and there was a difference.
Then the next line says:
JNZ SHORT asdf.0040100C
Which means, jump if not zero (the compare return value) to the given address 0040100C. So in theory you could just change the compare to compare to 0 instead to make it work while debugging, but if you do that the program wont run correctly if you are running it without a debugger. So a quick method of fixing this one is removing the compare and changing the jump to force it to bypass the ret so we would have this instead:
Code:
CALL DWORD PTR DS:[<&KERNEL32.IsDebuggerPresent>]
NOP
NOP
NOP
JMP SHORT asdf.0040100C
RET
CALL DWORD PTR DS:[<&MSVCR80._getch>]
XOR EAX, EAX
RET
NOP
NOP
NOP
JMP SHORT asdf.0040100C
RET
CALL DWORD PTR DS:[<&MSVCR80._getch>]
XOR EAX, EAX
RET
And then run the program after modifying it directly to the exe and it should run while being debugged.
Another method we could do is using OllyScript, a plugin for OllyDbg. The OllyScript plugin has a function already built into it to let you do this, so create a new file name it HideMe.txt and paste this:
Code:
;
; Hide From IsDebuggerPresent
; Resets PEB.IsBeingDebugged Flag
;
dbh
; Hide From IsDebuggerPresent
; Resets PEB.IsBeingDebugged Flag
;
dbh
Load the program, run the script and then run the progra All is well.
IsDebuggerPresent -- Another Bypass
Forgot that I was working on this post lol.. anyway, another bypass is to overwrite the _PEB.BeingDebugged Flag to bypsas IsDebuggerPresent. This is all IsDebuggerPresent uses when checking if the process is being debugged. To explain this a little more:
A quick bare minimum example project to call IsDebuggerPresent. Lets compile and look at the code in Olly.
The first call is a jmp to the IsDebuggerPresent API, lets follow it and see the code for this API.
The first part of this instruction is:
This obtains the _TEB pointer for the current process that is calling the function and moves it into EAX. TEB stands for Thread Environment Block. This block holds the current info of the main thread in the current process. You can obtain the TEB block information using a kernel command line debugger, or check out Intels website and it shows this as an example:
As you can see the PEB pointer is located in this block which we need. The PEB, or Process Environment Block, holds the information of the current process as a whole. Now, the PEB usually NEVER changes for any process, this is usually hard coded for the given OS, but you can obtain it easily using this method yourself as well. Anyway, the next instruction line grabs the PEB pointer and moves it into EAX:
EAX already had contained the TEB block pointer, which then +0x30 is the pointer to the PEB. So EAX now contains the PEB pointer.
The next line:
Gets the BeingDebugged flag from the PEB block and moves it into EAX. PEB+0x02 is the offset in the PEB block that holds the flag if it is being debugged or not. MOVZX moves the higher byte of the 16bit register into the 32bit register. Meaning the high byte of BYTE PTR DS:[eax+2] is moved into EAX.
Then EAX is returned via RET to the calling code. So we have learned that IsDebuggerPresent simply reads from the PEB block of the process which can be overwritten by us in usermode
There are a few different methods you can use to overwrite this. Easily enough, you can reverse the above ASM and write back to the flag, heres an example hook that creates a thread to constantly write the flag to ensure that it doesn't reset.
Forgot that I was working on this post lol.. anyway, another bypass is to overwrite the _PEB.BeingDebugged Flag to bypsas IsDebuggerPresent. This is all IsDebuggerPresent uses when checking if the process is being debugged. To explain this a little more:
Code:
#define _WIN32_WINNT 0x0400
#include
int main()
{
return IsDebuggerPresent();
}
#include
int main()
{
return IsDebuggerPresent();
}
A quick bare minimum example project to call IsDebuggerPresent. Lets compile and look at the code in Olly.
Code:
00401000 - FF25 00204000 JMP DWORD PTR DS:[<&KERNEL32.IsDebuggerP>; kernel32.IsDebuggerPresent
00401006 3B0D 00304000 CMP ECX,DWORD PTR DS:[403000]
0040100C 75 02 JNZ SHORT asdf.00401010
0040100E F3: PREFIX REP:
0040100F C3 RET
00401006 3B0D 00304000 CMP ECX,DWORD PTR DS:[403000]
0040100C 75 02 JNZ SHORT asdf.00401010
0040100E F3: PREFIX REP:
0040100F C3 RET
The first call is a jmp to the IsDebuggerPresent API, lets follow it and see the code for this API.
Code:
7C813093 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C813099 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
7C81309C 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
7C8130A0 C3 RET
7C813099 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
7C81309C 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
7C8130A0 C3 RET
The first part of this instruction is:
Code:
MOV EAX,DWORD PTR FS:[18]
This obtains the _TEB pointer for the current process that is calling the function and moves it into EAX. TEB stands for Thread Environment Block. This block holds the current info of the main thread in the current process. You can obtain the TEB block information using a kernel command line debugger, or check out Intels website and it shows this as an example:
Code:
kd> !teb
TEB at 7FFDE000
ExceptionList: 12ffb0
Stack Base: 130000
Stack Limit: 12d000
SubSystemTib: 0
FiberData: 1e00
ArbitraryUser: 0
Self: 7ffde000
EnvironmentPtr: 0
ClientId: 490.458
Real ClientId: 490.458
RpcHandle: 0
Tls Storage: 0
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks:0
HardErrorsMode: 0
TEB at 7FFDE000
ExceptionList: 12ffb0
Stack Base: 130000
Stack Limit: 12d000
SubSystemTib: 0
FiberData: 1e00
ArbitraryUser: 0
Self: 7ffde000
EnvironmentPtr: 0
ClientId: 490.458
Real ClientId: 490.458
RpcHandle: 0
Tls Storage: 0
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks:0
HardErrorsMode: 0
As you can see the PEB pointer is located in this block which we need. The PEB, or Process Environment Block, holds the information of the current process as a whole. Now, the PEB usually NEVER changes for any process, this is usually hard coded for the given OS, but you can obtain it easily using this method yourself as well. Anyway, the next instruction line grabs the PEB pointer and moves it into EAX:
Code:
7C813099 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
EAX already had contained the TEB block pointer, which then +0x30 is the pointer to the PEB. So EAX now contains the PEB pointer.
The next line:
Code:
7C81309C 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
Gets the BeingDebugged flag from the PEB block and moves it into EAX. PEB+0x02 is the offset in the PEB block that holds the flag if it is being debugged or not. MOVZX moves the higher byte of the 16bit register into the 32bit register. Meaning the high byte of BYTE PTR DS:[eax+2] is moved into EAX.
Then EAX is returned via RET to the calling code. So we have learned that IsDebuggerPresent simply reads from the PEB block of the process which can be overwritten by us in usermode
There are a few different methods you can use to overwrite this. Easily enough, you can reverse the above ASM and write back to the flag, heres an example hook that creates a thread to constantly write the flag to ensure that it doesn't reset.
Code:
#pragma comment( linker, "/ENTRY:DllMain" )
#include
BOOL bWantsExit = FALSE;
DWORD SetDebugFlag()
{
while( !bWantsExit )
{
_asm
{
//////////////////////////////////////////////////////////////
//
// IsDebuggerPresent Resetter
//
//////////////////////////////////////////////////////////////
mov eax, dword ptr fs:[0x18] // Obtain _TEB Pointer
mov eax, dword ptr ds:[eax+0x30] // Obtain _PEB Pointer
mov byte ptr ds:[eax+0x2], 0x0 // Overwrite BeingDebugged Flag
}
Sleep( 10 );
}
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)SetDebugFlag, 0, NULL, NULL );
return TRUE;
case DLL_PROCESS_DETACH:
bWantsExit = TRUE;
return TRUE;
}
return FALSE;
}
#include
BOOL bWantsExit = FALSE;
DWORD SetDebugFlag()
{
while( !bWantsExit )
{
_asm
{
//////////////////////////////////////////////////////////////
//
// IsDebuggerPresent Resetter
//
//////////////////////////////////////////////////////////////
mov eax, dword ptr fs:[0x18] // Obtain _TEB Pointer
mov eax, dword ptr ds:[eax+0x30] // Obtain _PEB Pointer
mov byte ptr ds:[eax+0x2], 0x0 // Overwrite BeingDebugged Flag
}
Sleep( 10 );
}
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)SetDebugFlag, 0, NULL, NULL );
return TRUE;
case DLL_PROCESS_DETACH:
bWantsExit = TRUE;
return TRUE;
}
return FALSE;
}
No hay comentarios:
Publicar un comentario