Kayaker

OllyDbg 2.x Plugin Writing - Creating the OLLYDBG.LIB file

Rate this Entry
While the current version of OllyDbg (2.01e as of writing) is still in development, it now has full plugin API support.
Compiling a plugin requires linking with an ollydbg.lib file describing the imports defined in the plugin.h header file.
For Borland compilers this is a straight forward procedure and the LIB file should be fully compatible with the plugin declarations.
For MSVC compilers (Visual Studio, Visual C++) the situation is a bit more complicated.
I will describe what worked for me and how to go about producing an MSVC compatible ollydbg.lib file.

For MSVC you first need to create a DEF file in the proper format and then create the LIB file with the LIB /DEF: command. In OllyDbg 2.x the plugin exports are a mixture of _cdecl and _stdcall.

Cdecl functions can be declared in the DEF file as FunctionName @Ordinal.

Stdcall functions must be declared in the DEF file in the decorated format FunctionName@<number of parameter bytes> @Ordinal.

The attachment contains an MSVC suitable ollydbg.lib file (tested in VC6++ and VS 2010), the DEF file, and the python script I wrote to extract the number of parameter bytes from each _stdcall function in plugin.h and create the required DEF file format. Since the plugin.h function declarations may change in future versions as OllyDbg 2.x is developed, the python script may come in useful to create an updated ollydbg.lib file.

Stop reading now if you don't need the details.


For Borland compilers one can simply use IMPLIB.exe to create a LIB file in the following manner, and you're ready to go.

implib ollydbg.lib ollydbg.exe

For MSVC, well, here's the MS definition of the steps:

Given a .DLL (in our case OLLYDBG.EXE) with functions exported via a C interface, you can create an import library by following these steps:

  1. Use DUMPBIN /EXPORTS <.DLL file name> to obtain the list of exported symbols for the .DLL in question. The symbols appear in the "name" column of the table whose headings are "ordinal hint name."
  2. Create a .DEF file that contains an EXPORTS section with the names of the functions listed in the "name" column of the DUMPBIN output.
  3. For _cdecl functions, the symbol appears just as it would when used in the calling program. Just place this symbol in the EXPORTS section of the .DEF file.
  4. Use LIB /DEF:<.DEF file name> to generate the import library and exports file. The base name of the import library will be the base name of the .DEF file. Use /OUT: to control the output library name.


http://msdn.microsoft.com/en-us/library/d91k01sh(v=vs.80).aspx
http://support.microsoft.com/default.aspx?scid=kb;en-us;131313


For OllyDbg 1.x all export functions were in _cdecl format and the above instructions were suitable. Modified PDK's and explanations were provided in the following links:

http://www.ollydbg.de/whatsnew.htm
http://nezumi-lab.org/blog/?p=151

OllyDbg 2.x now has a mixture of _stdcall and _cdecl exported functions in plugin.h. If you try to link with an ollydbg.lib produced in the previous way you'll get compiler errors such as this when trying to compile the bookmark.c plugin example:

error LNK2019: unresolved external symbol _Commentaddress@16 referenced in function _Bookmarkdraw

I found that is was required to declare the stdcall function in the DEF file as follows:

Commentaddress@16 @278

Here is a useful article which sums up some of the differences in calling conventions for _stdcall and _cdecl for various compilers:

http://wyw.dcweb.cn/stdcall.htm


I guess the rest of the details are in the python script below. For those who are interested in that kind of thing you might read it easier from the zip file copy with syntax highlighting.

There is one further modification of plugin.h for MSVC, two of the exported functions (Heapsort/Heapsortex) use the Borland _USERENTRY type in their parameter list which generates a compiler error. The solution is provided by the Borland defs.h file:

_USERENTRY Specifies the calling convention the RTL expects user
compiled functions to use (for callbacks)

#define _USERENTRY __cdecl
You simply need to add #define _USERENTRY __cdecl to the plugin.h file.


All that's left is for the RE community to start updating old 1.x version plugins or creating new ones and adding them to the CRCETL here:

http://www.woodmann.com/collaborative/tools/index.php/Category:OllyDbg_2.x_Extensions

Cheers,
Kayaker

Code:
# makedef.py

# Generates a correct EXPORTS .DEF file for OllyDbg 2.x in MSVC format in order to
# create the OLLYDBG.LIB file required for writing OllyDbg plugins


# Instructions:

# 1. Create a DUMPBIN.EXE /EXPORTS file for Ollydbg.exe, i.e.:
#       C:\Program Files\Microsoft Visual Studio 10.0\VC>dumpbin /exports ollydbg.exe > olly_exports.txt

