Flag: Tornado!
Hurricane!
|
|
Topic created on: August 24, 2006 00:01 CDT by ryanlrussell .
I'm trying to stalk an app that has a silly antidebugger check (IsDebuggerPresent). I know how to deal with this in IDA Pro or Ollydbg. However, I can't start debugging under those, and then attach with PaiMei. I believe you only get one debugger. And I don't believe you can detach a debugger sucessfully before Win2K3. Even if you could, I assume it would just put the debugging flag back.
Ideally, I'd like to avoid patching the binary. It would be easy enough in this case, but less so in others.
So, it would appear that pydbg will be the only debugger you can use when stalking. Question is have you planned for any kind of scripted startup when attaching to or launching a process with PaiMei? (Such as, the obvious clearing the debug flag in the PEB.) And/or thought about adding the ability to break in with some debugger UI while stalking?
There are no plans for using any other debugger other then PyDbg for stalking.
There is an unfinished routine in the pydbg.py class, hide_debugger(), which is supposed to accomplish the very task you need.
Defeating IsDebuggerPresent() is a joke, it's a simple matter of updating a flag. If you complete that portion of the routine, please submit a patch. Hopefully, as the need arises, other people can add more sophisticated anti-anti-debugging logic to that routine.
|
write a small wrapper for writeprocessmemory in so you can patch fx in memory or overwrite the peb flag at runtime
|
Ryan: I needed the feature, like 5 minutes ago, so I thought I'd share. I haven't debugged this, but it appears to be working. Replace the current shell function with the following:
def hide_debugger (self):
'''
Hide the presence of the debugger. This routine requires an active context and therefore can not be called
immediately after a load() for example. Call it from the first chance breakpoint handler. This routine hides
the debugger in the following ways:
- Modifies the PEB flag that IsDebuggerPresent() checks for.
@raise pdx: An exception is raised if we are unable to hide the debugger for various reasons.
'''
selector_entry = LDT_ENTRY()
# a current thread context is required.
if not self.context:
raise pdx("hide_debugger(): a thread context is required. Call me from a breakpoint handler.")
if not kernel32.GetThreadSelectorEntry(self.h_thread, self.context.SegFs, byref(selector_entry)):
self.win32_error("GetThreadSelectorEntry()")
fs_base = selector_entry.BaseLow
fs_base += (selector_entry.HighWord.Bits.BaseMid << 16) + (selector_entry.HighWord.Bits.BaseHi << 24)
# http://openrce.org/reference_library/files/reference/Windows Memory Layout, User-Kernel Address Spaces.pdf
# find the peb.
peb = self.read_process_memory(fs_base + 0x30, 4)
peb = self.flip_endian_dword(peb)
# zero out the flag. (3rd byte)
self.write_process_memory(peb+2, "\x00", 1)
return self.ret_self()
|
> pedram: Ryan: I needed the feature, like 5 minutes ago, so I thought I\'d share. I haven\'t debugged this, but it appears to be working. Replace the current shell function with the following:
Cool, thanks. So far, my attempts at learning Python in a couple of hours hadn't gotten me there yet. I think you had the minimum functionality there already, with just a typo. Though it looks like you've added some useful error checking.
But where I was stuck was trying to figure out the right place to add a hide_debugger call in the rest of the Paimei code.
If you know where, I'd be happy to test it out. I've got a DebuggerPresent.exe sitting right here that I compiled up...
|
> ryanlrussell: > If you know where, I'd be happy to test it out. I've got a DebuggerPresent.exe sitting right here that I compiled up...
Register a breakpoint handler and hide the debugger at the first breakpoint:
#!c:\python\python.exe
from pydbg import *
from pydbg.defines import *
def handler_bp (dbg):
if dbg.first_breakpoint:
dbg.hide_debugger()
return DBG_CONTINUE
dbg = pydbg()
dbg.set_callback(EXCEPTION_BREAKPOINT, handler_bp)
dbg.load("IsDebuggerPresent.exe")
dbg.run()
|
Works perfectly, both your hide_debugger update, and your sample code. Verified both the positive and negative case by REMing out the hide_debugger call. Thanks again!
|
I also used the new code in QTFairUse6 v2. Works perfectly :)
|
Works perfectly!
I have a stupid question however. I have a program that uses SEH for anti-debugging.
What I don't understand is that the exceptions generated by the program don't seem to be caught by PyDbg. Moreover, the custom handlers of the program are not executed either, resulting in the debugger being detected anyway.
Any idea ?
|
So, a few more random things related to this topic. Does anyone (and I hate to keep bugging Pedram) know where one would insert the hide_debugger call in Paimei? I thought this was going to do it:
####################################################################################################################
def handler_breakpoint (self, dbg):
'''
The breakpoint handler is of course responsible for logging the code coverage.
'''
dbg.hide_debugger()
if dbg.get_attr("first_breakpoint"):
return DBG_CONTINUE
if self.print_bps:
self.log("debugger hit %08x cc #%d" % (dbg.exception_address, self.cc.num))
is_function = 0
for module in self.pida_modules.values():
if module.nodes.has_key(dbg.context.Eip):
is_function = 1
break
self.cc.add(dbg, is_function)
return DBG_CONTINUE
in process_stalker.py
Doesn't seem to work, though. It looks like there are 4 handlers, but this one is the one that matches the example. My excuse for being lame is that I'm trying to learn Python and Pedram's framework at the same time. I'm about to trying throwing in some print debugging, but I thought I'd ask here first.
Since it looks like at least a couple of us are looking at iTunes, a quick brain dump. If one doesn't have Paimei set up to do the silly IsDebuggerPresent bypass yet, then you have to patch the executable. Version 6.0.4.2. Right below this call:
0066CF90 call ds:IsDebuggerPresent ;
Is a condition jump, which you can change to an absolute jump. But once you've patched the file at all, you also have to fix this:
004FB8AA call check_MD5
A couple of lines below that, you have another conditional jump which you can change to an absolute jump to make it always pass the MD5 check.
As for Roady's question, which is basically "what about more advanced antidebugger tricks?". I certainly don't know why pydbg isn't picking up the exception you expected. Do you have your own wrapper code, and did you register a callback for that exception type? For example, I see this in the stalk function in process_stalker:
self.pydbg.set_callback(EXCEPTION_BREAKPOINT, self.handler_breakpoint)
self.pydbg.set_callback(LOAD_DLL_DEBUG_EVENT, self.handler_load_dll)
self.pydbg.set_callback(EXCEPTION_ACCESS_VIOLATION, self.handler_access_violation)
self.pydbg.set_callback(USER_CALLBACK_DEBUG_EVENT, self.handler_user_callback)
But there are also the general cases, where this won't help either. This kind of thing is one of the reasons I was asking about the possibility of getting a live debugger GUI working with pydbg.
So, you can always patch an executable, or unpack it ahead of time. I suppose the latter doesn't help if you're trying to get pydbg to do the unpacking for you.
Since stalker is already doing branch tracing, it might be interesting if one could tell it to take the other branch in key places. Maybe modify the instruction, or temporarily flipping the zero flag or something. I guess right now, IDA is the GUI for prepping a binary, so you'd want a way in IDA to flag a branch for bypass.
Might be cool if I could load a set of patches once the process has been loaded. If I could do that, I wouldn't have had to patch out the MD5 check, which goes after the file on disk. The hide_debugger call is a patch of sorts, so I don't see why not. The hardest bit is the storage format, and the interface for building the list of patches.
Anyway, back to trying to make myself competent enough to do some of these on my own, rather than just asking others to do it for me.
|
Ah, there! See, I was assuming that process_stalker.py would be the in in the utils directory in the paimei install. But no, python or something else squirreled away a copy in C:\Python24\lib\site-packages\utils. In case I messed it up by editing it or something, I guess...
WTF?
Well, that only took all day, and the use of filemon to figure out...
So my quick and dirty change to handler_breakpoint earlier in the thread does actually function.
|
Too bad I only saw your post now, I had the same problem earlier.
|
The anti-debug trick is using a int3. I would have thought that int3 would be caught by "exception_breakpoint".
|
> ryanlrussell: Ah, there! See, I was assuming that process_stalker.py would be the in in the utils directory in the paimei install. But no, python or something else squirreled away a copy in C:\\Python24\\lib\\site-packages\\utils. In case I messed it up by editing it or something, I guess...
That's the result of answering "y" to "Install PaiMei framework libraries to Python site packages?" in __install_requirements.py.
|
> Roady: The anti-debug trick is using a int3. I would have thought that int3 would be caught by \"exception_breakpoint\".
Do you want to post the snippet of antidebugger disassembly you're trying to bypass?
|
> igorsk:
> That\'s the result of answering \"y\" to \"Install PaiMei framework libraries to Python site packages?\" in __install_requirements.py.
I think you're probably right. Well, that will teach me. Even so, I would have thought it would pick up the local edited copy, since that what the includes pointed to.
|
> ryanlrussell: > Roady: The anti-debug trick is using a int3. I would have thought that int3 would be caught by \\\"exception_breakpoint\\\".
>
> Do you want to post the snippet of antidebugger disassembly you\'re trying to bypass?
I will try to make a functionnally equivalent program. But this week I will be really busy.
|
This code detects PyDBG:
#include <windows.h>
#include <stdio.h>
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
{
lpTopLevelExceptionFilter->ContextRecord->Eax++;
lpTopLevelExceptionFilter->ContextRecord->Eip++;
return -1;
}
int main(int argc, char **argv)
{
int result = 0;
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
__asm
{
push eax
xor eax,eax
int 3
mov result,eax
pop eax }
if (result == 0)
{
printf("debugger detected!\n");
}
else
{
printf("debugger not detected!\n");
}
}
Here is my script:
from pydbg import *
from pydbg.defines import *
def handler_bp (pydbg):
if pydbg.first_breakpoint:
dbg.hide_debugger()
print "Exception caught from %d @%08x => debugger hidden" % (pydbg.dbg.dwThreadId, pydbg.exception_address)
return DBG_CONTINUE
print "Exception caught from %d @%08x" % (pydbg.dbg.dwThreadId, pydbg.exception_address)
return DBG_CONTINUE
dbg = pydbg()
dbg.set_callback(EXCEPTION_BREAKPOINT, handler_bp)
print "\nLoading program\n"
dbg.load("int3test.exe")
dbg.run()
## my indentation got messed up when pasting :-/
I am not really good at this exception stuff, any idea?
|
> Roady: This code detects PyDBG:
I can't get your example C program to detect me.
D:\paimei>c:\Python24\python.exe antidebug_int3.py
Exception caught from 3132 @7c901230 => debugger hidden
debugger not detected!
Same when running in the debugger in IDA or MSVC. I'm not sure your example is correct.
|
I don't know which version you tried, but the first one I posted was full of bugs (and strangely it still worked on my system). I think I fixed it now.
I also think that it is better to compile in "release" mode.
|
Roady: PyDbg does not call the registered handler if the triggered breakpoint is not one that was explicitly set. On that note, there is a bug in the current release regarding the handling of exceptions triggered by the debuggee. This has been fixed for a few revisions now in subversion along with some other bugs.
I'll do a new release this week sometime.
-pedram
|
> pedram: PyDbg does not call the registered handler if the triggered breakpoint is not one that was explicitly set.
And it does not pass the exception to the application either ?
|
Note: Registration is required to post to the forums.
|
|
|
There are 31,313 total registered users.
|
|