c++reverse-engineeringldnmreadelf

How to patch 3rd party .so file, to access non-exported symbol (C++)


I have some binary .fic files in a proprietary format , I have a wd250hf64.so from this vendor that contains a C++ method CComposanteHyperFile::HExporteXML(wchar_t* const path)

that I can see using nm

$ nm  --demangle   wd250hf64.so  --defined-only 


0000000000118c90 t CComposanteHyperFile::HExporteXML(wchar_t const*)

the unmangled version _ZN20CComposanteHyperFile11HExporteXMLEPKw is identical to what I have using my local g++ version

readelf gives

readelf -Ws wd250hf64.so  | grep _ZN20CComposanteHyperFile11HExporteXMLEPK

 19684: 0000000000118c90   119 FUNC    LOCAL  DEFAULT   11 _ZN20CComposanteHyperFile11HExporteXMLEPKw

now I try writing a very simple program

class CComposanteHyperFile {
    public:
    static void  HExporteXML(wchar_t const*);
};



int main() {
    CComposanteHyperFile::HExporteXML(L"file.fic");
    return 0;
}

but when I compile it with g++ toto.cpp -L. -l:wd250hf64.so

I got toto.cpp:(.text+0x10): undefined reference to 'CComposanteHyperFile::HExporteXML(wchar_t const*)'

I don't have more luck with dlopen

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int
main(int argc, char **argv)
{
    void *handle;
    void (*exportXML)(wchar_t const*);
    char *error;

   handle = dlopen("wd250hf64.so", RTLD_LAZY);
   *(void **) (&exportXML) = dlsym(handle, "_ZN20CComposanteHyperFile11HExporteXMLEPKw");

   if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }

    dlclose(handle);
    exit(EXIT_SUCCESS);
}
gcc -rdynamic -o foo toto.c -ldl
./foo
wd250hf64.so: undefined symbol: _ZN20CComposanteHyperFile11HExporteXMLEPKw

I understand that as it is not shown by nm with --extern-only it may be that this symbol is not "exported" and so it's not supposed to work normally

my question is

What is the hackish way of making the program to compile, by all means, even if it means manually patching the .so file ?


Solution

  • If you really want to be able to get at that symbol in any way possible, you could try to get its address relative to that of a known exported symbol, assuming that they're in the same section. For example, I made a simple dummy library with one exported function and one non-exported function.

    0x0000000000003890    12 FUNC    GLOBAL DEFAULT    16 function_we_exported
    0x00000000000038a0    12 FUNC    LOCAL  HIDDEN     16 function_we_forgot_to_export
    

    Because this library was contrived just for this answer, the non-exported function happens to be right next to it, 0x10 after function_we_exported. Using this information, we can hackily do this.

    const auto address = reinterpret_cast<char*>(dlsym(library, "function_we_exported"));
    reinterpret_cast<your_function_type>(address + 0x10)(...);
    

    As you can probably tell, this is pretty hacky, but if you have no other choice, I suppose this can be a way. Another way may be to patch the library and forcibly export it. Considering that they show up as GLOBAL/LOCAL and DEFAULT/HIDDEN, it's probably a flip of a flag or something, but I don't know how to do that at the moment. I'll update this answer if I do.