Skip to main content

Patchelfy

Patchelfy is a library to inject new code into an ELF without breaking it. It can be used when you can't attach to the process to alter part of the execution or simply log informations while reversing it.

The code has to be written in assembly and some methods are offered to add calls to any libc function or to access specific addresses in the binary.

Log

The simplest use for this library is to log data during the execution.

You can write as many shellcodes as you want on the same address, but you just have to keep in mind that they will be called from the last to the fist you set and the memory will be restored between each of them.

from patchelfy import Patchelf
p = Patchelf("./binary")
# Warning the \n must be escaped
p.log(0x1651, "the function returned %ld\\n", ["rax"])
p.log(0x1651, "I'm at address: 0x%lx\\n", [0x1651])
p.log(0x1651, "This is a log\\n", [])
#p.save("./binary_patched")
# If left empty the name for the new binary will be <name>_patched
p.save()

output:

$ ./binary_patched
This is a log
I'm at address: 0x1651
the function returned 0x7f41f656a000

Access variables on stack

To access variables on the stack, or in another stackframe, you can load them into a register with pre. Remember to save the original value and restore it after.

from patchelfy import Patchelf
p = Patchelf("./binary")

shellcode = """
push rbp
push rbx
mov rbp, qword ptr [rbp]
mov ebx, dword ptr [rbp - 0x4]
"""

p.log(0x154c, "[process: %d] returned %ld\\n", ["rbx", "rax"], pre=shellcode, post="pop rbx\npop rbp")
p.save()

Place shellcodes

You can insert your own shellcodes inside the binary and reffer to functions in the binary by their relative address

from patchelfy import Patchelf
p = Patchelf("./binary")

# Skip a portion of code
shellcode = """
jmp 0x1537
"""

p.place_shellcode(0x1526, shellcode)
p.save()

Calling a function

If you have to call a function in your shellcode you can use the wrapper Patchelf.shellcode_call to save all registers and parse your arguments. If the function you want to call is in the libc you can get a relative address to it with Patchelf.setup_libc to build your shellcode.

from patchelfy import Patchelf
p = Patchelf("./binary")

system_address = p.setup_libc("memcpy")
ptable = list(range(0x100))
shellcode = p.shellcode_call(system_address, [0x205153, ptable, 0x100])
p.place_shellcode(0x1526, shellcode)
p.save()

Edge cases

To place your shellcode we overwrite 6 bytes of the binary. It is not a problem except if the program has to jump right in that area. Try not to place it there, but if you REALY have to you can use restoring_shellcode that restore the memory once executed. Unfortunately there is nothing I can do if you jump into that area before running your shellcode.

from patchelfy import Patchelf
p = Patchelf("./binary")

# overwrite return value with True
shellcode = """
xor rax, rax
inc rax
"""

p.restoring_shellcode(0x15a3, shellcode)
# If you are in a loop and want your shellcode to be executed each time you need a point to set again your shellcode. This will be developed in a future version
#p.restoring_shellcode(0x15a3, shellcode, loop = 0x15bb)
p.save()

If you need it for a log, use the argument p.log(..., restoring=True)

Installation

stable

pip3 install patchelfy

Limitations

  • Can not yet patch a section that will be unpacked at runtime. I will look into a setup to handle it.
  • Setup_libc requires the binary to be relro or at least the last function in the got table to be loaded when called. You can inject a restoring_shellcode on entry to the last function of the plt or disable ASLR, load the absolute address in a register and call that register.
  • You can only write 2MB of shellcode... Do you really have a case where it isn't enough ?

Metadata

Release files for patchelfy 0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for patchelfy 0.1
File Size Uploaded
patchelfy-0.1.tar.gz 18.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for patchelfy 0.1
File Interpreter ABI Platform
patchelfy-0.1-py3-none-any.whl Python 3 none any Details

Total release size: 40.0 kB

Release files / patchelfy-0.1.tar.gz

Download URL patchelfy-0.1.tar.gz
Size 18.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a11525a209849c126cdd36ab49dfe5e14fc7b8fff3c75ed16172d5bf00de7df5
BLAKE2b-256 checksum
How to use checksums
642fc7069e60ad5b341ea348570e94a5db001f177d6da175d59b671a96f65f8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.10.6

Release files / patchelfy-0.1-py3-none-any.whl

Download URL patchelfy-0.1-py3-none-any.whl
Size 21.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25c3f94214470d44994a8d48410a58ac406cb3f16f4f3335655c6b241e8c1511
BLAKE2b-256 checksum
How to use checksums
9d6d531a5c19e8d5494a348e85e40ebc237da549a747e245be8d4e5c46070a04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.10.6

Release history Release notifications | RSS feed

This release

0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page