0

In runtime I'm trying to recover an address of a function that is not exported but is available through shared library's symbols table and therefore is visible to the debugger.

I'm working on advanced debugging procedure that needs to capture certain events and manipulate runtime. One of the actions requires knowledge of an address of a private function (just the address) which is used as a key elsewhere.

My current solution calculates offset of that private function relative to a known exported function at build time using nm. This solution restricts debugging capabilities since it depends on a particular build of the shared library.

The preferable solution should be capable of recovering the address in runtime.

I was hoping to communicate with the attached debugger from within the app, but struggle to find any API for that.

What are my options?

Kentzo
  • 3,881
  • 29
  • 54
  • What are you trying to communicate to the debugger? The address of the function? Can you just print it out and then enter that manually into the debugger? – kaylum Jan 08 '20 at 00:34
  • Function in question is from a 3rd party image, outside of my control. I need to recover its address by name _using the debugger_ so I can use it elsewhere in app's code. – Kentzo Jan 08 '20 at 00:37
  • Sound like what you really want to do is _query the debug database_ for information on the symbol. – 1201ProgramAlarm Jan 08 '20 at 00:50
  • I'm not entirely sure that this symbol is only available through the debug database: there may as well be a linker flag I'm not aware of. – Kentzo Jan 08 '20 at 01:12

2 Answers2

1

In runtime I'm trying to recover an address of a function that is not exported but is available through shared library's symbols table and therefore is visible to the debugger.

Debugger is not a magical unicorn. If the symbol table is available to the debugger, it is also available to your application.

I need to recover its address by name using the debugger ...

That is entirely wrong approach.

Instead of using the debugger, read the symbol table for the library in your application, and use the info gained to call the target function.

Reading ELF symbol table is pretty easy. Example. If you are not on ELF platform, getting equivalent info should not be much harder.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • My target platform is macOS, so the format is MachO. Do you happen to know whether headers with necessary struct definitions are present in the system? – Kentzo Jan 08 '20 at 02:54
  • @Kentzo I don't know much about MacOS, except that both GDB and LLDB can be built on it. For GDB, nothing more was required than Xcode, so the required headers *must* be available in Xcode. – Employed Russian Jan 08 '20 at 03:39
  • You can parse Mach-O binaries and extact the DWARF info from those, too. Here's a sample in Python: https://github.com/sevaa/dwex/blob/master/dwex/formats.py The `filebytes` and `pyelftools` libraries are both available via pip. – Seva Alekseyev Feb 07 '20 at 18:19
1

In lldb you can quickly find the address by setting a symbolic breakpoint if it's known to the debugger by whatever means:

b symbolname

If you want call a non exported function from a library without a debugger attached there are couple of options but each will not be reliable in the long run:

  • Hardcode the offset from an exported library and call exportedSymbol+offset (this will work for a particular library binary version but will likely break for anything else)
  • Attempt to search for a binary signature of your nonexported function in the loaded library. (slightly less prone to break but the binary signature might always change)

Perhaps if you provide more detailed context what are you trying achieve better options can be considered.

Update:
Since lldb is somehow aware of the symbol I suspect it's defined in Mach-O LC_SYMTAB load command of your library. To verify that you could inspect your lib binary with tools like MachOView or MachOExplorer . Or Apple's otool or Jonathan Levin's jtool/jtool2 in console.

Here's an example from very 1st symbol entry yielded from LC_SYMTAB in MachOView. This is /usr/lib/dyld binary enter image description here In the example here 0x1000 is virtual address. Your library most likely will be 64bit so expect 0x10000000 and above. The actual base gets randomized by ASLR, but you can verify the current value with

sample yourProcess

yourProcess being an executable using the library you're after. The output should contain:

Binary Images:
       0x10566a000 -        0x105dc0fff  com.apple.finder (10.14.5 - 1143.5.1) <3B0424E1-647C-3279-8F90-4D374AA4AC0D> /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
       0x1080cb000 -        0x1081356ef  dyld (655.1.1) <D3E77331-ACE5-349D-A7CC-433D626D4A5B> /usr/lib/dyld
...

These are the loaded addresses 0x100000000 shifted by ASLR. There might be more nuances how exactly those addresses are chosen for dylibs but you get the idea.

Tbh I've never needed to find such address programmatically but it's definitely doable (as /usr/bin/sample is able to do it).

From here to achieve something practically:

  1. Parse Mach-o header of your lib binary (check this & this for starters)
  2. Find LC_SYMTAB load command
  3. Find your symbol text based entry and find the virtual address (the red box stuff)
  4. Calculate ASLR and apply the shift

There is some C Apple API for parsing Mach-O. Also some Python code exists in the wild (being popular among reverse engineering folks).

Hope that helps.

Kamil.S
  • 5,205
  • 2
  • 22
  • 51
  • You got a good grasp of the problem: my current solution is almost exactly what you suggested! Edited the Q with more details. – Kentzo Jan 08 '20 at 19:09
  • Could you elaborate regarding MachO parsing? – Kentzo Jan 08 '20 at 20:26
  • Addes some links, you could also inspect MachOView or MachOExplorer , though I'd recommend sticking to something fairly simple. Also check out existing answers on SO that cover some processing of Mach-O to get an idea how it's done. Before you do any coding I recommend verifying what I written in MachOView to verify if it's really applicable to your case. – Kamil.S Jan 08 '20 at 20:29
  • Nice, that seems to be easier than I thought. Have to figure out how to locate library's load address in runtime. – Kentzo Jan 08 '20 at 20:47
  • @Kentzo please share your findings and/or code as an answer if you move further as it's fairly interesting case. – Kamil.S Jan 09 '20 at 05:29