48

I want to add some functionality to an existing binary file. The binary file was created using gcc.

  • Do I need to decompile the binary first, even though I sufficiently understand the functioning of the program ?
  • How should I go about adding the necessary code ?
  • Do I need any tools to be able to do this ?
3
  • 4
    for what platform eg windows, linux?
    – Remko
    Mar 23, 2013 at 14:06
  • What is the functionality that you want to add? because depending on that there are different approaches. For example for automating a GUI you use a different technique that for example changing a database engine.
    – sw.
    Apr 6, 2013 at 12:30

8 Answers 8

39

There are several broad ways in which you could do this.

  1. Dynamic instrumentation

    Tools such as PIN, Valgrind, or DynamoRIO allow you to dynamically change the behavior of a program. For instance, you can add calls to new functions at particular addresses, intercept library calls and change them, and much more.

    The downside is that dynamic instrumentation often has high overhead.

  2. Static instrumentation

    You can also try to statically modify the program to add the desired behavior. One challenge is that you often need to muck around with the executable file format. Some tools, such as elfsh from the ERESI project exist for this, but I have found them buggy and difficult to use.

    Another strategy for static instrumentation is to "recompile". You can do this by decompiling the program, modifying the source code, and recompiling. In theory, you could also use a tool like BAP to lift the program to IL, modify it, and then re-compile it using LLVM. However, the current version is probably not mature enough for this.

  3. Dynamic loading

    You can use LD_PRELOAD to override functions that are going to be dynamically linked. This is a nice option when you want to change the behavior of a library function. Naturally, it does not work on statically linked binaries, or for static functions.

  4. Binary patching

    You can often make simple changes to a binary using a hex-editor. For instance, if there is a function call or branch you would like to skip, you can often replace it with nop instructions. If you need to add a large amount of new code, you will probably need to use something like elfsh from the ERESI project to help you resize the binary.

0
18

Very often, you can change the behavior of a program by carefully hooking into it. Whether you can add the functionality you want this way depends on how the program is constructed. It helps if the program comes in the form of one main executable plus several libraries.

You can hook into any call that the program makes to shared libraries by linking your own library in first, with LD_PRELOAD. Write a library that defines a function foo, and set the environment variable LD_PRELOAD to the path to your compiled (.so) library when you start the program: then the program will call your foo instead of the one it intends. You can call the original foo function from your replacement by obtaining a pointer to it with dlsym().

Here are a few examples and tutorials:

Some examples of programs that use LD_PRELOAD:

The limitation of LD_PRELOAD is that you can only intercept function calls that are resolved at runtime (dynamic linking). If you want to intercept an internal call, you'll have to resort to heavier-weight techniques (modifying the executable on-disk, or in-memory with ptrace).

7

I want to add some functionality to an existing binary file.

So in general these four bigger Questions apply to modifying an Executeable:

The first basic Question posed: Is the Program wary of Code Modifications (Self-Checking, Anti-Debug-Tricks, Copy protection, ...)?

If so:

  1. Is it even possible to remove/circumevent these protections (e.g. unpacking, if it is packed) easily
  2. Is it worth the time to do so?

The second Question is:
Can you find out, which Compiler/Language was used to produce the executeable?

More Details are better, but most basic constructs (if and other control-structures) should map quite similarly over a variety of compilers.

This is related to a previous Question on the RE-Stackexchange.

The third Question is:
How is the user interface implemented (CLI, Win32-Window Controls, Custom, ...)?

If this is known:
Can you figure out the mapping of common HLL-Constructs (Menues, Dropdown-Menues, Checkboxes, ...) in conjunction with the used Compiler/Language that you want to modify?

The fourth and biggest Question is:
How can you create the desired functionality in the Program?

In essence this can require quite a bit of reverse engineering, to find out how to best hook into the program without upsetting it.

Central Point: How can you utilize existing internal API's to reach your Goal, without breaking Stuff (like CRTL+Z, Versioning, Recovery features)?

  • existing Datastructures (and how are they related?)
  • existing Functions (Parameters, Parameter-Format, ...)
    • What does it do?
    • What else can it do?
    • What does it REALLY do?
    • ...
  • existing Processes (= How the program goes about internally, stepwise to implement similar features)
    • What functions are called, in which order?
    • Which Data-Structures are utilized?
  • Where is the Meat of the feature/program (the data, e.g. the main painting area, and how does it relate internally?)
  • Stuff to look out for (if it concerns the desired feature):
    • Journaling
    • Recovery Features
    • Versioning
  • How is Metadata handled (e.g. Shutter speed, f-Stops, ...), that is related to the desired Feature.

