From 455b2bca6e5a2b168b0373301b5b55c95e4a6978 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:09:43 +0000 Subject: [PATCH 1/4] Bump setuptools from 78.1.1 to 83.0.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 78.1.1 to 83.0.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v78.1.1...v83.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-version: 83.0.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2d99b4731..8593824a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "antlr4-python3-runtime" @@ -413,7 +413,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (==7.2.5) ; python_version >= \"3.9\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.2.2) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "mypy (==v1.5.1) ; python_version >= \"3.8\"", "pre-commit (==3.4.0) ; python_version >= \"3.8\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==7.4.0) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==4.1.0) ; python_version >= \"3.8\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.0.0) ; python_version >= \"3.8\"", "sphinx-autobuild (==2021.3.14) ; python_version >= \"3.9\"", "sphinx-rtd-theme (==1.3.0) ; python_version >= \"3.9\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.11.0) ; python_version >= \"3.8\""] +dev = ["Sphinx (==7.2.5) ; python_version >= \"3.9\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.2.2) ; python_version >= \"3.8\"", "mypy (==0.910) ; python_version < \"3.6\"", "mypy (==0.971) ; python_version == \"3.6\"", "mypy (==1.4.1) ; python_version == \"3.7\"", "mypy (==1.5.1) ; python_version >= \"3.8\"", "pre-commit (==3.4.0) ; python_version >= \"3.8\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==7.4.0) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==4.1.0) ; python_version >= \"3.8\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.0.0) ; python_version >= \"3.8\"", "sphinx-autobuild (==2021.3.14) ; python_version >= \"3.9\"", "sphinx-rtd-theme (==1.3.0) ; python_version >= \"3.9\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.11.0) ; python_version >= \"3.8\""] [[package]] name = "multiprocess" @@ -812,24 +812,24 @@ files = [ [[package]] name = "setuptools" -version = "78.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +version = "83.0.0" +description = "Most extensible Python build backend with support for C/C++ extension modules" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "setuptools-78.1.1-py3-none-any.whl", hash = "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561"}, - {file = "setuptools-78.1.1.tar.gz", hash = "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d"}, + {file = "setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3"}, + {file = "setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.13.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] +enabler = ["pytest-enabler (>=3.4)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.18.*)", "pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] [[package]] name = "termcolor" From b61a29991f822be9985b0710d016764a64082f88 Mon Sep 17 00:00:00 2001 From: warsang Date: Fri, 11 Sep 2026 00:48:38 +0200 Subject: [PATCH 2/4] Add AArch64 code-range blob example with UDF trapping Maps a raw AArch64 blob executable with scratch stack plus stub page, logs invalid instructions as structured UDF faults and stops at unmapped accesses instead of misexecuting. --- examples/hello_arm64_range_blob.py | 81 ++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 examples/hello_arm64_range_blob.py diff --git a/examples/hello_arm64_range_blob.py b/examples/hello_arm64_range_blob.py new file mode 100644 index 000000000..50ae3e945 --- /dev/null +++ b/examples/hello_arm64_range_blob.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Emulate a raw AArch64 code-range blob with UDF trapping. + +Maps a code blob (e.g. one architecture range extracted from a hybrid +image) as executable in an AArch64 session with a scratch stack and a +stub page standing in for loader-populated tables. Invalid instructions +surface as structured UDF faults (immediate logged, never skipped); +unmapped accesses stop at the loader-table boundary. + +Usage: qiling_aarch64_chpe.py [max-steps] +""" +import struct +import sys +from pathlib import Path + +STUB_ADDR = 0x70000000 +STACK_TOP = 0x100000000 + + +def u32(mem_read, addr): + return struct.unpack(" int: + from qiling import Qiling + from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE + + if len(sys.argv) < 3: + print("usage: qiling_aarch64_chpe.py [max-steps]") + return 2 + code = Path(sys.argv[1]).read_bytes() + load_at = int(sys.argv[2], 16) + max_steps = int(sys.argv[3]) if len(sys.argv) > 3 else 4096 + + ql = Qiling(code=code, archtype=QL_ARCH.AARCH64, ostype=QL_OS.BLOB, + profile="blob_raw.ql", verbose=QL_VERBOSE.OFF) + ql.mem.map(load_at, (len(code) + 0xFFF) & ~0xFFF) + ql.mem.write(load_at, code) + ql.mem.map(STUB_ADDR, 0x1000) + ql.mem.write(STUB_ADDR, b"\x00\x00\x00\x00") # UDF landing pad + ql.mem.map(STACK_TOP - 0x4000, 0x4000) + + events: list = [] + state = {"steps": 0} + + def hook_code(ql_inner): + state["steps"] += 1 + if state["steps"] >= max_steps: + events.append({"type": "STEP-CAP", "steps": state["steps"]}) + ql_inner.emu_stop() + + def hook_unmapped(ql_inner, access, addr, size, value): + events.append({"type": "UNMAPPED", "pc": hex(ql_inner.arch.regs.pc), + "addr": hex(addr), "size": size}) + return False + + ql.hook_code(hook_code) + ql.hook_mem_unmapped(hook_unmapped) + + ql.arch.regs.sp = STACK_TOP - 0x100 + try: + ql.run(begin=load_at, end=load_at + len(code), count=max_steps) + events.append({"type": "RETURNED", "steps": state["steps"]}) + except Exception as exc: + pc = ql.arch.regs.pc + try: + word = u32(ql.mem.read, pc) + except Exception: + events.append({"type": "STOP", "pc": hex(pc), + "detail": f"{type(exc).__name__}: {exc}"}) + else: + # True UDF (D42...) and zero-top-half padding share the logger. + imm = word & 0xFFFF if (word & 0xFFFF0000) == 0 else (word >> 5) & 0xFFFF + events.append({"type": "UDF", "pc": hex(pc), + "imm": hex(imm), "word": hex(word)}) + print(f"blob={sys.argv[1]} load={hex(load_at)} steps={state['steps']} events={events}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 55714bc1ea0c742316efeba3021928a075c24f83 Mon Sep 17 00:00:00 2001 From: warsang Date: Mon, 14 Sep 2026 00:52:15 -0700 Subject: [PATCH 3/4] Fix example usage string to match filename hello_arm64_range_blob.py printed qiling_aarch64_chpe.py in --help/usage. Align both docstring and runtime message with the actual filename per hello_arm_blob_raw.py conventions. No behavior change. --- examples/hello_arm64_range_blob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hello_arm64_range_blob.py b/examples/hello_arm64_range_blob.py index 50ae3e945..3c8847baf 100644 --- a/examples/hello_arm64_range_blob.py +++ b/examples/hello_arm64_range_blob.py @@ -7,7 +7,7 @@ surface as structured UDF faults (immediate logged, never skipped); unmapped accesses stop at the loader-table boundary. -Usage: qiling_aarch64_chpe.py [max-steps] +Usage: hello_arm64_range_blob.py [max-steps] """ import struct import sys @@ -26,7 +26,7 @@ def main() -> int: from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE if len(sys.argv) < 3: - print("usage: qiling_aarch64_chpe.py [max-steps]") + print("usage: hello_arm64_range_blob.py [max-steps]") return 2 code = Path(sys.argv[1]).read_bytes() load_at = int(sys.argv[2], 16) From eac26e6da6cb9b3fb4700b9a0c2b7cc5c05ab597 Mon Sep 17 00:00:00 2001 From: warsang Date: Mon, 14 Sep 2026 02:24:07 -0700 Subject: [PATCH 4/4] Address review: correct arch enum, hook signatures, unmapped stop QL_ARCH.AARCH64 does not exist, use QL_ARCH.ARM64. Code hooks receive (ql, address, size), accept all three. Qiling _hook_mem_cb ignores the unmapped-hook return value, so stop explicitly with emu_stop() and return True; track intentional stops so exactly one terminal event is recorded (no RETURNED/STOP after UNMAPPED/STEP-CAP). Verified: NOP blob returns, UDF blob logs single UDF fault, ret-to-unmapped stops with single UNMAPPED, tight loop hits STEP-CAP at cap. --- examples/hello_arm64_range_blob.py | 37 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/examples/hello_arm64_range_blob.py b/examples/hello_arm64_range_blob.py index 3c8847baf..35573bbd5 100644 --- a/examples/hello_arm64_range_blob.py +++ b/examples/hello_arm64_range_blob.py @@ -32,7 +32,7 @@ def main() -> int: load_at = int(sys.argv[2], 16) max_steps = int(sys.argv[3]) if len(sys.argv) > 3 else 4096 - ql = Qiling(code=code, archtype=QL_ARCH.AARCH64, ostype=QL_OS.BLOB, + ql = Qiling(code=code, archtype=QL_ARCH.ARM64, ostype=QL_OS.BLOB, profile="blob_raw.ql", verbose=QL_VERBOSE.OFF) ql.mem.map(load_at, (len(code) + 0xFFF) & ~0xFFF) ql.mem.write(load_at, code) @@ -41,18 +41,21 @@ def main() -> int: ql.mem.map(STACK_TOP - 0x4000, 0x4000) events: list = [] - state = {"steps": 0} + state: dict = {"steps": 0, "stop": None} - def hook_code(ql_inner): + def hook_code(ql_inner, address, size): state["steps"] += 1 if state["steps"] >= max_steps: + state["stop"] = "STEP-CAP" events.append({"type": "STEP-CAP", "steps": state["steps"]}) ql_inner.emu_stop() def hook_unmapped(ql_inner, access, addr, size, value): + state["stop"] = "UNMAPPED" events.append({"type": "UNMAPPED", "pc": hex(ql_inner.arch.regs.pc), "addr": hex(addr), "size": size}) - return False + ql_inner.emu_stop() + return True ql.hook_code(hook_code) ql.hook_mem_unmapped(hook_unmapped) @@ -60,19 +63,23 @@ def hook_unmapped(ql_inner, access, addr, size, value): ql.arch.regs.sp = STACK_TOP - 0x100 try: ql.run(begin=load_at, end=load_at + len(code), count=max_steps) - events.append({"type": "RETURNED", "steps": state["steps"]}) + if state["stop"] is None: + events.append({"type": "RETURNED", "steps": state["steps"]}) except Exception as exc: - pc = ql.arch.regs.pc - try: - word = u32(ql.mem.read, pc) - except Exception: - events.append({"type": "STOP", "pc": hex(pc), - "detail": f"{type(exc).__name__}: {exc}"}) + if state["stop"] is not None: + pass else: - # True UDF (D42...) and zero-top-half padding share the logger. - imm = word & 0xFFFF if (word & 0xFFFF0000) == 0 else (word >> 5) & 0xFFFF - events.append({"type": "UDF", "pc": hex(pc), - "imm": hex(imm), "word": hex(word)}) + pc = ql.arch.regs.pc + try: + word = u32(ql.mem.read, pc) + except Exception: + events.append({"type": "STOP", "pc": hex(pc), + "detail": f"{type(exc).__name__}: {exc}"}) + else: + # True UDF (D42...) and zero-top-half padding share the logger. + imm = word & 0xFFFF if (word & 0xFFFF0000) == 0 else (word >> 5) & 0xFFFF + events.append({"type": "UDF", "pc": hex(pc), + "imm": hex(imm), "word": hex(word)}) print(f"blob={sys.argv[1]} load={hex(load_at)} steps={state['steps']} events={events}") return 0