1. Overview

A Dynamic-link library, or DLL, file is a shared library used on Windows that contains functions and data. Like other executables on Windows, DLL files are of the Portable Executable (PE) file format. PE files have a variety of data in their file headers, which include import tables and export tables.

Import tables are an array of functions and data that the PE file includes from other shared libraries. Export tables are an array of functions and data contained within the PE file wishes to provide to the outside world. These are different than internal functions, which the PE file uses internally.

There are many reasons to need the export data of a DLL file. One of them is so that we can write a program that makes use of the functions in the DLL program. On Linux, we can use a few different tools to extract the export table of a DLL file.

In this tutorial, we’re going to discuss how to extract exported symbols using these tools, which consist of a few different command-line tools and a Python module as well.

2. Wine

Wine is a compatibility tool that aims to run windows programs on POSIX-compliant systems, like Linux. It also comes with a set of supporting tools to work with PE files.

The winedump tool for interacting with DLL PE files. We can use the winedump command to display information about a PE file.

A PE file stores its exported symbols in its export address table. The PE file then stores its export address table in the “export” section of the PE file.

Let’s install winedump:

sudo apt-get install wine64-tools

We use the -j option to display the exported symbols in the export section:

$ winedump -j export kernel32.dll
Contents of kernel32.dll: 725696 bytes

Exports table:

  Name:            KERNEL32.dll
  [...]
  Entry Pt  Ordn  Name
  0009417F     1 AcquireSRWLockExclusive (-> NTDLL.RtlAcquireSRWLockExclusive)
  000941B5     2 AcquireSRWLockShared (-> NTDLL.RtlAcquireSRWLockShared)
  0001E860     3 ActivateActCtx
  0001A570     4 ActivateActCtxWorker
  00021A70     5 AddAtomA
  0000FE30     6 AddAtomW
  00022DE0     7 AddConsoleAliasA
  00022DF0     8 AddConsoleAliasW
  0009423B     9 AddDllDirectory (-> api-ms-win-core-libraryloader-l1-1-0.AddDllDirectory)
  000375A0    10 AddIntegrityLabelToBoundaryDescriptor
  00053150    11 AddLocalAlternateComputerNameA
  000531B0    12 AddLocalAlternateComputerNameW
  00020570    13 AddRefActCtx
  0001E3C0    14 AddRefActCtxWorker
  00035FB0    15 AddResourceAttributeAce
  0001F3C0    16 AddSIDToBoundaryDescriptor
  00035FC0    17 AddScopedPolicyIDAce
  00034600    18 AddSecureMemoryCacheCallback
  00094374    19 AddVectoredContinueHandler (-> NTDLL.RtlAddVectoredContinueHandler)
  000943B4    20 AddVectoredExceptionHandler (-> NTDLL.RtlAddVectoredExceptionHandler)
  0001A6B0    21 AdjustCalendarDate
  00022A30    22 AllocConsole
  [...]
  00019E40  1614 lstrlen
  00019E40  1615 lstrlenA
  00017930  1616 lstrlenW
  0001D240  1617 timeBeginPeriod
  0001C2F0  1618 timeEndPeriod
  00020470  1619 timeGetDevCaps
  00061590  1620 timeGetSystemTime
  0001CFE0  1621 timeGetTime
  0001A510  1622 uaw_lstrcmpW
  00017950  1623 uaw_lstrcmpiW
  00033380  1624 uaw_lstrlenW
  000333D0  1625 uaw_wcschr
  000333F0  1626 uaw_wcscpy
  00033420  1627 uaw_wcsicmp
  00033430  1628 uaw_wcslen
  00033450  1629 uaw_wcsrchr

Done dumping kernel32.dll

3. Radare2

Radare2 is a UNIX-like reverse engineering framework. The Radare2 framework also comes with a set of command-line tools to work with binaries. We can use Radare2 on both Linux and Windows, and it supports the PE file format.

