Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix movable instructions computation in x86-64 detour backend #53

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/_elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,17 +484,17 @@ def read_mem_from_file(self, address, size):
mem += self.ncontent[start : end]
return mem

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
raise NotImplementedError()

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

detour_attempts = range(-1*detour_size, 0+1)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
movable_bb_size = self.project.factory.block(movable_bb_start, num_inst=len(movable_instructions)).size
l.debug("movable_bb_size: %d", movable_bb_size)
l.debug("movable bb instructions:\n%s", "\n".join([utils.instruction_to_str(i) for i in movable_instructions]))

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/aarch64.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def bytes_to_comparable_str(ibytes, offset):
pos3 = bytes_to_comparable_str(instruction_bytes, 0xfe000000)
return pos1 == pos2 and pos2 == pos3

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -318,7 +318,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
Expand Down Expand Up @@ -393,7 +393,7 @@ def insert_detour(self, patch):
l.debug("inserting detour for patch: %s", (map(hex, (block_addr, block.size, patch.addr))))

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def bytes_to_comparable_str(ibytes, offset):
pos3 = bytes_to_comparable_str(instruction_bytes, 0xfe000000)
return pos1 == pos2 and pos2 == pos3

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -357,7 +357,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
Expand Down Expand Up @@ -427,7 +427,7 @@ def insert_detour(self, patch):
arm_nop = b"\x00\xF0\x20\xE3"

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/avr.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def check_if_movable(self, instruction, is_thumb=False):
mnemonic = self.disassemble(instruction.bytes)[0]['mnemonic']
return mnemonic != "rjmp" and mnemonic != "rcall" and not mnemonic.startswith("br")

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -319,7 +319,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
Expand Down Expand Up @@ -381,7 +381,7 @@ def insert_detour(self, patch):
avr_nop = b"\x00\x00"

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/cgc.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ def read_mem_from_file(self, address, size):
mem += self.ncontent[start : end]
return mem

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr): # pylint:disable=unused-argument
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -825,7 +825,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

detour_attempts = range(-1*detour_size, 0+1)

Expand Down Expand Up @@ -891,7 +891,7 @@ def insert_detour(self, patch):
detour_size = 5
one_byte_nop = b'\x90'

movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")
detour_pos = self.find_detour_pos(block, detour_size, patch.addr)
Expand Down
32 changes: 27 additions & 5 deletions patcherex/backends/detourbackends/i386.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,18 +375,40 @@ def bytes_to_comparable_str(ibytes, offset, bits):
return False
return pos1 == pos2 and pos2 == pos3

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
# get movable_instructions in the bb
original_bbcode = block.bytes
instructions = utils.disassemble(original_bbcode, block.addr, bits=self.structs.elfclass)
movable_instructions = []
if self.structs.elfclass == 64:
# get largest contiguous block of movable instructions in the bb containing the patch address
# to ensure instructions using rip are excluded
for instr_index, instruction in enumerate(instructions):
if instruction.address <= patch_addr < instruction.address + instruction.size:
# Found instruction containing the patch address. Compute group from this index
# First we go backward towards start of list
for instr in reversed(instructions[:instr_index]):
if self.check_if_movable(instr):
movable_instructions.insert(0, instr)
else:
break

# Now we go forward towards end of list
for instr in instructions[instr_index:]:
if self.check_if_movable(instr):
movable_instructions.append(instr)
else:
break

if self.check_if_movable(instructions[-1]):
movable_instructions = instructions
break
else:
movable_instructions = instructions[:-1]
if self.check_if_movable(instructions[-1]):
movable_instructions = instructions
else:
movable_instructions = instructions[:-1]

return movable_instructions

Expand Down Expand Up @@ -436,7 +458,7 @@ def insert_detour(self, patch):
one_byte_nop = b'\x90'

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/mips.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def bytes_to_comparable_str(ibytes, offset):
pos3 = bytes_to_comparable_str(instruction_bytes, 0xfe000000)
return pos1 == pos2 and pos2 == pos3

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -324,7 +324,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
Expand Down Expand Up @@ -399,7 +399,7 @@ def insert_detour(self, patch):
l.debug("inserting detour for patch: %s", (map(hex, (block_addr, block.size, patch.addr))))

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
6 changes: 3 additions & 3 deletions patcherex/backends/detourbackends/ppc.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def bytes_to_comparable_str(ibytes, offset):
pos3 = bytes_to_comparable_str(instruction_bytes, 0xfe000000)
return pos1 == pos2 and pos2 == pos3

def get_movable_instructions(self, block):
def get_movable_instructions(self, block, patch_addr):
# TODO there are two improvements here:
# 1) being able to move the jmp and call at the end of a bb
# 2) detect cases like call-pop and dependent instructions (which should not be moved)
Expand All @@ -338,7 +338,7 @@ def get_movable_instructions(self, block):

def find_detour_pos(self, block, detour_size, patch_addr):
# iterates through the instructions to find where the detour can be stored
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch_addr)

movable_bb_start = movable_instructions[0].address
movable_bb_size = self.project.factory.block(block.addr, num_inst=len(movable_instructions)).size
Expand Down Expand Up @@ -413,7 +413,7 @@ def insert_detour(self, patch):
l.debug("inserting detour for patch: %s", (map(hex, (block_addr, block.size, patch.addr))))

# get movable instructions
movable_instructions = self.get_movable_instructions(block)
movable_instructions = self.get_movable_instructions(block, patch.addr)
if len(movable_instructions) == 0:
raise DetourException("No movable instructions found")

Expand Down
Binary file added test_binaries/rip-relative-addressing.out
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,15 @@ def test_replace_function_patch_with_function_reference_and_rodata(self):
extern int printf(const char *format, ...);
int multiply(int a, int b){ printf("%sWorld %s %s %s %d\\n", "Hello ", "Hello ", "Hello ", "Hello ", a * b);printf("%sWorld\\n", "Hello "); return a * b; }
'''
self.run_test("replace_function_patch", [ReplaceFunctionPatch(0x4006a2, 0x48, code, symbols={
"add": 0x400660, "subtract": 0x400681})], expected_output=b"-21-21")
self.run_test("replace_function_patch", [ReplaceFunctionPatch(0x4006a2, 0x48, code, symbols={"printf" : 0x400520})],
expected_output=b"Hello World Hello Hello Hello 21\nHello World\n2121")

def test_rip_relative_addressing_insert_code(self):
patch_asm = "sub rax, 10"
patch_addr = 0x401154
bins_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, "test_binaries")
target_binary = os.path.join(bins_dir, "rip-relative-addressing.out")
self.run_test(target_binary, [InsertCodePatch(patch_addr, patch_asm)], expected_output=b"Goodbye!\n")

def run_test(self, filename, patches, set_oep=None, inputvalue=None, expected_output=None, expected_returnCode=None):
filepath = os.path.join(self.bin_location, filename)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_samples/rip-relative-addressing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdio.h>

// Compiled on Ubuntu 20.04 with flag `-no-pie`

int main(void)
{
char *str2 = "Goodbye!\n", *str1 = "Hello world!\n";
printf(str1);
return 0;
}

Loading