예제 #1
0
    def compile_function_stub(self, mfunction, nbargs, address_after):
        # Now encode the stub
        stub_label = asm.Label("Stub_label_" + str(mfunction.name))

        # The call to that will be compiled after the stub compilation is over
        address = mfunction.allocator.encode_stub(asm.LABEL(stub_label))

        # Save the association
        mfunction.allocator.jump_labels[address] = asm.LABEL(stub_label)

        # Save the rsp for the stub
        mfunction.allocator.encode_stub(asm.MOV(asm.rdi, asm.registers.rsp))

        # Call the stub function in C
        function_address = int(
            ffi.cast("intptr_t", ffi.addressof(lib, "function_stub")))

        # Align the stack on 16 bits
        mfunction.allocator.encode_stub(asm.MOV(asm.rax, asm.registers.rsp))
        mfunction.allocator.encode_stub(asm.AND(asm.registers.rsp, -16))
        mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp))

        mfunction.allocator.encode_stub(asm.MOV(asm.r10, function_address))
        mfunction.allocator.encode_stub(asm.CALL(asm.r10))

        # Now put additional informations for the stub
        # Force min 8 bits encoding for this value
        nbargs_bytes = encode_bytes(nbargs)

        stub_function = StubFunction()
        self.stub_dictionary[address_after] = stub_function

        self.data_addresses[
            address_after] = jitcompiler_instance.global_allocator.stub_offset

        address_after_bytes = address_after.to_bytes(
            address_after.bit_length(), "little")

        # Write after the stub
        jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction(
            nbargs_bytes, jitcompiler_instance.global_allocator.stub_offset)
        jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction(
            address_after_bytes,
            jitcompiler_instance.global_allocator.stub_offset)

        for i in range(5):
            mfunction.allocator.encode_stub(asm.NOP())

        return address
예제 #2
0
    def encode_stub_test(self, branch, label, type_value):
        # Giving rsp to C function
        address = self.mfunction.allocator.encode_stub(
            asm.MOV(asm.rdi, asm.registers.rsp))

        # Save the association
        self.mfunction.allocator.jump_labels[address] = label

        # Call to C to compile the block
        reg_id = asm.r10
        function_address = int(
            ffi.cast("intptr_t", ffi.addressof(lib, "type_test_stub")))

        # Align the stack on 16 bits
        self.mfunction.allocator.encode_stub(
            asm.MOV(asm.rax, asm.registers.rsp))
        self.mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp))

        self.mfunction.allocator.encode_stub(asm.MOV(reg_id, function_address))
        self.mfunction.allocator.encode_stub(asm.CALL(reg_id))

        # Compute the return address to link this stub to self
        return_address = lib.get_address(
            ffi.from_buffer(
                jitcompiler_instance.global_allocator.code_section),
            jitcompiler_instance.global_allocator.stub_offset)

        return_address = return_address & -16
        # Associate this return address to self in the stub_handler
        stubhandler_instance.stub_dictionary[return_address] = self

        variable_id = encode_bytes(self.variable)
        type_bytes = encode_bytes(type_value)

        offset = jitcompiler_instance.global_allocator.stub_offset

        stubhandler_instance.data_addresses[return_address] = offset

        jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction(
            variable_id, offset)
        jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction(
            type_bytes, jitcompiler_instance.global_allocator.stub_offset)

        # Saving some space for the cleaning
        for i in range(5):
            self.mfunction.allocator.encode_stub(asm.NOP())

        return return_address
