Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8db61c1
Be more tolerant to missing regs
elicn Oct 10, 2024
210daf5
Replace deprecated ARM regs with CP regs definitions
elicn Oct 10, 2024
72a5187
Introduce CP regs accessors to ARM/ARM64
elicn Oct 10, 2024
3c5ff2d
Modify ARM code to use new CP regs accessors
elicn Oct 10, 2024
9ee9b3c
Better distinguish x86 registers supported only on 64-bit mode
elicn Oct 10, 2024
20d0bf5
Decorate Uc subclasses as such
elicn Oct 10, 2024
b15e541
Set x86 tests that use SSE to a CPU model that supports it
elicn Oct 10, 2024
4bbe038
Change mmap address to make MIPS programs happy
elicn Oct 10, 2024
2bb0a2e
Use weakrefs in circular references to improve GC
elicn Oct 10, 2024
a027f46
Opportunistic styling and annotation fixes
elicn Oct 10, 2024
7f5cca4
Adjust MIPS regs
elicn Oct 31, 2024
d05ac7b
Improve ELF AUXV compatibility
elicn Oct 31, 2024
8b34114
Fix ARM fcall unwind
elicn Oct 31, 2024
7ec1187
Refactor CC arguments lists
elicn Nov 17, 2024
32984d6
Fix FreeBSD breakage
elicn Nov 17, 2024
65881e6
Remove overlooked MIPS regs
elicn Feb 16, 2025
c22740e
Bumping up Unicorn dependency
elicn Feb 17, 2025
78f20a5
Revert "Bumping up Unicorn dependency"
elicn Feb 17, 2025
0cef716
Fix stack alignment for MIPS
elicn Feb 17, 2025
b1f999c
Bumping up Unicorn dependency (again)
elicn Feb 17, 2025
057ac44
Merge branch 'dev' into uc2.2-adjustments
elicn Feb 17, 2025
e82ae36
Narrow down import scope
elicn Feb 17, 2025
5e60b04
Adjust count to address MIPS delay slot random breakage
elicn Feb 18, 2025
bd648a2
Enable access to ARM coprocessor regs as properties
elicn Feb 18, 2025
a406907
Save and restore ARM coprocessor register as part of Qiling save and …
elicn Feb 18, 2025
0bc8dec
Insignificant endianess fix
elicn Feb 18, 2025
857addc
Have QDB save and restore arch-specific regs
elicn Feb 20, 2025
b25c910
Fix QDB bug where prophecy target could be None
elicn Feb 20, 2025
0328e26
Fix QDB bug that mistreats prophecy prediction
elicn Feb 20, 2025
76d7e5e
Publish ContextRender, Prophecy and BranchPredictor to allow effectiv…
elicn Feb 20, 2025
ba87678
Housekeeping for QDB
elicn Feb 20, 2025
7929c36
Extract publicly used decorators
elicn Feb 20, 2025
273d79d
Import necessary types
elicn Feb 20, 2025
2a3e205
Fix historical issue with http server tests
elicn Feb 20, 2025
ad97484
Add missin xreg to transform output
elicn Feb 20, 2025
eb86d8c
Add missin xreg to transform output
elicn Feb 20, 2025
02f121b
Enable debug logging on Android tests
elicn Mar 3, 2025
8ac63bc
join threads
wtdcode Mar 7, 2025
853b9e9
join threads correctly
wtdcode Mar 7, 2025
4f9fd30
Unicorn 2.1.3
wtdcode Mar 7, 2025
ad799ff
More timeout
wtdcode Mar 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ keywords = [
[tool.poetry.dependencies]
python = "^3.8"
capstone = "^4"
unicorn = "2.0.1.post1"
unicorn = "2.1.3"
pefile = ">=2022.5.30"
python-registry = "^1.3.1"
keystone-engine = "^0.9.2"
Expand Down
16 changes: 14 additions & 2 deletions qiling/arch/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch import arm_const
from qiling.arch.cpr import QlCprManager
from qiling.arch.models import ARM_CPU_MODEL
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
Expand Down Expand Up @@ -69,6 +70,17 @@ def is_thumb(self) -> bool:
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL

@cached_property
def cpr(self) -> QlCprManager:
"""Coprocessor Registers.
"""

regs_map = dict(
**arm_const.reg_cpr
)

return QlCprManager(self.uc, regs_map)

@property
def effective_pc(self) -> int:
"""Get effective PC value, taking Thumb mode into account.
Expand Down Expand Up @@ -119,6 +131,6 @@ def assembler(self) -> Ks:

def enable_vfp(self) -> None:
# set full access to cp10 and cp11
self.regs.c1_c0_2 = self.regs.c1_c0_2 | (0b11 << 20) | (0b11 << 22)
self.cpr.CPACR |= (0b11 << 20) | (0b11 << 22)

self.regs.fpexc = (1 << 30)
self.regs.fpexc = (0b1 << 30)
14 changes: 13 additions & 1 deletion qiling/arch/arm64.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch import arm64_const
from qiling.arch.cpr64 import QlCpr64Manager
from qiling.arch.models import ARM64_CPU_MODEL
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
Expand Down Expand Up @@ -56,6 +57,17 @@ def regs(self) -> QlRegisterManager:
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL

@cached_property
def cpr(self) -> QlCpr64Manager:
"""Coprocessor Registers.
"""

regs_map = dict(
**arm64_const.reg_cpr
)

return QlCpr64Manager(self.uc, regs_map)

@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_ARM64, CS_MODE_ARM)
Expand All @@ -65,4 +77,4 @@ def assembler(self) -> Ks:
return Ks(KS_ARCH_ARM64, KS_MODE_ARM)

def enable_vfp(self):
self.regs.cpacr_el1 = self.regs.cpacr_el1 | 0x300000
self.regs.cpacr_el1 |= (0b11 << 20)
29 changes: 27 additions & 2 deletions qiling/arch/arm64_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@

from unicorn.arm64_const import *


# coprocessor registers
reg_cpr = {
'TPIDR_EL0': (3, 3, 13, 0, 2),
'TPIDRRO_EL0': (3, 3, 13, 0, 3),
'TPIDR_EL1': (3, 0, 13, 0, 4),
'ELR_EL1': (3, 0, 4, 0, 1),
'ELR_EL2': (3, 4, 4, 0, 1),
'ELR_EL3': (3, 6, 4, 0, 1),
'SP_EL0': (3, 0, 4, 1, 0),
'SP_EL1': (3, 4, 4, 1, 0),
'SP_EL2': (3, 6, 4, 1, 0),
'TTBR0_EL1': (3, 0, 2, 0, 0),
'TTBR1_EL1': (3, 0, 2, 0, 1),
'ESR_EL1': (3, 0, 5, 2, 0),
'ESR_EL2': (3, 4, 5, 2, 0),
'ESR_EL3': (3, 6, 5, 2, 0),
'FAR_EL1': (3, 0, 6, 0, 0),
'FAR_EL2': (3, 4, 6, 0, 0),
'FAR_EL3': (3, 6, 6, 0, 0),
'PAR_EL1': (3, 0, 7, 4, 0),
'MAIR_EL1': (3, 0, 10, 2, 0),
'VBAR_EL1': (3, 0, 12, 0, 0),
'VBAR_EL2': (3, 4, 12, 0, 0),
'VBAR_EL3': (3, 6, 12, 0, 0)
}

reg_map = {
"x0": UC_ARM64_REG_X0,
"x1": UC_ARM64_REG_X1,
Expand Down Expand Up @@ -41,8 +68,6 @@
"pc": UC_ARM64_REG_PC,
"lr": UC_ARM64_REG_LR,
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0,
"pstate": UC_ARM64_REG_PSTATE
}

reg_map_b = {
Expand Down
39 changes: 23 additions & 16 deletions qiling/arch/arm_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,34 @@

from unicorn.arm_const import *


# coprocessor registers
reg_cpr = {
'CPACR': (15, 0, 1, 0, 2, 0, False),
'TPIDRURO': (15, 0, 13, 0, 3, 0, False)
}

reg_map = {
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r10": UC_ARM_REG_R10,
"r11": UC_ARM_REG_R11,
"r12": UC_ARM_REG_R12,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,

"cpsr": UC_ARM_REG_CPSR,
"c1_c0_2": UC_ARM_REG_C1_C0_2,
"c13_c0_3": UC_ARM_REG_C13_C0_3,
"apsr": UC_ARM_REG_APSR,
"cpsr": UC_ARM_REG_CPSR,
"spsr": UC_ARM_REG_SPSR,
"fpexc": UC_ARM_REG_FPEXC
}

Expand Down
73 changes: 27 additions & 46 deletions qiling/arch/arm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from typing import Mapping

from qiling import Qiling
from qiling.const import QL_ENDIAN


def init_linux_traps(ql: Qiling, address_map) -> None:
def init_linux_traps(ql: Qiling, address_map: Mapping[str, int]) -> None:
# If the compiler for the target does not provides some primitives for some
# reasons (e.g. target limitations), the kernel is responsible to assist
# with these operations.
Expand All @@ -16,51 +18,32 @@ def init_linux_traps(ql: Qiling, address_map) -> None:
# https://elixir.bootlin.com/linux/latest/source/arch/arm/kernel/entry-armv.S#L899

trap_map = {
'memory_barrier':
# @ 0xffff0fa0
# mcr p15, 0, r0, c7, c10, 5
# nop
# mov pc, lr
'''
ba 0f 07 ee
00 f0 20 e3
0e f0 a0 e1
''',
'memory_barrier': bytes.fromhex(
'ba 0f 07 ee' # mcr p15, 0, r0, c7, c10, 5
'00 f0 20 e3' # nop
'0e f0 a0 e1' # mov pc, lr
),

'cmpxchg':
# @ 0xffff0fc0
# ldr r3, [r2]
# subs r3, r3, r0
# streq r1, [r2]
# rsbs r0, r3, #0
# mov pc, lr
'''
00 30 92 e5
00 30 53 e0
00 10 82 05
00 00 73 e2
0e f0 a0 e1
''',
'cmpxchg': bytes.fromhex(
'00 30 92 e5' # ldr r3, [r2]
'00 30 53 e0' # subs r3, r3, r0
'00 10 82 05' # streq r1, [r2]
'00 00 73 e2' # rsbs r0, r3, #0
'0e f0 a0 e1' # mov pc, lr
),

'get_tls':
# @ 0xffff0fe0
# ldr r0, [pc, #(16 - 8)]
# mov pc, lr
# mrc p15, 0, r0, c13, c0, 3
# padding (e7 fd de f1)
# data:
# "\x00\x00\x00\x00"
# "\x00\x00\x00\x00"
# "\x00\x00\x00\x00"
'''
08 00 9f e5
0e f0 a0 e1
70 0f 1d ee
e7 fd de f1
00 00 00 00
00 00 00 00
00 00 00 00
'''
'get_tls': bytes.fromhex(
'08 00 9f e5' # ldr r0, [pc, #(16 - 8)]
'0e f0 a0 e1' # mov pc, lr
'70 0f 1d ee' # mrc p15, 0, r0, c13, c0, 3
'e7 fd de f1' # padding (e7 fd de f1)
'00 00 00 00' # data
'00 00 00 00' # data
'00 00 00 00' # data
)
}

if address_map:
Expand All @@ -74,16 +57,14 @@ def init_linux_traps(ql: Qiling, address_map) -> None:

ql.mem.map(base, size, info="[arm_traps]")

for trap_name, trap_hex in trap_map.items():
trap_code = bytes.fromhex(trap_hex)

if ql.arch.endian == QL_ENDIAN.EB:
for trap_name, trap_code in trap_map.items():
if ql.arch.endian is QL_ENDIAN.EB:
trap_code = swap_endianness(trap_code)

if trap_name in address_map:
ql.mem.write(address_map[trap_name], trap_code)

ql.log.debug(f'Set kernel trap: {trap_name} at {address_map[trap_name]:#x}')
ql.log.debug(f'Setting kernel trap {trap_name} at {address_map[trap_name]:#x}')


def swap_endianness(s: bytes, blksize: int = 4) -> bytes:
Expand Down
97 changes: 97 additions & 0 deletions qiling/arch/cpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from __future__ import annotations

import weakref

from typing import TYPE_CHECKING, Dict, Mapping, Tuple

if TYPE_CHECKING:
from unicorn import Uc

_CPR_T = Tuple[int, int, int, int, int, int, bool]


class QlCprManager:
"""Enables access to ARM coprocessor registers.
"""

# for more information about various aarch32 coprocessor register, pelase refer to:
# https://developer.arm.com/documentation/ddi0601/latest/AArch32-Registers

def __init__(self, uc: Uc, regs_map: Mapping[str, _CPR_T]) -> None:
"""Initialize the coprocessor registers manager.
"""

# this funny way of initialization is used to avoid calling self setattr and
# getattr upon init. if it did, it would go into an endless recursion
self.register_mapping: Dict[str, _CPR_T]
super().__setattr__('register_mapping', regs_map)

self.uc: Uc = weakref.proxy(uc)

def __getattr__(self, name: str) -> int:
if name in self.register_mapping:
return self.read(*self.register_mapping[name])

else:
return super().__getattribute__(name)

def __setattr__(self, name: str, value: int) -> None:
if name in self.register_mapping:
self.write(*self.register_mapping[name], value)

else:
super().__setattr__(name, value)

def read(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool) -> int:
"""Read a coprocessor register value.

Args:
coproc : coprocessor to access, value varies between 0 and 15
opc1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
opc2 : opcode 2, value varies between 0 and 7
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
is_64 : indicates whether this is a 64-bit register

Returns: value of coprocessor register
"""

return self.uc.cpr_read(coproc, opc1, crn, crm, opc2, el, is_64)

def write(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool, value: int) -> None:
"""Write a coprocessor register value.

Args:
coproc : coprocessor to access, value varies between 0 and 15
opc1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
opc2 : opcode 2, value varies between 0 and 7
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
is_64 : indicates whether this is a 64-bit register
value : value to write
"""

self.uc.cpr_write(coproc, opc1, crn, crm, opc2, el, is_64, value)

def save(self) -> Dict[str, int]:
"""Save registers.
"""

return dict((name, self.read(*reg)) for name, reg in self.register_mapping.items())

def restore(self, context: Mapping[str, int]) -> None:
"""Restore registers.
"""

for name, val in context.items():
self.write(*self.register_mapping[name], val)


__all__ = ['QlCprManager']
Loading
Loading