Thursday, September 11, 2014

Write Python Program to Use DBGHELP.DLL to Access a PDB File

The DBGHELP.DLL is a library provided by Microsoft to access PDB file. This page teaches us,
  1. How to use Python to load DLL.
  2. How to read PDB file to report file path and line number by a given IP register value.
  3. How to use python ctypes.
I select Python to try DBGHELP.DLL because Python is easy for use. Below is the source code. You can run it with the command.
> python DbgHelpTest.py

You can see the results.

DbgHelpTest.py

#
# This source code is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This Python program demonstrates the usage of Windows DBGHELP.DLL by the
# below steps.
# 
# 1. Locate functions of DBGHELP.DLL.
# 2. Load a PDB file at base address.
# 3. Input IP register value.
# 4. Output file path and line number of source code.
#
# Copyright (c) 2014 Count Chu.
#

import os
import ctypes

#
# Please set parameters here.
#

PdbFileName = "VcApp.pdb"               # A PDB file that was created by you.
BaseAddr = ctypes.c_uint64 (0x400000)   # Base address of the PDB file that will be loaded.
IpAddr = ctypes.c_uint64 (0x4113B0)     # IP register value that will be input.

#
# Locate functions of DBGHELP.DLL that are used in the program.
#

DbgHelp = ctypes.WinDLL (r"dbghelp.dll")
print DbgHelp                           # for debug.
SymInitialize = DbgHelp ["SymInitialize"]    
SymSetOptions = DbgHelp ["SymSetOptions"]
SymLoadModule64 = DbgHelp ["SymLoadModule64"]
SymGetLineFromAddr64 = DbgHelp ["SymGetLineFromAddr64"]
SymUnloadModule64 = DbgHelp ["SymUnloadModule64"]

#
# Define enumeration that is used in SymSetOptions.
#

class SymOpt:
    LOAD_LINES = 0x00000010
    UNDNAME = 0x00000002
    DEFERRED_LOADS = 0x00000004
    
#
# Call SymSetOptions().
#

print ("Call SymSetOptions()")          # for debug.
SymSetOptions (SymOpt.LOAD_LINES | SymOpt.UNDNAME | SymOpt.DEFERRED_LOADS)

#
# Get the current process handle. It should be -1.
#

ProcessHandle = ctypes.windll.kernel32.GetCurrentProcess ()
print ("ProcessHandle = %xh" % ProcessHandle) # for debug.

#
# Call SymInitialize().
#

print ("Call SymInitialize()")          # for debug.
Status = SymInitialize (ProcessHandle, None, False)
if not Status:
    print "Error. Calling SymInitialize() failed."
    
#
# Call SymLoadModule64().
#    

FileSize = os.path.getsize (PdbFileName)
print ("FileSize = %d" % FileSize)      # for debug.
print ("Call SymLoadModule64()")        # for debug.
OutBaseAddr = SymLoadModule64 (         # DWORD64 WINAPI SymLoadModule64 (
             ProcessHandle,             #   _In_      HANDLE hProcess,
             0,                         #   _In_opt_  HANDLE hFile,
             PdbFileName,               #   _In_opt_  PCSTR ImageName,
             None,                      #   _In_opt_  PCSTR ModuleName,
             BaseAddr,                  #   _In_      DWORD64 BaseOfDll,
             FileSize)                  #   _In_      DWORD SizeOfDll
                                        #   );
print ("OutBaseAddr = %xh" % OutBaseAddr) # for debug.

#
# Declare IMAGEHLP_LINE64 structure and create a instance of it.
#

class IMAGEHLP_LINE64 (ctypes.Structure):   # typedef struct _IMAGEHLP_LINE64 {
    _fields_ = [                            #   
        ('SizeOfStruct', ctypes.c_uint32),  #   DWORD   SizeOfStruct;    
        ('Key', ctypes.c_void_p),           #   PVOID   Key;
        ('LineNumber', ctypes.c_uint32),    #   DWORD   LineNumber;
        ('FileName', ctypes.c_char_p),      #   PTSTR   FileName;
        ('Address', ctypes.c_uint64)        #   DWORD64 Address;
        ]                                   # } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;

Line = IMAGEHLP_LINE64 ()                # Create a instance of IMAGEHLP_LINE64.

#
# Call SymGetLineFromAddr64()
#

print ("Call SymGetLineFromAddr64()")   # for debug.
Displacement = ctypes.c_uint32 (0)
Status = SymGetLineFromAddr64 (             # BOOL WINAPI SymGetLineFromAddr64 (
            ProcessHandle,                  #   _In_   HANDLE hProcess,
            IpAddr,                         #   _In_   DWORD64 dwAddr,
            ctypes.byref (Displacement),    #   _Out_  PDWORD pdwDisplacement,
            ctypes.byref (Line))            #   _Out_  PIMAGEHLP_LINE64 Line
                                            # );
print ("Status = %xh" % Status)         # for debug.
if not Status:
    print "Error. Calling SymGetLineFromAddr64() failed."
    
#
# Dump Displacement and IMAGEHLP_LINE64.
#    
    
print ("Displacement = %d" % Displacement.value)
print ("Dump Line:")
print ("  SizeOfStruct = %d" % Line.SizeOfStruct)
print ("  Key          = %s" % Line.Key)
print ("  LineNumber   = %d" % Line.LineNumber)
print ("  FileName     = %s" % Line.FileName)
print ("  Address      = %xh" % Line.Address)

#
# Call SymUnloadModule64()
#
    
Status = SymUnloadModule64 (ProcessHandle, BaseAddr)
if not Status:
    print "Error. Calling SymUnloadModule64() failed."

No comments:

Post a Comment