Skip to content

Commit ffd34cb

Browse files
committed
Merge remote-tracking branch 'eli/uc-py-next' into staged
2 parents d1da4de + 0a876d3 commit ffd34cb

File tree

9 files changed

+1517
-941
lines changed

9 files changed

+1517
-941
lines changed

bindings/python/sample_all.sh

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
#!/bin/sh
22

3-
./sample_x86.py
3+
python3 ./sample_arm.py
44
echo "=========================="
5-
./shellcode.py
5+
python3 ./sample_armeb.py
66
echo "=========================="
7-
./sample_arm.py
7+
python3 ./sample_arm64.py
88
echo "=========================="
9-
./sample_arm64.py
9+
python3 ./sample_arm64eb.py
1010
echo "=========================="
11-
./sample_mips.py
11+
python3 ./sample_m68k.py
1212
echo "=========================="
13-
./sample_sparc.py
13+
python3 ./sample_mips.py
1414
echo "=========================="
15-
./sample_m68k.py
15+
python3 ./sample_ppc.py
16+
echo "=========================="
17+
python3 ./sample_riscv.py
18+
echo "=========================="
19+
python3 ./sample_s390x.py
20+
echo "=========================="
21+
python3 ./sample_sparc.py
22+
echo "=========================="
23+
python3 ./sample_tricore.py
24+
echo "=========================="
25+
python3 ./sample_x86.py
26+
echo "=========================="
27+
python3 ./shellcode.py
28+
echo "=========================="
29+
python3 ./sample_ctl.py

bindings/python/sample_ctl.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# By Lazymio(@wtdcode), 2021
44

55
from unicorn import *
6-
from unicorn.unicorn import UC_HOOK_EDGE_GEN_CB
76
from unicorn.x86_const import *
87
from datetime import datetime
98

@@ -58,7 +57,7 @@ def test_uc_ctl_tb_cache():
5857
# Now we clear cache for all TBs.
5958
for i in range(8):
6059
uc.ctl_remove_cache(addr + i * 512, addr + i * 512 + 1)
61-
60+
6261
evicted = time_emulation(uc, addr, addr + len(code))
6362

6463
print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}")
@@ -93,7 +92,7 @@ def test_uc_ctl_exits():
9392
uc.hook_add(UC_HOOK_EDGE_GENERATED, trace_new_edge)
9493

9594
# Trace cmp instruction.
96-
uc.hook_add(UC_HOOK_TCG_OPCODE, trace_tcg_sub, UC_TCG_OP_SUB, UC_TCG_OP_FLAG_CMP)
95+
uc.hook_add(UC_HOOK_TCG_OPCODE, trace_tcg_sub, aux1=UC_TCG_OP_SUB, aux2=UC_TCG_OP_FLAG_CMP)
9796

9897
uc.ctl_exits_enabled(True)
9998

bindings/python/unicorn/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Unicorn Python bindings, by Nguyen Anh Quynnh <[email protected]>
1+
# New and improved Unicorn Python bindings by elicn
2+
# based on Nguyen Anh Quynnh's work
3+
24
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const, riscv_const, s390x_const, tricore_const
35
from .unicorn_const import *
4-
from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__
6+
from .unicorn import Uc, ucsubclass, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__

bindings/python/unicorn/arch/__init__.py

Whitespace-only changes.

bindings/python/unicorn/arch/arm.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# AArch32 classes and structures.
2+
#
3+
# @author elicn
4+
5+
from typing import Any, Tuple
6+
7+
import ctypes
8+
9+
from .. import Uc
10+
from .. import arm_const as const
11+
12+
from .types import UcTupledReg, UcReg128
13+
14+
ARMCPReg = Tuple[int, int, int, int, int, int, int, int]
15+
16+
17+
class UcRegCP(UcTupledReg[ARMCPReg]):
18+
"""ARM coprocessors registers for instructions MRC, MCR, MRRC, MCRR
19+
"""
20+
21+
_fields_ = (
22+
('cp', ctypes.c_uint32),
23+
('is64', ctypes.c_uint32),
24+
('sec', ctypes.c_uint32),
25+
('crn', ctypes.c_uint32),
26+
('crm', ctypes.c_uint32),
27+
('opc1', ctypes.c_uint32),
28+
('opc2', ctypes.c_uint32),
29+
('val', ctypes.c_uint64)
30+
)
31+
32+
@property
33+
def value(self) -> int:
34+
return self.val
35+
36+
37+
class UcAArch32(Uc):
38+
"""Unicorn subclass for ARM architecture.
39+
"""
40+
41+
REG_RANGE_Q = range(const.UC_ARM_REG_Q0, const.UC_ARM_REG_Q15 + 1)
42+
43+
@staticmethod
44+
def __select_reg_class(reg_id: int):
45+
"""Select class for special architectural registers.
46+
"""
47+
48+
reg_class = (
49+
(UcAArch32.REG_RANGE_Q, UcReg128),
50+
)
51+
52+
return next((cls for rng, cls in reg_class if reg_id in rng), None)
53+
54+
def reg_read(self, reg_id: int, aux: Any = None):
55+
# select register class for special cases
56+
reg_cls = UcAArch32.__select_reg_class(reg_id)
57+
58+
if reg_cls is None:
59+
if reg_id == const.UC_ARM_REG_CP_REG:
60+
return self._reg_read(reg_id, UcRegCP, *aux)
61+
62+
else:
63+
# fallback to default reading method
64+
return super().reg_read(reg_id, aux)
65+
66+
return self._reg_read(reg_id, reg_cls)
67+
68+
def reg_write(self, reg_id: int, value) -> None:
69+
# select register class for special cases
70+
reg_cls = UcAArch32.__select_reg_class(reg_id)
71+
72+
if reg_cls is None:
73+
if reg_id == const.UC_ARM_REG_CP_REG:
74+
self._reg_write(reg_id, UcRegCP, value)
75+
76+
else:
77+
# fallback to default writing method
78+
super().reg_write(reg_id, value)
79+
80+
else:
81+
self._reg_write(reg_id, reg_cls, value)