# 2. Create a DEF file by running this python script on the file, 
#    making sure the Ollydbg plugin.h file is in the same directory
#       > python makedef.py olly_exports.txt

# 3. Create a LIB file by running LIB.EXE /DEF on the output file from this script (ollydbg.def) 
#       C:\Program Files\Microsoft Visual Studio 10.0\VC>lib /def:ollydbg.def /OUT:ollydbg.lib

# 4. Compile your Ollydbg plugin linked to the lib file


# In OllyDbg 2.x the plugin exports are a mixture of _cdecl and _stdcall. 

# Cdecl functions can be declared in the DEF file as
#       FunctionName @Ordinal

# Stdcall functions must be declared in the DEF file in the decorated format
#       FunctionName@<number of parameter bytes> @Ordinal



# There is one further modification of plugin.h for MSVC, two of the exported functions (Heapsort/Heapsortex)
# use the Borland _USERENTRY type in their parameter list which generates a compiler error.
# The solution is provided by the Borland defs.h file:

#   _USERENTRY      Specifies the calling convention the RTL expects user
#                   compiled functions to use (for callbacks)

#   #define _USERENTRY __cdecl
    
# You simply need to add #define _USERENTRY __cdecl to the plugin.h file.

###################################################################################################


import string, re, sys, os

def main():    
    
    if len(sys.argv) < 2:
        sys.stderr.write("USAGE: %s <DUMPBIN /EXPORTS output filename>" % (sys.argv[0],))
        return 1

    if not os.path.exists(sys.argv[1]):
        sys.stderr.write("ERROR: File %r was not found!" % (sys.argv[1],))
        return 1        
    
    if not os.path.exists("plugin.h"):
        sys.stderr.write("ERROR: Ollydbg plugin.h file must be in same directory!")
        return 1 
        