Thus, we can use the rabin2 tool to extract program info from a binary of our choosing.

Let’s install radare2 and rabin2 first:

$ sudo apt-get install radare2

We use the -s option to show the exported symbols of a PE file on the command line:

$ rabin2 -s kernel32.dll
[Symbols]
000 0x0009277f 0x18009417f GLOBAL   FUNC    0 KERNEL32.dll_AcquireSRWLockExclusive
001 0x000927b5 0x1800941b5 GLOBAL   FUNC    0 KERNEL32.dll_AcquireSRWLockShared
002 0x0001dc60 0x18001e860 GLOBAL   FUNC    0 KERNEL32.dll_ActivateActCtx
003 0x00019970 0x18001a570 GLOBAL   FUNC    0 KERNEL32.dll_ActivateActCtxWorker
004 0x00020e70 0x180021a70 GLOBAL   FUNC    0 KERNEL32.dll_AddAtomA
005 0x0000f230 0x18000fe30 GLOBAL   FUNC    0 KERNEL32.dll_AddAtomW
[...]

Also, we need to be aware that the exported symbols from rabin2 are shown a bit differently from the output of winedump.

4. objdump

objdump is a tool that we can use to dump information about binary files. The PE file format is one of the formats supported by the objdump tool.

Let’s install objdump first:

$ sudo apt-get install binutils

Now, we can use objdump to dump the export address table of a PE file:

$ objdump -p kernel32.dll

kernel32.dll:     file format pei-x86-64
[...]
The Data Directory
Entry 0 0000000000090190 0000dd40 Export Directory [.edata (or where ever we found it)]

[Ordinal/Name Pointer] Table
        [   0] AcquireSRWLockExclusive
        [   1] AcquireSRWLockShared
        [   2] ActivateActCtx
        [   3] ActivateActCtxWorker
        [   4] AddAtomA
        [   5] AddAtomW
[...]

A disadvantage of using objdump for this is that the export table is shown alongside other information about the PE. Thus, we would need to parse out the lines pertaining to the exported symbols if we wanted to process it more.

We also need to be aware that we may have thousands of lines of data as output, and so we may need to grep and parse accordingly.

5. pefile

Finally, pefile is a python module that we can use to work with PE files.

Let’s install the pefile module using pip:

$ pip3 install pefile

We can use the “exports” flag to dump the exports of a DLL file:

$ python3 -m pefile exports kernel32.dll
0x18009417f b'AcquireSRWLockExclusive' 1
0x1800941b5 b'AcquireSRWLockShared' 2
0x18001e860 b'ActivateActCtx' 3
0x18001a570 b'ActivateActCtxWorker' 4
0x180021a70 b'AddAtomA' 5
[...]

Additionally, we can remove the “b” from the beginning of the symbol names with a one Python liner:

$ python3 -c 'import sys;import pefile;pe = pefile.PE(sys.argv[1]);wr = sys.stdout.write; [wr("%s %s %i\n"%(hex(pe.OPTIONAL_HEADER.ImageBase + exp.address), exp.name.decode("utf-8"), exp.ordinal)) for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols]' kernel32.dll
0x18009417f AcquireSRWLockExclusive 1
0x1800941b5 AcquireSRWLockShared 2
0x18001e860 ActivateActCtx 3
0x18001a570 ActivateActCtxWorker 4
0x180021a70 AddAtomA 5
[...]

Lastly, one thing we should note is that we do need Python on our Linux system to make use of the pefile module.

6. Conclusion

In this article, we looked at using the winedump command-line tool that is part of the wine tool to show exported symbols of a DLL.

Then, we looked at using the rabin2 tool from the Radare2 reverse engineering toolset. After this, we covered using the objdump tool to show information about a PE file.

Finally, we covered using the pefile Python module to extract information from a DLL. We can use any of these options to find out what symbols a DLL file exports on Linux.