bindings/python/unicorn/arch/arm64.py

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# AArch64 classes and structures.
2+
#
3+
# @author elicn
4+
5+
from typing import Any, Callable, NamedTuple, Tuple
6+
7+
import ctypes
8+
9+
from .. import Uc, UcError
10+
from .. import arm64_const as const
11+
12+
from ..unicorn import uccallback
13+
from ..unicorn_const import UC_ERR_ARG, UC_HOOK_INSN
14+
from .types import uc_engine, UcTupledReg, UcReg128
15+
16+
ARM64CPReg = Tuple[int, int, int, int, int, int]
17+
18+
HOOK_INSN_SYS_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p)
19+
20+
21+
class UcRegCP(UcTupledReg[ARM64CPReg]):
22+
"""ARM64 coprocessors registers for instructions MRS, MSR
23+
"""
24+
25+
_fields_ = (
26+
('crn', ctypes.c_uint32),
27+
('crm', ctypes.c_uint32),
28+
('op0', ctypes.c_uint32),
29+
('op1', ctypes.c_uint32),
30+
('op2', ctypes.c_uint32),
31+
('val', ctypes.c_uint64)
32+
)
33+
34+
@property
35+
def value(self) -> int:
36+
return self.val
37+
38+
39+
class UcAArch64(Uc):
40+
"""Unicorn subclass for ARM64 architecture.
41+
"""
42+
43+
REG_RANGE_Q = range(const.UC_ARM64_REG_Q0, const.UC_ARM64_REG_Q31 + 1)
44+
REG_RANGE_V = range(const.UC_ARM64_REG_V0, const.UC_ARM64_REG_V31 + 1)
45+
46+
def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, aux1: int = 0, aux2: int = 0) -> int:
47+
if htype != UC_HOOK_INSN:
48+
return super().hook_add(htype, callback, user_data, begin, end, aux1, aux2)
49+
50+
insn = ctypes.c_int(aux1)
51+
52+
def __hook_insn_sys():
53+
@uccallback(HOOK_INSN_SYS_CFUNC)
54+
def __hook_insn_sys_cb(handle: int, reg: int, pcp_reg: Any, key: int) -> int:
55+
cp_reg = ctypes.cast(pcp_reg, ctypes.POINTER(UcRegCP)).contents
56+
57+
class CpReg(NamedTuple):
58+
crn: int
59+
crm: int
60+
op0: int
61+
op1: int
62+
op2: int
63+
val: int
64+
65+
cp_reg = CpReg(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val)
66+
67+
return callback(self, reg, cp_reg, user_data)
68+
69+
return __hook_insn_sys_cb
70+
71+
handlers = {
72+
const.UC_ARM64_INS_MRS : __hook_insn_sys,
73+
const.UC_ARM64_INS_MSR : __hook_insn_sys,
74+
const.UC_ARM64_INS_SYS : __hook_insn_sys,
75+
const.UC_ARM64_INS_SYSL : __hook_insn_sys
76+
}
77+
78+
handler = handlers.get(insn.value)
79+
80+
if handler is None:
81+
raise UcError(UC_ERR_ARG)
82+
83+
fptr = handler()
84+
85+
return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn)
86+
87+
@staticmethod
88+
def __select_reg_class(reg_id: int):
89+
"""Select class for special architectural registers.
90+
"""
91+
92+
reg_class = (
93+
(UcAArch64.REG_RANGE_Q, UcReg128),
94+
(UcAArch64.REG_RANGE_V, UcReg128)
95+
)
96+
97+
return next((cls for rng, cls in reg_class if reg_id in rng), None)
98+
99+
def reg_read(self, reg_id: int, aux: Any = None):
100+
# select register class for special cases
101+
reg_cls = UcAArch64.__select_reg_class(reg_id)
102+
103+
if reg_cls is None:
104+
if reg_id == const.UC_ARM64_REG_CP_REG:
105+
return self._reg_read(reg_id, UcRegCP, *aux)
106+
107+
else:
108+
# fallback to default reading method
109+
return super().reg_read(reg_id, aux)
110+
111+
return self._reg_read(reg_id, reg_cls)
112+
113+
def reg_write(self, reg_id: int, value) -> None:
114+
# select register class for special cases
115+
reg_cls = UcAArch64.__select_reg_class(reg_id)
116+
117+
if reg_cls is None:
118+
if reg_id == const.UC_ARM64_REG_CP_REG:
119+
self._reg_write(reg_id, UcRegCP, value)
120+
121+
else:
122+
# fallback to default writing method
123+
super().reg_write(reg_id, value)
124+
125+
else:
126+
self._reg_write(reg_id, reg_cls, value)

0 commit comments

Comments
 (0)