Skip to content

Commit 767d00a

Browse files
authored
Merge pull request #920 from elicn/reg-improv
Refactored QlRegisterManager
2 parents 69b825c + 7af96f1 commit 767d00a

File tree

5 files changed

+93
-73
lines changed

5 files changed

+93
-73
lines changed

qiling/arch/arm.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def __init__(self, ql: Qiling):
2424
for reg_maper in reg_maps:
2525
self.ql.reg.expand_mapping(reg_maper)
2626

27-
self.ql.reg.create_reverse_mapping()
2827
self.ql.reg.register_sp(reg_map["sp"])
2928
self.ql.reg.register_pc(reg_map["pc"])
3029

qiling/arch/arm64.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ def __init__(self, ql: Qiling):
2323
for reg_maper in reg_maps:
2424
self.ql.reg.expand_mapping(reg_maper)
2525

26-
self.ql.reg.create_reverse_mapping()
2726
self.ql.reg.register_sp(reg_map["sp"])
2827
self.ql.reg.register_pc(reg_map["pc"])
2928

qiling/arch/mips.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def __init__(self, ql: Qiling):
2424
for reg_maper in reg_maps:
2525
self.ql.reg.expand_mapping(reg_maper)
2626

27-
self.ql.reg.create_reverse_mapping()
2827
self.ql.reg.register_sp(reg_map["sp"])
2928
self.ql.reg.register_pc(reg_map["pc"])
3029

qiling/arch/register.py

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

