1

I am learning how to create a simple debugger in python, using ctypes on Windows 7. My problem is that the code given below seems to be returning an invalid address(this is my guess as to the problem), but I can't figure out why. I thought perhaps it is because the handle being returned is either no longer valid by the time my code uses it, or maybe I am being returned a handle that has nothing to do with my script due my simply providing "msvcrt.dll" as the module. I've done a lot of tinkering around, but haven't found a solution as yet.

EDIT: The address is returning as False. I am getting the following error from the snippet below:

ERROR 126 : The specified module could not be found.

def func_resolve(self, dll, function):
    error = None

    handle = kernel32.GetModuleHandleA(dll)
    if handle == False:
        print "Handle is FALSE"
        error = GetLastError()

    address = kernel32.GetProcAddress(handle, function)
    if address == False:
        print "Address is FALSE"
        error = GetLastError()

    if error is not None:
        print "ERROR %d : %s" % (error, FormatError(error))
        return False

    kernel32.CloseHandle(handle)

    return address

The above code is called from this short test script:

import my_debugger

debugger = my_debugger.debugger()

pid = raw_input("Input PID of process to attach to: ")
debugger.attach(int(pid))

printf_address = debugger.func_resolve("msvcrt.dll", "printf")
print "[*] Address of printf: 0x%08x" % printf_address
debugger.bp_set(printf_address)

debugger.run()
debugger.detach()

This is the script whose process I am attaching to:

from ctypes import *
import time

msvcrt = cdll.msvcrt
counter = 0

while True:
    msvcrt.printf("Loop iteration %d /n", counter)
    time.sleep(2)
    counter += 1

So I run the above script, find the PID in task manager, then run my test script and give it the PID. I get the following output each time. The user-defined breakpoint at printf is never reached, and it's address is always given as 0x00000000, which doesn't seem right.

Input PID of process to attach to: 8124
Process PID: 8124
[*] Address of printf: 0x00000000       
[*] Setting breakpoint at 0x00000000    
[*] Waiting for debug events...
[*] Event Code: 3       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 6       Thread ID: 7664
[*] Event Code: 2       Thread ID: 7124
[*] Event Code: 1       Thread ID: 7124
[*] Exception address: 0x76e00590
[*] Hit the first breakpoint
[*] Event Code: 4       Thread ID: 7124

Can anyone see what I am doing wrong? I can provide all the code if need be..

