Example #1
0
    def test_conflicting_symbols(self):
        filepath = os.path.join(self.bin_location, "printf_nopie")

        patches = []
        backend = DetourBackend(filepath)
        patches.append(AddRODataPatch(b"0123456789abcdef", "aaa"))
        patches.append(AddRODataPatch(b"\n", "aaa"))
        exc = False
        try:
            backend.apply_patches(patches)
        except ValueError:
            exc = True
        self.assertTrue(exc)

        patches = []
        backend = DetourBackend(filepath)
        patches.append(AddRODataPatch(b"0123456789abcdef", "aaa"))
        added_code = '''
            nop
        '''
        patches.append(AddCodePatch(added_code, "aaa"))
        exc = False
        try:
            backend.apply_patches(patches)
        except ValueError:
            exc = True
        self.assertTrue(exc)
    def test_complex1(self):
        patches = []
        added_code = '''
                mov     eax, 4
                mov     ebx, 1
                mov     ecx, 0x080484f3
                mov     edx, 2
                int     0x80
                call    {added_function}
            '''
        patches.append(AddEntryPointPatch(added_code))

        added_code = '''
                mov     eax, 1
                mov     ebx, 0x34
                int     0x80
            '''
        patches.append(AddEntryPointPatch(added_code))

        test_str = b"testtesttest\n\x00"
        added_code = '''
                mov     eax, 4
                mov     ebx, 1
                mov     ecx, {added_data}
                mov     edx, %d
                int     0x80
                ret
            ''' % (len(test_str))
        patches.append(AddCodePatch(added_code, "added_function"))
        patches.append(AddRODataPatch(test_str, "added_data"))

        self.run_test("printf_nopie",
                      patches,
                      expected_output=b'%s' + test_str,
                      expected_returnCode=0x34)
