diff --git a/examples/hello_arm64_range_blob.py b/examples/hello_arm64_range_blob.py new file mode 100644 index 000000000..35573bbd5 --- /dev/null +++ b/examples/hello_arm64_range_blob.py @@ -0,0 +1,88 @@ +#!/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: hello_arm64_range_blob.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: hello_arm64_range_blob.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.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) + 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: dict = {"steps": 0, "stop": None} + + 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}) + ql_inner.emu_stop() + return True + + 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) + if state["stop"] is None: + events.append({"type": "RETURNED", "steps": state["steps"]}) + except Exception as exc: + if state["stop"] is not None: + pass + else: + 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()) 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"