GaloisInc / macaw

Open source binary analysis tools.
BSD 3-Clause "New" or "Revised" License
190 stars 19 forks source link

Adjust PLT stub heuristics to account for `glink` in PowerPC #386

Open RyanGlScott opened 1 week ago

RyanGlScott commented 1 week ago

macaw-base's PLT-stub detection machinery is currently equipped to handle x86-64 and AArch32, but not any other architectures. Recently, I found myself wanting to extend these heuristics to PowerPC. Unfortunately, the current heuristics that we use for detecting PLT stubs in macaw-base is not equipped to handle the layout of PLT stubs in PowerPC. To see what I mean, observe that if you compile the following program using a PPC32 cross compiler:

#include <stdlib.h>

int main(void) {
    int* x = malloc(sizeof(int));
    *x = 42;
    free(x);
    return 0;
}
$ ~/Software/powerpc-linux-musl-cross/bin/powerpc-linux-musl-gcc test.c -O0 -no-pie -o test.exe
$ ~/Software/powerpc-linux-musl-cross/bin/powerpc-linux-musl-objdump -DR test.exe

The objdump output will show the following:

``` Disassembly of section .text: 1000047c
: 1000047c: 94 21 ff e0 stwu r1,-32(r1) 10000480: 7c 08 02 a6 mflr r0 10000484: 90 01 00 24 stw r0,36(r1) 10000488: 93 c1 00 18 stw r30,24(r1) 1000048c: 93 e1 00 1c stw r31,28(r1) 10000490: 7c 3f 0b 78 mr r31,r1 10000494: 42 9f 00 05 bcl 20,4*cr7+so,10000498 10000498: 7f c8 02 a6 mflr r30 1000049c: 3f de 00 02 addis r30,r30,2 100004a0: 3b de 7a 7c addi r30,r30,31356 100004a4: 38 60 00 04 li r3,4 100004a8: 48 00 00 49 bl 100004f0 100004ac: 7c 69 1b 78 mr r9,r3 100004b0: 91 3f 00 08 stw r9,8(r31) 100004b4: 81 3f 00 08 lwz r9,8(r31) 100004b8: 39 40 00 2a li r10,42 100004bc: 91 49 00 00 stw r10,0(r9) 100004c0: 80 7f 00 08 lwz r3,8(r31) 100004c4: 48 00 00 6d bl 10000530 100004c8: 39 20 00 00 li r9,0 100004cc: 7d 23 4b 78 mr r3,r9 100004d0: 39 7f 00 20 addi r11,r31,32 100004d4: 80 0b 00 04 lwz r0,4(r11) 100004d8: 7c 08 03 a6 mtlr r0 100004dc: 83 cb ff f8 lwz r30,-8(r11) 100004e0: 83 eb ff fc lwz r31,-4(r11) 100004e4: 7d 61 5b 78 mr r1,r11 100004e8: 4e 80 00 20 blr 100004ec: 60 00 00 00 nop 100004f0 : 100004f0: 3d 60 10 02 lis r11,4098 100004f4: 81 6b 00 00 lwz r11,0(r11) 100004f8: 7d 69 03 a6 mtctr r11 100004fc: 4e 80 04 20 bctr 10000500 <__deregister_frame_info@plt>: 10000500: 3d 60 10 02 lis r11,4098 10000504: 81 6b 00 04 lwz r11,4(r11) 10000508: 7d 69 03 a6 mtctr r11 1000050c: 4e 80 04 20 bctr 10000510 <__libc_start_main@plt>: 10000510: 3d 60 10 02 lis r11,4098 10000514: 81 6b 00 08 lwz r11,8(r11) 10000518: 7d 69 03 a6 mtctr r11 1000051c: 4e 80 04 20 bctr 10000520 <__register_frame_info@plt>: 10000520: 3d 60 10 02 lis r11,4098 10000524: 81 6b 00 0c lwz r11,12(r11) 10000528: 7d 69 03 a6 mtctr r11 1000052c: 4e 80 04 20 bctr 10000530 : 10000530: 3d 60 10 02 lis r11,4098 10000534: 81 6b 00 10 lwz r11,16(r11) 10000538: 7d 69 03 a6 mtctr r11 1000053c: 4e 80 04 20 bctr 10000540 <__glink>: 10000540: 60 00 00 00 nop 10000544: 60 00 00 00 nop 10000548: 60 00 00 00 nop 1000054c: 60 00 00 00 nop 10000550 <__glink_PLTresolve>: 10000550: 3d 80 10 02 lis r12,4098 10000554: 3d 6b f0 00 addis r11,r11,-4096 10000558: 80 0c ff f8 lwz r0,-8(r12) 1000055c: 39 6b fa c0 addi r11,r11,-1344 10000560: 7c 09 03 a6 mtctr r0 10000564: 7c 0b 5a 14 add r0,r11,r11 10000568: 81 8c ff fc lwz r12,-4(r12) 1000056c: 7d 60 5a 14 add r11,r0,r11 10000570: 4e 80 04 20 bctr 10000574: 60 00 00 00 nop 10000578: 60 00 00 00 nop 1000057c: 60 00 00 00 nop 10000580: 60 00 00 00 nop 10000584: 60 00 00 00 nop 10000588: 60 00 00 00 nop 1000058c: 60 00 00 00 nop Disassembly of section .plt: 10020000 <.plt>: 10020000: 10 00 05 40 vsubcuq v0,v0,v0 10020000: R_PPC_JMP_SLOT malloc 10020004: 10 00 05 44 vorc v0,v0,v0 10020004: R_PPC_JMP_SLOT __deregister_frame_info 10020008: 10 00 05 48 vncipher v0,v0,v0 10020008: R_PPC_JMP_SLOT __libc_start_main 1002000c: 10 00 05 4c vbpermq v0,v0,v0 1002000c: R_PPC_JMP_SLOT __register_frame_info 10020010: 10 00 05 50 .long 0x10000550 10020010: R_PPC_JMP_SLOT free ```

This looks quite different from how PLT stubs work on x86-64 and AArch32. Unlike on those architectures, on PowerPC we see that the main function invokes PLT stubs that are located in the .text section rather than the .plt section. This is bad news for macaw's current heuristics, which always assume that PLT stubs are located in a .plt or .plt.got section. What's more, there is a .plt section, but it contains the addresses of PLT entries (which refer to a special __glink function) rather than the PLT stubs themselves. In this sense, the .plt section on PowerPC behaves more like the .got/.got.plt section on other architectures. (This blog post explains the nuances better than I can.)

As such, macaw's heuristics will return incorrect PLT stub addresses when used on a PowerPC binary. It is likely possible to generalize the heuristics to make it work over PowerPC, but we may need to revisit some assumptions that we currently make about what things live where.