Skip to content

Commit ab9532d

Browse files
authored
Merge pull request #903 from elicn/arch-refactor
Arch Refactor
2 parents 427e308 + c81d666 commit ab9532d

File tree

12 files changed

+389
-433
lines changed

12 files changed

+389
-433
lines changed

qiling/arch/a8086.py

Lines changed: 0 additions & 53 deletions
This file was deleted.

qiling/arch/arch.py

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,92 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55

6-
from abc import ABC, abstractmethod
6+
from abc import ABC
7+
from typing import Optional
78

9+
from unicorn import Uc
10+
from unicorn.unicorn import UcContext
811
from capstone import Cs
912
from keystone import Ks
1013

1114
from qiling import Qiling
12-
from .utils import QlArchUtils, ql_create_disassembler, ql_create_assembler
13-
from qiling.const import QL_ARCH
15+
from .utils import QlArchUtils
1416

1517
class QlArch(ABC):
1618
def __init__(self, ql: Qiling):
1719
self.ql = ql
1820
self.utils = QlArchUtils(ql)
1921

22+
self._disasm: Optional[Cs] = None
23+
self._asm: Optional[Ks] = None
24+
2025
# ql.init_Uc - initialized unicorn engine
2126
@property
22-
def init_uc(self):
23-
return self.ql.arch.get_init_uc()
27+
def init_uc(self) -> Uc:
28+
return self.get_init_uc()
29+
30+
31+
def stack_push(self, value: int) -> int:
32+
"""Push a value onto the architectural stack.
33+
34+
Args:
35+
value: a numeric value to push
36+
37+
Returns: the top of stack after pushing the value
38+
"""
2439

40+
self.ql.reg.arch_sp -= self.ql.pointersize
41+
self.ql.mem.write(self.ql.reg.arch_sp, self.ql.pack(value))
2542

26-
# push value to stack
27-
@abstractmethod
28-
def stack_push(self, data: int) -> int:
29-
pass
43+
return self.ql.reg.arch_sp
3044

3145

32-
# pop value to stack
33-
@abstractmethod
3446
def stack_pop(self) -> int:
35-
pass
47+
"""Pop a value from the architectural stack.
3648
49+
Returns: the value at the top of stack
50+
"""
3751

38-
# write stack value
39-
@abstractmethod
40-
def stack_write(self, offset: int, data: int) -> None:
41-
pass
52+
data = self.ql.unpack(self.ql.mem.read(self.ql.reg.arch_sp, self.ql.pointersize))
53+
self.ql.reg.arch_sp += self.ql.pointersize
54+
55+
return data
4256

4357

44-
# read stack value
45-
@abstractmethod
4658
def stack_read(self, offset: int) -> int:
47-
pass
48-
59+
"""Peek the architectural stack at a specified offset from its top, without affecting
60+
the top of the stack.
61+
62+
Note that this operation violates the FIFO property of the stack and may be used cautiously.
63+
64+
Args:
65+
offset: offset in bytes from the top of the stack, not necessarily aligned to the
66+
native stack item size. the offset may be either positive or netagive, where
67+
a 0 value means overwriting the value at the top of the stack
68+
69+
Returns: the value at the specified address
70+
"""
4971

50-
# set PC
51-
def set_pc(self, address: int):
72+
return self.ql.unpack(self.ql.mem.read(self.ql.reg.arch_sp + offset, self.ql.pointersize))
73+
74+
75+
def stack_write(self, offset: int, value: int) -> None:
76+
"""Write a value to the architectural stack at a specified offset from its top, without
77+
affecting the top of the stack.
78+
79+
Note that this operation violates the FIFO property of the stack and may be used cautiously.
80+
81+
Args:
82+
offset: offset in bytes from the top of the stack, not necessarily aligned to the
83+
native stack item size. the offset may be either positive or netagive, where
84+
a 0 value means overwriting the value at the top of the stack
85+
"""
86+
87+
self.ql.mem.write(self.ql.reg.arch_sp + offset, self.ql.pack(value))
88+
89+
90+
# set PC
91+
def set_pc(self, address: int) -> None:
5292
self.ql.reg.arch_pc = address
5393

