Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api hooking error: hook ExtTextOutW in gdi32.dll and return unreadable codes #22

Closed
langway opened this issue Jun 22, 2017 · 3 comments
Closed

Comments

@langway
Copy link

langway commented Jun 22, 2017

I am using winappdbg to hook ExtTextOutW function in gdi32.dll, the code is blow:

# BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCTSTR lpString, UINT cbCount, CONST INT *lpDx);
class MyEventHandler(EventHandler):
    # Here we set which API calls we want to intercept.
    apiHooks = {

        # Hooks for the kernel32 library.
        'gdi32.dll': [

            #  Function            Parameters
            # HDC hdc, int X, int Y, UINT fuOptions, CONST RECT * lprc, LPCTSTR lpString, UINT cbCount, CONST INT * lpDx
            ('ExtTextOutW', (HDC, c_int, c_int, UINT, RECT, LPCWSTR, UINT,INT)),
        ],
    }

# HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCTSTR lpString, UINT cbCount, CONST INT *lpDx
    def pre_ExtTextOutW(self, event, ra, hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx):

        self.__print_pre_textout(event, lpString, "ExtTextOutW")

    def post_ExtTextOutW(self, event, retval):
        self.__print_post_textout(event, retval, "ExtTextOutW")


    def __print_pre_textout(self, event, string, tag):
        tid = event.get_tid()
        print  "%d: pre_textout %s: %s" % (tid, tag, string)



    def __print_post_textout(self, event, retval, tag):
        tid = event.get_tid()
        if retval:
            print "%d: post_textout Success %s: %x" % (tid, tag, retval)
        else:
            print "%d: post_textout Failed! %s" % (tid, tag)



def simple_debugger(argv):
    # Instance a Debug object, passing it the MyEventHandler instance.
    with Debug(MyEventHandler(), bKillOnExit=True) as debug:
        # Start a new process for debugging.
        debug.execv(argv)

        # Wait for the debugee to finish.
        debug.loop()


# When invoked from the command line,
# the first argument is an executable file,
# and the remaining arguments are passed to the newly created process.
if __name__ == "__main__":
    program_to_debug = "c:\\windows\\system32\\notepad.exe"

    sys.argv.append(program_to_debug)
    simple_debugger(sys.argv[1:])

There are two type errors:
The first likes this :

File "D:\ProgramData\Anaconda2\lib\site-packages\winappdbg\breakpoint.py", line 1197, in call
13660: pre_textout ExtTextOutW:
params = self._get_function_arguments(aProcess, aThread)
File "D:\ProgramData\Anaconda2\lib\site-packages\winappdbg\breakpoint.py", line 1453, in _get_function_arguments
offset = win32.sizeof(win32.LPVOID))
File "D:\ProgramData\Anaconda2\lib\site-packages\winappdbg\thread.py", line 1481, in read_stack_structure
for (name, type) in stackData.fields ])
ValueError: invalid string pointer 0xA6011B1F

warnings.warn(msg, BreakpointCallbackWarning)

the second likes this:

13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: 筀̶彟̀蛠̶蚠̶��
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: čďČĖĄČĉăęďēĆćĆĄȊĕČȊĔĆđЄċČ̓ĄČđĔĄčȎ
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW:
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: čďČĖĄČĉăęďēĆćĆĄȊĕČȊĔĆđЄċČ̓ĄČđĔĄčȎ
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: 琠扡敬✠猥㨧渠潣畬湭渠浡摥✠猥‧獩瀠敲敳瑮.
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: ǖ
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: 琠扡敬✠猥㨧渠潣畬湭渠浡摥✠猥‧獩瀠敲敳瑮.
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: ǖ
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: 琠扡敬✠猥㨧渠潣畬湭渠浡摥✠猥‧獩瀠敲敳瑮.
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: ǖ
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: 琠扡敬✠猥㨧渠潣畬湭渠浡摥✠猥‧獩瀠敲敳瑮.
13660: post_textout Success ExtTextOutW: 1
13660: pre_textout ExtTextOutW: ǖ

Is there something wrong?

@langway
Copy link
Author

langway commented Jun 22, 2017

BTW:
My OS is windows10 X64,
Anaconda python 2.7.13 32 bits

 thanks

@MarioVilas
Copy link
Owner

Hi! The problem here is in using the ctypes pointer types. Ctypes assumes all pointer types point to data within your own process memory (a reasonable assumption!) which is not true in this case - your string is in another process, so you cannot read it directly with ctypes.

You need to use WinAppDbg to fetch the string from the debugee's memory. You can define your hook to use void* types for all pointers so ctypes will just give you the address but not try to access the memory. Then just pass the address to process.read_string() to get the actual string.

http://winappdbg.sourceforge.net/doc/v1.5/reference/winappdbg.process.Process-class.html#read_string

Cheers!

@langway
Copy link
Author

langway commented Jun 23, 2017

Hi,Mario Vilas:
Thanks a lot for your rapidlly response! I understand that the string is really a pointer which should be used to "extract" the real string from the process. But when I use process.read_string(), I found another problem appears: character "a" will be extract as "D", "b" will be extract as "E", "1" will be extract as "x14",and so on.
I use your code in "WinAppDbg Documentation Release 1.4",only change peek() to read_string() :