###################################################################################################
  
    # PART 1: 
    # Extract all __stdcall ("stdapi") functions from plugin.h, determine the number of parameters for each,
    #   and create a list of them in the decorated format 
    #       FunctionName@<number of parameter bytes>, i.e. Absolutizepath@4
    #
    #   This list will be merged later with the rest of the undecorated __cdecl functions
    #   and exported variables to create a DEF file
    

    #########################################################
    #
    # Parse plugin.h to isolate STDAPI functions
    #
    #########################################################
    

    # List to hold original complete function declaration string
    
    list_fx = []
    
    # List to hold decorated 'function@parameters' export name string for __stdcall functions
    
    list_decorated = []    
        
    
    # Regex pattern to match Type portion of function string for removal, i.e. "stdapi (int)  "     
    
    # pattern_stdapi = re.compile(r"(?i)^stdapi.+?\)\s*")
    
    # RegexBuddy is my buddy ;)    
    # (?i)^stdapi.+?\)\s*
    # Options: case insensitive; ^ and $ match at line breaks

    pattern_stdapi = re.compile(r"""
        (?i)                    # Match the remainder of the regex with the options: case insensitive (i) <?i>
        ^                       # Assert position at the beginning of a line (at beginning of the string or after a line break character) <^>
        stdapi                  # Match the characters "stdapi" literally <stdapi>
        .+?                     # Match any single character that is not a line break character <.+?>
                                #   Between one and unlimited times, as few times as possible, expanding as needed (lazy) <+?>
        \)                      # Match the character ")" literally <\)>
        \s*                     # Match a single character that is a "whitespace character" (spaces, tabs, line breaks, etc.) <\s*>
                                #   Between zero and unlimited times, as many times as possible, giving back as needed (greedy) <*>
        """, re.VERBOSE)    
    
    
    # Regex pattern to split function into separate Name and (Parameters) strings

    pattern_fx = re.compile(r"(?i)^(.*)(\(.*\))")    

    # (?i)^(.*)(\(.*\))
    #
    # Match the remainder of the regex with the options: case insensitive (i) <(?i)>
    # Assert position at the beginning of a line (at beginning of the string or after a line break character) <^>
    # Match the regular expression below and capture its match into backreference number 1 <(.*)>
    #    Match any single character that is not a line break character <.*>
    #       Between zero and unlimited times, as many times as possible, giving back as needed (greedy) <*>
    # Match the regular expression below and capture its match into backreference number 2 <(\(.*\))>
    #    Match the character "(" literally <\(>
    #    Match any single character that is not a line break character <.*>
    #       Between zero and unlimited times, as many times as possible, giving back as needed (greedy) <*>
    #    Match the character ")" literally <\)>



    # Open plugin.h for reading
         
    f = open("plugin.h", "r")
    
    # Read each line    
    
    for line in f:
        
        # Remove any leading/trailing whitespace characters
        
        fx = line.strip()
        
        if fx.startswith('stdapi'):
            
            while True:
                
                # Some functions are split over 2 or more lines, find ending ";"
            
                if not fx.endswith(';'):
                  
                    # Concatenate with next line until complete function is found
                
                    fx = fx + f.next().strip()                    

                if fx.endswith(';'): 
                    
                    # Found complete function
                                        
                    break
                    
                    
            # Confirm string begins with "stdapi (type)"                    
    
            m = pattern_stdapi.match(fx)
            
            if m:
                
                # Pattern match found 
                
                # print 'Match found: ', m.group()      ### Match found:  stdapi (int)
                
                # Remove "stdapi (type)" pattern from function, leaving raw function definition                  
                
                fx = pattern_stdapi.sub("", fx)
                
                # Store remaining portion of function string in a List                

                list_fx.append(fx)            
                
            else:
                
                print 'No match'
                
        
    # We have the complete functions, cleaned up and temporarily stored, close plugin.h
        
    f.close() 

        

    #########################################################
    #
    # Determine number of parameters for each function
    #
    #########################################################
    
        
    # Sort list alphabetically
    
    list_fx.sort()
    
    num_params = 0
    num_param_bytes = 0
    num_doubles = 0    

    for item in list_fx:
        
        # print item        ### Absolutizepath(wchar_t *path);
                
        m = pattern_fx.match(item)
            
        if m:
            
            # print 'Match found: ', m.group(0)      ## Absolutizepath(wchar_t *path)
            # print 'Match found: ', m.group(1)      ## Absolutizepath
            # print 'Match found: ', m.group(2)      ## (wchar_t *path)            

            # Copy capturing group to regular string
                        
            fxname = m.group(1)
            

            # Add function name + @parameter count to List                    
            
            if m.group(2) == "(void)":

                # print m.group(0) + " has " + str(num_params) + " parameters"
                     ### Checkfordebugevent(void) has 0 parameters
               
                num_params = 0
                                     
                     
            # There are two special situations where our regex pattern doesn't match,
            # Heapsort and Heapsortex both have non-standard parameter strings
            # Rather than trying to come up with a protocol to deal with it, just handle it manually
            #
            # Note that these two functions also require the following to be added to the plugin.h file (from Borland defs.h):
            # #define _USERENTRY __cdecl                                 
                     
            elif fxname.startswith("Heapsort("):

                # Heapsort(void *data,const int count,const int size,int (_USERENTRY *compare)(const void *,const void *));
                
                # borked match                
                # print m.group(1)        ### Heapsort(void *data,const int count,const int size,int (_USERENTRY *compare)
                
                fxname = "Heapsort"

                num_params = 4                

            elif fxname.startswith("Heapsortex("):
                
                # Heapsortex(void *data,const int count,const int size,int (_USERENTRY *compareex)(const void *,const void *,ulong),ulong lp);

                fxname = "Heapsortex"

                num_params = 5

            else:
                
                # Number of DWORD size parameters can be determined by counting the number of commas and adding 1                
                
                num_params = m.group(2).count(",") + 1
            
                # print m.group(0) + " has " + str(num_params) + " parameters"
                     ### Absolutizepath(wchar_t *path) has 1 parameters
                
                # For DOUBLEWORD size parameters we need to add 4 bytes
            
                num_doubles = m.group(2).count("double")
                
                if num_doubles:
                    
                    num_params = num_params + num_doubles
            
                    # print m.group(0) + " has " + str(num_doubles) + " DOUBLEWORD parameters"
                         ### Printfloat10(wchar_t *s,long double ext) has 1 DOUBLEWORD parameters
                         
                               
            # Convert number of parameters to bytes
                
            num_param_bytes = num_params * 4
            
            # Write function name + parameter bytes decoration to List            

            list_decorated.append(string.ljust(fxname + "@" + str(num_param_bytes), 40)) 
            
        else:
            
            print 'No match' 
                   
        
        
    # We should now have a list of the __stdcall function definitions (327 of them!)
    # ready for merging with the rest of the exports

    # for idx, item in enumerate(list_decorated):
                
        # print idx, item       ### 0 Absolutizepath@4
                                ### 1 Activatetablewindow@4
                             
                 