5494

@@ -58,7 +98,7 @@ def get_pc(self) -> int:
5898

5999

60100
# set stack pointer
61-
def set_sp(self, address: int):
101+
def set_sp(self, address: int) -> None:
62102
self.ql.reg.arch_sp = address
63103

64104

@@ -68,26 +108,24 @@ def get_sp(self) -> int:
68108

69109

70110
# Unicorn's CPU state save
71-
def context_save(self):
111+
def context_save(self) -> UcContext:
72112
return self.ql.uc.context_save()
73113

74114

75115
# Unicorn's CPU state restore method
76-
def context_restore(self, saved_context):
116+
def context_restore(self, saved_context: UcContext):
77117
self.ql.uc.context_restore(saved_context)
78118

79119

80120
def create_disassembler(self) -> Cs:
81-
if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB):
82-
reg_cpsr = self.ql.reg.cpsr
83-
else:
84-
reg_cpsr = None
85-
return ql_create_disassembler(self.ql.archtype, self.ql.archendian, reg_cpsr)
121+
"""Get disassembler insatnce bound to arch.
122+
"""
123+
124+
raise NotImplementedError(self.__class__.__name__)
86125

87126

88127
def create_assembler(self) -> Ks:
89-
if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB):
90-
reg_cpsr = self.ql.reg.cpsr
91-
else:
92-
reg_cpsr = None
93-
return ql_create_assembler(self.ql.archtype, self.ql.archendian, reg_cpsr)
128+
"""Get assembler insatnce bound to arch.
129+
"""
130+
131+
raise NotImplementedError(self.__class__.__name__)

qiling/arch/arm.py

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,105 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55

6-
from unicorn import *
7-
from unicorn.arm_const import *
8-
9-
from qiling.const import *
10-
from .arch import QlArch
11-
from .arm_const import *
6+
from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_THUMB, UC_MODE_BIG_ENDIAN
7+
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB
8+
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB
129

10+
from qiling import Qiling
11+
from qiling.const import QL_ARCH, QL_ENDIAN
12+
from qiling.arch.arch import QlArch
13+
from qiling.arch.arm_const import *
14+
from qiling.exception import QlErrorArch
1315

1416
class QlArchARM(QlArch):
15-
def __init__(self, ql):
16-
super(QlArchARM, self).__init__(ql)
17-
register_mappings = [
18-
reg_map
19-
]
17+
def __init__(self, ql: Qiling):
18+
super().__init__(ql)
19+
20+
reg_maps = (
21+
reg_map,
22+
)
2023

21-
for reg_maper in register_mappings:
24+
for reg_maper in reg_maps:
2225
self.ql.reg.expand_mapping(reg_maper)
2326

2427
self.ql.reg.create_reverse_mapping()
25-
2628
self.ql.reg.register_sp(reg_map["sp"])
2729
self.ql.reg.register_pc(reg_map["pc"])
30+
2831
self.arm_get_tls_addr = 0xFFFF0FE0
2932

30-
def stack_push(self, value):
31-
self.ql.reg.sp -= 4
32-
self.ql.mem.write(self.ql.reg.sp, self.ql.pack32(value))
33-
return self.ql.reg.sp
33+
# get initialized unicorn engine
34+
def get_init_uc(self) -> Uc:
35+
if self.ql.archendian == QL_ENDIAN.EB:
36+
mode = UC_MODE_ARM + UC_MODE_BIG_ENDIAN
3437

38+
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
39+
mode = UC_MODE_THUMB
3540

36-
def stack_pop(self):
37-
data = self.ql.unpack32(self.ql.mem.read(self.ql.reg.sp, 4))
38-
self.ql.reg.sp += 4
39-
return data
41+
elif self.ql.archtype == QL_ARCH.ARM:
42+
mode = UC_MODE_ARM
4043

44+
else:
45+
raise QlErrorArch(f'unsupported arch type {self.ql.archtype}')
4146

