Previously in this series of articles, we delinked an executable produced by a standard linker back into a relocatable object file by using Ghidra and lots of Python script snippets. In this article we will do the same thing, but this time we will use a Ghidra extension to do all the hard work for us.

Installing the Ghidra delinker extension

My Ghidra delinker extension largely automates the process of delinking by:

  1. Providing an analyzer that synthesizes relocation entries for a program ;
  2. Providing exporters to slice out parts of a program as relocatable object files.

To install the extension, we’ll follow the instruction laid out in the README.md file.

First, download the ZIP file from the releases page. Then, launch Ghidra and click on File > Install Extensions... to bring up the extension list window (this article is written with the 0.1.0 version). Click on the green “➕” button on the top-right corner with the tooltip Add extension, then select the ZIP file. The extension should appear in the list and be enabled:

Click OK to close the extension list window. Ghidra will show a pop-up requesting a restart for the installation to take effect. Dismiss it and restart Ghidra.

Now that the extension is installed, open up ascii-table.elf in the case study project. Ghidra will prompt to configure newly-detected extension plugins, click Yes. Enable the RelocationTableSynthesizedPlugin in the list by clicking on its checkbox:

Click OK to close the plugin list window.

Delinking our case study the lazy way

Now that the extension is installed and configured, select the .sbss, .sdata, .rodata and .data sections in the program tree, then right-click on them and use Select Addresses. The address range 0x00400150-0x00410b20 should now be selected in the listing view.

We’ll invoke the extension’s analyzer on this selection by clicking on Analysis > One Shot > Relocation table synthesizer. Then, click on Window > Relocation Table (synthesized) to display the recovered relocations:

Before exporting a subset of the program, we need to define what parts we want to export. Go to the print_number function in the listing view and remove it from the selection by holding Ctrl and then dragging a left click from the start to the end of the function. The selection should now cover the entire program except for the print_number function.

To bring up the export dialog, hit the O key (or click on File > Export Program...). Select the ELF Relocatable object format, make sure the Selection Only checkbox is checked and export the file as ascii-table.delinker.o.

Binary patching through delinking

Like in the previous part, we can take a peek at this file using the toolchain:

$ mips-linux-gnu-objdump --wide --file-headers ascii-table.delinker.o

ascii-table.delinker.o:     file format elf32-tradlittlemips
architecture: mips:3000, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000

$ mips-linux-gnu-objdump --wide --section-headers ascii-table.delinker.o

ascii-table.delinker.o:     file format elf32-tradlittlemips

Sections:
Idx Name          Size      VMA       LMA       File off  Algn  Flags
  0 .text         000007b8  00000000  00000000  000005c0  2**4  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .rodata       00000160  00000000  00000000  00000d80  2**4  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
  2 .sdata        00000008  00000000  00000000  00000ee0  2**4  CONTENTS, ALLOC, LOAD, DATA
  3 .sbss         0000000c  00000000  00000000  00000000  2**4  ALLOC

$ mips-linux-gnu-nm --format sysv --line-numbers --no-sort ascii-table.delinker.o


Symbols from ascii-table.delinker.o:

Name                  Value   Class        Type         Size     Line  Section

sys_getpid          |00000000|   T  |              FUNC|00000024|     |.text
sys_kill            |00000024|   T  |              FUNC|00000024|     |.text
sys_write           |00000048|   T  |              FUNC|00000024|     |.text
_nolibc_memcpy_up   |0000006c|   T  |              FUNC|00000038|     |.text
fileno              |000000a4|   T  |              FUNC|0000002c|     |.text
__start             |000000d0|   T  |              FUNC|00000064|     |.text
write               |00000134|   T  |              FUNC|00000048|     |.text
fputc               |0000017c|   T  |              FUNC|00000064|     |.text
putchar             |000001e0|   T  |              FUNC|0000002c|     |.text
print_ascii_entry   |0000020c|   T  |              FUNC|0000013c|     |.text
main                |00000348|   T  |              FUNC|000000c0|     |.text
isalnum             |00000414|   T  |              FUNC|00000038|     |.text
isalpha             |0000044c|   T  |              FUNC|00000038|     |.text
iscntrl             |00000484|   T  |              FUNC|00000038|     |.text
isdigit             |000004bc|   T  |              FUNC|00000038|     |.text
isgraph             |000004f4|   T  |              FUNC|00000038|     |.text
islower             |0000052c|   T  |              FUNC|00000038|     |.text
isprint             |00000564|   T  |              FUNC|00000038|     |.text
ispunct             |0000059c|   T  |              FUNC|00000038|     |.text
isspace             |000005d4|   T  |              FUNC|00000038|     |.text
isupper             |0000060c|   T  |              FUNC|00000038|     |.text
isxdigit            |00000644|   T  |              FUNC|00000038|     |.text
raise               |00000684|   T  |              FUNC|0000004c|     |.text
memmove             |000006d0|   T  |              FUNC|00000058|     |.text
memcpy              |00000728|   T  |              FUNC|00000028|     |.text
memset              |00000750|   T  |              FUNC|00000030|     |.text
abort               |00000780|   T  |              FUNC|00000038|     |.text
s_ascii_properties  |00000000|   R  |            OBJECT|00000050|     |.rodata
_ctype_             |00000050|   R  |            OBJECT|00000101|     |.rodata
COLUMNS             |00000000|   D  |            OBJECT|00000004|     |.sdata
NUM_ASCII_PROPERTIES|00000004|   D  |            OBJECT|00000004|     |.sdata
errno               |00000000|   B  |            OBJECT|00000004|     |.sbss
_auxv               |00000004|   B  |            OBJECT|00000004|     |.sbss
environ             |00000008|   B  |            OBJECT|00000004|     |.sbss
_gp                 |        |   U  |            NOTYPE|        |     |*UND*    ascii-table.delinker.o:0
print_number        |        |   U  |            NOTYPE|        |     |*UND*    ascii-table.delinker.o:0

$ mips-linux-gnu-objdump --wide --reloc ascii-table.delinker.o

ascii-table.delinker.o:     file format elf32-tradlittlemips

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
000000b8 R_MIPS_HI16       errno
000000c0 R_MIPS_LO16       errno
000000d0 R_MIPS_HI16       _gp
000000d4 R_MIPS_LO16       _gp
000000ec R_MIPS_HI16       environ
000000f0 R_MIPS_LO16       environ
00000108 R_MIPS_HI16       _auxv
0000010c R_MIPS_LO16       _auxv
00000120 R_MIPS_26         main
0000013c R_MIPS_HI16       sys_write
00000140 R_MIPS_LO16       sys_write
00000168 R_MIPS_HI16       errno
0000016c R_MIPS_LO16       errno
0000018c R_MIPS_HI16       fileno
00000190 R_MIPS_LO16       fileno
000001a8 R_MIPS_HI16       write
000001ac R_MIPS_LO16       write
000001ec R_MIPS_HI16       fputc
000001f0 R_MIPS_LO16       fputc
00000234 R_MIPS_HI16       print_number
00000238 R_MIPS_LO16       print_number
00000248 R_MIPS_HI16       putchar
0000024c R_MIPS_LO16       putchar
0000025c R_MIPS_HI16       isgraph
00000260 R_MIPS_LO16       isgraph
00000278 R_MIPS_HI16       putchar
0000027c R_MIPS_LO16       putchar
0000028c R_MIPS_HI16       putchar
00000290 R_MIPS_LO16       putchar
000002ac R_MIPS_HI16       putchar
000002b0 R_MIPS_LO16       putchar
000002c8 R_MIPS_HI16       putchar
000002cc R_MIPS_LO16       putchar
0000030c R_MIPS_HI16       putchar
00000310 R_MIPS_LO16       putchar
00000364 R_MIPS_HI16       putchar
00000368 R_MIPS_LO16       putchar
00000384 R_MIPS_GPREL16    COLUMNS
000003ac R_MIPS_HI16       s_ascii_properties
000003b0 R_MIPS_LO16       s_ascii_properties
000003bc R_MIPS_HI16       print_ascii_entry
000003c0 R_MIPS_LO16       print_ascii_entry
000003cc R_MIPS_GPREL16    COLUMNS
00000424 R_MIPS_HI16       _ctype_
00000428 R_MIPS_LO16       _ctype_
0000045c R_MIPS_HI16       _ctype_
00000460 R_MIPS_LO16       _ctype_
00000494 R_MIPS_HI16       _ctype_
00000498 R_MIPS_LO16       _ctype_
000004cc R_MIPS_HI16       _ctype_
000004d0 R_MIPS_LO16       _ctype_
00000504 R_MIPS_HI16       _ctype_
00000508 R_MIPS_LO16       _ctype_
0000053c R_MIPS_HI16       _ctype_
00000540 R_MIPS_LO16       _ctype_
00000574 R_MIPS_HI16       _ctype_
00000578 R_MIPS_LO16       _ctype_
000005ac R_MIPS_HI16       _ctype_
000005b0 R_MIPS_LO16       _ctype_
000005e4 R_MIPS_HI16       _ctype_
000005e8 R_MIPS_LO16       _ctype_
0000061c R_MIPS_HI16       _ctype_
00000620 R_MIPS_LO16       _ctype_
00000654 R_MIPS_HI16       _ctype_
00000658 R_MIPS_LO16       _ctype_
00000694 R_MIPS_HI16       sys_getpid
00000698 R_MIPS_LO16       sys_getpid
000006ac R_MIPS_HI16       sys_kill
000006b0 R_MIPS_LO16       sys_kill
00000730 R_MIPS_HI16       _nolibc_memcpy_up
00000734 R_MIPS_LO16       _nolibc_memcpy_up
00000788 R_MIPS_HI16       sys_getpid
0000078c R_MIPS_LO16       sys_getpid
000007a0 R_MIPS_HI16       sys_kill
000007a4 R_MIPS_LO16       sys_kill


RELOCATION RECORDS FOR [.rodata]:
OFFSET   TYPE              VALUE 
00000000 R_MIPS_32         isgraph
00000008 R_MIPS_32         isprint
00000010 R_MIPS_32         iscntrl
00000018 R_MIPS_32         isspace
00000020 R_MIPS_32         ispunct
00000028 R_MIPS_32         isalnum
00000030 R_MIPS_32         isalpha
00000038 R_MIPS_32         isdigit
00000040 R_MIPS_32         isupper
00000048 R_MIPS_32         islower


Again, we’ll modify our executable by linking that object file with print_number_octal.o:

$ mips-linux-gnu-gcc -EL -static -no-pie -nostdlib -o ascii-table.delinker.elf ascii-table.delinker.o print_number_octal.o

Finally, let’s observe the final product:

$ qemu-mipsel ascii-table.delinker.elf
0000     c              0040    p s             0100 @ gp  !            0140 ` gp  !     
0001     c              0041 ! gp  !            0101 A gp   Aa U        0141 a gp   Aa  l
0002     c              0042 " gp  !            0102 B gp   Aa U        0142 b gp   Aa  l
0003     c              0043 # gp  !            0103 C gp   Aa U        0143 c gp   Aa  l
0004     c              0044 $ gp  !            0104 D gp   Aa U        0144 d gp   Aa  l
0005     c              0045 % gp  !            0105 E gp   Aa U        0145 e gp   Aa  l
0006     c              0046 & gp  !            0106 F gp   Aa U        0146 f gp   Aa  l
0007     c              0047 ' gp  !            0107 G gp   Aa U        0147 g gp   Aa  l
0010     c              0050 ( gp  !            0110 H gp   Aa U        0150 h gp   Aa  l
0011     cs             0051 ) gp  !            0111 I gp   Aa U        0151 i gp   Aa  l
0012     cs             0052 * gp  !            0112 J gp   Aa U        0152 j gp   Aa  l
0013     cs             0053 + gp  !            0113 K gp   Aa U        0153 k gp   Aa  l
0014     cs             0054 , gp  !            0114 L gp   Aa U        0154 l gp   Aa  l
0015     cs             0055 - gp  !            0115 M gp   Aa U        0155 m gp   Aa  l
0016     c              0056 . gp  !            0116 N gp   Aa U        0156 n gp   Aa  l
0017     c              0057 / gp  !            0117 O gp   Aa U        0157 o gp   Aa  l
0020     c              0060 0 gp   A d         0120 P gp   Aa U        0160 p gp   Aa  l
0021     c              0061 1 gp   A d         0121 Q gp   Aa U        0161 q gp   Aa  l
0022     c              0062 2 gp   A d         0122 R gp   Aa U        0162 r gp   Aa  l
0023     c              0063 3 gp   A d         0123 S gp   Aa U        0163 s gp   Aa  l
0024     c              0064 4 gp   A d         0124 T gp   Aa U        0164 t gp   Aa  l
0025     c              0065 5 gp   A d         0125 U gp   Aa U        0165 u gp   Aa  l
0026     c              0066 6 gp   A d         0126 V gp   Aa U        0166 v gp   Aa  l
0027     c              0067 7 gp   A d         0127 W gp   Aa U        0167 w gp   Aa  l
0030     c              0070 8 gp   A d         0130 X gp   Aa U        0170 x gp   Aa  l
0031     c              0071 9 gp   A d         0131 Y gp   Aa U        0171 y gp   Aa  l
0032     c              0072 : gp  !            0132 Z gp   Aa U        0172 z gp   Aa  l
0033     c              0073 ; gp  !            0133 [ gp  !            0173 { gp  !     
0034     c              0074 < gp  !            0134 \ gp  !            0174 | gp  !     
0035     c              0075 = gp  !            0135 ] gp  !            0175 } gp  !     
0036     c              0076 > gp  !            0136 ^ gp  !            0176 ~ gp  !     
0037     c              0077 ? gp  !            0137 _ gp  !            0177     c       

For the fifth time, we have successfully modified our case study to print the ASCII table in octal.

The files for this case study can be found here: case-study.tar.gz

Conclusion

We have successfully delinked parts of a normal executable back into a relocatable object file with a Ghidra extension and used it to make a new, modified executable. Unlike the previous times where we built up our object file with lots of Python script snippets and manual work, this extension automated the entire process in two easy steps, making the delinking technique practical for everyday reverse-engineering work.

This concludes the currently planned articles in this series. While this series served as an introduction to reverse-engineering, there are many more topics, techniques and use-cases out there to study and practice in this field that is as much art as it is science.