6-
class QlRegisterManager():
7-
"""
8-
This class exposes the ql.reg features that allows you to directly access
6+
from typing import Any, Mapping, MutableMapping, Union
7+
8+
from qiling import Qiling
9+
10+
class QlRegisterManager:
11+
"""This class exposes the ql.reg features that allows you to directly access
912
or assign values to CPU registers of a particular architecture.
1013
1114
Registers exposed are listed in the *_const.py files in the respective
1215
arch directories and are mapped to Unicorn Engine's definitions
1316
"""
14-
def __init__(self, ql):
15-
self.register_mapping = {}
16-
self.reverse_mapping = {}
17+
18+
def __init__(self, ql: Qiling):
19+
# this funny way of initialization is used to avoid calling self setattr and
20+
# getattr upon init. if it did, it would go into an endless recursion
21+
self.register_mapping: MutableMapping[str, int]
22+
super().__setattr__('register_mapping', {})
23+
1724
self.ql = ql
1825
self.uc_pc = 0
1926
self.uc_sp = 0
20-
2127

22-
def __getattribute__(self, name):
28+
def __getattr__(self, name: str) -> Any:
2329
name = name.lower()
24-
if name in ("register_mapping", "ql", "uc_pc", "uc_sp"):
25-
return super(QlRegisterManager, self).__getattribute__(name)
2630

27-
elif name in self.register_mapping:
31+
if name in self.register_mapping:
2832
return self.ql.uc.reg_read(self.register_mapping[name])
2933

30-
return super(QlRegisterManager, self).__getattribute__(name)
34+
else:
35+
return super().__getattribute__(name)
3136

3237

33-
def __setattr__(self, name, value):
34-
name=name.lower()
35-
if name in ("register_mapping", "ql", "uc_pc", "uc_sp"):
36-
super(QlRegisterManager, self).__setattr__(name, value)
38+
def __setattr__(self, name: str, value: Any):
39+
name = name.lower()
3740

38-
elif name in self.register_mapping:
41+
if name in self.register_mapping:
3942
self.ql.uc.reg_write(self.register_mapping[name], value)
43+
4044
else:
41-
super(QlRegisterManager, self).__setattr__(name, value)
45+
super().__setattr__(name, value)
4246

4347

44-
def expand_mapping(self, expanded_map):
45-
self.register_mapping = {**self.register_mapping, **expanded_map}
48+
def expand_mapping(self, extra: Mapping[str, int]) -> None:
49+
"""Expand registers mapping with additional ones.
50+
"""
51+
52+
self.register_mapping.update(extra)
4653

4754

4855
# read register
49-
def read(self, register):
50-
if isinstance(register, str):
51-
register = self.register_mapping.get(register.lower(), None)
56+
def read(self, register: Union[str, int]):
57+
"""Read a register value.
58+
"""
59+
60+
if type(register) is str:
61+
register = self.register_mapping[register.lower()]
62+
5263
return self.ql.uc.reg_read(register)
5364

5465

55-
def write(self, register, value):
56-
if isinstance(register, str):
57-
register = self.register_mapping.get(register.lower(), None)
66+
def write(self, register: Union[str, int], value: int) -> None:
67+
"""Write a register value.
68+
"""
69+
70+
if type(register) is str:
71+
register = self.register_mapping[register.lower()]
72+
5873
return self.ql.uc.reg_write(register, value)
5974

6075

61-
def msr(self, msr, addr= None):
62-
if not addr:
76+
def msr(self, msr: int, value: int = None):
77+
"""Read or write a model-specific register (MSR) value.
78+
Intel architecture only
79+
"""
80+
81+
if value is None:
6382
return self.ql.uc.msr_read(msr)
64-
else:
65-
self.ql.uc.msr_write(msr, addr)
6683

84+
self.ql.uc.msr_write(msr, value)
6785

68-
# ql.reg.save
69-
def save(self):
70-
reg_dict = {}
71-
for reg in self.register_mapping:
72-
reg_v = self.read(reg)
73-
reg_dict[reg] = reg_v
74-
return reg_dict
7586

87+
def save(self) -> MutableMapping[str, Any]:
88+
"""Save CPU context.
89+
"""
7690

77-
# ql.reg.restore
78-
def restore(self, value = {}):
79-
for reg in self.register_mapping:
80-
reg_v= value[reg]
81-
self.write(reg, reg_v)
91+
return dict((reg, self.read(reg)) for reg in self.register_mapping)
8292

8393

84-
# ql.reg.bit() - Register bit
85-
#FIXME: This needs to be implemented for all archs
86-
def bit(self, uc_reg):
87-
return self.ql.arch.get_reg_bit(uc_reg)
94+
def restore(self, context: MutableMapping[str, Any] = {}) -> None:
95+
"""Restore CPU context.
96+
"""
97+
98+
for reg, val in context.items():
99+
self.write(reg, val)
100+
101+
102+
# TODO: This needs to be implemented for all archs
103+
def bit(self, reg: Union[str, int]) -> int:
104+
"""Get register size in bits.
105+
"""
106+
107+
if type(reg) is str:
108+
reg = self.register_mapping[reg]
109+
110+
return self.ql.arch.get_reg_bit(reg)
88111

89112

90113
# Generic methods to get SP and IP across Arch's #
91114
# These functions should only be used if the #
92115
# caller is dealing with multiple Arch's #
93-
def register_sp(self, sp_id):
116+
def register_sp(self, sp_id: int):
94117
self.uc_sp = sp_id
95118

96119

97-
def register_pc(self, pc_id):
120+
def register_pc(self, pc_id: int):
98121
self.uc_pc = pc_id
99122

100123

101124
@property
102-
def arch_pc(self):
125+
def arch_pc(self) -> int:
126+
"""Get the value of the architectural program counter register.
127+
"""
128+
103129
return self.ql.uc.reg_read(self.uc_pc)
104130

105131

106132
@arch_pc.setter
107-
def arch_pc(self, value):
133+
def arch_pc(self, value: int) -> None:
134+
"""Set the value of the architectural program counter register.
135+
"""
136+
108137
return self.ql.uc.reg_write(self.uc_pc, value)
109138

110139
@property
111-
def arch_pc_name(self):
112-
return self.ql.reg.reverse_mapping[self.uc_pc]
140+
def arch_pc_name(self) -> str:
141+
"""Get the architectural program counter register name.
142+
"""
143+
144+
return next(k for k, v in self.register_mapping.items() if v == self.uc_pc)
113145

114146
@property
115-
def arch_sp(self):
147+
def arch_sp(self) -> int:
148+
"""Get the value of the architectural stack pointer register.
149+
"""
150+
116151
return self.ql.uc.reg_read(self.uc_sp)
117152

118153

119154
@arch_sp.setter
120-
def arch_sp(self, value):
121-
return self.ql.uc.reg_write(self.uc_sp, value)
122-
155+
def arch_sp(self, value: int) -> None:
156+
"""Set the value of the architectural stack pointer register.
157+
"""
123158

124-
def get_uc_reg(self, uc_reg_name):
125-
return self.register_mapping.get(uc_reg_name, None)
126-
127-
128-
def create_reverse_mapping(self):
129-
self.reverse_mapping = {v:k for k, v in self.register_mapping.items()}
159+
return self.ql.uc.reg_write(self.uc_sp, value)

qiling/arch/x86.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#
55

66
from struct import pack
7-
from typing import Union
87

98
from unicorn import Uc, UC_ARCH_X86, UC_MODE_16, UC_MODE_32, UC_MODE_64
109
from capstone import Cs, CS_ARCH_X86, CS_MODE_16, CS_MODE_32, CS_MODE_64
@@ -18,10 +17,7 @@
1817
class QlArchIntel(QlArch):
1918

2019
# TODO: generalize this
21-
def get_reg_bit(self, register: Union[str, int]) -> int:
22-
if type(register) is str:
23-
register = self.ql.reg.get_uc_reg(register)
24-
20+
def get_reg_bit(self, register: int) -> int:
2521
# all regs in reg_map_misc are 16 bits except of eflags
2622
if register == UC_X86_REG_EFLAGS:
2723
return self.ql.archbit
@@ -52,7 +48,6 @@ def __init__(self, ql: Qiling):
5248
for reg_maper in reg_maps:
5349
self.ql.reg.expand_mapping(reg_maper)
5450

55-
self.ql.reg.create_reverse_mapping()
5651
self.ql.reg.register_pc(reg_map_16["sp"])
5752
self.ql.reg.register_sp(reg_map_16["ip"])
5853

@@ -87,7 +82,6 @@ def __init__(self, ql: Qiling):
8782
for reg_maper in reg_maps:
8883
self.ql.reg.expand_mapping(reg_maper)
8984

90-
self.ql.reg.create_reverse_mapping()
9185
self.ql.reg.register_sp(reg_map_32["esp"])
9286
self.ql.reg.register_pc(reg_map_32["eip"])
9387

@@ -127,7 +121,6 @@ def __init__(self, ql: Qiling):
127121
for reg_maper in reg_maps:
128122
self.ql.reg.expand_mapping(reg_maper)
129123

130-
self.ql.reg.create_reverse_mapping()
131124
self.ql.reg.register_sp(reg_map_64["rsp"])
132125
self.ql.reg.register_pc(reg_map_64["rip"])
133126

0 commit comments

Comments
 (0)