42-
def stack_read(self, offset):
43-
return self.ql.unpack32(self.ql.mem.read(self.ql.reg.sp + offset, 4))
47+
return Uc(UC_ARCH_ARM, mode)
4448

4549

46-
def stack_write(self, offset, data):
47-
return self.ql.mem.write(self.ql.reg.sp + offset, self.ql.pack32(data))
50+
# get PC
51+
def get_pc(self) -> int:
52+
append = 1 if self.check_thumb() == UC_MODE_THUMB else 0
4853

54+
return self.ql.reg.pc + append
55+
56+
def __is_thumb(self) -> bool:
57+
cpsr_v = {
58+
QL_ENDIAN.EL : 0b100000,
59+
QL_ENDIAN.EB : 0b100000 # FIXME: should be: 0b000000
60+
}[self.ql.archendian]
61+
62+
return bool(self.ql.reg.cpsr & cpsr_v)
63+
64+
def create_disassembler(self) -> Cs:
65+
# note: we do not cache the disassembler instance; rather we refresh it
66+
# each time to make sure thumb mode is taken into account
67+
68+
if self.ql.archtype == QL_ARCH.ARM:
69+
# FIXME: mode should take endianess into account
70+
mode = CS_MODE_THUMB if self.__is_thumb() else CS_MODE_ARM
4971

50-
# get initialized unicorn engine
51-
def get_init_uc(self):
52-
if self.ql.archendian == QL_ENDIAN.EB:
53-
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM + UC_MODE_BIG_ENDIAN)
5472
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
55-
uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
56-
elif self.ql.archtype == QL_ARCH.ARM:
57-
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
73+
mode = CS_MODE_THUMB
74+
5875
else:
59-
uc = None
60-
return uc
76+
raise QlErrorArch(f'unexpected arch type {self.ql.archtype}')
6177

78+
return Cs(CS_ARCH_ARM, mode)
79+
80+
81+
def create_assembler(self) -> Ks:
82+
# note: we do not cache the assembler instance; rather we refresh it
83+
# each time to make sure thumb mode is taken into account
84+
85+
if self.ql.archtype == QL_ARCH.ARM:
86+
# FIXME: mode should take endianess into account
87+
mode = KS_MODE_THUMB if self.__is_thumb() else KS_MODE_ARM
88+
89+
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
90+
mode = KS_MODE_THUMB
6291

63-
# get PC
64-
def get_pc(self):
65-
mode = self.ql.arch.check_thumb()
66-
if mode == UC_MODE_THUMB:
67-
append = 1
6892
else:
69-
append = 0
70-
71-
return self.ql.reg.pc + append
93+
raise QlErrorArch(f'unexpected arch type {self.ql.archtype}')
7294

95+
return Ks(KS_ARCH_ARM, mode)
7396

74-
def enable_vfp(self):
97+
def enable_vfp(self) -> None:
7598
self.ql.reg.c1_c0_2 = self.ql.reg.c1_c0_2 | (0xf << 20)
99+
76100
if self.ql.archendian == QL_ENDIAN.EB:
77101
self.ql.reg.fpexc = 0x40000000
78102
#self.ql.reg.fpexc = 0x00000040
79103
else:
80104
self.ql.reg.fpexc = 0x40000000
81-
self.ql.log.debug("Enable ARM VFP")
82-
83105

84106
def check_thumb(self):
85-
reg_cpsr = self.ql.reg.cpsr
86-
if self.ql.archendian == QL_ENDIAN.EB:
87-
reg_cpsr_v = 0b100000
88-
# reg_cpsr_v = 0b000000
89-
else:
90-
reg_cpsr_v = 0b100000
91-
92-
mode = UC_MODE_ARM
93-
if (reg_cpsr & reg_cpsr_v) != 0:
94-
mode = UC_MODE_THUMB
95-
self.ql.log.debug("Enable ARM THUMB")
96-
return mode
107+
return UC_MODE_THUMB if self.__is_thumb() else UC_MODE_ARM

0 commit comments

Comments
 (0)