###################################################################################################


    # PART 2: 
    # Extract list of all export functions from DUMPBIN /EXPORTS output file that we haven't already dealt with,
    #   in preparation for producing a properly formatted DEF file 
    
  
    #########################################################
    #
    # Extract export functions from DUMPBIN /EXPORTS output
    #
    #########################################################
    
    
    # List to hold undecorated export strings (__cdecl functions and exported variables)

    list_undecorated = []
    
    
    # Regex pattern to find "header" for list of EXPORTS in dumpbin output in order to find starting line for further processing
        #        ordinal hint RVA      name
        #
        #         34    0 00005D0C Absolutizepath

    pattern = re.compile(r"(?i)\bordinal.*\bhint.*\bRVA.*name")

    # pattern = re.compile(r"""
    # (?i)                  # Match the remainder of the regex with the options: case insensitive (i) <?i>
    # \bordinal             # Assert position at a word boundary <\b>
    #                       # Match the characters "ordinal" literally <ordinal>
    # .*                    # Match any single character that is not a line break character <.>
    #                       #   Between zero and unlimited times, as many times as possible, giving back as needed (greedy) <*>
    # \bhint.*\bRVA.*name   # repeat
    # """, re.VERBOSE)

    
    # Open DUMPBIN /EXPORTS output file
    
    f = open(sys.argv[1], "r") 
        
    # Read the whole file into memory
    
    lines = f.readlines()
          
    f.close()  
        

    # Find starting line of exports listing
    
    for i, line in enumerate(lines):
        
        if pattern.search(line):
            
            startpos = i + 2
            


    for i in xrange(startpos, len(lines)):

        # Read line
    
        line = lines[i]

        # Check for empty line and stop
    
        if not line.strip():

            break

        else:

            x = line.split()
        
            # print x         ### ['34', '0', '00005D0C', 'Absolutizepath']
            # print x[0]      ### 34
            # print x[1]      ### 0
            # print x[2]      ### 00005D0C
            # print x[3]      ### Absolutizepath
            
            # Check if this function has already been handled above
            # i.e. it's a __stdcall function we've already decorated with the @parameter qualifier and stored in List list_decorated[]
            
            
            NEWFUNCTION = True
            
            # Iterate both the index and the item
                        
            for idx, entry in enumerate(list_decorated):
                
                # Test if this string exists 
                                
                if x[3] in entry:

                    # print "Function exists at index " + str(idx) + " as " + list_decorated[idx]
                       ### Function exists at index 0 as Absolutizepath@4
                    
                    # Add the ordinal value to the function@parameters string we already have

                    list_decorated[idx] = list_decorated[idx] + "@" + x[0]                    

                    NEWFUNCTION = False

                    break
                    

            if NEWFUNCTION == True:
                
                # print x[3] + " is a new export"         ### Addtolist is a new export

                # Ollydbg exported variables preceded by an underscore ("oddata") also generate an MSVC compiler error
                # Remove underscores on these functions - they begin with small caps
                
                # This "slice" of string x[3] will return true for underscored small cap functions
                
                if str.islower(x[3][:2]):
                    
                    # print x[3]          ### _aqueue
                
                    # Remove underscore
                    
                    x[3] = x[3].lstrip("_")                


                # Add function and ordinal value to a new list

                list_undecorated.append(string.ljust(x[3], 40) + "@" + x[0])

                
                
    # Now merge the 2 unique lists together and we should have the full list in proper DEF format
    
    li_def = list_decorated + list_undecorated                
                
    li_def.sort()
                
    for item in li_def:
        
        print item


###################################################################################################


    # PART 3: Create the DEF file

    # Open output file for writing
        
    output = open("ollydbg.def", "w")   
    print >> output, "NAME           OllyDbg" 
    print >> output, "EXPORTS"
    
    for item in li_def:
        
        print >> output, "  " + item 
    
    # Close ollydbg.def
    
    output.close()


###################################################################################################

if __name__ == '__main__':
    main()
Attached Thumbnails Attached Files

Submit "OllyDbg 2.x Plugin Writing - Creating the OLLYDBG.LIB file" to Digg Submit "OllyDbg 2.x Plugin Writing - Creating the OLLYDBG.LIB file" to del.icio.us Submit "OllyDbg 2.x Plugin Writing - Creating the OLLYDBG.LIB file" to StumbleUpon Submit "OllyDbg 2.x Plugin Writing - Creating the OLLYDBG.LIB file" to Google

Categories
Uncategorized

Comments