Python bindings: Fix editable install + Execute Python2.7 workflow tests (#2044)

* Python binding:
- Added missing `license` field in pyproject.toml file
- Fixed editable mode install and some more code cleanup in setup.py
- Refreshed README.md
- Replaced f-string formatter in tests with `format` method in order to be py2-compatible
- Fixed typos
- PEP8 fixes

* GitHub Action: Install Python2.7 and run tests for re-tagged wheels on native arch runners only

* Python bindings:
- Use #x formatter to format hex values
This commit is contained in:
@Antelox
2024-12-07 07:52:21 +01:00
committed by GitHub
parent 3a01515367
commit f78a3f2f59
10 changed files with 152 additions and 114 deletions

View File

@@ -160,7 +160,9 @@ jobs:
run: | run: |
python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object { python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object {
python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName
break
} }
- name: '🚧 Python 2.7 wheels re-tagging - Non-Windows' - name: '🚧 Python 2.7 wheels re-tagging - Non-Windows'
if: matrix.os != 'windows-2019' if: matrix.os != 'windows-2019'
env: env:
@@ -168,6 +170,26 @@ jobs:
run: | run: |
python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl
- uses: LizardByte/setup-python-action@master
if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest' || (matrix.os == 'windows-2019' && matrix.arch == 'AMD64')
with:
python-version: 2.7
- name: 'Python 2.7 tests - Windows'
if: matrix.os == 'windows-2019' && matrix.arch == 'AMD64'
run: |
C:\Python27\python.exe -m pip install -U pip pytest && Get-ChildItem -Path wheelhouse/ -Filter *py2*.whl | Foreach-Object {
C:\Python27\python.exe -m pip install $_.FullName
C:\Python27\python.exe -m pytest bindings/python/tests
break
}
# we install and test python2.7 wheels only on native arch
# NOTE: no python2.7 support for macos-13: https://github.com/LizardByte/setup-python-action/issues/2
- name: 'Python 2.7 tests - Non-Windows'
if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest'
run: python2 -m pip install wheelhouse/*py2*.whl && python2 -m pip install -U pip pytest && python2 -m pytest bindings/python/tests
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-py38 name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-py38

View File

@@ -25,27 +25,21 @@ Originally written by Nguyen Anh Quynh, polished and redesigned by elicn, mainta
Install a prebuilt wheel from PyPI: Install a prebuilt wheel from PyPI:
```bash ```bash
pip3 install unicorn python3 -m pip install unicorn
``` ```
In case you would like to develop the bindings: In case you would like to develop the bindings:
```bash ```bash
# Python3 DEBUG=1 THREADS=4 python3 -m pip install --user -e .
DEBUG=1 THREADS=4 pip3 install --user -e .
# Workaround for Pylance # Workaround for Pylance
DEBUG=1 THREADS=4 pip3 install --user -e . --config-settings editable_mode=strict DEBUG=1 THREADS=4 python3 -m pip install --user -e . --config-settings editable_mode=strict
# Python2
DEBUG=1 THREADS=4 pip install -e .
``` ```
or install it by building it by yourself: or install it by building it by yourself:
```bash ```bash
# Python3 THREADS=4 python3 -m pip install --user .
THREADS=4 pip3 install --user .
# Python2, unfortunately `pip2` doesn't support in-tree build
THREADS=4 python3 setup.py install
``` ```
Explanations for arguments: Explanations for arguments:
@@ -59,4 +53,5 @@ Note that you should setup a valid building environment according to docs/COMPIL
## Python2 compatibility ## Python2 compatibility
By default, Unicorn python bindings will be maintained against Python3 as it offers more powerful features which improves developing efficiency. Meanwhile, Unicorn will only keep compatible with all features Unicorn1 offers regarding Python2 because Python2 has reached end-of-life for more than 3 years as the time of writing this README. While offering all features for both Python2 & Python3 is desirable and doable, it inevitably costs too much efforts to maintain and few users really rely on this. Therefore, we assume that if users still stick to Python2, previous Unicorn1 features we offer should be enough. If you really want some new features Unicorn2 offers, please check and pull request to `unicorn/unicorn_py2``. We are happy to review and accept! By default, Unicorn python bindings works with Python3.7 and above, as it offers more powerful features which improves developing efficiency compared to Python2. However, Unicorn will only keep compatible with all features Unicorn1 offers regarding Python2 because it has reached end-of-life for more than 3 years at the time of writing this README. While offering all features for both Python2 & Python3 is desirable and doable, it inevitably costs too much efforts to maintain and few users really rely on this. Therefore, we assume that if users still stick to Python2, previous Unicorn1 features should be enough. If you really want some new features Unicorn2 offers, please check and pull request to `unicorn/unicorn_py2`. We are happy to review and accept!
Even though the build of wheel packages requires Python3, it's still possible to re-tag the wheel produced from Python3 with `py2` tag and then run `python2 -m pip install <retagged-wheel-py>`. For detailed commands please refer to our workflow files.

View File

@@ -12,6 +12,7 @@ authors = [
description = "Unicorn CPU emulator engine" description = "Unicorn CPU emulator engine"
readme = "README.md" readme = "README.md"
keywords = ["emulation", "qemu", "unicorn"] keywords = ["emulation", "qemu", "unicorn"]
license = { text = "BSD License" }
classifiers = [ classifiers = [
'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',

View File

@@ -8,9 +8,8 @@ import shutil
import subprocess import subprocess
import sys import sys
from setuptools import setup from setuptools import setup
from setuptools.command.build import build from setuptools.command.build_py import build_py
from setuptools.command.sdist import sdist from setuptools.command.sdist import sdist
from setuptools.command.bdist_egg import bdist_egg
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -134,7 +133,7 @@ class CustomSDist(sdist):
return super().run() return super().run()
class CustomBuild(build): class CustomBuild(build_py):
def run(self): def run(self):
if 'LIBUNICORN_PATH' in os.environ: if 'LIBUNICORN_PATH' in os.environ:
log.info("Skipping building C extensions since LIBUNICORN_PATH is set") log.info("Skipping building C extensions since LIBUNICORN_PATH is set")
@@ -144,30 +143,7 @@ class CustomBuild(build):
return super().run() return super().run()
class CustomBDistEgg(bdist_egg):
def run(self):
self.run_command('build')
return super().run()
cmdclass = {'build': CustomBuild, 'sdist': CustomSDist, 'bdist_egg': CustomBDistEgg}
try:
from setuptools.command.develop import develop
class CustomDevelop(develop):
def run(self):
log.info("Building C extensions")
build_libraries()
return super().run()
cmdclass['develop'] = CustomDevelop
except ImportError:
print("Proper 'develop' support unavailable.")
setup( setup(
cmdclass=cmdclass, cmdclass={'build_py': CustomBuild, 'sdist': CustomSDist},
has_ext_modules=lambda: True, # It's not a Pure Python wheel has_ext_modules=lambda: True, # It's not a Pure Python wheel
) )

View File

@@ -87,7 +87,8 @@ def test_arm64_read_sctlr():
def test_arm64_hook_mrs(): def test_arm64_hook_mrs():
def _hook_mrs(uc, reg, cp_reg, _): def _hook_mrs(uc, reg, cp_reg, _):
print(f">>> Hook MRS instruction: reg = 0x{reg:x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}") print(">>> Hook MRS instruction: reg = {reg:#x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}".format(reg=reg,
cp_reg=cp_reg))
uc.reg_write(reg, 0x114514) uc.reg_write(reg, 0x114514)
print(">>> Write 0x114514 to X") print(">>> Write 0x114514 to X")
@@ -111,7 +112,7 @@ def test_arm64_hook_mrs():
# Start emulation # Start emulation
mu.emu_start(0x1000, 0x1000 + len(ARM64_MRS_CODE)) mu.emu_start(0x1000, 0x1000 + len(ARM64_MRS_CODE))
print(f">>> X2 = {mu.reg_read(UC_ARM64_REG_X2):x}") print(">>> X2 = {reg:#x}".format(reg=mu.reg_read(UC_ARM64_REG_X2)))
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)

View File

@@ -2,6 +2,8 @@
# Sample code for Unicorn. # Sample code for Unicorn.
# By Lazymio(@wtdcode), 2021 # By Lazymio(@wtdcode), 2021
import pytest
import sys
from unicorn import * from unicorn import *
from unicorn.x86_const import * from unicorn.x86_const import *
from datetime import datetime from datetime import datetime
@@ -20,7 +22,9 @@ def test_uc_ctl_read():
timeout = uc.ctl_get_timeout() timeout = uc.ctl_get_timeout()
print(f">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}") print(">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}".format(arch=arch, mode=mode,
page_size=page_size,
timeout=timeout))
def time_emulation(uc, start, end): def time_emulation(uc, start, end):
@@ -31,6 +35,8 @@ def time_emulation(uc, start, end):
return (datetime.now() - n).total_seconds() * 1e6 return (datetime.now() - n).total_seconds() * 1e6
# TODO: Check if worth adapting the ctl_request_cache method for py2 bindings
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
def test_uc_ctl_tb_cache(): def test_uc_ctl_tb_cache():
# Initialize emulator in X86-32bit mode # Initialize emulator in X86-32bit mode
uc = Uc(UC_ARCH_X86, UC_MODE_32) uc = Uc(UC_ARCH_X86, UC_MODE_32)
@@ -52,7 +58,7 @@ def test_uc_ctl_tb_cache():
# Now we request cache for all TBs. # Now we request cache for all TBs.
for i in range(8): for i in range(8):
tb = uc.ctl_request_cache(addr + i * 512) tb = uc.ctl_request_cache(addr + i * 512)
print(f">>> TB is cached at {hex(tb[0])} which has {tb[1]} instructions with {tb[2]} bytes") print(">>> TB is cached at {:#x} which has {} instructions with {} bytes".format(tb[0], tb[1], tb[2]))
# Do emulation with all TB cached. # Do emulation with all TB cached.
cached = time_emulation(uc, addr, addr + len(code)) cached = time_emulation(uc, addr, addr + len(code))
@@ -63,17 +69,22 @@ def test_uc_ctl_tb_cache():
evicted = time_emulation(uc, addr, addr + len(code)) evicted = time_emulation(uc, addr, addr + len(code))
print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}") print(">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}".format(standard=standard,
cached=cached,
evicted=evicted))
def trace_new_edge(uc, cur, prev, data): def trace_new_edge(uc, cur, prev, data):
print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}") print(">>> Getting a new edge from {:#x} to {:#x}".format(prev.pc + prev.size - 1, cur.pc))
def trace_tcg_sub(uc, address, arg1, arg2, size, data): def trace_tcg_sub(uc, address, arg1, arg2, size, data):
print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}") print(">>> Get a tcg sub opcode at {address:#x} with args: {arg1} and {arg2}".format(address=address, arg1=arg1,
arg2=arg2))
# TODO: Check if worth adapting the hook_add method for py2 bindings
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
def test_uc_ctl_exits(): def test_uc_ctl_exits():
uc = Uc(UC_ARCH_X86, UC_MODE_32) uc = Uc(UC_ARCH_X86, UC_MODE_32)
addr = 0x1000 addr = 0x1000
@@ -110,7 +121,7 @@ def test_uc_ctl_exits():
eax = uc.reg_read(UC_X86_REG_EAX) eax = uc.reg_read(UC_X86_REG_EAX)
ebx = uc.reg_read(UC_X86_REG_EBX) ebx = uc.reg_read(UC_X86_REG_EBX)
print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") print(">>> eax = {eax:#x} and ebx = {ebx:#x} after the first emulation".format(eax=eax, ebx=ebx))
# This should stop at ADDRESS + 8, even though we don't provide an exit. # This should stop at ADDRESS + 8, even though we don't provide an exit.
uc.emu_start(addr, 0) uc.emu_start(addr, 0)
@@ -118,7 +129,7 @@ def test_uc_ctl_exits():
eax = uc.reg_read(UC_X86_REG_EAX) eax = uc.reg_read(UC_X86_REG_EAX)
ebx = uc.reg_read(UC_X86_REG_EBX) ebx = uc.reg_read(UC_X86_REG_EBX)
print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") print(">>> eax = {eax:#x} and ebx = {ebx:#x} after the first emulation".format(eax=eax, ebx=ebx))
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -356,7 +356,7 @@ def hook_intr(uc, intno, user_data):
fd = args[0] fd = args[0]
how = args[1] how = args[1]
msg = "fd(%d) is shutted down because of %d" % (fd, how) msg = "fd(%d) is shut down because of %d" % (fd, how)
fd_chains.add_log(fd, msg) fd_chains.add_log(fd, msg)
print_sockcall(msg) print_sockcall(msg)

View File

@@ -639,13 +639,16 @@ def test_x86_16():
def mmio_read_cb(uc, offset, size, data): def mmio_read_cb(uc, offset, size, data):
print(f">>> Read IO memory at offset {hex(offset)} with {hex(size)} bytes and return 0x19260817") print(">>> Read IO memory at offset {offset:#x} with {size:#x} bytes and return 0x19260817".format(offset=offset,
size=size))
return 0x19260817 return 0x19260817
def mmio_write_cb(uc, offset, size, value, data): def mmio_write_cb(uc, offset, size, value, data):
print(f">>> Write value {hex(value)} to IO memory at offset {hex(offset)} with {hex(size)} bytes") print(">>> Write value {value:#x} to IO memory at offset {offset:#x} with {size:#x} bytes".format(value=value,
offset=offset,
size=size))
def test_i386_mmio(): def test_i386_mmio():
@@ -668,7 +671,7 @@ def test_i386_mmio():
mu.emu_start(0x10000, 0x10000 + len(X86_MMIO_CODE)) mu.emu_start(0x10000, 0x10000 + len(X86_MMIO_CODE))
# now print out some registers # now print out some registers
print(f">>> Emulation done. ECX={hex(mu.reg_read(UC_X86_REG_ECX))}") print(">>> Emulation done. ECX={reg:#x}".format(reg=mu.reg_read(UC_X86_REG_ECX)))
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)

View File

@@ -3,17 +3,16 @@
import ctypes import ctypes
import ctypes.util import ctypes.util
import distutils.sysconfig import distutils.sysconfig
from functools import wraps
import pkg_resources
import inspect import inspect
import os.path import os.path
import pkg_resources
import sys import sys
import weakref import weakref
import functools from functools import wraps, partial
from collections import namedtuple from collections import namedtuple
# We can't place this file in a separate folder due to Python2 limitations but # We can't place this file in a separate folder due to Python2 limitations, but
# anyway we just maintain it with minimum efforts and it has been more than 3 # anyway we just maintain it with minimum efforts, and it has been more than 3
# years since EOL of Python2 so it should be fine. # years since EOL of Python2 so it should be fine.
from . import x86_const, arm_const, arm64_const, unicorn_const as uc from . import x86_const, arm_const, arm64_const, unicorn_const as uc
@@ -23,16 +22,11 @@ ucsubclass = 0
if not hasattr(sys.modules[__name__], "__file__"): if not hasattr(sys.modules[__name__], "__file__"):
__file__ = inspect.getfile(inspect.currentframe()) __file__ = inspect.getfile(inspect.currentframe())
_python2 = sys.version_info[0] < 3 _lib = {'darwin': 'libunicorn.2.dylib',
if _python2: 'win32': 'unicorn.dll',
range = xrange 'cygwin': 'cygunicorn.dll',
'linux': 'libunicorn.so.2',
_lib = { 'darwin': 'libunicorn.2.dylib', 'linux2': 'libunicorn.so.2'}
'win32': 'unicorn.dll',
'cygwin': 'cygunicorn.dll',
'linux': 'libunicorn.so.2',
'linux2': 'libunicorn.so.2' }
# Windows DLL in dependency order # Windows DLL in dependency order
_all_windows_dlls = ( _all_windows_dlls = (
@@ -43,6 +37,7 @@ _all_windows_dlls = (
_loaded_windows_dlls = set() _loaded_windows_dlls = set()
def _load_win_support(path): def _load_win_support(path):
for dll in _all_windows_dlls: for dll in _all_windows_dlls:
if dll in _loaded_windows_dlls: if dll in _loaded_windows_dlls:
@@ -51,18 +46,20 @@ def _load_win_support(path):
lib_file = os.path.join(path, dll) lib_file = os.path.join(path, dll)
if ('/' not in path and '\\' not in path) or os.path.exists(lib_file): if ('/' not in path and '\\' not in path) or os.path.exists(lib_file):
try: try:
#print('Trying to load Windows library', lib_file) # print('Trying to load Windows library', lib_file)
ctypes.cdll.LoadLibrary(lib_file) ctypes.cdll.LoadLibrary(lib_file)
#print('SUCCESS') # print('SUCCESS')
_loaded_windows_dlls.add(dll) _loaded_windows_dlls.add(dll)
except OSError as e: except OSError as e:
#print('FAIL to load %s' %lib_file, e) # print('FAIL to load %s' %lib_file, e)
continue continue
# Initial attempt: load all dlls globally # Initial attempt: load all dlls globally
if sys.platform in ('win32', 'cygwin'): if sys.platform in ('win32', 'cygwin'):
_load_win_support('') _load_win_support('')
def _load_lib(path): def _load_lib(path):
try: try:
if sys.platform in ('win32', 'cygwin'): if sys.platform in ('win32', 'cygwin'):
@@ -70,12 +67,13 @@ def _load_lib(path):
lib_file = os.path.join(path, _lib.get(sys.platform, 'libunicorn.so.2')) lib_file = os.path.join(path, _lib.get(sys.platform, 'libunicorn.so.2'))
dll = ctypes.cdll.LoadLibrary(lib_file) dll = ctypes.cdll.LoadLibrary(lib_file)
#print('SUCCESS') # print('SUCCESS')
return dll return dll
except OSError as e: except OSError as e:
#print('FAIL to load %s' %lib_file, e) # print('FAIL to load %s' %lib_file, e)
return None return None
_uc = None _uc = None
# Loading attempts, in order # Loading attempts, in order
@@ -95,7 +93,7 @@ _path_list = [os.getenv('LIBUNICORN_PATH', None),
os.getenv('PATH', '')] os.getenv('PATH', '')]
# print(_path_list) # print(_path_list)
#print("-" * 80) # print("-" * 80)
for _path in _path_list: for _path in _path_list:
if _path is None: continue if _path is None: continue
@@ -104,6 +102,7 @@ for _path in _path_list:
else: else:
raise ImportError("ERROR: fail to load the dynamic library.") raise ImportError("ERROR: fail to load the dynamic library.")
# __version__ = "%u.%u.%u" % (uc.UC_VERSION_MAJOR, uc.UC_VERSION_MINOR, uc.UC_VERSION_EXTRA) # __version__ = "%u.%u.%u" % (uc.UC_VERSION_MAJOR, uc.UC_VERSION_MINOR, uc.UC_VERSION_EXTRA)
# setup all the function prototype # setup all the function prototype
@@ -112,7 +111,9 @@ def _setup_prototype(lib, fname, restype, *argtypes):
getattr(lib, fname).restype = restype getattr(lib, fname).restype = restype
getattr(lib, fname).argtypes = argtypes getattr(lib, fname).argtypes = argtypes
except AttributeError: except AttributeError:
raise ImportError("ERROR: Fail to setup some function prototypes. Make sure you have cleaned your unicorn1 installation.") raise ImportError(
"ERROR: Fail to setup some function prototypes. Make sure you have cleaned your unicorn1 installation.")
ucerr = ctypes.c_int ucerr = ctypes.c_int
uc_mode = ctypes.c_int uc_mode = ctypes.c_int
@@ -121,13 +122,15 @@ uc_engine = ctypes.c_void_p
uc_context = ctypes.c_void_p uc_context = ctypes.c_void_p
uc_hook_h = ctypes.c_size_t uc_hook_h = ctypes.c_size_t
class _uc_mem_region(ctypes.Structure): class _uc_mem_region(ctypes.Structure):
_fields_ = [ _fields_ = [
("begin", ctypes.c_uint64), ("begin", ctypes.c_uint64),
("end", ctypes.c_uint64), ("end", ctypes.c_uint64),
("perms", ctypes.c_uint32), ("perms", ctypes.c_uint32),
] ]
class uc_tb(ctypes.Structure): class uc_tb(ctypes.Structure):
""""TranslationBlock""" """"TranslationBlock"""
_fields_ = [ _fields_ = [
@@ -136,6 +139,7 @@ class uc_tb(ctypes.Structure):
("size", ctypes.c_uint16) ("size", ctypes.c_uint16)
] ]
_setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) _setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
_setup_prototype(_uc, "uc_arch_supported", ctypes.c_bool, ctypes.c_int) _setup_prototype(_uc, "uc_arch_supported", ctypes.c_bool, ctypes.c_int)
_setup_prototype(_uc, "uc_open", ucerr, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine)) _setup_prototype(_uc, "uc_open", ucerr, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine))
@@ -146,12 +150,15 @@ _setup_prototype(_uc, "uc_reg_read", ucerr, uc_engine, ctypes.c_int, ctypes.c_vo
_setup_prototype(_uc, "uc_reg_write", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_reg_write", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p)
_setup_prototype(_uc, "uc_mem_read", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) _setup_prototype(_uc, "uc_mem_read", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
_setup_prototype(_uc, "uc_mem_write", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) _setup_prototype(_uc, "uc_mem_write", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
_setup_prototype(_uc, "uc_emu_start", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) _setup_prototype(_uc, "uc_emu_start", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64,
ctypes.c_size_t)
_setup_prototype(_uc, "uc_emu_stop", ucerr, uc_engine) _setup_prototype(_uc, "uc_emu_stop", ucerr, uc_engine)
_setup_prototype(_uc, "uc_hook_del", ucerr, uc_engine, uc_hook_h) _setup_prototype(_uc, "uc_hook_del", ucerr, uc_engine, uc_hook_h)
_setup_prototype(_uc, "uc_mmio_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) _setup_prototype(_uc, "uc_mmio_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
_setup_prototype(_uc, "uc_mem_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) _setup_prototype(_uc, "uc_mem_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
_setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p) _setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32,
ctypes.c_void_p)
_setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t) _setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t)
_setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) _setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t)) _setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
@@ -163,9 +170,11 @@ _setup_prototype(_uc, "uc_context_size", ctypes.c_size_t, uc_engine)
_setup_prototype(_uc, "uc_context_reg_read", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_context_reg_read", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p)
_setup_prototype(_uc, "uc_context_reg_write", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_context_reg_write", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p)
_setup_prototype(_uc, "uc_context_free", ucerr, uc_context) _setup_prototype(_uc, "uc_context_free", ucerr, uc_context)
_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) _setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)),
ctypes.POINTER(ctypes.c_uint32))
# https://bugs.python.org/issue42880 # https://bugs.python.org/issue42880
_setup_prototype(_uc, "uc_hook_add", ucerr, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64) _setup_prototype(_uc, "uc_hook_add", ucerr, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64)
_setup_prototype(_uc, "uc_ctl", ucerr, uc_engine, ctypes.c_int) _setup_prototype(_uc, "uc_ctl", ucerr, uc_engine, ctypes.c_int)
UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p)
@@ -203,6 +212,7 @@ UC_HOOK_TCG_OPCODE_CB = ctypes.CFUNCTYPE(
None, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p None, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p
) )
# access to error code via @errno of UcError # access to error code via @errno of UcError
class UcError(Exception): class UcError(Exception):
def __init__(self, errno): def __init__(self, errno):
@@ -232,28 +242,30 @@ def version_bind():
def uc_arch_supported(query): def uc_arch_supported(query):
return _uc.uc_arch_supported(query) return _uc.uc_arch_supported(query)
# uc_reg_read/write and uc_context_reg_read/write. # uc_reg_read/write and uc_context_reg_read/write.
def reg_read(reg_read_func, arch, reg_id, opt=None): def reg_read(reg_read_func, arch, reg_id, opt=None):
if arch == uc.UC_ARCH_X86: if arch == uc.UC_ARCH_X86:
if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR,
x86_const.UC_X86_REG_TR]:
reg = uc_x86_mmr() reg = uc_x86_mmr()
status = reg_read_func(reg_id, ctypes.byref(reg)) status = reg_read_func(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return reg.selector, reg.base, reg.limit, reg.flags return reg.selector, reg.base, reg.limit, reg.flags
if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): if reg_id in xrange(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0 + 8):
reg = uc_x86_float80() reg = uc_x86_float80()
status = reg_read_func(reg_id, ctypes.byref(reg)) status = reg_read_func(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return reg.mantissa, reg.exponent return reg.mantissa, reg.exponent
if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): if reg_id in xrange(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0 + 8):
reg = uc_x86_xmm() reg = uc_x86_xmm()
status = reg_read_func(reg_id, ctypes.byref(reg)) status = reg_read_func(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return reg.low_qword | (reg.high_qword << 64) return reg.low_qword | (reg.high_qword << 64)
if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): if reg_id in xrange(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0 + 16):
reg = uc_x86_ymm() reg = uc_x86_ymm()
status = reg_read_func(reg_id, ctypes.byref(reg)) status = reg_read_func(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
@@ -291,7 +303,8 @@ def reg_read(reg_read_func, arch, reg_id, opt=None):
raise UcError(status) raise UcError(status)
return reg.val return reg.val
elif reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): elif reg_id in xrange(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31 + 1) or xrange(
arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31 + 1):
reg = uc_arm64_neon128() reg = uc_arm64_neon128()
status = reg_read_func(reg_id, ctypes.byref(reg)) status = reg_read_func(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
@@ -305,26 +318,28 @@ def reg_read(reg_read_func, arch, reg_id, opt=None):
raise UcError(status) raise UcError(status)
return reg.value return reg.value
def reg_write(reg_write_func, arch, reg_id, value): def reg_write(reg_write_func, arch, reg_id, value):
reg = None reg = None
if arch == uc.UC_ARCH_X86: if arch == uc.UC_ARCH_X86:
if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR,
x86_const.UC_X86_REG_TR]:
assert isinstance(value, tuple) and len(value) == 4 assert isinstance(value, tuple) and len(value) == 4
reg = uc_x86_mmr() reg = uc_x86_mmr()
reg.selector = value[0] reg.selector = value[0]
reg.base = value[1] reg.base = value[1]
reg.limit = value[2] reg.limit = value[2]
reg.flags = value[3] reg.flags = value[3]
if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): if reg_id in xrange(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0 + 8):
reg = uc_x86_float80() reg = uc_x86_float80()
reg.mantissa = value[0] reg.mantissa = value[0]
reg.exponent = value[1] reg.exponent = value[1]
if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): if reg_id in xrange(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0 + 8):
reg = uc_x86_xmm() reg = uc_x86_xmm()
reg.low_qword = value & 0xffffffffffffffff reg.low_qword = value & 0xffffffffffffffff
reg.high_qword = value >> 64 reg.high_qword = value >> 64
if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): if reg_id in xrange(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0 + 16):
reg = uc_x86_ymm() reg = uc_x86_ymm()
reg.first_qword = value & 0xffffffffffffffff reg.first_qword = value & 0xffffffffffffffff
reg.second_qword = (value >> 64) & 0xffffffffffffffff reg.second_qword = (value >> 64) & 0xffffffffffffffff
@@ -336,7 +351,8 @@ def reg_write(reg_write_func, arch, reg_id, value):
reg.value = value[1] reg.value = value[1]
if arch == uc.UC_ARCH_ARM64: if arch == uc.UC_ARCH_ARM64:
if reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): if reg_id in xrange(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31 + 1) or xrange(
arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31 + 1):
reg = uc_arm64_neon128() reg = uc_arm64_neon128()
reg.low_qword = value & 0xffffffffffffffff reg.low_qword = value & 0xffffffffffffffff
reg.high_qword = value >> 64 reg.high_qword = value >> 64
@@ -364,6 +380,7 @@ def reg_write(reg_write_func, arch, reg_id, value):
return return
def _catch_hook_exception(func): def _catch_hook_exception(func):
@wraps(func) @wraps(func)
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
@@ -396,6 +413,7 @@ class uc_arm_cp_reg(ctypes.Structure):
("val", ctypes.c_uint64) ("val", ctypes.c_uint64)
] ]
class uc_arm64_cp_reg(ctypes.Structure): class uc_arm64_cp_reg(ctypes.Structure):
"""ARM64 coprocessors registers for instructions MRS, MSR""" """ARM64 coprocessors registers for instructions MRS, MSR"""
_fields_ = [ _fields_ = [
@@ -407,21 +425,24 @@ class uc_arm64_cp_reg(ctypes.Structure):
("val", ctypes.c_uint64) ("val", ctypes.c_uint64)
] ]
class uc_x86_mmr(ctypes.Structure): class uc_x86_mmr(ctypes.Structure):
"""Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.""" """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR."""
_fields_ = [ _fields_ = [
("selector", ctypes.c_uint16), # not used by GDTR and IDTR ("selector", ctypes.c_uint16), # not used by GDTR and IDTR
("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs ("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs
("limit", ctypes.c_uint32), ("limit", ctypes.c_uint32),
("flags", ctypes.c_uint32), # not used by GDTR and IDTR ("flags", ctypes.c_uint32), # not used by GDTR and IDTR
] ]
class uc_x86_msr(ctypes.Structure): class uc_x86_msr(ctypes.Structure):
_fields_ = [ _fields_ = [
("rid", ctypes.c_uint32), ("rid", ctypes.c_uint32),
("value", ctypes.c_uint64), ("value", ctypes.c_uint64),
] ]
class uc_x86_float80(ctypes.Structure): class uc_x86_float80(ctypes.Structure):
"""Float80""" """Float80"""
_fields_ = [ _fields_ = [
@@ -437,6 +458,7 @@ class uc_x86_xmm(ctypes.Structure):
("high_qword", ctypes.c_uint64), ("high_qword", ctypes.c_uint64),
] ]
class uc_x86_ymm(ctypes.Structure): class uc_x86_ymm(ctypes.Structure):
"""256-bit ymm register""" """256-bit ymm register"""
_fields_ = [ _fields_ = [
@@ -446,6 +468,7 @@ class uc_x86_ymm(ctypes.Structure):
("fourth_qword", ctypes.c_uint64), ("fourth_qword", ctypes.c_uint64),
] ]
class uc_arm64_neon128(ctypes.Structure): class uc_arm64_neon128(ctypes.Structure):
"""128-bit neon register""" """128-bit neon register"""
_fields_ = [ _fields_ = [
@@ -453,10 +476,12 @@ class uc_arm64_neon128(ctypes.Structure):
("high_qword", ctypes.c_uint64), ("high_qword", ctypes.c_uint64),
] ]
# Subclassing ref to allow property assignment. # Subclassing ref to allow property assignment.
class UcRef(weakref.ref): class UcRef(weakref.ref):
pass pass
# This class tracks Uc instance destruction and releases handles. # This class tracks Uc instance destruction and releases handles.
class UcCleanupManager(object): class UcCleanupManager(object):
def __init__(self): def __init__(self):
@@ -486,6 +511,7 @@ class UcCleanupManager(object):
del self._refs[id(ref)] del self._refs[id(ref)]
ref._class.release_handle(ref._uch) ref._class.release_handle(ref._uch)
class Uc(object): class Uc(object):
_cleanup = UcCleanupManager() _cleanup = UcCleanupManager()
@@ -540,11 +566,11 @@ class Uc(object):
# return the value of a register # return the value of a register
def reg_read(self, reg_id, opt=None): def reg_read(self, reg_id, opt=None):
return reg_read(functools.partial(_uc.uc_reg_read, self._uch), self._arch, reg_id, opt) return reg_read(partial(_uc.uc_reg_read, self._uch), self._arch, reg_id, opt)
# write to a register # write to a register
def reg_write(self, reg_id, value): def reg_write(self, reg_id, value):
return reg_write(functools.partial(_uc.uc_reg_write, self._uch), self._arch, reg_id, value) return reg_write(partial(_uc.uc_reg_write, self._uch), self._arch, reg_id, value)
# read from MSR - X86 only # read from MSR - X86 only
def msr_read(self, msr_id): def msr_read(self, msr_id):
@@ -681,7 +707,8 @@ class Uc(object):
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, reg, uc_arm64_cp_reg_tuple(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val), data) return cb(self, reg,
uc_arm64_cp_reg_tuple(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val), data)
@_catch_hook_exception @_catch_hook_exception
def _hook_insn_out_cb(self, handle, port, size, value, user_data): def _hook_insn_out_cb(self, handle, port, size, value, user_data):
@@ -711,7 +738,7 @@ class Uc(object):
return self.__ctl(ctl, nr, uc.UC_CTL_IO_WRITE) return self.__ctl(ctl, nr, uc.UC_CTL_IO_WRITE)
def __ctl_rw(self, ctl, nr): def __ctl_rw(self, ctl, nr):
return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ_WRITE) return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ_WRITE)
def __ctl_r_1_arg(self, ctl, ctp): def __ctl_r_1_arg(self, ctl, ctp):
arg = ctp() arg = ctp()
@@ -795,7 +822,8 @@ class Uc(object):
cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB) cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB)
if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction
cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB) cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB)
if arg1 in (arm64_const.UC_ARM64_INS_MRS, arm64_const.UC_ARM64_INS_MSR, arm64_const.UC_ARM64_INS_SYS, arm64_const.UC_ARM64_INS_SYSL): if arg1 in (arm64_const.UC_ARM64_INS_MRS, arm64_const.UC_ARM64_INS_MSR, arm64_const.UC_ARM64_INS_SYS,
arm64_const.UC_ARM64_INS_SYSL):
cb = ctypes.cast(UC_HOOK_INSN_SYS_CB(self._hook_insn_sys_cb), UC_HOOK_INSN_SYS_CB) cb = ctypes.cast(UC_HOOK_INSN_SYS_CB(self._hook_insn_sys_cb), UC_HOOK_INSN_SYS_CB)
status = _uc.uc_hook_add( status = _uc.uc_hook_add(
self._uch, ctypes.byref(_h2), htype, cb, self._uch, ctypes.byref(_h2), htype, cb,
@@ -807,7 +835,8 @@ class Uc(object):
flags = ctypes.c_int(arg2) flags = ctypes.c_int(arg2)
status = _uc.uc_hook_add( status = _uc.uc_hook_add(
self._uch, ctypes.byref(_h2), htype, ctypes.cast(UC_HOOK_TCG_OPCODE_CB(self._hook_tcg_op_cb), UC_HOOK_TCG_OPCODE_CB), self._uch, ctypes.byref(_h2), htype,
ctypes.cast(UC_HOOK_TCG_OPCODE_CB(self._hook_tcg_op_cb), UC_HOOK_TCG_OPCODE_CB),
ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.cast(self._callback_count, ctypes.c_void_p),
ctypes.c_uint64(begin), ctypes.c_uint64(end), opcode, flags ctypes.c_uint64(begin), ctypes.c_uint64(end), opcode, flags
) )
@@ -905,7 +934,7 @@ class Uc(object):
raise UcError(status) raise UcError(status)
try: try:
for i in range(count.value): for i in xrange(count.value):
yield (regions[i].begin, regions[i].end, regions[i].perms) yield (regions[i].begin, regions[i].end, regions[i].perms)
finally: finally:
_uc.uc_free(regions) _uc.uc_free(regions)
@@ -940,11 +969,11 @@ class UcContext:
# return the value of a register # return the value of a register
def reg_read(self, reg_id, opt=None): def reg_read(self, reg_id, opt=None):
return reg_read(functools.partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt) return reg_read(partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt)
# write to a register # write to a register
def reg_write(self, reg_id, value): def reg_write(self, reg_id, value):
return reg_write(functools.partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value) return reg_write(partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value)
# Make UcContext picklable # Make UcContext picklable
def __getstate__(self): def __getstate__(self):
@@ -990,4 +1019,4 @@ def debug():
return "python-%s-c%u.%u-b%u.%u" % ( return "python-%s-c%u.%u-b%u.%u" % (
all_archs, major, minor, uc.UC_API_MAJOR, uc.UC_API_MINOR all_archs, major, minor, uc.UC_API_MAJOR, uc.UC_API_MINOR
) )

View File

@@ -283,7 +283,7 @@ class UcError(Exception):
def uc_version() -> Tuple[int, int, int]: def uc_version() -> Tuple[int, int, int]:
"""Retrieve Unicorn library version. """Retrieve Unicorn library version.
Returns: a tuple containing major, minor and a combined verion number Returns: a tuple containing major, minor and a combined version number
""" """
major = ctypes.c_int() major = ctypes.c_int()
@@ -300,7 +300,7 @@ def uc_version() -> Tuple[int, int, int]:
def version_bind() -> Tuple[int, int, int]: def version_bind() -> Tuple[int, int, int]:
"""Retrieve Unicorn bindings version. """Retrieve Unicorn bindings version.
Returns: a tuple containing major, minor and a combined verion number Returns: a tuple containing major, minor and a combined version number
""" """
major = uc.UC_API_MAJOR major = uc.UC_API_MAJOR
@@ -319,7 +319,7 @@ def uc_arch_supported(atype: int) -> bool:
def debug() -> str: def debug() -> str:
"""Get verbose verion string. """Get verbose version string.
""" """
archs = ( archs = (
@@ -559,7 +559,7 @@ class RegStateManager:
return self._reg_read_batch([__seq_tuple(elem) for elem in reg_data]) return self._reg_read_batch([__seq_tuple(elem) for elem in reg_data])
def reg_write_batch(self, reg_data: Sequence[Tuple[int, Any]]) -> None: def reg_write_batch(self, reg_data: Sequence[Tuple[int, Any]]) -> None:
"""Write a sequece of architectural registers. This provides with faster means to """Write a sequence of architectural registers. This provides with faster means to
write multiple registers. write multiple registers.
Args: Args:
@@ -599,7 +599,7 @@ def ucsubclass(cls):
# inherit from UcIntel and only then Uc, instead of Uc directly. that is: # inherit from UcIntel and only then Uc, instead of Uc directly. that is:
# Pegasus -> UcIntel -> Uc -> RegStateManager -> object # Pegasus -> UcIntel -> Uc -> RegStateManager -> object
# #
# note that all Pegasus subclasses will have the same inheritence chain, # note that all Pegasus subclasses will have the same inheritance chain,
# regardless of the arch and mode the might use to initialize. # regardless of the arch and mode the might use to initialize.
def __replace(seq: Tuple, item, repl) -> Tuple: def __replace(seq: Tuple, item, repl) -> Tuple:
@@ -713,7 +713,7 @@ class Uc(RegStateManager):
self._hook_exception: Optional[Exception] = None self._hook_exception: Optional[Exception] = None
# create a finalizer object that will apropriately free up resources when # create a finalizer object that will appropriately free up resources when
# this instance undergoes garbage collection. # this instance undergoes garbage collection.
self.__finalizer = weakref.finalize(self, Uc.release_handle, self._uch) self.__finalizer = weakref.finalize(self, Uc.release_handle, self._uch)
@@ -871,7 +871,7 @@ class Uc(RegStateManager):
# TODO: this is where mmio callbacks need to be released from cache, # TODO: this is where mmio callbacks need to be released from cache,
# but we cannot tell whether this is an mmio range. also, memory ranges # but we cannot tell whether this is an mmio range. also, memory ranges
# might be splitted by 'map_protect' after they were mapped, so the # might be split by 'map_protect' after they were mapped, so the
# (start, end) tuple may not be suitable for retrieving the callbacks. # (start, end) tuple may not be suitable for retrieving the callbacks.
# #
# here we try to do that on a best-effort basis: # here we try to do that on a best-effort basis:
@@ -910,10 +910,10 @@ class Uc(RegStateManager):
size : range size (in bytes) size : range size (in bytes)
read_cb : read callback to invoke upon read access. if not specified, reads \ read_cb : read callback to invoke upon read access. if not specified, reads \
from the mmio range will be silently dropped from the mmio range will be silently dropped
read_ud : optinal context object to pass on to the read callback read_ud : optional context object to pass on to the read callback
write_cb : write callback to invoke unpon a write access. if not specified, writes \ write_cb : write callback to invoke upon a write access. if not specified, writes \
to the mmio range will be silently dropped to the mmio range will be silently dropped
write_ud : optinal context object to pass on to the write callback write_ud : optional context object to pass on to the write callback
""" """
@uccallback(self, MMIO_READ_CFUNC) @uccallback(self, MMIO_READ_CFUNC)
@@ -946,7 +946,7 @@ class Uc(RegStateManager):
Returns: an iterator whose elements contain begin, end and perms properties of each range Returns: an iterator whose elements contain begin, end and perms properties of each range
Raises: `UcError` in case an itnernal error has been encountered Raises: `UcError` in case an internal error has been encountered
""" """
regions = ctypes.POINTER(uc_mem_region)() regions = ctypes.POINTER(uc_mem_region)()
@@ -1024,7 +1024,7 @@ class Uc(RegStateManager):
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# hold a reference to the funcion pointer to prevent it from being gc-ed # hold a reference to the function pointer to prevent it from being gc-ed
self._callbacks[handle.value] = fptr self._callbacks[handle.value] = fptr
return handle.value return handle.value
@@ -1056,7 +1056,7 @@ class Uc(RegStateManager):
def __hook_insn(): def __hook_insn():
# each arch is expected to overload hook_add and implement this handler on their own. # each arch is expected to overload hook_add and implement this handler on their own.
# if we got here, it means this particular architecture does not support hooking any # if we got here, it means this particular architecture does not support hooking any
# instruction and so we fail # instruction, and so we fail
raise UcError(uc.UC_ERR_ARG) raise UcError(uc.UC_ERR_ARG)
def __hook_code(): def __hook_code():