Example #3
0
    def test_complex1(self):
        patches = []
        added_code = '''
                dli $v0, 5001
                dli $a0, 1
                dla $a1, 0x120000cf8
                dli $a2, 2
                syscall
                jal {added_function}
                dli $v0, 5058
                dli $a0, 0x34
                syscall
            '''
        patches.append(AddEntryPointPatch(added_code))

        test_str = b"testtesttest\n\x00"
        added_code = '''
                dli $v0, 5001
                dli $a0, 1
                dla $a1, {added_data}
                dli $a2, %d
                syscall
                jr $ra
            ''' % (len(test_str))
        patches.append(AddCodePatch(added_code, "added_function"))
        patches.append(AddRODataPatch(test_str, "added_data"))

        self.run_test("printf_nopie",
                      patches,
                      expected_output=b'%s' + test_str,
                      expected_returnCode=0x34)
    def test_complex1(self):
        patches = []
        added_code = '''
                li r0, 4
                li r3, 1
                lis r4, 0x10000758@h
                ori r4, r4, 0x10000758@l
                li r5, 2
                sc
                bl {added_function}
                li r0, 1
                li r3, 0x34
                sc
            '''
        patches.append(AddEntryPointPatch(added_code))

        test_str = b"testtesttest\n\x00"
        added_code = '''
                li r0, 4
                li r3, 1
                lis r4, {added_data}@h
                ori r4, r4, {added_data}@l
                li r5, %d
                sc
                blr
            ''' % (len(test_str))
        patches.append(AddCodePatch(added_code, "added_function"))
        patches.append(AddRODataPatch(test_str, "added_data"))

        self.run_test("printf_nopie",
                      patches,
                      expected_output=b'%s' + test_str,
                      expected_returnCode=0x34)
    def test_complex1(self):
            patches = []
            added_code = '''
                mov x8, 0x40
                mov x0, 0x1
                ldr x1, =0x400648
                mov x2, 2
                svc 0
                bl {added_function}
                mov x8, 0x5d
                mov x0, 0x34
                svc 0
            '''
            patches.append(AddEntryPointPatch(added_code))

            test_str = b"testtesttest\n\x00"
            added_code = '''
                mov x8, 0x40
                mov x0, 0x1
                ldr x1, ={added_data}
                mov x2, %d
                svc 0
                ret
            ''' % (len(test_str))
            patches.append(AddCodePatch(added_code, "added_function"))
            patches.append(AddRODataPatch(test_str, "added_data"))

            self.run_test("printf_nopie", patches, expected_output=b'%s' + test_str, expected_returnCode=0x34)
    def test_complex1(self):
        patches = []
        added_code = '''
                mov r7, 0x4
                mov r0, 0x1
                ldr r1, =0x10450
                mov r2, 2
                svc 0
                bl {added_function}
                mov r7, 0x1
                mov r0, 0x34
                svc 0
            '''
        patches.append(AddEntryPointPatch(added_code))

        test_str = b"testtesttest\n\x00"
        added_code = '''
                mov r7, 0x4
                mov r0, 0x1
                ldr r1, ={added_data}
                mov r2, %d
                svc 0
                bx lr
            ''' % (len(test_str))
        patches.append(
            AddCodePatch(added_code, "added_function", is_thumb=True))
        patches.append(AddRODataPatch(test_str, "added_data"))

        self.run_test("printf_nopie",
                      patches,
                      expected_output=b'%s' + test_str,
                      expected_returnCode=0x34)
    def test_add_ro_data_patch(self, tlen=5):
        p1 = AddRODataPatch(b"A"*tlen, "added_data")
        added_code = '''
            li $v0, 4004
            li $a0, 1
            la $a1, {added_data}
            li $a2, %d
            syscall
        ''' % tlen
        p2 = InsertCodePatch(0x40076c, added_code, "added_code")

        self.run_test("printf_nopie", [p1, p2], expected_output=b"A"*tlen + b"Hi", expected_returnCode=0x0)
    def test_add_ro_data_patch(self, tlen=5):
        p1 = AddRODataPatch(b"A"*tlen, "added_data")
        added_code = '''
            mov x8, 0x40
            mov x0, 0x1
            ldr x1, ={added_data}
            mov x2, %d
            svc 0
        ''' % tlen
        p2 = InsertCodePatch(0x400580, added_code, "added_code")

        self.run_test("printf_nopie", [p1, p2], expected_output=b"A"*tlen + b"Hi", expected_returnCode=0x0)
    def test_insert_code_patch(self):
        test_str = b"qwertyuiop\n\x00"
        added_code = '''
            li $v0, 4004
            li $a0, 1
            la $a1, {added_data}
            li $a2, %d
            syscall
        ''' % (len(test_str))
        p1 = InsertCodePatch(0x40076c, added_code)
        p2 = AddRODataPatch(test_str, "added_data")

        self.run_test("printf_nopie", [p1, p2], expected_output=b"qwertyuiop\n\x00Hi", expected_returnCode=0)
    def test_insert_code_patch(self):
        test_str = b"qwertyuiop\n\x00"
        added_code = '''
            mov x8, 0x40
            mov x0, 0x1
            ldr x1, ={added_data}
            mov x2, %d
            svc 0
        ''' % (len(test_str))
        p1 = InsertCodePatch(0x400580, added_code)
        p2 = AddRODataPatch(test_str, "added_data")

        self.run_test("printf_nopie", [p1, p2], expected_output=b"qwertyuiop\n\x00Hi", expected_returnCode=0)
    def test_add_ro_data_patch(self, tlen=5):
        p1 = AddRODataPatch(b"A" * tlen, "added_data")
        added_code = '''
            mov eax, 4              ;sys_write
            mov ebx, 1              ;fd = stdout
            mov ecx, {added_data}   ;buf
            mov edx, %d             ;len
            int 0x80
        ''' % tlen
        p2 = InsertCodePatch(0x8048457, added_code, "added_code")

        self.run_test("printf_nopie", [p1, p2],
                      expected_output=b"A" * tlen + b"Hi",
                      expected_returnCode=0x0)
    def test_insert_code_patch(self):
        test_str = b"qwertyuiop\n\x00"
        added_code = '''
            mov     eax, 4
            mov     ebx, 1
            mov     ecx, {added_data}
            mov     edx, %d
            int     0x80
        ''' % (len(test_str))
        p1 = InsertCodePatch(0x8048457, added_code)
        p2 = AddRODataPatch(test_str, "added_data")

        self.run_test("printf_nopie", [p1, p2],
                      expected_output=b"qwertyuiop\n\x00Hi",
                      expected_returnCode=0)
    def test_add_ro_data_patch(self, tlen=5):
        p1 = AddRODataPatch(b"A" * tlen, "added_data")
        added_code = '''
            li r0, 4
            li r3, 1
            lis r4, {added_data}@h
            ori r4, r4, {added_data}@l
            li r5, %d
            sc
        ''' % tlen
        p2 = InsertCodePatch(0x100004f8, added_code, "added_code")

        self.run_test("printf_nopie", [p1, p2],
                      expected_output=b"A" * tlen + b"Hi",
                      expected_returnCode=0x0)
    def test_insert_code_patch(self):
        test_str = b"qwertyuiop\n\x00"
        added_code = '''
            li r0, 4
            li r3, 1
            lis r4, {added_data}@h
            ori r4, r4, {added_data}@l
            li r5, %d
            sc
        ''' % (len(test_str))
        p1 = InsertCodePatch(0x100004f8, added_code)
        p2 = AddRODataPatch(test_str, "added_data")

        self.run_test("printf_nopie", [p1, p2],
                      expected_output=b"qwertyuiop\n\x00Hi",
                      expected_returnCode=0)
    def test_sample_pie(self):
        patches = []
        transmit_code = '''
            pop rsi
            pop rax
            push rsi
            sub rsi, rax
            sub rsi, 0xa
            add rsi, {transmitted_string}
        	mov rax, 1
        	mov rdi, 1
        	syscall
        	mov rbx, (rsp)
        	add rsp, 8
        	pop r9
            pop r8
            pop r10
            pop rdx
            pop rsi
            pop rdi
            pop rax
            mov (rsp), rbx 
        	ret
          '''
        injected_code = '''
        push rax
        push rdi
        push rsi
        push rdx
        push r10
        push r8
        push r9
        mov rdx, 10
        push $
        call {transmit_function}
        '''
        patches.append(AddCodePatch(transmit_code, name="transmit_function"))
        patches.append(
            AddRODataPatch(b"---HI---\x00", name="transmitted_string"))
        patches.append(
            InsertCodePatch(0x400665,
                            injected_code,
                            name="injected_code_after_receive"))

        self.run_test("sample_x86-64_pie",
                      patches,
                      expected_output=b'---HI---\x00\x00Purdue')
    def test_double_patch_collision(self):
        test_str1 = b"1111111111\n\x00"
        test_str2 = b"2222222222\n\x00"
        added_code1 = '''
            li r0, 4
            li r3, 1
            lis r4, {str1}@h
            ori r4, r4, {str1}@l
            li r5, %d
            sc
        ''' % (len(test_str1))
        added_code2 = '''
            li r0, 4
            li r3, 1
            lis r4, {str2}@h
            ori r4, r4, {str2}@l
            li r5, %d
            sc
        ''' % (len(test_str2))

        p1 = InsertCodePatch(0x100004f8, added_code1, name="p1", priority=100)
        p2 = InsertCodePatch(0x100004f8, added_code2, name="p2", priority=1)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        self.run_test("printf_nopie", [p1, p2, p3, p4],
                      expected_output=test_str1 + b"Hi",
                      try_without_cfg=False)

        p1 = InsertCodePatch(0x100004f8, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x100004f8, added_code2, name="p2", priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi",
                                try_without_cfg=False)
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)

        p1 = InsertCodePatch(0x100004f8, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x100004f8 + 0x4,
                             added_code2,
                             name="p2",
                             priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi",
                                try_without_cfg=False)
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)
Example #17
0
    def test_double_patch_collision(self):
        test_str1 = b"1111111111\n\x00"
        test_str2 = b"2222222222\n\x00"
        added_code1 = '''
            dli $v0, 5001
            dli $a0, 1
            dla $a1, {str1}
            dli $a2, %d
            syscall
        ''' % (len(test_str1))
        added_code2 = '''
            dli $v0, 5001
            dli $a0, 1
            dla $a1, {str2}
            dli $a2, %d
            syscall
        ''' % (len(test_str2))

        p1 = InsertCodePatch(0x120000b20, added_code1, name="p1", priority=100)
        p2 = InsertCodePatch(0x120000b20, added_code2, name="p2", priority=1)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        self.run_test("printf_nopie", [p1, p2, p3, p4],
                      expected_output=test_str1 + b"Hi",
                      try_without_cfg=False)

        p1 = InsertCodePatch(0x120000b20, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x120000b20, added_code2, name="p2", priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi",
                                try_without_cfg=False)
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)

        p1 = InsertCodePatch(0x120000b20, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x120000b20 + 0x4,
                             added_code2,
                             name="p2",
                             priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi",
                                try_without_cfg=False)
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)
Example #18
0
 def test_sample_no_pie(self):
     patches = []
     transmit_code = '''
     	mov rax, 1
     	mov rdi, 1
     	syscall
     	mov rbx, (rsp)
     	add rsp, 8
     	pop r9
         pop r8
         pop r10
         pop rdx
         pop rsi
         pop rdi
         pop rax
         mov (rsp), rbx 
     	ret
       '''
     patches.append(AddCodePatch(transmit_code, name="transmit_function"))
     patches.append(
         AddRODataPatch(b"---HI---\x00", name="transmitted_string"))
     injected_code = '''
     push rax
     push rdi
     push rsi
     push rdx
     push r10
     push r8
     push r9
     mov rsi, {transmitted_string}
     mov rdx, 10
     call {transmit_function}
     '''
     patches.append(
         InsertCodePatch(0x400502,
                         injected_code,
                         name="injected_code_after_receive"))
     self.execute(patches, "sample_x86-64_no_pie",
                  b'---HI---\x00\x00Purdue')
    def test_double_patch_collision(self):
        test_str1 = b"1111111111\n\x00"
        test_str2 = b"2222222222\n\x00"
        added_code1 = '''
            mov r7, 0x4
            mov r0, 0x1
            ldr r1, ={str1}
            mov r2, %d
            svc 0
        ''' % (len(test_str1))
        added_code2 = '''
            mov r7, 0x4
            mov r0, 0x1
            ldr r1, ={str2}
            mov r2, %d
            svc 0
        ''' % (len(test_str2))

        p1 = InsertCodePatch(0x103ec, added_code1, name="p1", priority=100)
        p2 = InsertCodePatch(0x103ec, added_code2, name="p2", priority=1)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        self.run_test("printf_nopie", [p1, p2, p3, p4],
                      expected_output=test_str1 + b"Hi")

        p1 = InsertCodePatch(0x103ec, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x103ec, added_code2, name="p2", priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi")
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)

        p1 = InsertCodePatch(0x103ec, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x103ec + 0x4,
                             added_code2,
                             name="p2",
                             priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi")
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)
Example #20
0
    def compile_function(code,
                         compiler_flags="",
                         bits=32,
                         little_endian=False,
                         entry=0x0,
                         symbols=None,
                         data_only=False,
                         prefix=""):
        with utils.tempdir() as td:
            c_fname = os.path.join(td, "code.c")
            object_fname = os.path.join(td, "code.o")
            object2_fname = os.path.join(td, "code.2.o")
            linker_script_fname = os.path.join(td, "code.lds")
            data_fname = os.path.join(td, "data")
            rodata_sec_index = rodata_sym_index_old = rodata_sym_index_new = -1

            # C -> Object File
            with open(c_fname, 'w') as fp:
                fp.write(code)
            target = ("powerpcle-linux-gnu" if little_endian else
                      "powerpc-linux-gnu") if bits == 32 else (
                          "powerpc64le-linux-gnu"
                          if little_endian else "powerpc64-linux-gnu")
            res = utils.exec_cmd("clang -target %s -o %s -c %s %s" \
                            % (target, object_fname, c_fname, compiler_flags), shell=True)
            if res[2] != 0:
                raise CLangException("CLang error: " +
                                     str(res[0] + res[1], 'utf-8'))

            # Setup Linker Script
            linker_script = "SECTIONS { .text : { *(.text) "
            if symbols:
                for i in symbols:
                    if i == ".rodata":
                        linker_script += i + " = " + hex(symbols[i] - (
                            (entry - 0x10700000) & ~0xFFFF)) + ";"
                    else:
                        linker_script += i + " = " + hex(symbols[i] -
                                                         entry) + ";"
            linker_script += "} .rodata : { *(.rodata*) } }"
            with open(linker_script_fname, 'w') as fp:
                fp.write(linker_script)

            # Object File --LinkerScript--> Object File
            res = utils.exec_cmd(
                "ld.lld -relocatable %s -T %s -o %s" %
                (object_fname, linker_script_fname, object2_fname),
                shell=True)
            if res[2] != 0:
                raise Exception("Linking Error: " +
                                str(res[0] + res[1], 'utf-8'))

            # Load Object File
            ld = cle.Loader(object2_fname,
                            main_opts={"base_addr": 0x0},
                            perform_relocations=False)

            # Figure Out .text Section Size
            for section in ld.all_objects[0].sections:
                if section.name == ".text":
                    text_section_size = section.filesize
                    break

            # Modify Symbols in Object File to Trick Loader
            with open(object2_fname, "rb+") as f:
                elf = ELFFile(f)

                # Find the Index of .rodata Section
                for i in range(elf.num_sections()):
                    if elf.get_section(i).name == ".rodata":
                        rodata_sec_index = i
                        break

                # Find the Index of the src and dest Symbol
                symtab_section = elf.get_section_by_name(".symtab")
                for i in range(symtab_section.num_symbols()):
                    if symtab_section.get_symbol(
                            i
                    )['st_shndx'] == rodata_sec_index and symtab_section.get_symbol(
                            i)['st_info']['type'] == 'STT_SECTION':
                        rodata_sym_index_old = i
                    if symtab_section.get_symbol(i).name == ".rodata":
                        rodata_sym_index_new = i

                # Rewrite the Symbol
                if rodata_sym_index_new != -1 and rodata_sec_index != -1 and rodata_sym_index_old != -1:
                    for i in range(elf.num_sections()):
                        if elf.get_section(i).header[
                                'sh_name'] == symtab_section.header['sh_name']:
                            f.seek(0)
                            content = f.read()
                            f.seek(symtab_section['sh_offset'] +
                                   rodata_sym_index_new *
                                   symtab_section['sh_entsize'])
                            rodata_sym_new = f.read(
                                symtab_section['sh_entsize'])
                            content = utils.bytes_overwrite(
                                content, rodata_sym_new,
                                symtab_section['sh_offset'] +
                                rodata_sym_index_old *
                                symtab_section['sh_entsize'])
                            f.seek(0)
                            f.write(content)
                            f.truncate()
                            break

                # Replace all R_PPC_PLTREL24 to R_PPC_REL24
                rela_section = elf.get_section_by_name(".rela.text")
                if rela_section is not None:
                    for i in range(rela_section.num_relocations()):
                        if rela_section.get_relocation(i)['r_info_type'] == 18:
                            reloc = rela_section.get_relocation(i).entry
                            reloc['r_info'] -= 8

                            for j in range(elf.num_sections()):
                                if elf.get_section(j).header[
                                        'sh_name'] == rela_section.header[
                                            'sh_name']:
                                    f.seek(0)
                                    content = f.read()
                                    content = utils.bytes_overwrite(
                                        content,
                                        elf.structs.Elf_Rela.build(reloc),
                                        rela_section['sh_offset'] +
                                        i * rela_section['sh_entsize'])
                                    f.seek(0)
                                    f.write(content)
                                    f.truncate()
                                    break

            # Load the Modified Object File and Return compiled Data or Code
            ld = cle.Loader(object2_fname,
                            main_opts={
                                "base_addr": 0x0,
                                "entry_point": 0x0
                            })
            if data_only:
                patches = []
                for section in ld.all_objects[0].sections:
                    if section.name == ".rodata":
                        res = utils.exec_cmd(
                            "objcopy -B i386 -O binary -j %s %s %s" %
                            (section.name, object2_fname, data_fname),
                            shell=True)
                        if res[2] != 0:
                            raise ObjcopyException("Objcopy Error: " +
                                                   str(res[0] +
                                                       res[1], 'utf-8'))
                        with open(data_fname, "rb") as fp:
                            patches.append(
                                AddRODataPatch(fp.read(),
                                               name=prefix + section.name))
                        break
                return patches
            else:
                compiled = ld.memory.load(ld.all_objects[0].entry,
                                          text_section_size)
                return compiled