Totem
  • 7,189
  • 5
  • 39
  • 66
  • It is likely that you're trying to load the wrong DLL, as per the linked question. But you won't know for sure without adding in error handling. (There is some code in one of the answers to the linked question showing error handling.) – Harry Johnston Feb 23 '15 at 19:44
  • @MarkRansom: note that the solution in the linked question is to connect to `msvcrt.dll` (rather than `msvcr100.dll`) which this code already does. So it isn't an exact duplicate, though I suspect that the conceptual problem ("wrong DLL") may be the same. – Harry Johnston Feb 23 '15 at 19:45
  • @HarryJohnston if the return value from `GetModuleHandleA` had been checked, I wouldn't have jumped to conclusions. I'm still amenable to reopening if contradictory facts are presented. – Mark Ransom Feb 23 '15 at 19:54
  • I am attempting to incorporate GetLastError into the code. I just haven't done it before. But it should provide some insight into what's happening... I hope! – Totem Feb 23 '15 at 20:04
  • printf does appear to come from the cdll.msvcrt dll though. I just checked with ipython – Totem Feb 23 '15 at 20:08
  • I got GetLastError in there, which informs me that the address is being returned as False. I get: ERROR 126 : The specified module could not be found. – Totem Feb 23 '15 at 20:23
  • IPython is probably built with completely different libraries than CPython, explaining why the dll is not loaded. You can use [Dependency Walker](http://www.dependencywalker.com/) to find out what's loaded. – Mark Ransom Feb 23 '15 at 20:55
  • I'll check that out, I just downloaded process explorer also. I'm not actually using ipython to run anything, I just imported ctypes, and checked out which dll contained printf. I knew it was msvcrt because it works(it prints), but just making sure. – Totem Feb 23 '15 at 21:47
  • I am constantly getting returned a negative handle.. – Totem Feb 23 '15 at 22:50
  • You need to define `GetModuleHandleA.restype = c_void_p` to get a pointer value; otherwise the result is cast to a `c_int`. But if you're doing this in a library, please use `kernel32 = WinDLL('kernel32')` instead of `kernel32 = windll.kernel32`. The `windll` loader caches modules, which cache function pointers. It should be for scripts only. – Eryk Sun Feb 23 '15 at 22:55
  • Also, the module 'handle' should not be passed to `CloseHandle`, nor should it be passed to `FreeLibrary`. It's just the base address of the module, and `GetModuleHandle` doesn't increment the reference count. – Eryk Sun Feb 23 '15 at 22:57
  • Additionally you need to define `GetProcAddress.restype = c_void_p` and `GetProcAddress.argtypes = (c_void_p, c_char_p)`. – Eryk Sun Feb 23 '15 at 23:00
  • where do I define these? I have been following a tutorial, and it mentions none of this.. – Totem Feb 23 '15 at 23:02
  • 1
    Of course a quicker way to get the address of `msvcrt!printf` in the debugger process is simply `address = c_void_p.from_buffer(msvcrt.printf)`. And a simple way to get the module handle is `hmod = c_void_p(msvcrt._handle)`. – Eryk Sun Feb 23 '15 at 23:05
  • any idea why my original code above isn't working? I know it's worked for others ex: 2nd answer here: https://stackoverflow.com/questions/4834762/why-does-calling-kernel32-getmodulehandlea-for-msvcr100-fail-in-python – Totem Feb 23 '15 at 23:08
  • I already told you why it's not working. The default result and argument conversion casts to a C `int`. Obviously that won't work for a 64-bit pointer. – Eryk Sun Feb 23 '15 at 23:11
  • Are you assuming the address is the same in the debuggee? Adjust for the module's base address in the debuggee, such as from `kernel32.K32EnumProcessModules` (or `psapi.EnumProcessModules`). – Eryk Sun Feb 24 '15 at 00:14
  • As it so happens I have the exact same problem. And nearly identical code, because I would guess we are both basing the debugger on the same book. I have not found a solution either, but I do know what the problem is. As eryksun seems to be guessing, it is a 32/64 bit issue. The code works fine when run in 32 bit python to debug a 32 bit process, but not for 64 bits. Whenever I run it in 64 bits the func_resolve function returns an empty address (all zeroes). – trevorKirkby Mar 20 '15 at 19:17
  • I admit I am not particularly sure how the c_void_p statements you listed above should be implemented. Isn't void basically like python's "None"? How would that help retrieve the correct information? – trevorKirkby Mar 20 '15 at 19:19
  • One last note, as it happens I actually integrated dependency walker usage into my debugger a while back and it is indeed quite useful. The point of this, however, is that msvcrt.dll most certainly is loaded in the process. – trevorKirkby Mar 20 '15 at 19:23

2 Answers2

1

I've also been working through the GreyHat Python book, and have stubbornly converted the my_debugger.py file to support 64bit debugging. I have found the solution to this problem.

Setup your func_resolve to something like:

def func_resolve(self,dll,function):

    _GetModuleHandleA = kernel32.GetModuleHandleA
    _GetModuleHandleA.restype = POINTER(c_void_p)

    _GetProcAddress = kernel32.GetProcAddress
    _GetProcAddress.restype = c_void_p

    handle = _GetModuleHandleA(dll)
    if handle is None:
        print 'Error getting handle'

    address = _GetProcAddress(handle, function)
    if address is None:
        print 'Error getting address'

    kernel32.CloseHandle(handle)
    return address

This should return a valid 64bit address for the function.

omega
  • 11
  • 1
1

I have being putting together a repository in github using the book as a template but changing to support x64 on windows 10 https://github.com/stavinski/grayhat_python_redux/tree/master/chapter03, I came across same issue as mentioned in other comments the wrong DLL is being used, I used the following to fix the issue:

msvcrt = cdll[ctypes.util.find_msvcrt()]

If you make sure this is used whenever you want to resolve msvcrt you should be good to go.

Mike
  • 435
  • 3
  • 6