Symbol Porting

Introduction

Porting symbols from one binary to another is a standard using case in diffing. Indeed, it is handy in reverse-engineering to apply function symbols to a stripped binary if we are able to put our hands on a similar binary with symbols.

For the purpose of this practical we are going to use to versions of libsensorservice.so for which the second version has been stripped.


Question

Script the diff with python-bindiff and port the symbols to the second one with LIEF.

I. Performing the diff

To perform the diff with python-bindiff we first need to export the two binaries.

If we don’t have BinExport files we can perform the diff from the binaries with:

[ ]:
from bindiff import BinDiff

diff = BinDiff.from_binary_files("libsensorservice-1.so", "libsensorservice-2.so", "result.BinDiff")

If we already have the BinExport files, we can perform the diff with:

[13]:
from bindiff import BinDiff

diff = BinDiff.from_binexport_files("libsensorservice-1.so.BinExport", "libsensorservice-2.so.BinExport", "result.BinDiff")
diff
[13]:
<bindiff.bindiff.BinDiff at 0x7f07ff1766b0>

Exercise: Print the matches using the function_matches attributes

[25]:
for m in diff.function_matches[:10]:
    print(f"{m.address1:#08x}:{m.name1} --> {m.address2:#08x}:{m.name2}")
0x016560:__on_dlclose --> 0x016580:start
0x016570:__emutls_unregister_key --> 0x016590:sub_00016590
0x016580:__on_dlclose_late --> 0x0165a0:j_nullsub_1
0x016590:android::BatteryService::BatteryService(void) --> 0x0165b0:sub_000165B0
0x0165d0:android::BatteryService::enableSensorImpl(uint,int) --> 0x0165f0:sub_000165F0
0x0166c0:android::BatteryService::checkService(void) --> 0x0166e0:sub_000166E0
0x0167f0:android::BatteryService::disableSensorImpl(uint,int) --> 0x016810:sub_00016810
0x0168d0:android::BatteryService::cleanupImpl(uint) --> 0x0168f0:sub_000168F0
0x0169a0:android::Mutex::~Mutex() --> 0x0169c0:j__pthread_mutex_destroy
0x0169b0:android::SortedVector<android::BatteryService::Info>::~SortedVector() --> 0x0169d0:sub_000169D0

Note: Names are demangled here! To add symbols we will need to add mangled names.

II. Loading symbols

We now need to load symbols from the two ELF binaries to be able to port from one to the other using diff information. Let’s do this using LIEF that enables parsing and modifying ELF binaries. We are going to add static symbols for our purpose here.

Exercise: Load the two programs using LIEF, and compute a dictionary of address -> mangled name for the first binary.

[38]:
import lief

binary1 = lief.parse("libsensorservice-1.so")
binary2 = lief.parse("libsensorservice-2.so")

symbols = {s.value: s for s in binary1.static_symbols}

for addr, sym in list(symbols.items())[:10]:
    print(f"{addr:#08x}: {sym.name}")
0x000000: strdup
0x041850: __dso_handle
0x016560: __on_dlclose
0x016580: __on_dlclose_late
0x000270: abitag
0x016570: __emutls_unregister_key
0x016d30: _GLOBAL__sub_I_BatteryService.cpp
0x0169d0: _ZN7android12SortedVectorINS_14BatteryService4InfoEED0Ev
0x0169b0: _ZN7android12SortedVectorINS_14BatteryService4InfoEED2Ev
0x0168d0: _ZN7android14BatteryService11cleanupImplEj

III. Porting symbols

We can now assemble everything together to port symbols to binary2 by using:

Exercise: Use diffing result to add symbols to the second binary

[39]:
sym = lief.Symbol()
for match in diff.function_matches:
    if match.address1 in symbols:
        # Create the symbol
        sym = symbols[match.address1]
        sym.value = match.address2
        binary2.add_static_symbol(sym)

# Save the patched binary
s = "libsensorservice-2-with-symbols.so"
binary2.write(s)
print(f"new library created: {s}")
new library created: libsensorservice-2-with-symbols.so

By loading at the generated shared library the result is the following:

symbols after porting