From 807648891fb40a83b0fa8701f04e46cafed6dc1f Mon Sep 17 00:00:00 2001 From: Arvind Date: Wed, 22 Nov 2023 14:26:25 -0700 Subject: [PATCH 1/7] Modified movable_instructions in x86 to return largest contiguous set of instructions containing patch address instead of entire basic block --- patcherex/backends/detourbackends/_elf.py | 4 +-- patcherex/backends/detourbackends/aarch64.py | 6 ++-- patcherex/backends/detourbackends/arm.py | 6 ++-- patcherex/backends/detourbackends/avr.py | 6 ++-- patcherex/backends/detourbackends/cgc.py | 6 ++-- patcherex/backends/detourbackends/i386.py | 32 +++++++++++++++++--- patcherex/backends/detourbackends/mips.py | 6 ++-- patcherex/backends/detourbackends/ppc.py | 6 ++-- 8 files changed, 47 insertions(+), 25 deletions(-) diff --git a/patcherex/backends/detourbackends/_elf.py b/patcherex/backends/detourbackends/_elf.py index 33226b7..e78485c 100644 --- a/patcherex/backends/detourbackends/_elf.py +++ b/patcherex/backends/detourbackends/_elf.py @@ -484,12 +484,12 @@ 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) 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..f3d3a9c 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): # 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..76d4fde 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") From c13c6e3a86171d7ca1e16cd06b4f1c5126d7e3f2 Mon Sep 17 00:00:00 2001 From: Arvind Date: Wed, 22 Nov 2023 15:59:00 -0700 Subject: [PATCH 2/7] Lint --- patcherex/backends/detourbackends/cgc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patcherex/backends/detourbackends/cgc.py b/patcherex/backends/detourbackends/cgc.py index f3d3a9c..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, patch_addr): + 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) From 2483702bf245923603f7bd236829a900dc1e3972 Mon Sep 17 00:00:00 2001 From: Arvind Date: Wed, 22 Nov 2023 15:59:43 -0700 Subject: [PATCH 3/7] Fixed compare with end of instruction --- patcherex/backends/detourbackends/i386.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patcherex/backends/detourbackends/i386.py b/patcherex/backends/detourbackends/i386.py index 76d4fde..c2abe10 100644 --- a/patcherex/backends/detourbackends/i386.py +++ b/patcherex/backends/detourbackends/i386.py @@ -387,7 +387,7 @@ def get_movable_instructions(self, block, patch_addr): # 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: + 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]): From 2efcab2bfb54d8d6b074092c7c24dcd4d770a30b Mon Sep 17 00:00:00 2001 From: Arvind Date: Wed, 22 Nov 2023 16:20:01 -0700 Subject: [PATCH 4/7] Added test case --- test_binaries/rip-relative-addressing.out | Bin 0 -> 16632 bytes tests/test_detourbackend_x86-64.py | 7 +++++++ tests/test_samples/rip-relative-addressing.c | 11 +++++++++++ 3 files changed, 18 insertions(+) create mode 100755 test_binaries/rip-relative-addressing.out create mode 100644 tests/test_samples/rip-relative-addressing.c diff --git a/test_binaries/rip-relative-addressing.out b/test_binaries/rip-relative-addressing.out new file mode 100755 index 0000000000000000000000000000000000000000..e54e1ecd461d43004cf5617c9803cd2b67c9542a GIT binary patch literal 16632 zcmeHOeQX>@6`#BFSCjgDj+5dzq282Ka-rT_f@_?hG<&wsUL)s&#GwU}uFt-;ed0dc zyS>(~s^ThX)uWhNLWowXM39j9hf44dN&y8&j%tB~l=25fe25ZS;X)eRkU)e;j`wEY zJMXS9YK0I&VxP75n|bqoZ)V=i-pzVvz8D`K2nGUzB`6*gm^%{Dq{{vjqGzwnlCDQI z3Qe?&yG0#nYT(jj38I~1{AJKCU~C|}5FqkeF(FZN;dYr5f{|N@h`cq?C~4zGDr6K9 zyBK-3n54X*{xM#s3K-e0QlElk5Ji)~I7&Q5Cl6y2QEl_cgEo&*p}2GqG7q!JQ;4S! zkMSba$H?)-ZsXNM@$3m{ki)!<3T8kQ`6+~#_b~AuCVR$yvS-9PP>#KACI6lJju3CV zBHf7X6i-I3hmq_1H1IGkA5?glk5YZN8;8|^?U9kPVplpf*4?!$o$O4fvc;LsnV#;> z?k=^EQ};*$whQaPIW>6bxPWn6L>+}uj>TrmWBEND`=5UK?_%TkUb=RvE52{;3yp7B z>;vmW8x-X8l)yZe@rC&hDjUIYQx#*>Rntd7uT-%Y5QlN>cDI<$r?S?BFbq@~1uK!a zj7%bx6@$Y=y?w?Wb&vW0lul-HSt>QqVNmub0KLF|@VFG)Qg>k4PO5YDfF@Ho#?JoV zaOgR{8dTaH_$k6iQo6VZgm(Nd2SndWWZ^;AYn&dA{s7>vsWpxW@qp+~o_v5G4 zau#*iq&t3$it?Ki3riDg#!c;-I4A9&z9ONts-M4dKtF#Yqz7KpU%zEFS3J~{2QMGn zKezv0Xp1Pe9M?;4uRH*_=MKYs~7(HJ5Z(dTP_7=~t+;;WGSft|de zKOeuLmzHJyD{tH?mu2cFOUR5pG(5lmtM>~L`^-@37qR0*rR%YgSm~d~_4&@zpguNy z-%T8o6$5zs`I{lDefO`M+UGDD!=l+q8kM9BT-TuYDl*{<=E&A&tsEfjN8Fcbp zJ*0PH5nMB%QvgLcG7xV4RMW=C8|K7;wtXM{_|AJ!jrFAAng%=c!-933hU*Zta~~?> z;mEVWzQ(#op&^h(-`|333d&7DF%XWt5F84(ekBwSEAut+aL1Qx`@_*^>-2EXsrtdN zmJRpB!qHf`qc^PdhFg2Xk=}5F?9XMOFMuCzJG%45kANQmKLUOP{0R6F@FUk0* z0Y3u&s}YF8yICoOytk6^PHuR_wriyP3fXU^JnwVfMtRg4=KD)MhVK#HG6(u#b;iW z57{8jQvIxdj`G}Yd~QX7O|4S6et3f``!hxM@N!w^IiB#iDD$IKPW1m8@V;_yg;5$` zuYRzvZ=cd}e5{zYippNKOO1B!F3Q~Qr}jkEXqUR@K3nf)uFr>H=WZ*Fqq8&wqr&^v z-TIK=ed%s}jd-%EpW4bia@*Hc=8s!nUzulaeS@erJ|P%w9#^;h2Ep^gt>0LgM{Ygb zRaN30f>RbsRee}!RrO7ka~p2|5yAJTTi*;fmgZE+bO?5c^L~71VMw%y(W>^F1>f&( z`z?a+bGQC3QElFZ;BMo6;BI@ko9KRZ>$gJlHK$6ZL!wRKeh{yO1b-W?mEe6?Bi<{o zY3vk!&jm%3Sn$Ldws%PVqDQ}j^n$-XZ8zc*fB{E)us)OlxB~F)%1CsPH>%o|q57*FT2W&sX{By+}OIyaU`8%ZmdFBpm9j2O>lrqiZnCe@y3_wMRO z#zZQcG7|ZGV%9LTR(@7Yi65n@pR+7&;7A$yC-T7EI{I zS_`m)!bql#yqV68CoB`aS*dYjIt4UitWY5L@&u1zj2!Hv;0U!ao3RpOfL7kdDNexw zATvKL)NIZ&)yZs8ozCZ`&Ac`1P>mH+aB3=*Bvq_;sMAVJ3TZx-C`<`8Ih%zlY_#%r z$zx`|kjiCU83W3o`3WRYYC3HRRrXDVq*^e?g=(2Ipu|Z6MmZUC)tsVfF_i=-oU^I+ z{ID6Eh8hx?6gZpAfiL(#_s~KdKox=|RG6t5n0@jl`44>HFA>~I@Lg{UuY>qJB(IC9 zSlV(L@gs2IT9o;`UgGnA3Y0m8*Qu=QhJ=&P>nc76xRVVD`Fo1hLmob_zn-K8w!(&l zUj9R%{~N4*x&3ECnzTGaeC~f8@xAR&fF9TS%wHt_BJoFwhke92GoRPxDbS%Q^A%cu zE7VYao&Xam=))>GJ_USyHsCz3Bd3V3P-WislUmF}0!^9E z>&@qg&(9-VKevbb|8?T`l0$yJIZJ%DLq4Xx{BME|W5WIguZQU6n|Gx}Zh>LT%K%Qk zLVShz3fVD`8h*oU`8I%)uMuA(1Gc*z|GOSO|6U#;BL2QQ#q00;9zL(HSKy}|rO}4U zr?>wvcX?+wU}nw734Bd-%MrA61Bm&K<(z z&#K=*0@uQHyH(bue9qQu&w0ib;5Iq z{y||LkK;|yVHcUt&mF6@U}8JuqA;H^2xKR}@_Z7t?G+m;pUh+2=;8CaQX&5B>Su?| zz(-%)eqN_ViO==0J+{>;cTxRhUMXV!$0`~-!F(rH=B~qblpL + +// Compiled on Ubuntu 20.04 with flag `-no-pie` + +int main(void) +{ + char *str2 = "Goodbye!\n", *str1 = "Hello world!\n"; + printf(str1); + return 0; +} + From 222b772b341253902e120006426bc2bc20130eab Mon Sep 17 00:00:00 2001 From: Arvind Date: Wed, 22 Nov 2023 17:02:41 -0700 Subject: [PATCH 5/7] Rename x86-64 tests file so that it runs in CI --- ...{test_detourbackend_x86-64.py => test_detourbackend_x86_64.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_detourbackend_x86-64.py => test_detourbackend_x86_64.py} (100%) diff --git a/tests/test_detourbackend_x86-64.py b/tests/test_detourbackend_x86_64.py similarity index 100% rename from tests/test_detourbackend_x86-64.py rename to tests/test_detourbackend_x86_64.py From a8a1e251209629e95892eb870d1c61849d0694f9 Mon Sep 17 00:00:00 2001 From: Arvind Date: Thu, 23 Nov 2023 13:22:22 -0700 Subject: [PATCH 6/7] Use address of first movable instruction instead of block to determine size of movable basic block --- patcherex/backends/detourbackends/_elf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patcherex/backends/detourbackends/_elf.py b/patcherex/backends/detourbackends/_elf.py index e78485c..f8f7aaf 100644 --- a/patcherex/backends/detourbackends/_elf.py +++ b/patcherex/backends/detourbackends/_elf.py @@ -494,7 +494,7 @@ def find_detour_pos(self, block, detour_size, 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])) From 7affb2d8426cd7a24654e3a2d5c898fd71bb4c4d Mon Sep 17 00:00:00 2001 From: Arvind Date: Thu, 23 Nov 2023 15:55:03 -0700 Subject: [PATCH 7/7] Fixed error in test case --- tests/test_detourbackend_x86_64.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_detourbackend_x86_64.py b/tests/test_detourbackend_x86_64.py index f378015..a0be944 100755 --- a/tests/test_detourbackend_x86_64.py +++ b/tests/test_detourbackend_x86_64.py @@ -118,8 +118,8 @@ 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"