trustedsec / ELFLoader

Other
251 stars 43 forks source link

Relocations issue when compiling with clang #3

Open alangh1k opened 2 years ago

alangh1k commented 2 years ago

Hi,

I was playing with this and noticed that when using clang to compile the target binary the ELFLoader segfaults when processing the relocations in section .rela.eh_frame.

This is the ELFLoader debug output from the gcc compiled target:

Section Header Entry Counter: 9
        Name is .rela.eh_frame
        Type is 0x4
        Flags are 0x40
        Addr is 0x0
        Offset is 0x250
        Size is 24
        Link is 10
        Info is 8
        AddrAlign is 8
        EntSize is 24
        Relocation Entries 1:
                Symbol: 
                Type: 0x2
                SymbolValue: 0x0
                Shndx: 0x1
                FirstAddress(NoAddend): 0x789b94ea6000
                FirstAddress: 0x789b94ea6000
                SecondAddress(NoOffset): 0x789b94eb5000
                SecondAddress: 0x789b94eb5020
                RelativeOffset: 0xffff0fe0
                Info: 0x200000002
                Offset: 0x20
                Addend: 0x0

And this is the output from the crashing one compiled with clang-14:

Section Header Entry Counter: 8
    Name is .rela.eh_frame
    Type is 0x4
    Flags are 0x40
    Addr is 0x0
    Offset is 0x1c0
    Size is 24
    Link is 10
    Info is 7
    AddrAlign is 8
    EntSize is 24
    Relocation Entries 1:
        Symbol: 
        Type: 0x2
        SymbolValue: 0x0
        Shndx: 0x2
        FirstAddress(NoAddend): 0x7ffff8000000
        FirstAddress: 0x7ffff8000000
        SecondAddress(NoOffset): (nil)
        SecondAddress: 0x20
        RelativeOffset: 0xf7ffffe0

As we see in the clang-generated binary the SecondAddress is null and therefore the target address for the memcpy will be 0x0 + 0x20 which will segfault:

memcpy(elfinfo.sectionMappings[elfinfo.sectHeader[counter].sh_info]+rel[c2].r_offset, &relativeOffset, 4);

Do you know why is the case? Are relocations created by clang different then the one created by gcc? The only thing I noticed is that they have different sh_info and shndx value but I don't know enough details about ELF to troubleshoot this further. I also tried an older version of clang (11) and, even though the ELFLoader wasn't crashing, the relocations were not correct (I couldn't see my printf when running the binary):

...Trying to run ptr......
�Returned from ptr
Cleaning up...

Build commands:

gcc -g -shared -DDEBUG -fPIC -fvisibility=hidden -DLIBRARY -Wall -I ./includes/ -DTESTING_MAIN ./src/beacon_compatibility.c ./src/ELFLoader.c -ldl -o libELFLoader.so
gcc -g -I ./includes/ runner.c -o runner.out -L . -lELFLoader
clang-14 -fPIC -c test.c -o test.o

And run with

LD_LIBRARY_PATH=. ./runner.out ./test.o 

My test application:

#include <stdio.h>

int main() {
   printf("Hello, World!\n");
   return 0;
}

Testing with Debian Bullseye and clang version 14.0.6 (and 11.1.0)

Thanks a lot

kev169 commented 2 years ago

I'll look into it and let you know if I figure anything out.

kev169 commented 2 years ago

Ok looks like clang is doing string relocations differently and a few function relocations so I'll have to add in those cases. Won't be done anytime soon and will work on that when I get time. For now we know that gcc builds the objects properly for them to run with the currently supported relocation types.

Additional note, the objects need to be built with a "go" function defined which is the entry point and is defined like this

int go(char* indata, int* outlen);
alangh1k commented 2 years ago

Thanks for taking a quick look!

Ok looks like clang is doing string relocations differently and a few function relocations so I'll have to add in those cases. Won't be done anytime soon and will work on that when I get time. For now we know that gcc builds the objects properly for them to run with the currently supported relocation types.

Which code/spec are you using as reference to do the implementation? Isn't there maybe some obscure clang flag that tells it to do relocations "gcc style" ? The nice thing about using LLVM/clang is that we can just plug in all the public obfuscation passes available for it.

Additional note, the objects need to be built with a "go" function defined which is the entry point and is defined like this

int go(char* indata, int* outlen);

Yeah I noticed that, I just changed the runner.c to run main instead of the predefined go which seems to work (with the gcc version of course)

kev169 commented 2 years ago

So inside of this I'm not specifically handling any relocation types for x86_64 just default to what I was seeing in my tests (which is the problem) and I have two handled for x86 only. I'm not sure what I was looking at initially, but I plan on using this to figure out how to handle the relocation types when I update it.

https://refspecs.linuxbase.org/elf/index.html

Will work on this in my spare time, but its not going to be a high priority for me at the moment so it probably won't be updated anytime soon. If you happen to figure out how to handle them before I get to it I'll be happy to merge a pull request in for it.