Skip to content

Commit 5dbde89

Browse files
authored
Merge pull request #1499 from elicn/uc2.2-adjustments
Adjust Qiling to comply with latest changed in Unicorn 2.1.2
2 parents 0f9311a + ad799ff commit 5dbde89

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+676
-379
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ keywords = [
2929
[tool.poetry.dependencies]
3030
python = "^3.8"
3131
capstone = "^4"
32-
unicorn = "2.0.1.post1"
32+
unicorn = "2.1.3"
3333
pefile = ">=2022.5.30"
3434
python-registry = "^1.3.1"
3535
keystone-engine = "^0.9.2"

qiling/arch/arm.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from qiling import Qiling
1414
from qiling.arch.arch import QlArch
1515
from qiling.arch import arm_const
16+
from qiling.arch.cpr import QlCprManager
1617
from qiling.arch.models import ARM_CPU_MODEL
1718
from qiling.arch.register import QlRegisterManager
1819
from qiling.const import QL_ARCH, QL_ENDIAN
@@ -69,6 +70,17 @@ def is_thumb(self) -> bool:
6970
def endian(self) -> QL_ENDIAN:
7071
return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
7172

73+
@cached_property
74+
def cpr(self) -> QlCprManager:
75+
"""Coprocessor Registers.
76+
"""
77+
78+
regs_map = dict(
79+
**arm_const.reg_cpr
80+
)
81+
82+
return QlCprManager(self.uc, regs_map)
83+
7284
@property
7385
def effective_pc(self) -> int:
7486
"""Get effective PC value, taking Thumb mode into account.
@@ -119,6 +131,6 @@ def assembler(self) -> Ks:
119131

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

124-
self.regs.fpexc = (1 << 30)
136+
self.regs.fpexc = (0b1 << 30)

qiling/arch/arm64.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from qiling import Qiling
1414
from qiling.arch.arch import QlArch
1515
from qiling.arch import arm64_const
16+
from qiling.arch.cpr64 import QlCpr64Manager
1617
from qiling.arch.models import ARM64_CPU_MODEL
1718
from qiling.arch.register import QlRegisterManager
1819
from qiling.const import QL_ARCH, QL_ENDIAN
@@ -56,6 +57,17 @@ def regs(self) -> QlRegisterManager:
5657
def endian(self) -> QL_ENDIAN:
5758
return QL_ENDIAN.EL
5859

60+
@cached_property
61+
def cpr(self) -> QlCpr64Manager:
62+
"""Coprocessor Registers.
63+
"""
64+
65+
regs_map = dict(
66+
**arm64_const.reg_cpr
67+
)
68+
69+
return QlCpr64Manager(self.uc, regs_map)
70+
5971
@cached_property
6072
def disassembler(self) -> Cs:
6173
return Cs(CS_ARCH_ARM64, CS_MODE_ARM)
@@ -65,4 +77,4 @@ def assembler(self) -> Ks:
6577
return Ks(KS_ARCH_ARM64, KS_MODE_ARM)
6678

6779
def enable_vfp(self):
68-
self.regs.cpacr_el1 = self.regs.cpacr_el1 | 0x300000
80+
self.regs.cpacr_el1 |= (0b11 << 20)

qiling/arch/arm64_const.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@
55

66
from unicorn.arm64_const import *
77

8+
9+
# coprocessor registers
10+
reg_cpr = {
11+
'TPIDR_EL0': (3, 3, 13, 0, 2),
12+
'TPIDRRO_EL0': (3, 3, 13, 0, 3),
13+
'TPIDR_EL1': (3, 0, 13, 0, 4),
14+
'ELR_EL1': (3, 0, 4, 0, 1),
15+
'ELR_EL2': (3, 4, 4, 0, 1),
16+
'ELR_EL3': (3, 6, 4, 0, 1),
17+
'SP_EL0': (3, 0, 4, 1, 0),
18+
'SP_EL1': (3, 4, 4, 1, 0),
19+
'SP_EL2': (3, 6, 4, 1, 0),
20+
'TTBR0_EL1': (3, 0, 2, 0, 0),
21+
'TTBR1_EL1': (3, 0, 2, 0, 1),
22+
'ESR_EL1': (3, 0, 5, 2, 0),
23+
'ESR_EL2': (3, 4, 5, 2, 0),
24+
'ESR_EL3': (3, 6, 5, 2, 0),
25+
'FAR_EL1': (3, 0, 6, 0, 0),
26+
'FAR_EL2': (3, 4, 6, 0, 0),
27+
'FAR_EL3': (3, 6, 6, 0, 0),
28+
'PAR_EL1': (3, 0, 7, 4, 0),
29+
'MAIR_EL1': (3, 0, 10, 2, 0),
30+
'VBAR_EL1': (3, 0, 12, 0, 0),
31+
'VBAR_EL2': (3, 4, 12, 0, 0),
32+
'VBAR_EL3': (3, 6, 12, 0, 0)
33+
}
34+
835
reg_map = {
936
"x0": UC_ARM64_REG_X0,
1037
"x1": UC_ARM64_REG_X1,
@@ -41,8 +68,6 @@
4168
"pc": UC_ARM64_REG_PC,
4269
"lr": UC_ARM64_REG_LR,
4370
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
44-
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0,
45-
"pstate": UC_ARM64_REG_PSTATE
4671
}
4772

4873
reg_map_b = {

qiling/arch/arm_const.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,34 @@
55

66
from unicorn.arm_const import *
77

8+
9+
# coprocessor registers
10+
reg_cpr = {
11+
'CPACR': (15, 0, 1, 0, 2, 0, False),
12+
'TPIDRURO': (15, 0, 13, 0, 3, 0, False)
13+
}
14+
815
reg_map = {
9-
"r0": UC_ARM_REG_R0,
10-
"r1": UC_ARM_REG_R1,
11-
"r2": UC_ARM_REG_R2,
12-
"r3": UC_ARM_REG_R3,
13-
"r4": UC_ARM_REG_R4,
14-
"r5": UC_ARM_REG_R5,
15-
"r6": UC_ARM_REG_R6,
16-
"r7": UC_ARM_REG_R7,
17-
"r8": UC_ARM_REG_R8,
18-
"r9": UC_ARM_REG_R9,
16+
"r0": UC_ARM_REG_R0,
17+
"r1": UC_ARM_REG_R1,
18+
"r2": UC_ARM_REG_R2,
19+
"r3": UC_ARM_REG_R3,
20+
"r4": UC_ARM_REG_R4,
21+
"r5": UC_ARM_REG_R5,
22+
"r6": UC_ARM_REG_R6,
23+
"r7": UC_ARM_REG_R7,
24+
"r8": UC_ARM_REG_R8,
25+
"r9": UC_ARM_REG_R9,
1926
"r10": UC_ARM_REG_R10,
2027
"r11": UC_ARM_REG_R11,
2128
"r12": UC_ARM_REG_R12,
22-
"sp": UC_ARM_REG_SP,
23-
"lr": UC_ARM_REG_LR,
24-
"pc": UC_ARM_REG_PC,
29+
"sp": UC_ARM_REG_SP,
30+
"lr": UC_ARM_REG_LR,
31+
"pc": UC_ARM_REG_PC,
2532

26-
"cpsr": UC_ARM_REG_CPSR,
27-
"c1_c0_2": UC_ARM_REG_C1_C0_2,
28-
"c13_c0_3": UC_ARM_REG_C13_C0_3,
33+
"apsr": UC_ARM_REG_APSR,
34+
"cpsr": UC_ARM_REG_CPSR,
35+
"spsr": UC_ARM_REG_SPSR,
2936
"fpexc": UC_ARM_REG_FPEXC
3037
}
3138

qiling/arch/arm_utils.py

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55

6+
from typing import Mapping
7+
68
from qiling import Qiling
79
from qiling.const import QL_ENDIAN
810

911

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

1820
trap_map = {
19-
'memory_barrier':
2021
# @ 0xffff0fa0
21-
# mcr p15, 0, r0, c7, c10, 5
22-
# nop
23-
# mov pc, lr
24-
'''
25-
ba 0f 07 ee
26-
00 f0 20 e3
27-
0e f0 a0 e1
28-
''',
22+
'memory_barrier': bytes.fromhex(
23+
'ba 0f 07 ee' # mcr p15, 0, r0, c7, c10, 5
24+
'00 f0 20 e3' # nop
25+
'0e f0 a0 e1' # mov pc, lr
26+
),
2927

30-
'cmpxchg':
3128
# @ 0xffff0fc0
32-
# ldr r3, [r2]
33-
# subs r3, r3, r0
34-
# streq r1, [r2]
35-
# rsbs r0, r3, #0
36-
# mov pc, lr
37-
'''
38-
00 30 92 e5
39-
00 30 53 e0
40-
00 10 82 05
41-
00 00 73 e2
42-
0e f0 a0 e1
43-
''',
29+
'cmpxchg': bytes.fromhex(
30+
'00 30 92 e5' # ldr r3, [r2]
31+
'00 30 53 e0' # subs r3, r3, r0
32+
'00 10 82 05' # streq r1, [r2]
33+
'00 00 73 e2' # rsbs r0, r3, #0
34+
'0e f0 a0 e1' # mov pc, lr
35+
),
4436

45-
'get_tls':
4637
# @ 0xffff0fe0
47-
# ldr r0, [pc, #(16 - 8)]
48-
# mov pc, lr
49-
# mrc p15, 0, r0, c13, c0, 3
50-
# padding (e7 fd de f1)
51-
# data:
52-
# "\x00\x00\x00\x00"
53-
# "\x00\x00\x00\x00"
54-
# "\x00\x00\x00\x00"
55-
'''
56-
08 00 9f e5
57-
0e f0 a0 e1
58-
70 0f 1d ee
59-
e7 fd de f1
60-
00 00 00 00
61-
00 00 00 00
62-
00 00 00 00
63-
'''
38+
'get_tls': bytes.fromhex(
39+
'08 00 9f e5' # ldr r0, [pc, #(16 - 8)]
40+
'0e f0 a0 e1' # mov pc, lr
41+
'70 0f 1d ee' # mrc p15, 0, r0, c13, c0, 3
42+
'e7 fd de f1' # padding (e7 fd de f1)
43+
'00 00 00 00' # data
44+
'00 00 00 00' # data
45+
'00 00 00 00' # data
46+
)
6447
}
6548

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

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

77-
for trap_name, trap_hex in trap_map.items():
78-
trap_code = bytes.fromhex(trap_hex)
79-
80-
if ql.arch.endian == QL_ENDIAN.EB:
60+
for trap_name, trap_code in trap_map.items():
61+
if ql.arch.endian is QL_ENDIAN.EB:
8162
trap_code = swap_endianness(trap_code)
8263

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

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

8869

8970
def swap_endianness(s: bytes, blksize: int = 4) -> bytes:

qiling/arch/cpr.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
from __future__ import annotations
7+
8+
import weakref
9+
10+
from typing import TYPE_CHECKING, Dict, Mapping, Tuple
11+
12+
if TYPE_CHECKING:
13+
from unicorn import Uc
14+
15+
_CPR_T = Tuple[int, int, int, int, int, int, bool]
16+
17+
18+
class QlCprManager:
19+
"""Enables access to ARM coprocessor registers.
20+
"""
21+
22+
# for more information about various aarch32 coprocessor register, pelase refer to:
23+
# https://developer.arm.com/documentation/ddi0601/latest/AArch32-Registers
24+
25+
def __init__(self, uc: Uc, regs_map: Mapping[str, _CPR_T]) -> None:
26+
"""Initialize the coprocessor registers manager.
27+
"""
28+
29+
# this funny way of initialization is used to avoid calling self setattr and
30+
# getattr upon init. if it did, it would go into an endless recursion
31+
self.register_mapping: Dict[str, _CPR_T]
32+
super().__setattr__('register_mapping', regs_map)
33+
34+
self.uc: Uc = weakref.proxy(uc)
35+
36+
def __getattr__(self, name: str) -> int:
37+
if name in self.register_mapping:
38+
return self.read(*self.register_mapping[name])
39+
40+
else:
41+
return super().__getattribute__(name)
42+
43+
def __setattr__(self, name: str, value: int) -> None:
44+
if name in self.register_mapping:
45+
self.write(*self.register_mapping[name], value)
46+
47+
else:
48+
super().__setattr__(name, value)
49+
50+
def read(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool) -> int:
51+
"""Read a coprocessor register value.
52+
53+
Args:
54+
coproc : coprocessor to access, value varies between 0 and 15
55+
opc1 : opcode 1, value varies between 0 and 7
56+
crn : coprocessor register to access (CRn), value varies between 0 and 15
57+
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
58+
opc2 : opcode 2, value varies between 0 and 7
59+
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
60+
is_64 : indicates whether this is a 64-bit register
61+
62+
Returns: value of coprocessor register
63+
"""
64+
65+
return self.uc.cpr_read(coproc, opc1, crn, crm, opc2, el, is_64)
66+
67+
def write(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool, value: int) -> None:
68+
"""Write a coprocessor register value.
69+
70+
Args:
71+
coproc : coprocessor to access, value varies between 0 and 15
72+
opc1 : opcode 1, value varies between 0 and 7
73+
crn : coprocessor register to access (CRn), value varies between 0 and 15
74+
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
75+
opc2 : opcode 2, value varies between 0 and 7
76+
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
77+
is_64 : indicates whether this is a 64-bit register
78+
value : value to write
79+
"""
80+
81+
self.uc.cpr_write(coproc, opc1, crn, crm, opc2, el, is_64, value)
82+
83+
def save(self) -> Dict[str, int]:
84+
"""Save registers.
85+
"""
86+
87+
return dict((name, self.read(*reg)) for name, reg in self.register_mapping.items())
88+
89+
def restore(self, context: Mapping[str, int]) -> None:
90+
"""Restore registers.
91+
"""
92+
93+
for name, val in context.items():
94+
self.write(*self.register_mapping[name], val)
95+
96+
97+
__all__ = ['QlCprManager']

0 commit comments

Comments
 (0)