Example #21
0
    def get_patches(self):
        cfg = self.patcher.cfg
        patches = []

        pnum = 0
        used_spec_chars = []
        for func, (func_name, func_obj) in self.ident.matches.items():
            if func_name not in PRINTF_VARIANTS:
                continue
            if func_obj.format_spec_char is None:
                l.warning("func_obj.format_spec_char is None")
                continue

            fmt_arg_pos = PRINTF_VARIANTS[func_name]
            callers = set.union(
                set(),
                *(cfg.get_predecessors(node)
                  for node in cfg.get_all_nodes(func.addr)))

            handled_addrs = set()
            func_to_cfg = {}
            for caller in callers:
                if caller.addr in handled_addrs:
                    continue

                try:
                    args, _ = self.ident.get_call_args(func, caller.addr)
                except KeyError:
                    continue

                fmt_str = args[fmt_arg_pos]
                if not claripy.is_true(claripy.Or(*(claripy.And(seg.min_addr <= fmt_str, fmt_str <= seg.max_addr)\
                        for seg in self.ro_segments))):
                    # we bad
                    break

                handled_addrs.add(caller.addr)
            else:
                # patch not necessary for this function
                continue

            pnum += 1  # we need this to ensure always different labels
            used_spec_chars.append(func_obj.format_spec_char)
            check = """
                ; is the address not in RO memory?
                cmp dword [esp+{stack_offset}], {{max_ro_addr}}
                jbe _end_printfcheck_%d

                ; int 3
                ; is the address in the flag page?
                cmp dword [esp+{stack_offset}], {flag_page}
                jb _check_for_percent_%d
                cmp dword [esp+{stack_offset}], {flag_page_almost_end}
                ja _check_for_percent_%d

                ; they're trying to read from the flag page! f**k them.
                jmp 0x41414141

            _check_for_percent_%d:
                push esi ; = pointer to string
                mov esi, [esp+{stack_offset_2}]
                push eax
                xor eax, eax; hash

            _loop_printfcheck_%d:
                cmp byte [esi], 0
                je _final_check_printfcheck_%d
                xor al, byte[esi]
                cmp byte[esi], {format_spec_char}
                jne _continue_printfcheck_%d
                    mov ah, 0x1
                _continue_printfcheck_%d:
                    inc esi
                    jmp _loop_printfcheck_%d

            _final_check_printfcheck_%d:
                test ah, ah
                je _restore_printfcheck_%d
                test al, al ; avoid al==0 as we do in the hash algorithm
                jne _do_not_inc_%d
                    inc al
                _do_not_inc_%d: ; the dynamic hash will always be bigger than 0, the hash list is null terminated
                    mov esi, {{hash_list_{format_spec_char}}}
                _hash_check_loop_%d:
                    cmp byte[esi], 0 ; the end of the list has been reached
                    je 0x41414141
                    cmp byte[esi], al ; esi points to the hash list
                    je _restore_printfcheck_%d
                    inc esi
                    jmp _hash_check_loop_%d

            _restore_printfcheck_%d:
                pop eax
                pop esi

            _end_printfcheck_%d:
            """.format(
                stack_offset=(fmt_arg_pos + 1) * 4,
                stack_offset_2=(fmt_arg_pos + 2) * 4,
                flag_page=FLAG_PAGE,
                flag_page_almost_end=FLAG_PAGE + 0xffc,
                format_spec_char=ord(func_obj.format_spec_char),
            ) % tuple([pnum] * 18)

            patches.append(
                InsertCodePatch(func.addr,
                                check,
                                priority=250,
                                name="noflagprintf_%d" % pnum))
            l.info("function at %#08x protected" % func.addr)

        if patches:
            max_ro_addr = max(seg.max_addr for seg in self.ro_segments)
            patches.append(AddLabelPatch(max_ro_addr, name="max_ro_addr"))

        # print repr(self.hash_dict)
        for fspec in set(used_spec_chars):
            hash_list = b"".join(self.hash_dict[fspec]) + b"\x00"
            patches.append(
                AddRODataPatch(hash_list,
                               name="hash_list_{format_spec_char}".format(
                                   format_spec_char=ord(fspec))))
        # print "\n".join(map(str,patches))
        return patches
    def test_double_patch_collision(self):
        test_str1 = b"1111111111\n\x00"
        test_str2 = b"2222222222\n\x00"
        added_code1 = '''
            pusha
            mov     eax, 4
            mov     ebx, 1
            mov     ecx, {str1}
            mov     edx, %d
            int     0x80
            popa
        ''' % (len(test_str1))
        added_code2 = '''
            pusha
            mov     eax, 4
            mov     ebx, 1
            mov     ecx, {str2}
            mov     edx, %d
            int     0x80
            popa
        ''' % (len(test_str2))

        p1 = InsertCodePatch(0x8048457, added_code1, name="p1", priority=100)
        p2 = InsertCodePatch(0x8048457, added_code2, name="p2", priority=1)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        self.run_test("printf_nopie", [p1, p2, p3, p4],
                      expected_output=test_str1 + b"Hi")

        p1 = InsertCodePatch(0x8048457, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x8048457, added_code2, name="p2", priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi")
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)

        p1 = InsertCodePatch(0x8048457, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x8048457 + 3,
                             added_code2,
                             name="p2",
                             priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str2 + b"Hi")
        self.assertNotIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)

        p1 = InsertCodePatch(0x8048457, added_code1, name="p1", priority=1)
        p2 = InsertCodePatch(0x8048457 + 0x11,
                             added_code2,
                             name="p2",
                             priority=100)
        p3 = AddRODataPatch(test_str1, "str1")
        p4 = AddRODataPatch(test_str2, "str2")
        backend = self.run_test("printf_nopie", [p1, p2, p3, p4],
                                expected_output=test_str1 + test_str2 + b"Hi")
        self.assertIn(p1, backend.added_patches)
        self.assertIn(p2, backend.added_patches)