Example projects:

  • Building a new kind of painting tool into a graphics program (without plugin-API).
  • Extending the plugin-API of a program.
  • Building a plugin-API into a program without one.
  • Adding a new save/export-format for files (if there is no way to convert the output-format into a desired format, or if crucial information is missing in the exported files).
  • Adding a new import-format (if there is no way to convert the input-format into a importable-format or if some information is not correctly imported).
  • Extending Mspaint with a colour search-and-replace tool (within a selection, or in the whole picture)
  • Adding Proxy Support/Basic Proxy-Authentication to a Program.
  • Adding (new) Command line switches to a program that expose new/existing Features.
  • Adding a API for Remote Procedure-Calls to Manage the Operations of the Programm externally.
  • Adding Scripting-Support to automate often repeated Operations (If there is no plugin-/scripting-API to begin with) or support batch processing.

Regarding wrapped Code & Decompilers:
I will not talk about wrapped Code in other Languages that is packaged with a VM / an Interpreter (Py2Exe, Java 2 Exe, ...), or uses an installed one (JVM, C#). There are pretty good Decompilers for some of those cases. After a successful decompilation it pretty much boils down to defeating the Code Obfuscation (if there is one).

Regarding C/C++-Decompilers:
I cannot talk about C/C++-Decompilers, though it would boil down to best-effort HLL-Remapping (for stuff the Decompiler did not get) and Code-Deobfuscation (if it was compiled without Symbols) provided there is no further Protection in the Executeable.

Reccommendation regarding HLL-mapping:
In essence a big part of this Question concerns "HLL mapping" (High level language mapping (in machine code)) of and the modification of these constructs in the corresponding machine code.

I found an excellent downloadable starting course, that uses "IDA Free", on this Topic here (binary-auditing.com).

0
7

(Slightly outdated, but as that wasn't mentioned previously in this thread)

Long ago, I spend months extending a software with only the binary.

  • I used IDA for analysis and SoftICE for live debugging. Decompiling is not required if you can understand the target at opcode/bytecode level.
  • Then, because it was an x86 PE binary, I used Tasm and Iczelion's Code Snippet Creator: It's not a famous tool anymore, but it allowed to use Tasm transparently and re-inject code, with PE transformations, etc...

    It added code at EntryPoint, so I did my own patches manually, then jumped to original EntryPoint.

A bit old-school now - I'd probably inject a DLL these days - but it certainly worked.

And at least, it gives you full control via ASM, while keeping maintainability via automated patching.

6

You do not need to decompile the binary. If you understand what changes you want to make, and those changes can be made by only modifying the binary file or its dependencies, then you can just make those modifications on disk or in memory.

You have a few choices on how to effect the modification itself.

You could use LD_PRELOAD to have the linker load a shared object before the binary runs. Then you don't need to modify the binary on disk at all. This is kind of what valgrind does, it loads as a shared object but then begins dynamic binary instrumentation.

You could use valgrind. Valgrind would allow you to dynamically re-write the program and modify its behavior arbitrarily. Valgrind is a dynamic binary instrumentation program that allows its tools to edit the program while it executes. If you just want to change program behavior this might work, but valgrind also incurs a global slowdown and if you wanted to patch and redistribute a program, it probably is not ideal.

You could also use tools like elfsh/eresi to insert new code into the program. Those tools should take care of the act of injecting your code with relation to stuff like the ELF program header. There is a concept of "ELF infector" that you could google for, where your injected code becomes the new program entry point, does something, then jumps to the old program entry point.

6

Doing that on Windows

Although this question focuses on Linux, where personally I would go with the easy LD_PRELOAD method as outlined in other answers, Windows knows a similar mechanism that in fact has been abused in the more recent past (also see alternative approaches below). I used that method to "crack" one dongle system.

Enter ...

DLL placement (aka preloading, aka hijacking) attacks

The name has been given to the method fairly recently when it turned out that placing DLLs on remote shares and then navigating to shares in, say a media player, would result in the media player loading the remote DLL instead of a local version. This is by design. Changing it now would break hundreds if not thousands of applications.

This has been addressed by Microsoft in certain ways, although the only real solution is proper implementation on the application side. But then, many developers haven't even grasped NT security even though we have to deal with it ever since Windows 2000 became the first consumer OS based on the NT platform.

What does it have to do with your described goal?

Adding functionality doesn't necessarily imply that you patch the executable on-disk. You can also do it in memory.

How can you leverage it?

Whenever an application uses a DLL, and you can tell the load order with Dependency Walker or under a debugger, you can pick one of the DLLs it imports and replace that (in its current location) or placing another DLL in a path that precedes the existing DLL in the load order.

An alternative method is to change the name of the imported DLLs. In rare cases (well known DLLs, for example) this is the only viable method to load an alternative DLL and may still fail for certain special cases.

Limitations

If the used DLL exists in the first location in the DLL search order, you'll literally have to replace the file on disk, unless you rename the import as briefly mentioned above.

Implementations

A manual approach can be used for DLLs with only few exported symbols. The easiest would be to create a module definition file from the the DLL and from that create a DLL with only function forwarders. This way your placed DLL would get loaded already and would simply pass through the calls.

However, this approach will fail with exported variables (as opposed to functions).

Here's a simple Python script based on pefile which I wrote for another answer over at StackOverflow:

import os
import sys
import re

def main(pename):
    from pefile import PE
    print "Parsing %s" % pename
    pe = PE(pename)
    modname = os.path.basename(pename)
    libname = re.sub(r"(?i)^.*?([^\\/]+)\.(?:dll|exe|sys|ocx)$", r"\1.lib", modname)
    defname = libname.replace(".lib", ".def")
    print "Writing module definition file %s for %s" % (defname, modname)
    f = open(defname, "w") # want it to throw, no sophisticated error handling here
    f.write("LIBRARY %s\n\n" % modname)
    f.write("EXPORTS\n")
    numexp = 0
    for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
        if exp.name:
            numexp += 1
            f.write("\t%s\n" % exp.name)
    print "Wrote %s with %d exports" % (defname, numexp)
    print "\n\nUse this to create the export lib:\n\tlib /def:%s /out:%s" % (defname, libname)

if __name__ == '__main__':
    if len(sys.argv) != 2:
        sys.stderr.write("ERROR:\n\tSyntax: fakelib <dllfile>\n")
        sys.exit(1)
    sys.exit(main(sys.argv[1]))

You could adjust it to create function forwarders instead of a simple module definition with exported names.

So this way you can shuttle your code into the target application and go from there.

Alternative approaches

Instrumentation and hooking have been mentioned already. Detours is an often mentioned example of hooking with an inconvenient EULA for most practical purposes. Refer to the existing answers for this kind of approach.

You can also use the AppInit_DLL registry value to inject a DLL early on. Or you could write a little launcher with a debugger loop and use Image File Execution Options to have your target launch your debugger first. A debugger can also influence the DLL loading or simply intercept - conveniently - calls at the boundary between executable and DLLs.

Trivia: this (Image File Execution Options) is how Process Explorer replaces Task Manager when you choose the option inside Process Explorer.


You'll notice how you can sort these approaches into the categories Ed McMan mentioned in his answer already. However, I'll leave that as an exercise to the reader :)

3

I did this with Notepad.exe on Windows. I wanted to add one top-menu item to open calc.exe just for fun (I know your question is tagged Linux and gcc compiler, but the idea is probably the same).

So I used Resource Hacker tool to add Calc menu and opened notepad.exe on Immunity Debugger looking for some space in the code where I could put my WinExec shellcode. Initially I didn't change the executable, I had to look at the program in memory to find some space where I could paste my assembly instructions without crashing notepad.

Once I found enough space (changing original code by eliminating some not required assembly instructions or even optimizing them) I opened notepad.exe on XVI Hex Editor and searched for the opcodes that was running on Immunity. I mean, the debugger was running some opcodes right? I just searched for a sequence of opcodes to be sure I was at the right piece of the software that I wanted to change and pacthed it with my shellcode (now this is not assembly code but the "compiled" assembly - machine code)

Again: I know your question is tagged Linux and gcc compiler, but maybe someone could point out some tools in Linux to achieve the same I did on Windows. The idea is probably the same.

2

"ptrace()" was casually mentioned by Giles. But I think it deserved a whole section by itself. "ptrace()" is a system call API provided by OS (Linux and all UNIX have it, and so do Windows) to exert debug control over another process. When you used PTRACE_ATTACH (as part of ptrace()) to attach to another process, the kernel will pause the CPU running that process completely, allowing you to make changes to ANY part of the process: CPU, any registers, any part of that process memory etc. That is how dynamic inline hooking work. (ptrace() attach, modify binary in-memory, and then ptrace() unattached). As far as I know, all dynamic modification of another process has to use ptrace() - as that is the only mechanism provided by kernel to guarantee integrity via system call at this point.

But recently similar API like utrace() is popping up, and so inline hooking is also theoretically possible:

http://landley.net/kdocs/ols/2007/ols2007v1-pages-215-224.pdf

For kernel hooking, there are many methods: syscall, interrupt, and inline hooking. This is for interrupt hooking:

http://mammon.github.io/Text/linux_hooker.txt

When the CPU is in STOP mode, basically you can do anything you like to the CPU/memory space/register - just make sure you restore back to its original state before returning to the original address where it stopped.

And if you use library inject technique, you can implement any functionalities - calling remote libraries, remote shell etc:

https://attack.mitre.org/techniques/T1055/001/

https://stackoverflow.com/questions/24355344/inject-shared-library-into-a-process

https://backtrace.io/blog/backtrace/elf-shared-library-injection-forensics/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.