Kayaker
August 25th, 2012, 21:41
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:
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:
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/Category:OllyDbg_2.x_Extensions
Cheers,
Kayaker
OllyDbg2.x_MSVC_PDK.zip (93.9 KB)
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:
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."
Create a .DEF file that contains an EXPORTS section with the names of the functions listed in the "name" column of the DUMPBIN output.
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.
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:
Quote:
_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/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 /defllydbg.def /OUT
llydbg.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()