diff --git a/patcherex/backends/detourbackends/_elf.py b/patcherex/backends/detourbackends/_elf.py index 33226b7..f8f7aaf 100644 --- a/patcherex/backends/detourbackends/_elf.py +++ b/patcherex/backends/detourbackends/_elf.py @@ -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])) diff --git a/patcherex/backends/detourbackends/aarch64.py b/patcherex/backends/detourbackends/aarch64.py index 4571f83..2b8b1b5 100644 --- a/patcherex/backends/detourbackends/aarch64.py +++ b/patcherex/backends/detourbackends/aarch64.py @@ -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) @@ -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 @@ -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") diff --git a/patcherex/backends/detourbackends/arm.py b/patcherex/backends/detourbackends/arm.py index 4d924b4..e2be831 100644 --- a/patcherex/backends/detourbackends/arm.py +++ b/patcherex/backends/detourbackends/arm.py @@ -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) @@ -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 @@ -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") diff --git a/patcherex/backends/detourbackends/avr.py b/patcherex/backends/detourbackends/avr.py index 4c9c85f..9adf87a 100644 --- a/patcherex/backends/detourbackends/avr.py +++ b/patcherex/backends/detourbackends/avr.py @@ -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) @@ -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 @@ -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") diff --git a/patcherex/backends/detourbackends/cgc.py b/patcherex/backends/detourbackends/cgc.py index dece30f..316a67d 100644 --- a/patcherex/backends/detourbackends/cgc.py +++ b/patcherex/backends/detourbackends/cgc.py @@ -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) @@ -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) @@ -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) diff --git a/patcherex/backends/detourbackends/i386.py b/patcherex/backends/detourbackends/i386.py index c3040bb..c2abe10 100644 --- a/patcherex/backends/detourbackends/i386.py +++ b/patcherex/backends/detourbackends/i386.py @@ -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 @@ -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") diff --git a/patcherex/backends/detourbackends/mips.py b/patcherex/backends/detourbackends/mips.py index fd18d28..fb92f19 100644 --- a/patcherex/backends/detourbackends/mips.py +++ b/patcherex/backends/detourbackends/mips.py @@ -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) @@ -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 @@ -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") diff --git a/patcherex/backends/detourbackends/ppc.py b/patcherex/backends/detourbackends/ppc.py index 5426b48..7077f9f 100644 --- a/patcherex/backends/detourbackends/ppc.py +++ b/patcherex/backends/detourbackends/ppc.py @@ -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) @@ -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 @@ -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") diff --git a/test_binaries/rip-relative-addressing.out b/test_binaries/rip-relative-addressing.out new file mode 100755 index 0000000..e54e1ec Binary files /dev/null and b/test_binaries/rip-relative-addressing.out differ diff --git a/tests/test_detourbackend_x86-64.py b/tests/test_detourbackend_x86_64.py similarity index 90% rename from tests/test_detourbackend_x86-64.py rename to tests/test_detourbackend_x86_64.py index cfa6501..a0be944 100755 --- a/tests/test_detourbackend_x86-64.py +++ b/tests/test_detourbackend_x86_64.py @@ -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) diff --git a/tests/test_samples/rip-relative-addressing.c b/tests/test_samples/rip-relative-addressing.c new file mode 100644 index 0000000..a72b967 --- /dev/null +++ b/tests/test_samples/rip-relative-addressing.c @@ -0,0 +1,11 @@ +#include + +// Compiled on Ubuntu 20.04 with flag `-no-pie` + +int main(void) +{ + char *str2 = "Goodbye!\n", *str1 = "Hello world!\n"; + printf(str1); + return 0; +} +