From 810e864cfa259019f1878bcacdf1d8c7ed1a24ac Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 18 Nov 2021 13:49:35 +0200 Subject: [PATCH 1/5] cc can now reserve args slots --- qiling/cc/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/qiling/cc/__init__.py b/qiling/cc/__init__.py index ef1cbdb0d..af08c0f9d 100644 --- a/qiling/cc/__init__.py +++ b/qiling/cc/__init__.py @@ -70,6 +70,17 @@ def setReturnValue(self, val: int) -> None: raise NotImplementedError + def reserve(self, nslots: int) -> None: + """Reserve slots for function arguments. + + This may be used to stage a new frame before executing a native function. + + Args: + nslots: number of arg slots to reserve + """ + + raise NotImplementedError + def unwind(self, nslots: int) -> int: """Unwind frame and return from function call. @@ -149,3 +160,11 @@ def getReturnValue(self) -> int: def setReturnValue(self, value: int) -> None: self.ql.reg.write(self._retreg, value) + + def reserve(self, nslots: int) -> None: + assert nslots < len(self._argregs), 'too many slots' + + # count how many slots should be reserved on the stack + si = self._argregs[:nslots].count(None) + + self.ql.reg.arch_sp -= (self._shadow + si) * self._asize From 82e1ed16edc341fc3703b114256174c573915808 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 18 Nov 2021 13:50:18 +0200 Subject: [PATCH 2/5] cc can now set return address --- qiling/cc/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qiling/cc/__init__.py b/qiling/cc/__init__.py index af08c0f9d..7d69ed49a 100644 --- a/qiling/cc/__init__.py +++ b/qiling/cc/__init__.py @@ -70,6 +70,15 @@ def setReturnValue(self, val: int) -> None: raise NotImplementedError + def setReturnAddress(self, addr: int) -> None: + """Set function return address. + + Args: + addr: return address to set + """ + + raise NotImplementedError + def reserve(self, nslots: int) -> None: """Reserve slots for function arguments. From db677d14fa1266afcaafa1f0356f4babb85745b3 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 18 Nov 2021 13:50:39 +0200 Subject: [PATCH 3/5] implement setReturnAddress for Intel --- qiling/cc/intel.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qiling/cc/intel.py b/qiling/cc/intel.py index c047ab781..9d90ab54e 100644 --- a/qiling/cc/intel.py +++ b/qiling/cc/intel.py @@ -25,6 +25,9 @@ def __init__(self, ql: Qiling): super().__init__(ql, retreg) + def setReturnAddress(self, addr: int) -> None: + self.ql.arch.stack_push(addr) + def unwind(self, nslots: int) -> int: # no cleanup; just pop out the return address return self.ql.arch.stack_pop() From 92b00aa6bb81173161d03b9144af0b39733f39b8 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 18 Nov 2021 13:50:50 +0200 Subject: [PATCH 4/5] implement setReturnAddress for ARM --- qiling/cc/arm.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/qiling/cc/arm.py b/qiling/cc/arm.py index 330c84322..3d0aaa254 100644 --- a/qiling/cc/arm.py +++ b/qiling/cc/arm.py @@ -11,30 +11,31 @@ from qiling import Qiling from . import QlCommonBaseCC -class aarch64(QlCommonBaseCC): - _argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8 - - def __init__(self, ql: Qiling) -> None: - super().__init__(ql, UC_ARM64_REG_X0) +class QlArmBaseCC(QlCommonBaseCC): + """Calling convention base class for ARM-based systems. + Supports arguments passing over registers and stack. + """ @staticmethod def getNumSlots(argbits: int) -> int: return 1 + def setReturnAddress(self, addr: int) -> None: + # TODO: do we need to update LR? + self.ql.arch.stack_push(addr) + def unwind(self, nslots: int) -> int: # TODO: cleanup? return self.ql.arch.stack_pop() -class aarch32(QlCommonBaseCC): - _argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12 +class aarch64(QlArmBaseCC): + _argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8 def __init__(self, ql: Qiling) -> None: - super().__init__(ql, UC_ARM_REG_R0) + super().__init__(ql, UC_ARM64_REG_X0) - @staticmethod - def getNumSlots(argbits: int) -> int: - return 1 +class aarch32(QlArmBaseCC): + _argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12 - def unwind(self, nslots: int) -> int: - # TODO: cleanup? - return self.ql.arch.stack_pop() + def __init__(self, ql: Qiling) -> None: + super().__init__(ql, UC_ARM_REG_R0) From fc79eddfa11238b2c0c4929d255256b1c6f96654 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 18 Nov 2021 13:51:25 +0200 Subject: [PATCH 5/5] Let fcall stage and call native functions --- qiling/os/fcall.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/qiling/os/fcall.py b/qiling/os/fcall.py index 89c90728e..1a4cfd54b 100644 --- a/qiling/os/fcall.py +++ b/qiling/os/fcall.py @@ -173,3 +173,25 @@ def call(self, func: CallHook, proto: Mapping[str, Any], params: Mapping[str, An retaddr = -1 if passthru else self.cc.unwind(nslots) return targs, retval, retaddr + + def call_native(self, addr: int, args: Sequence[Tuple[Any, int]], ret: Optional[int]) -> None: + """Call a native function after properly staging its arguments and return address. + + Args: + addr: function entry point + args: a sequence of 2-tuples containing parameters types and values to pass to the function; may be empty + ret: return address; may be None + """ + + # reserve slots for arguments + nslots = self.__count_slots(atype for atype, _ in args) + self.cc.reserve(nslots) + + # set arguments values + self.writeParams(args) + + if ret is not None: + self.cc.setReturnAddress(ret) + + # call + self.ql.reg.arch_pc = addr