#------------------------------------------------------------------------------
# BOOL TextOut(
# __in HDC hdc,
# __in int nXStart,
# __in int nYStart,
# __in LPCTSTR lpString,
# __in int cbString
# );
def TextOutA(event, ra, hdc, nXStart, nYStart, lpString, cbString):
    log_ansi(event, "TextOutA", lpString, cbString)
def TextOutW(event, ra, hdc, nXStart, nYStart, lpString, cbString):
    log_wide(event, "TextOutW", lpString, cbString)
# BOOL ExtTextOut(
# __in HDC hdc,
# __in int X,
# __in int Y,
# __in UINT fuOptions,
# __in const RECT * lprc,
# __in LPCTSTR lpString,
# __in UINT cbCount,
# __in const INT * lpDx
# );
def ExtTextOutA(event, ra, hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx):
    log_ansi(event, "ExtTextOutA", lpString, cbCount)
def ExtTextOutW(event, ra, hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx):
    log_wide(event, "ExtTextOutW", lpString, cbCount)
# typedef struct _POLYTEXT {
# int x;
# int y;
# UINT n;
# LPCTSTR lpstr;
# UINT uiFlags;
# RECT rcl;
# int * pdx;
# } POLYTEXT,* PPOLYTEXT;
class POLYTEXT(Structure):
    _fields_ = [
    ('x', c_int),
    ('y', c_int),
    ('n', c_uint),
    ('lpstr', c_void_p),
    ('uiFlags', c_uint),
    ('rcl', c_uint*4),
    ('pdx', POINTER(c_int)),
    ]
# BOOL PolyTextOut(
# __in HDC hdc,
# __in const POLYTEXT * pptxt,
# __in int cStrings
# );

def PolyTextOutA(event, ra, hdc, pptxt, cStrings):
    process = event.get_process()
    sizeof_polytext = sizeof(POLYTEXT)
    while cStrings:
        txt = process.read_structure(pptxt, POLYTEXT)
        log_ansi(event, "PolyTextOutA", txt.lpstr, txt.n)
        pptxt = pptxt + sizeof_polytext
        cStrings = cStrings - 1

def PolyTextOutW(event, ra, hdc, pptxt, cStrings):
    process = event.get_process()
    sizeof_polytext = sizeof(POLYTEXT)
    while cStrings:
        txt = process.read_structure(pptxt, POLYTEXT)
        log_wide(event, "PolyTextOutW", txt.lpstr, txt.n)
        pptxt = pptxt + sizeof_polytext
        cStrings = cStrings - 1
#------------------------------------------------------------------------------
def log_ansi(event, fn, lpString, nCount):
    if lpString and nCount:
        if c_int(nCount).value == -1:
            lpString = event.get_process().read_string(lpString, fUnicode = False)
        else:
            lpString = event.get_process().read_string(lpString, nCount)
        print (DebugLog.log_text("%s( %r );" % (fn, lpString)))
def log_wide(event, fn, lpString, nCount):
    if lpString and nCount:
        if c_int(nCount).value == -1:
            lpString = event.get_process().read_string(lpString, fUnicode = True)
        else:
            # lpString = event.get_process().peek(lpString, nCount * 2)
            # lpString = unicode(lpString, 'U16', 'strict')

            lpString=event.get_process().read_string(lpString, nCount,fUnicode = True)


        print (DebugLog.log_text("%s( %r );" % (fn, lpString)))

class MyEventHandler( EventHandler ):
    def load_dll(self, event):
        pid = event.get_pid()
        module = event.get_module()
        if module.match_name("gdi32.dll"):
            event.debug.hook_function(pid, module.resolve("TextOutA"), TextOutA, paramCount = 5)
            event.debug.hook_function(pid, module.resolve("TextOutW"), TextOutW, paramCount = 5)
            event.debug.hook_function(pid, module.resolve("ExtTextOutA"), ExtTextOutA, paramCount = 8)
            event.debug.hook_function(pid, module.resolve("ExtTextOutW"), ExtTextOutW, paramCount = 8)
            event.debug.hook_function(pid, module.resolve("PolyTextOutA"), PolyTextOutA, paramCount = 2)
            event.debug.hook_function(pid, module.resolve("PolyTextOutW"), PolyTextOutW, paramCount = 2)
def simple_debugger(argv):
    print (DebugLog.log_text("Trace started on %s" % argv[0]))
    debug = Debug(MyEventHandler())
    try:
        debug.execv(argv)
        debug.loop()
    finally:
        debug.stop()
    print (DebugLog.log_text("Trace stopped on %s" % argv[0]))

if __name__=="__main__":
    program_to_debug="c:\\windows\\system32\\notepad.exe"

    sys.argv.append(program_to_debug)
    simple_debugger(sys.argv[1:])

Can you help me to figure it out where I was wrong! Thanks!

                                Alex Liang

snap_screen_20170623101854

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants