I'm trying to see if I can append function code in the .text section of an ELF while still maintaining the execution flow of the original ELF. Ideally, I want to call the new function but that's it's own mountain to climb. I'm more concerned with just adding the code. Is this realistic? Or am I way over my head? I've been able to add the code by simply overwriting bytes. However, I would like to extend the .text section and insert it. If there's a better method of inserting new functionality into an ELF I'm all ears. But any guidance is helpful.
-
1There are well-known techniques that can be used to add code to an ELF binary. What is your goal? Is the binary statically linked? Is the section header table present in the binary?– julian ♦Commented Aug 17, 2019 at 11:30
-
1Hi and welcome to RE.SE. Adding to what @julian wrote: what has python to do with it?– 0xC0000022L ♦Commented Aug 17, 2019 at 21:56
-
it does have the standard dynamically linked libraries but I want to add a user defined function to the ELF. The section header table is present– staygeCommented Aug 19, 2019 at 13:50
3 Answers
It is possible to do what you want. There are some difficulties you may encounter though. I'll show you a short instruction how it can be done in several different cases.
- Have a look at ELF specification to know how every file in that format looks like, which sections it consists of and how they are located in the process image at the runtime. Even when there exist tools that can perform the job automatically for you, it's good to know what has to be done "underneath".
- Write and compile the code you want to add. You can do it later of course, but this way you will know how much space you need to add.
- Having the knowledge obtained in
1.
, you can now interpret the output ofreadelf -S executableToPatch
command. It will list the sections present in the executable along with the information such as their offset in the file. - If you are lucky, the offset of the section you want to update + its basic size + size of the code you want to add is less than the offset of the next section. In such a case, you simply want to create a file containing the new content of this section (that is, entire content before your modification and the extra code appended by you). Then you can simply run:
objcopy --update-section .text=fileWithTheNewContent nameOfExe nameOfExePatched
and you are done. - If you are not so lucky, you can still run the command from the previous point, but you will get messages similar to this:
objcopy: nameOfExe: section .text offset adjusted to newOffset
, which simply means thatobjcopy
has moved some subsequent sections forward in such a way that they don't overlap with each other (which, from1.
, is one of the requirements of the ELF specification). However, contrary to the previous point, you cannot simply finish here, because it could be the case that some section with data has changed its offset as well. - Check which sections have been relocated. If none of them contains data, you can now test the program whether it behaves the same as before your modification. If it does, your job is done - your code has been successfully updated.
- If it behaves differently or some of your sections with data has changed its offset, it is necessary to fix the data references to this section, that is to patch the code, so that it references offsets in the new data section location. For example, when
.rodata
has been moved forward by4096
bytes, you will have to change all references to.rodata
by adding4096
to each address, i.e. if you havemov rax, [rip+0x20]
you want to change it tomov rax, [rip+0x1020]
, but it isn't straightforward to do, although it's still possible.
-
Thank you! I've recently tried the obj copy - I now see that I have .rodata references in the .text section which explains quite a bit. Now going through and updating those will definitely be the next challenge. I appreciate it!– staygeCommented Aug 19, 2019 at 23:08
I would like to extend the .text section and insert [code]. If there's a better method of inserting new functionality into an ELF I'm all ears.
Techniques for adding arbitrary code to ELF files were pioneered by Linux virus writers beginning all the way back in the 1990s. In comparison with the methods they developed, as well as with more modern techniques, taking the objcopy
route seems rather crude. There is no need to re-invent the wheel.
More modern approaches to manipulating ELF binary runtime behavior have emerged since then, including a variety of injection techniques and instrumentation frameworks.
For the purposes of this question we can divide approaches to modifying binary functionality into 2 approaches:
- adding code to the file itself (static modification, occurs prior to program runtime)
- inducing new behavior via injection (runtime modification).
Static Modification Techniques
Text Segment Padding Infection
The classic ELF text
segment (not section) padding infection method seems to be the most closely related to what you are currently attempting.
Page padding at segment borders however provides a practical location for parasite code given that its size is able. This space will not interfere with the original segments, requiring no relocation. Following the guideline just given of preferencing the text segment, we can see that the padding at the end of the text segment is a viable solution. 1
The algorithm to accomplish this is as follows:
- Increase
p_shoff
byPAGE_SIZE
in the ELF header- Patch the insertion code (parasite) to jump to the entry point (original)
- Locate the text segment program header
- Modify the entry point of the ELF header to point to the new code (
p_vaddr
+p_filesz
)- Increase
p_filesz
by account for the new code (parasite)- Increase
p_memsz
to account for the new code (parasite)- For each
phdr
who's segment is after the insertion (text segment)
- increase
p_offset
byPAGE_SIZE
- For the last
shdr
in the text segment
- increase
sh_len
by the parasite length- For each
shdr
who's section resides after the insertion
- Increase
sh_offset
by PAGE_SIZE- Physically insert the new code (parasite) and pad to
PAGE_SIZE
, into the file - text segmentp_offset
+p_filesz
(original) 1
In addition to an explanation of the technique as well as the source code for a virus that implements this technique included with the 1998 article [1], there is a 2016 (it still works, after all these years) tutorial on ELF segment padding infection available at 0x00sec called ELFun File Injector.
Besides the text segment infection approach, there is also data segment infection, which is quite similar. One can also add an additional segment if they so choose.
PLT/GOT Infection
First detailed in a Phrack article back in 2000 2, this method involves patching the PLT/GOT to point to code inside the binary, which has been inserted via an infection method rather than pointing at code in a shared library dynamically linked to the binary:
It can be noted that we can change [function name in GOT] to point to our own code, thus replacing library calls. If we save the state of the GOT before replacing, we can call the old library routine and thus redirect any library call. 2
The easiest way to implement this method seems to be using the LIEF instrumentation framework. A tutorial for doing this is provided in the LIEF documentation under the title Infecting the plt/got. Note that in the tutorial the GOT is patched instead of the PLT, and code is added to the file by creating a new segment (one of the aforementioned infection techniques).
There are additional techiniques besides this for modifying the file directly, but segment padding infection and PLT/GOT infection seem to be the most straightforward.
Runtime Modification Techniques
Since DLL injection and runtime process manipulation are less directly related to your current task, I won't go into much detail.
- DLL injection via
LD_PRELOAD
is incredibly powerful and useful. 2 tutorials I have found to be useful are- The LD_PRELOAD trick - for binaries compiled from C source
- Notes on function interposition in C++ for binaries compiled from C++ source
- DLL hooking can be accomplished with LIEF as well: see ELF Hooking
- process code injection using
ptrace()
Some methods are more obscure, such as the DT_NEEDED
infection detailed in The Cerberus ELF Interface and "subversive dynamic linking" described in Cheating the ELF
References
Unix Viruses. Silvio Cesare
SHARED LIBRARY CALL REDIRECTION VIA ELF PLT INFECTION, Silvio Cesare
-
While I was thinking about that route - I don't want my code to be executed first. I want to modify an original function call in main to call my code. While I change the entry point to my code would that not execute my code immediately?– staygeCommented Aug 21, 2019 at 15:08
-
@stayge You have been presented with many routes.
I want to modify an original function call in main to call my code.
<- Is a shared library function being called? Is patching theCALL
address to jump to your inserted code sufficient? Why are you operating under this constraint? Please clearly describe the problem.– julian ♦Commented Aug 21, 2019 at 15:23 -
A user-defined function is being called. I have tried putting my code into a .so and using LD_PRELOAD but it only overrides library functions. I would like to patch the call so it can jump to my function and then return back to main and continue executing. It's just a matter of where to put the code without having to rework the whole ELF– staygeCommented Aug 21, 2019 at 17:10
-
@stayge Yes, this approach to using DLL injection will not work, since you want to interpose code resident within the binary itself rather than code in a DLL. As far as I know, you can use the text segment infection method without patching the entry point; the
CALL
address of the function can be patched instead to point to your code in the text segment. If you are calling a function via this kind of detour (same with theobjcopy
route), stack alignment may be an issue.– julian ♦Commented Aug 22, 2019 at 6:32
I wrote a small utility back in high school to do something similar: https://github.com/jdefrancesco/elfy
You inject the .note section with the payload and it will modify the ELF entry pointing to the .note section. Afterwards it will jump back to the original entry point.