예제 #3
0
    def compile_class_stub(self, mfunction):
        # Push on the stack the address of the class' stub
        c_buffer = ffi.from_buffer(
            jitcompiler_instance.global_allocator.code_section)
        address_stub = lib.get_address(
            c_buffer, jitcompiler_instance.global_allocator.stub_offset)

        mfunction.allocator.encode(asm.MOV(asm.r10, address_stub))
        mfunction.allocator.encode(asm.PUSH(asm.r10))

        # Save the address after the PUSH to be able to jump there later
        c_buffer = ffi.from_buffer(
            jitcompiler_instance.global_allocator.code_section)
        address_code = lib.get_address(
            c_buffer, jitcompiler_instance.global_allocator.code_offset)

        # Be able to find them in the collection during the later callback
        self.class_stub_addresses.append(address_stub)

        # Call the stub function
        mfunction.allocator.encode_stub(asm.MOV(asm.rdi, asm.registers.rsp))

        function_address = int(
            ffi.cast("intptr_t", ffi.addressof(lib, "class_stub")))
        mfunction.allocator.encode_stub(asm.MOV(asm.r10, function_address))
        mfunction.allocator.encode_stub(asm.CALL(asm.r10))

        offset = jitcompiler_instance.global_allocator.stub_offset

        return_address = lib.get_address(
            ffi.from_buffer(
                jitcompiler_instance.global_allocator.code_section),
            jitcompiler_instance.global_allocator.stub_offset)

        stub = StubClass()
        stub.data_address = offset
        stub.return_address = address_code

        # Indicate this offset correspond to the "return address" on the stub after the call to C returned
        self.data_addresses[return_address] = offset
        self.stub_dictionary[return_address] = stub

        # Reserve some space for the cleanup
        for i in range(5):
            mfunction.allocator.encode_stub(asm.NOP())
예제 #4
0
    def compile_stub(self, mfunction, stub):

        # The call to that will be compiled after the stub compilation is over
        stub_label = "Stub_label_" + str(id(stub))

        # Now we store the stack pointer to patch it later
        address = mfunction.allocator.encode_stub(
            asm.MOV(asm.rdi, asm.registers.rsp))

        # Save the association
        mfunction.allocator.jump_labels[address] = stub_label

        reg_id = asm.r10

        function_address = int(
            ffi.cast("intptr_t", ffi.addressof(lib, "bb_stub")))

        # Align the stack on 16 bits

        mfunction.allocator.encode_stub(asm.MOV(asm.rax, asm.registers.rsp))
        mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp))

        mfunction.allocator.encode_stub(asm.MOV(reg_id, function_address))

        mfunction.allocator.encode_stub(asm.CALL(reg_id))

        # Save the offset
        offset = jitcompiler_instance.global_allocator.stub_offset

        return_address = lib.get_address(
            ffi.from_buffer(
                jitcompiler_instance.global_allocator.code_section),
            jitcompiler_instance.global_allocator.stub_offset)

        # Indicate this offset correspond to the "return address" on the stub after the call to C returned
        self.data_addresses[return_address] = offset
        self.stub_dictionary[return_address] = stub

        # Save some space for cleaning instructions
        for i in range(15):
            mfunction.allocator.encode_stub(asm.NOP())

        return address
