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

1

8

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 ?

Theism answered 29/12, 2021 at 23:39 Comment(15)
You usually do not specify the library file directly as ordinary input to gcc but rather via the -l flag. I.e. it should be g++ toto.cpp -L. -l:wd250hf64.so. Does this solve the problem? Otherwise, you could always try to load the library dynamically via dlopen.Devilry
@Devilry thanks for your help , however g++ toto.cpp -L. -l:wd250hf64.so returns the same errorTheism
@user17732522 yes certainly a error,Theism
@Devilry I tried with dlopen, but same issue (I edited my question to put the code i used for dlopen )Theism
I'd try producing my own dummy shared library, then comparing the two.Platen
See this answer. I think it explains why you can't bind the app to that symbol. I think this symbol will disappear after strip.Hostility
Does LD_LIBRARY_PATH contains the path to your library wd250hf64.so? Also, why do you specify a colon : in the command -l:wd250hf64.so?Fourpence
@Fourpence not specifying : i.e" g++ toto.cpp -L. -l wd250hf64.so" (with or without the space after the -l cause a /usr/bin/ld: cannot find -lwd250hf64.soTheism
@allan.simon, is the path to the library present in LD_LIBRARY_PATH?Fourpence
@Fourpence I put the .so in my current directory, and normally -L. is here for that, in any case, I added also the absolute path by re-exporting export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH but still the same issueTheism
@273K thanks, unfortunately I did know that, and it's more about "but then how to workaround that" , as the symbol is there , the code is there in the file, so what is the hack to do to make it visible . I've edited my question to make it clearer that I know there will be no 'clean' solutionTheism
In this case the first answer to your question is worth to attempt. Why do you think the method is static and returning void? It can be non-static and return a smart pointer.Hostility
@273K , indeed the answer seems worth trying , it's in my todo for today :) regarding static and return type, actually I don't know , my knowledge are very limited , so i assumed that if there was no return type it was void, so you mean there's no way for me to know purely based on the symbol, to know the return type and static/not static ?Theism
Yes, there is no way to know the return type and static or not static method.Hostility
@273K ok so if it's not static or it return something else, at worse it will segfault , because the linked itself can not know that i'm wrong, right ?Theism
T
2

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.

Taenia answered 30/6, 2022 at 18:52 Comment(3)
thanks, yes I know that i will have to resort to a hackish solution, as I don't own the source of the .so :) I think the patching thing may be do-able with lief-project.github.io/doc/latest/tutorials/… but It's not my area of expertise, so maybe that can help you helping me hahaTheism
ok so I wasn't able to go much further but i was able to change from "LOCAL" to global by using lief and doing ` x = lief.parse('wd250hf64.so') ; w = x.get_symbol('_ZN20CComposanteHyperFile11HExporteXMLEPKw') ; w.exported = true ; x.write('newlib.so') ` and now newlib.so contains the symbol with local changed to globalTheism
import lief x = lief.parse('newstuff.so') x.add_exported_function(0x118c90, '_ZN20CComposanteHyperFile11HExporteXMLEPKw') x.write('newstuff.so') and now it works :) 0x118c90 being the address I saw with nmTheism

© 2022 - 2024 — McMap. All rights reserved.