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.
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
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.
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.
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);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.
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.