예제 #5
0
    def patch_instruction(self, first_offset):

        if isinstance(self.instruction, asm.MOV):
            # Moving an address inside a register, we need to change the address here

            # If the MOVE + JUMP is supposed to go just after, put NOP instead
            diff = first_offset - self.position
            if diff > 0 and diff <= 13:
                nop = asm.NOP().encode()
                for i in range(diff):
                    jitcompiler_instance.global_allocator.write_instruction(
                        nop, self.position)
                    self.position += 1
            else:
                # It's a real jump, just compile it and patch the code
                new_address = lib.get_address(
                    ffi.from_buffer(
                        jitcompiler_instance.global_allocator.code_section),
                    first_offset)
                new_instruction = asm.MOV(self.instruction.operands[0],
                                          new_address)

                # Create the new encoded instruction and replace the old one in the code section
                encoded = new_instruction.encode()
                jitcompiler_instance.global_allocator.write_instruction(
                    encoded, self.position)
        elif isinstance(self.instruction, asm.JG):

            new_operand = first_offset - self.position - len(
                self.instruction.encode())

            # Update to the new position
            new_instruction = asm.JG(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # If the previous instruction was a 32 bits offset, force it to the new one
            if len(self.instruction.encode()) > 2:
                encoded = bytearray(len(self.instruction.encode()))

                # Force the 32 encoding of the JG instruction
                encoded[0] = 0x0F
                encoded[1] = 0x8F
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JGE):
            new_operand = first_offset - self.position - len(
                self.instruction.encode())

            # Update to the new position
            new_instruction = asm.JGE(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # If the previous instruction was a 32 bits offset, force it to the new one
            if len(encoded) != 6 and len(self.instruction.encode()) > 2:
                encoded = bytearray(6)

                # Force the 32 encoding of the JGE
                encoded[0] = 0x0F
                encoded[1] = 0x8D
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                # Keep the same value for the jump
                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JE):

            new_operand = first_offset - self.position - len(
                self.instruction.encode())

            # Update to the new position
            new_instruction = asm.JE(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # If the previous instruction was a 32 bits offset, force it to the new one
            if len(self.instruction.encode()) > 2:
                encoded = bytearray(len(self.instruction.encode()))

                # Force the 32 encoding of the JE instruction
                encoded[0] = 0x0F
                encoded[1] = 0x84
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JL):
            new_operand = first_offset - self.position - len(
                self.instruction.encode())

            new_instruction = asm.JL(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # If the previous instruction was a 32 bits offset, force the same length for the new one
            if len(self.instruction.encode()) > 2 and len(encoded) != 6:
                encoded = bytearray(len(self.instruction.encode()))

                # Force the 32 encoding of the JL instruction
                encoded[0] = 0x0F
                encoded[1] = 0x8C
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                size = custom_ceil(new_operand / 255)
                bytes = None
                if size < 0 or new_operand < 0:
                    size = 4
                    bytes = new_operand.to_bytes(size, 'little', signed=True)
                else:
                    bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JLE):
            new_operand = first_offset - self.position - len(
                self.instruction.encode())

            # Update to the new position
            new_instruction = asm.JLE(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # If the previous instruction was a 32 bits offset, force it to the new one
            if len(encoded) != 6 and len(self.instruction.encode()) > 2:
                encoded = bytearray(6)

                # Force the 32 encoding of the JGE
                encoded[0] = 0x0F
                encoded[1] = 0x8E
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                # Keep the same value for the jump
                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JNE):
            new_operand = first_offset - self.position - len(
                self.instruction.encode())
            if new_operand < 255:
                # We will encode the instruction
                new_operand = first_offset - self.position - 2

            new_instruction = asm.JNE(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # Need to add some NOP instruction to fill the space left from the previous longer instruction
            if len(encoded) < len(self.instruction.encode()):

                diff = len(self.instruction.encode()) - len(encoded)

                for i in range(diff):
                    encoded += asm.NOP().encode()
            else:
                # If the previous instruction was a 32 bits offset, force the same length for the new one
                encoded = bytearray(len(self.instruction.encode()))

                # Force the 32 encoding of the JNE instruction
                encoded[0] = 0x0F
                encoded[1] = 0x85
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        elif isinstance(self.instruction, asm.JB):
            old_instruction_encoded = self.instruction.encode()
            new_operand = first_offset - self.position - len(
                old_instruction_encoded)

            if new_operand < 255:
                # We will encode the instruction
                new_operand = first_offset - self.position - len(
                    old_instruction_encoded)

            new_instruction = asm.JB(
                asm.operand.RIPRelativeOffset(new_operand))
            encoded = new_instruction.encode()

            # Need to add some NOP instruction to fill the space left from the previous longer instruction
            if len(old_instruction_encoded) != len(new_instruction.encode()):
                # If the previous instruction was a 32 bits offset, force the same length for the new one
                encoded = bytearray(len(old_instruction_encoded))

                # Force the 32 encoding of the JNE instruction
                encoded[0] = 0x0F
                encoded[1] = 0x82
                encoded[2] = 0
                encoded[3] = 0
                encoded[4] = 0
                encoded[5] = 0

                size = custom_ceil(new_operand / 255)
                bytes = new_operand.to_bytes(size, 'little')

                for i in range(0, len(bytes)):
                    encoded[i + 2] = bytes[i]

            jitcompiler_instance.global_allocator.write_instruction(
                encoded, self.position)
        else:
            print("Not yet implemented patch " + str(self.instruction))