From 30eba376fced05c84ed148dec552f5b84e74dba9 Mon Sep 17 00:00:00 2001
From: xwings
Date: Sun, 12 Apr 2026 16:18:34 +0800 Subject: [PATCH 2/4] Fix docker.yaml --- poetry.lock | 518 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 444 insertions(+), 74 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3933b97cf..189014727 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,24 +1,58 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.3 and should not be changed by hand. + +[[package]] +name = "antlr4-python3-runtime" +version = "4.8" +description = "ANTLR 4.8 runtime for Python 3.7" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "antlr4-python3-runtime-4.8.tar.gz", hash = "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33"}, +] [[package]] name = "antlr4-python3-runtime" version = "4.13.2" description = "ANTLR 4.13.2 runtime for Python 3" -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "antlr4_python3_runtime-4.13.2-py3-none-any.whl", hash = "sha256:fe3835eb8d33daece0e799090eda89719dbccee7aa39ef94eed3818cafa5a7e8"}, {file = "antlr4_python3_runtime-4.13.2.tar.gz", hash = "sha256:909b647e1d2fc2b70180ac586df3933e38919c85f98ccc656a96cd3f25ef3916"}, ] +[[package]] +name = "asciimatics" +version = "1.14.0" +description = "A cross-platform package to replace curses (mouse/keyboard input & text colours/positioning) and create ASCII animations" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "asciimatics-1.14.0-py2.py3-none-any.whl", hash = "sha256:277fe925d0d7a029b35245cde01ead009b4a1336130543ace5c8821f38df1da7"}, + {file = "asciimatics-1.14.0.tar.gz", hash = "sha256:16d20ce42210b434eb05ba469ecdb8293ac7ed3c0ce0dd4f70e30d72d7602227"}, +] + +[package.dependencies] +future = "*" +Pillow = ">=2.7.0" +pyfiglet = ">=0.7.2" +pywin32 = {version = "*", markers = "sys_platform == \"win32\""} +wcwidth = "*" + [[package]] name = "asciimatics" version = "1.15.0" description = "A cross-platform package to replace curses (mouse/keyboard input & text colours/positioning) and create ASCII animations" -category = "main" optional = false python-versions = ">= 3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "asciimatics-1.15.0-py3-none-any.whl", hash = "sha256:0fe068a6bed522929bd04bb5b8a2fb6ebf0aef1b7a9b3843cf71030a34bc38d5"}, {file = "asciimatics-1.15.0.tar.gz", hash = "sha256:cfdd398042727519d8b73e62b8ef82c0becfed4eb420899c3b96c98d0b96821a"}, @@ -32,26 +66,36 @@ wcwidth = "*" [[package]] name = "capstone" -version = "4.0.2" +version = "5.0.7" description = "Capstone disassembly engine" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "capstone-4.0.2-py2.py3-none-manylinux1_i686.whl", hash = "sha256:da442f979414cf27e4621e70e835880878c858ea438c4f0e957e132593579e37"}, - {file = "capstone-4.0.2-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:9d1a9096c5f875b11290317722ed44bb6e7c52e50cc79d791f142bce968c49aa"}, - {file = "capstone-4.0.2-py2.py3-none-win32.whl", hash = "sha256:c3d9b443d1adb40ee2d9a4e7341169b76476ddcf3a54c03793b16cdc7cd35c5a"}, - {file = "capstone-4.0.2-py2.py3-none-win_amd64.whl", hash = "sha256:0d65ffe8620920976ceadedc769f22318f6f150a592368d8a735612367ac8a1a"}, - {file = "capstone-4.0.2.tar.gz", hash = "sha256:2842913092c9b69fd903744bc1b87488e1451625460baac173056e1808ec1c66"}, + {file = "capstone-5.0.7-py3-none-macosx_10_9_universal2.whl", hash = "sha256:388af4ddb9224d3b4f9269673ee575b3f94f77774d48b3f1a283ad13c29a106a"}, + {file = "capstone-5.0.7-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:a9f64e3d75d8c4d7b3d26bba153b2992aadcf6b8d57674b4ef176b4ecdd9822f"}, + {file = "capstone-5.0.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:acb89f5bf6f625745a104a3a44819d3acea173228055c1eadc60d2282ae490bb"}, + {file = "capstone-5.0.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c58546c814567c95e4b9a63bdb8624c960cb8508855c7c767d5f108d7bc09ce2"}, + {file = "capstone-5.0.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b809a9654844ce0d35099121a851ddd2ab2689df1ff6687037babcedcaae6391"}, + {file = "capstone-5.0.7-py3-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0f1b93fc703c419fda8cf84cfa017fd8909be62a4e88024273126ab16f006"}, + {file = "capstone-5.0.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:467716e6555d50cb3526b290f0dbdccb5f961839b1f1e299b484fb5d814173e6"}, + {file = "capstone-5.0.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e551311d4b6dc344fe5518ef6decf4c2dfafe37bba9ad027a53a406930bc5c63"}, + {file = "capstone-5.0.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a13437b28b136c886600e88bee192d25adf56ba1db5597ff5a0bec758bb9c533"}, + {file = "capstone-5.0.7-py3-none-win_amd64.whl", hash = "sha256:4ab8bcb7da8f221ff45926ca168ca33e76f7237d06fbf3c10780002faa2670e1"}, + {file = "capstone-5.0.7.tar.gz", hash = "sha256:796bdd69b05fa124fc2aa2e74b9a0b3d4c4e7f3e02add5e583cf2f3bca282ede"}, ] +[package.dependencies] +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} + [[package]] name = "cffi" version = "1.17.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -129,9 +173,9 @@ pycparser = "*" name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, @@ -144,9 +188,10 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -156,9 +201,9 @@ files = [ name = "dacite" version = "1.8.1" description = "Simple creation of data classes from dictionaries." -category = "main" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "dacite-1.8.1-py3-none-any.whl", hash = "sha256:cc31ad6fdea1f49962ea42db9421772afe01ac5442380d9a99fcf3d188c61afe"}, ] @@ -170,9 +215,9 @@ dev = ["black", "coveralls", "mypy", "pre-commit", "pylint", "pytest (>=5)", "py name = "dill" version = "0.3.9" description = "serialize all of Python" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -186,9 +231,9 @@ profile = ["gprof2dot (>=2022.7.29)"] name = "enum-compat" version = "0.0.3" description = "enum/enum34 compatibility package" -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e"}, {file = "enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157"}, @@ -198,21 +243,34 @@ files = [ name = "first" version = "2.0.2" description = "Return the first true value of an iterable." -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "first-2.0.2-py2.py3-none-any.whl", hash = "sha256:8d8e46e115ea8ac652c76123c0865e3ff18372aef6f03c22809ceefcea9dec86"}, {file = "first-2.0.2.tar.gz", hash = "sha256:ff285b08c55f8c97ce4ea7012743af2495c9f1291785f163722bd36f6af6d3bf"}, ] +[[package]] +name = "future" +version = "0.18.3" +description = "Clean single-source support for Python 3 and 2" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, +] + [[package]] name = "fuzzercorn" version = "0.0.1" description = "Libfuzzer bindings for Unicorn.." -category = "main" optional = true python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Linux\" and extra == \"fuzz\"" files = [ {file = "fuzzercorn-0.0.1-py3-none-manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:304ddcd19803c779a8e4a6a900c51f7c3f5ba0e8c3322bd9502d59ea0825d424"}, {file = "fuzzercorn-0.0.1-py3-none-manylinux1_i686.whl", hash = "sha256:7fa9cbffbcbf45c0af5707abc86ee8fa0d3a396f0ff48ce67c355ef9d3047c4f"}, @@ -228,9 +286,9 @@ unicorn = ">=2.0.0rc5" name = "gevent" version = "24.2.1" description = "Coroutine-based network library" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, @@ -285,19 +343,20 @@ greenlet = [ "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] +monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +recommended = ["cffi (>=1.12.2) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +test = ["cffi (>=1.12.2) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] [[package]] name = "greenlet" version = "3.1.1" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\"" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -382,9 +441,10 @@ test = ["objgraph", "psutil"] name = "importlib-resources" version = "6.4.5" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.8\"" files = [ {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, @@ -394,20 +454,37 @@ files = [ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] type = ["pytest-mypy"] +[[package]] +name = "jsonpath-ng" +version = "1.6.0" +description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "jsonpath-ng-1.6.0.tar.gz", hash = "sha256:5483f8e9d74c39c9abfab554c070ae783c1c8cbadf5df60d561bc705ac68a07e"}, + {file = "jsonpath_ng-1.6.0-py3-none-any.whl", hash = "sha256:6fd04833412c4b3d9299edf369542f5e67095ca84efa17cbb7f06a34958adc9f"}, +] + +[package.dependencies] +ply = "*" + [[package]] name = "jsonpath-ng" version = "1.6.1" description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "jsonpath-ng-1.6.1.tar.gz", hash = "sha256:086c37ba4917304850bd837aeab806670224d3f038fe2833ff593a672ef0a5fa"}, {file = "jsonpath_ng-1.6.1-py3-none-any.whl", hash = "sha256:8f22cd8273d7772eea9aaa84d922e0841aa36fdb8a2c6b7f6c3791a16a9bc0be"}, @@ -420,9 +497,9 @@ ply = "*" name = "keystone-engine" version = "0.9.2" description = "Keystone assembler engine" -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "keystone-engine-0.9.2.tar.gz", hash = "sha256:2f7af62dab0ce6c2732dbb4f31cfa2184a8a149e280b96b92ebc0db84c6e50f5"}, {file = "keystone_engine-0.9.2-py2.py3-none-macosx_10_14_x86_64.whl", hash = "sha256:dafcc3d9450c239cbc54148855b79c4b387777099c6d054005c835768cf955f2"}, @@ -436,9 +513,9 @@ files = [ name = "loguru" version = "0.7.2" description = "Python logging made (stupidly) simple" -category = "main" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, @@ -449,15 +526,15 @@ 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)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +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" version = "0.70.17" description = "better multiprocessing and multithreading in Python" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multiprocess-0.70.17-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ddb24e5bcdb64e90ec5543a1f05a39463068b6d3b804aa3f2a4e16ec28562d6"}, {file = "multiprocess-0.70.17-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d729f55198a3579f6879766a6d9b72b42d4b320c0dcb7844afb774d75b573c62"}, @@ -480,13 +557,27 @@ files = [ [package.dependencies] dill = ">=0.3.9" +[[package]] +name = "overrides" +version = "7.4.0" +description = "A decorator to automatically detect mismatch when overriding a method." +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "overrides-7.4.0-py3-none-any.whl", hash = "sha256:3ad24583f86d6d7a49049695efe9933e67ba62f0c7625d53c59fa832ce4b8b7d"}, + {file = "overrides-7.4.0.tar.gz", hash = "sha256:9502a3cca51f4fac40b5feca985b6703a5c1f6ad815588a7ca9e285b9dca6757"}, +] + [[package]] name = "overrides" version = "7.7.0" description = "A decorator to automatically detect mismatch when overriding a method." -category = "main" optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, @@ -496,21 +587,91 @@ files = [ name = "pefile" version = "2024.8.26" description = "Python PE parsing module" -category = "main" optional = false python-versions = ">=3.6.0" +groups = ["main"] files = [ {file = "pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f"}, {file = "pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632"}, ] +[[package]] +name = "pillow" +version = "10.0.1" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "Pillow-10.0.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a"}, + {file = "Pillow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd"}, + {file = "Pillow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"}, + {file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"}, + {file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7"}, + {file = "Pillow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf"}, + {file = "Pillow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"}, + {file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + [[package]] name = "pillow" version = "10.4.0" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -599,16 +760,16 @@ docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] +typing = ["typing-extensions ; python_version < \"3.10\""] xmp = ["defusedxml"] [[package]] name = "ply" version = "3.11" description = "Python Lex & Yacc" -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, @@ -618,9 +779,9 @@ files = [ name = "prompt-toolkit" version = "3.0.50" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, @@ -633,9 +794,10 @@ wcwidth = "*" name = "pycparser" version = "2.22" description = "C parser in Python" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -645,44 +807,104 @@ files = [ name = "pyelftools" version = "0.32" description = "Library for analyzing ELF files and DWARF debugging information" -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyelftools-0.32-py3-none-any.whl", hash = "sha256:013df952a006db5e138b1edf6d8a68ecc50630adbd0d83a2d41e7f846163d738"}, {file = "pyelftools-0.32.tar.gz", hash = "sha256:6de90ee7b8263e740c8715a925382d4099b354f29ac48ea40d840cf7aa14ace5"}, ] +[[package]] +name = "pyfiglet" +version = "0.8.post1" +description = "Pure-python FIGlet implementation" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "pyfiglet-0.8.post1-py2.py3-none-any.whl", hash = "sha256:d555bcea17fbeaf70eaefa48bb119352487e629c9b56f30f383e2c62dd67a01c"}, + {file = "pyfiglet-0.8.post1.tar.gz", hash = "sha256:c6c2321755d09267b438ec7b936825a4910fec696292139e664ca8670e103639"}, +] + [[package]] name = "pyfiglet" version = "1.0.2" description = "Pure-python FIGlet implementation" -category = "main" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "pyfiglet-1.0.2-py3-none-any.whl", hash = "sha256:889b351d79c99e50a3f619c8f8e6ffdb27fd8c939fc43ecbd7559bd57d5f93ea"}, {file = "pyfiglet-1.0.2.tar.gz", hash = "sha256:758788018ab8faaddc0984e1ea05ff330d3c64be663c513cc1f105f6a3066dab"}, ] +[[package]] +name = "pyperclip" +version = "1.8.2" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, +] + [[package]] name = "pyperclip" version = "1.9.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] +[[package]] +name = "python-fx" +version = "0.3.1" +description = "A python-native fx-alike terminal JSON viewer." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "python-fx-0.3.1.tar.gz", hash = "sha256:76044ba32195b8e0ce444aa714981cb1481f9df44c3381c5ed4b43a4f6812c73"}, + {file = "python_fx-0.3.1-py3-none-any.whl", hash = "sha256:e7cfbb8421831aaff5684dd1ae6ec855b92bcd089f9f76b06a3f2baa4670447a"}, +] + +[package.dependencies] +antlr4-python3-runtime = "4.8" +asciimatics = "1.14.0" +click = {version = "8.1.7", markers = "python_version >= \"3.7\""} +dacite = {version = "1.8.1", markers = "python_version >= \"3.6\""} +first = "2.0.2" +future = {version = "0.18.3", markers = "python_version >= \"2.6\" and python_version not in \"3.0, 3.1, 3.2, 3.3\""} +jsonpath-ng = "1.6.0" +loguru = {version = "0.7.2", markers = "python_version >= \"3.5\""} +overrides = {version = "7.4.0", markers = "python_version >= \"3.6\""} +pillow = {version = "10.0.1", markers = "python_version >= \"3.8\""} +ply = "3.11" +pyfiglet = {version = "0.8.post1", markers = "python_version >= \"3.9\""} +pyperclip = "1.8.2" +pyyaml = {version = "6.0.1", markers = "python_version >= \"3.6\""} +urwid = {version = "2.2.1", markers = "python_full_version >= \"3.7.0\""} +wcwidth = "0.2.6" +yamale = {version = "4.0.4", markers = "python_version >= \"3.6\""} + [[package]] name = "python-fx" version = "0.3.2" description = "A python-native fx-alike terminal JSON viewer." -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "python_fx-0.3.2-py3-none-any.whl", hash = "sha256:5498475b0f391b1649732328b58d188d9fc4b3f90f5bfb77d5c6e2ece2432c5f"}, {file = "python_fx-0.3.2.tar.gz", hash = "sha256:9646f58c716e2db6698bff3dfa55fa721b8b0cb741506287a87bc08055a96ceb"}, @@ -711,9 +933,9 @@ yamale = {version = "5.2.1", markers = "python_version >= \"3.8\""} name = "python-registry" version = "1.3.1" description = "Read access to Windows Registry files." -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "python-registry-1.3.1.tar.gz", hash = "sha256:99185f67d5601be3e7843e55902d5769aea1740869b0882f34ff1bd4b43b1eb2"}, {file = "python_registry-1.3.1-py2-none-any.whl", hash = "sha256:59d3b00c04bca0c4e1a12be0404da6ccf76b87537ee3a3ad2d8fc1bccf6f63ca"}, @@ -728,9 +950,10 @@ unicodecsv = "*" name = "pywin32" version = "308" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -752,13 +975,76 @@ files = [ {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "pyyaml" version = "6.0.2" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -819,9 +1105,9 @@ files = [ name = "questionary" version = "2.1.0" description = "Python library to build pretty command line user prompts ⭐️" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, @@ -834,9 +1120,10 @@ prompt_toolkit = ">=2.0,<4.0" name = "r2libr" version = "5.7.4" description = "Yet anohter radare2 python bindings." -category = "main" optional = true python-versions = ">=3.6" +groups = ["main"] +markers = "extra == \"re\"" files = [ {file = "r2libr-5.7.4-py3-none-macosx_10_15_universal2.whl", hash = "sha256:c752e57085fed9d34527d2ff6692068c2a162c3477c0539e9e61cd7eb5acdcfc"}, {file = "r2libr-5.7.4-py3-none-manylinux1_x86_64.whl", hash = "sha256:80df7902492de77e2cb770ecb7ffbe215908192962d95f0e43b42d3c3d464096"}, @@ -847,30 +1134,30 @@ files = [ name = "setuptools" version = "75.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.5.2) ; sys_platform != \"cygwin\""] +core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.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)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "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)", "jaraco.develop (>=7.21)", "mypy (>=1.12.0,<1.13.0)", "pytest-mypy"] +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.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.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.12.*)", "pytest-mypy"] [[package]] name = "termcolor" version = "2.4.0" description = "ANSI color formatting for output in terminal" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, @@ -883,9 +1170,10 @@ tests = ["pytest", "pytest-cov"] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -895,9 +1183,9 @@ files = [ name = "unicodecsv" version = "0.14.1" description = "Python2's stdlib csv module is nice, but it doesn't support unicode. This module is a drop-in replacement which *does*." -category = "main" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "unicodecsv-0.14.1.tar.gz", hash = "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"}, ] @@ -906,9 +1194,9 @@ files = [ name = "unicorn" version = "2.1.3" description = "Unicorn CPU emulator engine" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] files = [ {file = "unicorn-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cbf99c139a238ee6ccfaadea35e65a88461c0ae0dcf78058c8266ff90f8866c"}, {file = "unicorn-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e7b9396a7b76503b1d32c4b83d35e03e8b2ee81e80a2c7aee77dac7b71f25c"}, @@ -989,15 +1277,16 @@ files = [ importlib_resources = {version = "*", markers = "python_version < \"3.9\""} [package.extras] -test = ["capstone (==5.0.1)", "capstone (==6.0.0a2)"] +test = ["capstone (==5.0.1) ; python_version <= \"3.7\"", "capstone (==6.0.0a2) ; python_version > \"3.7\""] [[package]] name = "unicornafl" version = "2.1.0" description = "Unicornafl" -category = "main" optional = true python-versions = "*" +groups = ["main"] +markers = "platform_system != \"Windows\" and extra == \"fuzz\"" files = [ {file = "unicornafl-2.1.0-py3-none-macosx_11_7_x86_64.whl", hash = "sha256:8827c010376274730776b85f884cf9037d08ca6cd866b364bcfea8eee6db6090"}, {file = "unicornafl-2.1.0-py3-none-manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa3130b3c811291719874cfd7e728ce0c8147cc63c8853069a5195e528bafb43"}, @@ -1010,13 +1299,60 @@ files = [ [package.dependencies] unicorn = ">=2.0.1" +[[package]] +name = "urwid" +version = "2.2.1" +description = "A full-featured console (xterm et al.) user interface library" +optional = false +python-versions = ">=3.7.0" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "urwid-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7aa95e2f8941e323f0534a301f9d8d965d869110d326b3c9dff63e1c116772cd"}, + {file = "urwid-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ea22e5eabace2c66e2f52cc2494308c1a0091bcb89b3ceedf72ff91733f4dbb2"}, + {file = "urwid-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b88bdc8efe95b318201bd054ae69bed68bb9f506f127b21ae7234ffb7db3a4"}, + {file = "urwid-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba58f24fdff58975ef603ee803909d57faaaebd407fd50042652dcc9a8dd2f2"}, + {file = "urwid-2.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab2d5704cbd32f729a60d2b56d076e16652b3b97ebe6773c54a192cb9f49c169"}, + {file = "urwid-2.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc88edae9cb34644905e3d00e9fd2dc9a2c1eaeb2e311c1aec0d36a51d77b10"}, + {file = "urwid-2.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2fa3e83730a811466d272ab68340f9f9418ee7ca5f6de3548dd7a5661eafbbee"}, + {file = "urwid-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f87d7efbbf1d716bbcf025d453b3481aa0d9e1c91581aa8edc9ae7af64efa85"}, + {file = "urwid-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdafd72d6a539e16e6c179dd16609601643b85edf97b1543fc208e4fb7e6c249"}, + {file = "urwid-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e17eed4190220873531e2c11c885764d2e3bcabe9e35d5a578e84056a2c58199"}, + {file = "urwid-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7949a8d4384d170e4dfa41151e6264f6238b3ba2520649c25110bc6451978568"}, + {file = "urwid-2.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c96c57714fd0eac79a5e8a9d38d15d68ab9b6a96c2fe282ccd61cb707dd4be2"}, + {file = "urwid-2.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1bee2f966063de86a093908abb5bd56910e5d5630e021b351f240ab3b972207e"}, + {file = "urwid-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:57e28adf4457fb50b751838836bb94122e904ccad4429d42c4f318a3287a4802"}, + {file = "urwid-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad5ff26140b66ebb69957c51fd168a86f212adb578c83ed590ffd7e032da973f"}, + {file = "urwid-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:653b1fb9c52c4a32c326f701dd3ceb6edd1f30f32033c040fd5edc55d3d60cdb"}, + {file = "urwid-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4438be6b4b303d1012b8208cf5ff2ef71bdc19a7732771257ece36e2d1d16283"}, + {file = "urwid-2.2.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868c2cfd3fadd6cf42a4a2dbb2cf87a92d6c12dc5ed8b991ff96e66f0ba8c38"}, + {file = "urwid-2.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c3f00ca72de0736f3df4971a01d2278065628bef179fbd4fce37aacf93bbeb2c"}, + {file = "urwid-2.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5354319e3ac4e612a4a280421e21fa4243023df73e48ee701e4e944e769d87c"}, + {file = "urwid-2.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f3fdbd58a3ef1f19393a5f1d8b61adcfc89d0e235a2e05927cbecbf8012120c"}, + {file = "urwid-2.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55d13d1c6f15bd594a49ad165837edf34678c7c2362834f0d771990821e3bb8c"}, + {file = "urwid-2.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:93f081f1c53d7d307694ae20eb07ac731b4337d517dd6ee9dd91bae78bcb67bf"}, + {file = "urwid-2.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d42a9e4939d18c77f73183593a589c9b3b5d5fa3615d94a32e15cd97b00d3536"}, + {file = "urwid-2.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5eeb4e1760e9356471f8b50e3296c24881e242aae57f738a6f8534438848fb2e"}, + {file = "urwid-2.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d894543b4f3f3f2ce9837782e45cc3797df0a1697264a2939a2391076d07f641"}, + {file = "urwid-2.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3205f486e9fa4c6193aa5b9623abe05db864465acd02825305702849572c0828"}, + {file = "urwid-2.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccfbbc37565f24156e2bdb37504b010fedee8f4a70cfc353c0d9782354087484"}, + {file = "urwid-2.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4add23f02ef43497c13592e1804640b8b19fe781e8d62f445000c7acca60e2e2"}, + {file = "urwid-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:476f03705095fed744413d8256c7da998694b2f81e7e6e665e6244d1d3159d1e"}, + {file = "urwid-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85614e0436eb8c77bb21950f7d52bf93668b4ba8a11a2986bc111b48d28390f6"}, + {file = "urwid-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf09c272b46ec0f78b6eaf2a515ded30e952a0e77dbbb3535593d3f05354eb82"}, + {file = "urwid-2.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8de3025ee488d9f56db9e8842d6c7f1c290e01fd6749320d8c171fef5cbef35"}, + {file = "urwid-2.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2eea5fba3dab9f0977fcf17608370686da9d18a6077fd180a98eba72c59ff5d"}, + {file = "urwid-2.2.1.tar.gz", hash = "sha256:e33911ab18f2c73fddbe9bf216d021e74e20b2d5aa9be30403c58f55131bb8a1"}, +] + [[package]] name = "urwid" version = "2.6.15" description = "A full-featured console (xterm et al.) user interface library" -category = "main" optional = false python-versions = ">3.7" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "urwid-2.6.15-py3-none-any.whl", hash = "sha256:71b3171cabaa0092902f556768756bd2f2ebb24c0da287ee08f081d235340cb7"}, {file = "urwid-2.6.15.tar.gz", hash = "sha256:9ecc57330d88c8d9663ffd7092a681674c03ff794b6330ccfef479af7aa9671b"}, @@ -1027,7 +1363,7 @@ typing-extensions = "*" wcwidth = "*" [package.extras] -curses = ["windows-curses"] +curses = ["windows-curses ; sys_platform == \"win32\""] glib = ["PyGObject"] lcd = ["pyserial"] serial = ["pyserial"] @@ -1036,13 +1372,27 @@ trio = ["exceptiongroup", "trio (>=0.22.0)"] twisted = ["twisted"] zmq = ["zmq"] +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + [[package]] name = "wcwidth" version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -1052,24 +1402,26 @@ files = [ name = "win32-setctime" version = "1.2.0" description = "A small Python utility to set file creation time on Windows" -category = "main" optional = false python-versions = ">=3.5" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, ] [package.extras] -dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] +dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] [[package]] name = "windows-curses" version = "2.4.1" description = "Support for the standard curses module on Windows" -category = "main" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "windows_curses-2.4.1-cp310-cp310-win32.whl", hash = "sha256:53d711e07194d0d3ff7ceff29e0955b35479bc01465d46c3041de67b8141db2f"}, {file = "windows_curses-2.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:325439cd4f37897a1de8a9c068a5b4c432f9244bf9c855ee2fbeb3fa721a770c"}, @@ -1089,13 +1441,30 @@ files = [ {file = "windows_curses-2.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:4588213f7ef3b0c24c5cb9e309653d7a84c1792c707561e8b471d466ca79f2b8"}, ] +[[package]] +name = "yamale" +version = "4.0.4" +description = "A schema and validator for YAML." +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version < \"3.11\" or platform_python_implementation != \"CPython\"" +files = [ + {file = "yamale-4.0.4-py3-none-any.whl", hash = "sha256:04f914c0886bda03ac20f8468272cfd9374a634a062549490eff2beedeb30497"}, + {file = "yamale-4.0.4.tar.gz", hash = "sha256:e524caf71cbbbd15aa295e8bdda01688ac4b5edaf38dd60851ddff6baef383ba"}, +] + +[package.dependencies] +pyyaml = "*" + [[package]] name = "yamale" version = "5.2.1" description = "A schema and validator for YAML." -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\"" files = [ {file = "yamale-5.2.1-py3-none-any.whl", hash = "sha256:e44cd30cf3055ee4b34c1c71d6fe35490a127dcbd36f82f27859d105a9989922"}, {file = "yamale-5.2.1.tar.gz", hash = "sha256:19bbe713d588f07177bc519a46070c0793ed126ea37f425a76055b99703f835a"}, @@ -1108,29 +1477,30 @@ pyyaml = "*" name = "zipp" version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.8\"" files = [ {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [[package]] name = "zope-event" version = "5.0" description = "Very basic event publishing system" -category = "main" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, @@ -1147,9 +1517,9 @@ test = ["zope.testrunner"] name = "zope-interface" version = "7.2" description = "Interfaces for Python" -category = "main" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -1199,10 +1569,10 @@ test = ["coverage[toml]", "zope.event", "zope.testing"] testing = ["coverage[toml]", "zope.event", "zope.testing"] [extras] -fuzz = ["unicornafl", "fuzzercorn"] +fuzz = ["fuzzercorn", "unicornafl"] re = ["r2libr"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.8" -content-hash = "f0a6ca5220bfd011fc4c9e54e151f7752bcbd5b4cf3d80698cfc4f3a53665aa4" +content-hash = "9191e91c28fe22a05ac8185ae5fd4a1fc969cecad63b41d649981dfbc50e86ae" From cb875f84889ad0883796e634d2cf4c2831d5ad3a Mon Sep 17 00:00:00 2001 From: xwingsDate: Sun, 12 Apr 2026 16:48:53 +0800 Subject: [PATCH 3/4] more qiling style. update logging and exception --- qiling/exception.py | 3 ++ qiling/os/posix/kernel_proxy/__init__.py | 36 +++++++++++------------- qiling/os/posix/kernel_proxy/ipc.py | 6 ++-- qiling/os/posix/kernel_proxy/proxy.py | 7 +++-- tests/test_kernel_proxy.py | 5 ++-- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/qiling/exception.py b/qiling/exception.py index e6d460609..a544b254f 100644 --- a/qiling/exception.py +++ b/qiling/exception.py @@ -74,6 +74,9 @@ class QlMemoryMappedError(QlErrorBase): class QlGDTError(QlErrorBase): pass +class QlProxyConnectionError(QlErrorBase): + pass + class QlSyscallError(QlErrorBase): def __init__(self, errno, msg): super(QlSyscallError, self).__init__(msg) diff --git a/qiling/os/posix/kernel_proxy/__init__.py b/qiling/os/posix/kernel_proxy/__init__.py index b0fae205f..f7ac75eab 100644 --- a/qiling/os/posix/kernel_proxy/__init__.py +++ b/qiling/os/posix/kernel_proxy/__init__.py @@ -24,18 +24,16 @@ import sys import socket import subprocess -import logging -from typing import Optional, TYPE_CHECKING +from typing import Dict, Optional, TYPE_CHECKING from qiling.const import QL_INTERCEPT, QL_OS +from qiling.exception import QlErrorArch, QlErrorSyscallError, QlErrorSyscallNotFound from qiling.os.posix.kernel_proxy.ipc import ProxyClient from qiling.os.posix.kernel_proxy.proxy_fd import ql_proxy_fd if TYPE_CHECKING: from qiling import Qiling -log = logging.getLogger(__name__) - class KernelProxy: """Forward specific syscalls to a real Linux kernel via a helper process. @@ -46,13 +44,13 @@ class KernelProxy: def __init__(self, ql: Qiling): if sys.platform != 'linux': - raise RuntimeError("KernelProxy requires a Linux host") + raise QlErrorArch("KernelProxy requires a Linux host") self.ql = ql self._process: Optional[subprocess.Popen] = None self._client: Optional[ProxyClient] = None - self._forwarded: dict = {} # name -> syscall_nr - self._reverse_table: Optional[dict] = None # name -> nr (built on first use) + self._forwarded: Dict[str, int] = {} # name -> syscall_nr + self._reverse_table: Optional[Dict[str, int]] = None # name -> nr (built on first use) self._start_proxy() @@ -78,14 +76,13 @@ def _start_proxy(self): child_sock.close() self._client = ProxyClient(parent_sock) - log.info(f"kernel proxy started (pid={self._process.pid})") + self.ql.log.info(f"kernel proxy started (pid={self._process.pid})") - def _build_reverse_table(self) -> dict: + def _build_reverse_table(self) -> Dict[str, int]: """Build name -> syscall_nr mapping from the guest architecture's syscall table.""" if self._reverse_table is not None: return self._reverse_table - from qiling.os.linux.map_syscall import get_syscall_mapper from qiling.const import QL_ARCH # get the raw syscall table dict for this architecture @@ -102,7 +99,7 @@ def _build_reverse_table(self) -> dict: table_name = arch_tables.get(self.ql.arch.type) if table_name is None: - raise RuntimeError(f"KernelProxy: unsupported architecture {self.ql.arch.type}") + raise QlErrorArch(f"KernelProxy: unsupported architecture {self.ql.arch.type}") import qiling.os.linux.map_syscall as mod table = getattr(mod, table_name) @@ -115,7 +112,7 @@ def _resolve_syscall_nr(self, name: str) -> int: """Resolve a syscall name to its number for the guest architecture.""" table = self._build_reverse_table() if name not in table: - raise ValueError( + raise QlErrorSyscallNotFound( f"KernelProxy: syscall '{name}' not found in {self.ql.arch.type.name} syscall table" ) return table[name] @@ -135,8 +132,8 @@ def forward_syscall(self, name: str, returns_fd: bool = False): forwarder = self._make_forwarder(name, nr, returns_fd) self.ql.os.set_syscall(name, forwarder, QL_INTERCEPT.CALL) - log.info(f"forwarding syscall '{name}' (nr={nr}) to kernel proxy" - f"{' [returns FD]' if returns_fd else ''}") + self.ql.log.info(f"forwarding syscall '{name}' (nr={nr}) to kernel proxy" + f"{' [returns FD]' if returns_fd else ''}") def _make_forwarder(self, name: str, guest_nr: int, returns_fd: bool): """Create a CALL hook closure for one syscall.""" @@ -170,17 +167,16 @@ def _get_host_syscall_nr(self, name: str) -> int: self._host_table = self._load_host_syscall_table() if name not in self._host_table: - raise RuntimeError(f"KernelProxy: syscall '{name}' not available on host") + raise QlErrorSyscallNotFound(f"KernelProxy: syscall '{name}' not available on host") return self._host_table[name] - def _load_host_syscall_table(self) -> dict: + def _load_host_syscall_table(self) -> Dict[str, int]: """Load the host's syscall name->nr mapping. Uses the same Qiling tables, indexed by the host architecture. """ import platform - from qiling.const import QL_ARCH import qiling.os.linux.map_syscall as mod machine = platform.machine() @@ -195,7 +191,7 @@ def _load_host_syscall_table(self) -> dict: table_name = host_arch_map.get(machine) if table_name is None: - raise RuntimeError(f"KernelProxy: unsupported host architecture '{machine}'") + raise QlErrorArch(f"KernelProxy: unsupported host architecture '{machine}'") table = getattr(mod, table_name) return {name: nr for nr, name in table.items()} @@ -208,7 +204,7 @@ def _alloc_fd(ql, fd_obj) -> int: ql.os.fd[i] = fd_obj return i - raise OSError("kernel_proxy: FD table full") + raise QlErrorSyscallError("kernel_proxy: FD table full") def stop(self): """Stop the proxy process.""" @@ -226,7 +222,7 @@ def stop(self): except subprocess.TimeoutExpired: self._process.kill() self._process.wait() - log.info(f"kernel proxy stopped (pid={self._process.pid})") + self.ql.log.info(f"kernel proxy stopped (pid={self._process.pid})") self._process = None def __del__(self): diff --git a/qiling/os/posix/kernel_proxy/ipc.py b/qiling/os/posix/kernel_proxy/ipc.py index 8bf26eaa7..59202c190 100644 --- a/qiling/os/posix/kernel_proxy/ipc.py +++ b/qiling/os/posix/kernel_proxy/ipc.py @@ -17,6 +17,8 @@ import socket from enum import IntEnum +from qiling.exception import QlProxyConnectionError + class MsgType(IntEnum): SYSCALL = 1 @@ -64,7 +66,7 @@ def _recvall(sock: socket.socket, n: int) -> bytes: while len(buf) < n: chunk = sock.recv(n - len(buf)) if not chunk: - raise ConnectionError("kernel proxy connection closed") + raise QlProxyConnectionError("kernel proxy connection closed") buf.extend(chunk) return bytes(buf) @@ -162,7 +164,7 @@ def recv_request(self) -> tuple: return MsgType.FD_OP, (FdOp(op), proxy_fd, arg1, arg2, data) else: - raise ValueError(f"unknown message type: {msg_type}") + raise QlProxyConnectionError(f"unknown message type: {msg_type}") def send_syscall_response(self, retval: int, errno_val: int): payload = struct.pack(SYSCALL_RESP_FMT, retval, errno_val) diff --git a/qiling/os/posix/kernel_proxy/proxy.py b/qiling/os/posix/kernel_proxy/proxy.py index 41bceac0c..af4934bd6 100644 --- a/qiling/os/posix/kernel_proxy/proxy.py +++ b/qiling/os/posix/kernel_proxy/proxy.py @@ -13,6 +13,7 @@ python -m qiling.os.posix.kernel_proxy.proxy """ +import logging import os import sys import ctypes @@ -24,10 +25,12 @@ ProxyServer, MsgType, FdOp ) +log = logging.getLogger("qiling.os.posix.kernel_proxy.proxy") + # load libc for raw syscall() _libc_path = ctypes.util.find_library("c") if _libc_path is None: - print("kernel_proxy: cannot find libc", file=sys.stderr) + log.critical("kernel_proxy: cannot find libc") sys.exit(1) _libc = ctypes.CDLL(_libc_path, use_errno=True) @@ -86,7 +89,7 @@ def handle_fd_op(op: FdOp, proxy_fd: int, arg1: int, arg2: int, data: bytes) -> def main(): if len(sys.argv) != 2: - print(f"usage: {sys.argv[0]} ", file=sys.stderr) + log.error(f"usage: {sys.argv[0]} ") sys.exit(1) sock_fd = int(sys.argv[1]) diff --git a/tests/test_kernel_proxy.py b/tests/test_kernel_proxy.py index d6690ad7a..52495238c 100644 --- a/tests/test_kernel_proxy.py +++ b/tests/test_kernel_proxy.py @@ -283,13 +283,14 @@ def test_ipc_roundtrip(self): # ------------------------------------------------------------------------- def test_forward_invalid_syscall_name(self): - """forward_syscall with bogus name raises ValueError.""" + """forward_syscall with bogus name raises QlErrorSyscallNotFound.""" from qiling.os.posix.kernel_proxy import KernelProxy + from qiling.exception import QlErrorSyscallNotFound ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) proxy = KernelProxy(ql) - with self.assertRaises(ValueError): + with self.assertRaises(QlErrorSyscallNotFound): proxy.forward_syscall('nonexistent_syscall_xyz') proxy.stop() From 486369d37502f5f6161837270fa33b247bcca0c4 Mon Sep 17 00:00:00 2001 From: xwings Date: Mon, 27 Apr 2026 21:52:49 +0800 Subject: [PATCH 4/4] better New SYSCALL_EX IPC message, FD arg type and Forwarder closure --- .codex | 0 qiling/os/posix/kernel_proxy/__init__.py | 150 ++++++++++++++++---- qiling/os/posix/kernel_proxy/argtypes.py | 73 ++++++++++ qiling/os/posix/kernel_proxy/ipc.py | 113 +++++++++++++-- qiling/os/posix/kernel_proxy/proxy.py | 35 +++++ tests/test_kernel_proxy.py | 168 +++++++++++++++++++++++ 6 files changed, 506 insertions(+), 33 deletions(-) create mode 100644 .codex create mode 100644 qiling/os/posix/kernel_proxy/argtypes.py diff --git a/.codex b/.codex new file mode 100644 index 000000000..e69de29bb diff --git a/qiling/os/posix/kernel_proxy/__init__.py b/qiling/os/posix/kernel_proxy/__init__.py index f7ac75eab..538f90b06 100644 --- a/qiling/os/posix/kernel_proxy/__init__.py +++ b/qiling/os/posix/kernel_proxy/__init__.py @@ -8,13 +8,22 @@ Usage: from qiling import Qiling - from qiling.os.posix.kernel_proxy import KernelProxy + from qiling.os.posix.kernel_proxy import KernelProxy, FD, PtrIn, PtrOut ql = Qiling(argv=["/bin/myserver"], rootfs="rootfs/x8664_linux") proxy = KernelProxy(ql) - proxy.forward_syscall("epoll_create", returns_fd=True) - proxy.forward_syscall("epoll_ctl") - proxy.forward_syscall("epoll_wait") + + # integer-arg syscall returning a new FD + proxy.forward_syscall("epoll_create1", returns_fd=True) + + # epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) + proxy.forward_syscall("epoll_ctl", + arg_types=(FD, "int", FD, PtrIn(size=12))) + + # epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) + proxy.forward_syscall("epoll_wait", + arg_types=(FD, PtrOut(size=lambda a: a[2] * 12), "int", "int")) + ql.run() """ @@ -24,10 +33,14 @@ import sys import socket import subprocess -from typing import Dict, Optional, TYPE_CHECKING +import weakref +from typing import Dict, Optional, Sequence, Tuple, TYPE_CHECKING -from qiling.const import QL_INTERCEPT, QL_OS +from qiling.const import QL_INTERCEPT from qiling.exception import QlErrorArch, QlErrorSyscallError, QlErrorSyscallNotFound +from qiling.os.posix.kernel_proxy.argtypes import ( + INT, FD, PtrIn, PtrOut, PtrInOut, is_pointer, +) from qiling.os.posix.kernel_proxy.ipc import ProxyClient from qiling.os.posix.kernel_proxy.proxy_fd import ql_proxy_fd @@ -35,6 +48,9 @@ from qiling import Qiling +__all__ = ['KernelProxy', 'INT', 'FD', 'PtrIn', 'PtrOut', 'PtrInOut'] + + class KernelProxy: """Forward specific syscalls to a real Linux kernel via a helper process. @@ -117,40 +133,74 @@ def _resolve_syscall_nr(self, name: str) -> int: ) return table[name] - def forward_syscall(self, name: str, returns_fd: bool = False): + def forward_syscall(self, name: str, returns_fd: bool = False, + arg_types: Optional[Sequence] = None): """Register a CALL hook that forwards this syscall to the kernel proxy. Args: - name: syscall name (e.g. "epoll_create", "eventfd2") + name: syscall name (e.g. "epoll_create1", "eventfd2"). returns_fd: if True, wrap the return value in ql_proxy_fd and store - in the Qiling FD table. Use this for syscalls that return - file descriptors (epoll_create, eventfd, timerfd_create, etc.) + it in the Qiling FD table. Use this for syscalls that + return file descriptors (epoll_create1, eventfd2, etc.). + arg_types: optional per-arg descriptors. Each entry is one of: + INT (or "int") — pass through unchanged (default). + FD (or "fd") — guest FD; translated to the proxy FD. + PtrIn(size) — pointer; bytes copied from guest to proxy. + PtrOut(size) — pointer; bytes copied back from proxy to guest. + PtrInOut(size) — pointer; both directions. + If omitted, all arguments are treated as INT. """ nr = self._resolve_syscall_nr(name) self._forwarded[name] = nr - forwarder = self._make_forwarder(name, nr, returns_fd) + forwarder = self._make_forwarder(name, nr, returns_fd, arg_types) self.ql.os.set_syscall(name, forwarder, QL_INTERCEPT.CALL) - self.ql.log.info(f"forwarding syscall '{name}' (nr={nr}) to kernel proxy" - f"{' [returns FD]' if returns_fd else ''}") + kind = [] + if returns_fd: + kind.append('returns FD') + if arg_types: + kind.append(f'arg_types={tuple(type(a).__name__ if not isinstance(a, str) else a for a in arg_types)}') - def _make_forwarder(self, name: str, guest_nr: int, returns_fd: bool): - """Create a CALL hook closure for one syscall.""" + suffix = f" [{', '.join(kind)}]" if kind else '' + self.ql.log.info(f"forwarding syscall '{name}' (nr={nr}) to kernel proxy{suffix}") + + def _make_forwarder(self, name: str, guest_nr: int, returns_fd: bool, + arg_types: Optional[Sequence]): + """Create a CALL hook closure for one syscall. + + Captures only the data the closure needs (host syscall nr, client, weakref + to self) so the registered hook does not keep the KernelProxy alive. + """ + # resolve once at registration time so the hot path stays simple + host_nr = self._get_host_syscall_nr(name) client = self._client + weak_self = weakref.ref(self) - def _forwarder(ql, *args): - # use the HOST syscall number, not the guest number. - # for now, resolve from the host's syscall table at runtime. - host_nr = self._get_host_syscall_nr(name) + # normalize arg_types to a tuple, treating the string aliases as-is + spec = tuple(arg_types) if arg_types else () + has_pointers = any(is_pointer(s) for s in spec) - padded = args + (0,) * (6 - len(args)) - retval = client.syscall(host_nr, padded[:6]) + def _forwarder(ql, *args): + self_ref = weak_self() + if self_ref is None: + ql.log.error(f"kernel_proxy: {name}() called after proxy was destroyed") + return -1 + + translated = self_ref._translate_args(name, args, spec) + + if has_pointers: + in_bufs, out_specs, out_arg_indices = self_ref._collect_buffers( + ql, translated, spec + ) + retval, out_data = client.syscall_ex(host_nr, translated, in_bufs, out_specs) + self_ref._writeback_buffers(ql, args, out_arg_indices, out_data) + else: + retval = client.syscall(host_nr, translated) if returns_fd and retval >= 0: - # the proxy created a real FD. wrap it and store in Qiling's FD table. proxy_fd_obj = ql_proxy_fd(client, retval) - guest_fd = self._alloc_fd(ql, proxy_fd_obj) + guest_fd = self_ref._alloc_fd(ql, proxy_fd_obj) ql.log.debug(f"kernel_proxy: {name}() -> proxy_fd={retval}, guest_fd={guest_fd}") return guest_fd @@ -160,6 +210,60 @@ def _forwarder(ql, *args): _forwarder.__name__ = f'ql_syscall_{name}' return _forwarder + def _translate_args(self, name: str, args: Tuple[int, ...], + spec: Tuple) -> Tuple[int, ...]: + """Translate guest FD args to proxy FD numbers; pad to 6 args. + + Pointer args are left untouched here — _collect_buffers replaces them + with the proxy-side buffer addresses just before invocation. + """ + out = list(args) + [0] * (6 - len(args)) + + for idx, kind in enumerate(spec): + if kind == FD: + guest_fd = args[idx] + fd_obj = self.ql.os.fd[guest_fd] if 0 <= guest_fd < len(self.ql.os.fd) else None + + if not isinstance(fd_obj, ql_proxy_fd): + raise QlErrorSyscallError( + f"kernel_proxy: {name}() arg{idx} guest_fd={guest_fd} " + f"does not refer to a proxy-owned FD" + ) + + out[idx] = fd_obj._proxy_fd + + return tuple(out[:6]) + + def _collect_buffers(self, ql, args: Tuple[int, ...], spec: Tuple): + """Read PtrIn/PtrInOut buffers from guest memory; collect PtrOut sizes.""" + in_bufs = [] + out_specs = [] + out_arg_indices = [] + + for idx, kind in enumerate(spec): + if isinstance(kind, (PtrIn, PtrInOut)): + size = kind.resolve(args) + if size > 0: + data = bytes(ql.mem.read(args[idx], size)) + in_bufs.append((idx, data)) + + if isinstance(kind, (PtrOut, PtrInOut)): + size = kind.resolve(args) + if size > 0: + out_specs.append((idx, size)) + out_arg_indices.append(idx) + + return in_bufs, out_specs, out_arg_indices + + @staticmethod + def _writeback_buffers(ql, args: Tuple[int, ...], + out_arg_indices: Sequence[int], + out_data: Sequence[bytes]): + """Write PtrOut/PtrInOut response buffers back into guest memory.""" + for idx, data in zip(out_arg_indices, out_data): + if data: + ql.mem.write(args[idx], data) + def _get_host_syscall_nr(self, name: str) -> int: """Get the syscall number on the HOST architecture.""" # we are running on Linux — read from the host's syscall table diff --git a/qiling/os/posix/kernel_proxy/argtypes.py b/qiling/os/posix/kernel_proxy/argtypes.py new file mode 100644 index 000000000..e9cf9a3c8 --- /dev/null +++ b/qiling/os/posix/kernel_proxy/argtypes.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +""" +Argument type descriptors for forwarded syscalls. + +When forwarding a syscall whose arguments are not all plain integers, the user +declares each argument's role so the forwarder knows how to marshal it: + + INT — pass through unchanged (the default). + FD — guest file descriptor; translate to the proxy-side FD before + forwarding. If the FD does not refer to a ql_proxy_fd, an error + is raised. + PtrIn(s) — pointer to a buffer of `s` bytes. Read from guest memory and + copied to the proxy. + PtrOut(s) — pointer to a buffer of `s` bytes. Allocated on the proxy and + copied back to guest memory after the syscall. + PtrInOut — both directions. + +`s` may be an integer or a callable taking the raw arg tuple and returning the +buffer length (e.g. for syscalls where the size depends on another argument). +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Callable, Tuple, Union + + +INT = 'int' +FD = 'fd' + +SizeSpec = Union[int, Callable[[Tuple[int, ...]], int]] + + +def _resolve_size(size: SizeSpec, args: Tuple[int, ...]) -> int: + if callable(size): + return int(size(args)) + + return int(size) + + +@dataclass(frozen=True) +class PtrIn: + """Input pointer — buffer of `size` bytes is read from guest memory.""" + size: SizeSpec + + def resolve(self, args: Tuple[int, ...]) -> int: + return _resolve_size(self.size, args) + + +@dataclass(frozen=True) +class PtrOut: + """Output pointer — buffer of `size` bytes is written to guest memory.""" + size: SizeSpec + + def resolve(self, args: Tuple[int, ...]) -> int: + return _resolve_size(self.size, args) + + +@dataclass(frozen=True) +class PtrInOut: + """In/out pointer — buffer is read from guest, then written back.""" + size: SizeSpec + + def resolve(self, args: Tuple[int, ...]) -> int: + return _resolve_size(self.size, args) + + +def is_pointer(spec) -> bool: + return isinstance(spec, (PtrIn, PtrOut, PtrInOut)) diff --git a/qiling/os/posix/kernel_proxy/ipc.py b/qiling/os/posix/kernel_proxy/ipc.py index 59202c190..20320dd12 100644 --- a/qiling/os/posix/kernel_proxy/ipc.py +++ b/qiling/os/posix/kernel_proxy/ipc.py @@ -6,9 +6,10 @@ """ IPC protocol between Qiling and the kernel proxy process. -Two message types: - SYSCALL — forward a raw syscall (number + 6 integer args) - FD_OP — perform an operation on a proxy-side FD (read/write/close/dup/fcntl/ioctl) +Three message types: + SYSCALL — forward a raw syscall (number + 6 integer args, no buffers) + SYSCALL_EX — forward a syscall with input/output buffer marshaling + FD_OP — perform an operation on a proxy-side FD (read/write/close/dup/fcntl/ioctl) All messages are length-prefixed binary over a Unix socketpair. """ @@ -16,13 +17,15 @@ import struct import socket from enum import IntEnum +from typing import List, Sequence, Tuple from qiling.exception import QlProxyConnectionError class MsgType(IntEnum): - SYSCALL = 1 - FD_OP = 2 + SYSCALL = 1 + FD_OP = 2 + SYSCALL_EX = 3 class FdOp(IntEnum): @@ -35,14 +38,19 @@ class FdOp(IntEnum): # Wire format: -# Request header: [msg_type: u8][payload_len: u32] -# SYSCALL payload: [syscall_nr: u32][args: 6 x i64] -# FD_OP payload: [op: u8][proxy_fd: i32][arg1: i64][arg2: i64][data_len: u32][data: bytes] +# Request header: [msg_type: u8][payload_len: u32] +# SYSCALL payload: [syscall_nr: u32][args: 6 x i64] +# SYSCALL_EX payload: [syscall_nr: u32][args: 6 x i64] +# [num_in: u8] then num_in * [arg_idx: u8][len: u32][data: bytes] +# [num_out: u8] then num_out * [arg_idx: u8][len: u32] +# FD_OP payload: [op: u8][proxy_fd: i32][arg1: i64][arg2: i64][data_len: u32][data: bytes] # # Response header: [status: i8][payload_len: u32] # status 0 = success, -1 = error -# SYSCALL response payload: [return_value: i64][errno: i32] -# FD_OP response payload: [return_value: i64][errno: i32][data_len: u32][data: bytes] +# SYSCALL response payload: [return_value: i64][errno: i32] +# SYSCALL_EX response payload: [return_value: i64][errno: i32][num_out: u8] +# then num_out * [len: u32][data: bytes] +# FD_OP response payload: [return_value: i64][errno: i32][data_len: u32][data: bytes] HEADER_FMT = '!BI' # msg_type/status (u8) + payload_len (u32) HEADER_SIZE = struct.calcsize(HEADER_FMT) @@ -53,6 +61,9 @@ class FdOp(IntEnum): SYSCALL_RESP_FMT = '!qi' # return_value (i64) + errno (i32) SYSCALL_RESP_SIZE = struct.calcsize(SYSCALL_RESP_FMT) +SYSCALL_EX_RESP_HEAD_FMT = '!qiB' # return_value (i64) + errno (i32) + num_out (u8) +SYSCALL_EX_RESP_HEAD_SIZE = struct.calcsize(SYSCALL_EX_RESP_HEAD_FMT) + FD_OP_REQ_FMT = '!BiqqI' # op (u8) + proxy_fd (i32) + arg1 (i64) + arg2 (i64) + data_len (u32) FD_OP_REQ_SIZE = struct.calcsize(FD_OP_REQ_FMT) @@ -94,6 +105,54 @@ def syscall(self, nr: int, args: tuple) -> int: retval, errno_val = struct.unpack(SYSCALL_RESP_FMT, resp_payload) return retval + def syscall_ex(self, nr: int, args: Sequence[int], + in_bufs: Sequence[Tuple[int, bytes]], + out_specs: Sequence[Tuple[int, int]]) -> Tuple[int, List[bytes]]: + """Forward a syscall with buffer marshaling. + + Args: + nr: host syscall number. + args: 6 integer arg values; for buffer args these are placeholders — + the proxy replaces them with the buffer address before invoking. + in_bufs: list of (arg_idx, data) — buffers to copy in. + out_specs: list of (arg_idx, length) — buffers to copy out. + + Returns: + (retval, out_bufs) where out_bufs is a list aligned with out_specs. + """ + padded = tuple(args) + (0,) * (6 - len(args)) + payload = bytearray(struct.pack(SYSCALL_REQ_FMT, nr, *padded[:6])) + + payload.append(len(in_bufs)) + for arg_idx, data in in_bufs: + payload += struct.pack('!BI', arg_idx, len(data)) + payload += data + + payload.append(len(out_specs)) + for arg_idx, length in out_specs: + payload += struct.pack('!BI', arg_idx, length) + + header = struct.pack(HEADER_FMT, MsgType.SYSCALL_EX, len(payload)) + self._sock.sendall(header + bytes(payload)) + + resp_header = _recvall(self._sock, HEADER_SIZE) + _, resp_len = struct.unpack(HEADER_FMT, resp_header) + resp_payload = _recvall(self._sock, resp_len) + + retval, _errno, num_out = struct.unpack( + SYSCALL_EX_RESP_HEAD_FMT, resp_payload[:SYSCALL_EX_RESP_HEAD_SIZE] + ) + + out_bufs: List[bytes] = [] + offset = SYSCALL_EX_RESP_HEAD_SIZE + for _ in range(num_out): + (length,) = struct.unpack('!I', resp_payload[offset:offset + 4]) + offset += 4 + out_bufs.append(resp_payload[offset:offset + length]) + offset += length + + return retval, out_bufs + def _fd_op(self, op: FdOp, proxy_fd: int, arg1: int = 0, arg2: int = 0, data: bytes = b'') -> tuple: """Send an FD operation. Returns (return_value, data).""" payload = struct.pack(FD_OP_REQ_FMT, op, proxy_fd, arg1, arg2, len(data)) @@ -157,6 +216,31 @@ def recv_request(self) -> tuple: fields = struct.unpack(SYSCALL_REQ_FMT, payload) return MsgType.SYSCALL, fields # (nr, a0, a1, a2, a3, a4, a5) + elif msg_type == MsgType.SYSCALL_EX: + offset = SYSCALL_REQ_SIZE + fixed = struct.unpack(SYSCALL_REQ_FMT, payload[:offset]) + nr = fixed[0] + args = list(fixed[1:]) + + num_in = payload[offset] + offset += 1 + in_bufs: List[Tuple[int, bytes]] = [] + for _ in range(num_in): + arg_idx, length = struct.unpack('!BI', payload[offset:offset + 5]) + offset += 5 + in_bufs.append((arg_idx, payload[offset:offset + length])) + offset += length + + num_out = payload[offset] + offset += 1 + out_specs: List[Tuple[int, int]] = [] + for _ in range(num_out): + arg_idx, length = struct.unpack('!BI', payload[offset:offset + 5]) + offset += 5 + out_specs.append((arg_idx, length)) + + return MsgType.SYSCALL_EX, (nr, args, in_bufs, out_specs) + elif msg_type == MsgType.FD_OP: fixed = struct.unpack(FD_OP_REQ_FMT, payload[:FD_OP_REQ_SIZE]) op, proxy_fd, arg1, arg2, data_len = fixed @@ -171,6 +255,15 @@ def send_syscall_response(self, retval: int, errno_val: int): header = struct.pack(HEADER_FMT, 0, len(payload)) self._sock.sendall(header + payload) + def send_syscall_ex_response(self, retval: int, errno_val: int, out_bufs: Sequence[bytes]): + payload = bytearray(struct.pack(SYSCALL_EX_RESP_HEAD_FMT, retval, errno_val, len(out_bufs))) + for buf in out_bufs: + payload += struct.pack('!I', len(buf)) + payload += buf + + header = struct.pack(HEADER_FMT, 0, len(payload)) + self._sock.sendall(header + bytes(payload)) + def send_fd_op_response(self, retval: int, errno_val: int, data: bytes = b''): payload = struct.pack(FD_OP_RESP_FMT, retval, errno_val, len(data)) payload += data diff --git a/qiling/os/posix/kernel_proxy/proxy.py b/qiling/os/posix/kernel_proxy/proxy.py index af4934bd6..1e7256118 100644 --- a/qiling/os/posix/kernel_proxy/proxy.py +++ b/qiling/os/posix/kernel_proxy/proxy.py @@ -51,6 +51,36 @@ def raw_syscall(nr: int, a0: int, a1: int, a2: int, a3: int, a4: int, a5: int) - return result, 0 +def raw_syscall_ex(nr: int, args: list, in_bufs: list, out_specs: list) -> tuple: + """Execute a syscall with buffer marshaling. + + For each in_buf (arg_idx, data): allocate a ctypes buffer initialized with + data and place its address in args[arg_idx]. For each out_spec (arg_idx, length): + allocate a zeroed buffer and place its address in args[arg_idx]. After the + syscall, return the contents of each out buffer. + """ + keepalive = [] # keep ctypes buffers alive until after the syscall + out_buffers = [] # parallel to out_specs + + args = list(args) + + for arg_idx, data in in_bufs: + buf = ctypes.create_string_buffer(data, len(data)) + keepalive.append(buf) + args[arg_idx] = ctypes.addressof(buf) + + for arg_idx, length in out_specs: + buf = ctypes.create_string_buffer(length) + keepalive.append(buf) + out_buffers.append(buf) + args[arg_idx] = ctypes.addressof(buf) + + retval, err = raw_syscall(nr, *args) + + out_data = [bytes(buf.raw) for buf in out_buffers] + return retval, err, out_data + + def handle_fd_op(op: FdOp, proxy_fd: int, arg1: int, arg2: int, data: bytes) -> tuple: """Handle an FD operation on a proxy-side FD. Returns (retval, errno, data).""" try: @@ -107,6 +137,11 @@ def main(): retval, err = raw_syscall(nr, a0, a1, a2, a3, a4, a5) server.send_syscall_response(retval, err) + elif msg_type == MsgType.SYSCALL_EX: + nr, args, in_bufs, out_specs = fields + retval, err, out_bufs = raw_syscall_ex(nr, args, in_bufs, out_specs) + server.send_syscall_ex_response(retval, err, out_bufs) + elif msg_type == MsgType.FD_OP: op, proxy_fd, arg1, arg2, data = fields retval, err, resp_data = handle_fd_op(op, proxy_fd, arg1, arg2, data) diff --git a/tests/test_kernel_proxy.py b/tests/test_kernel_proxy.py index 52495238c..4f7aae4dd 100644 --- a/tests/test_kernel_proxy.py +++ b/tests/test_kernel_proxy.py @@ -352,5 +352,173 @@ def test_guest_table_reverse_lookup(self): del ql + # ------------------------------------------------------------------------- + # FD translation (#3) + # ------------------------------------------------------------------------- + + def test_fd_arg_translated_to_proxy_fd(self): + """When a forwarded syscall's arg is declared FD, the guest fd is replaced + with the underlying proxy fd before forwarding.""" + from qiling.os.posix.kernel_proxy import KernelProxy, FD + from qiling.os.posix.kernel_proxy.proxy_fd import ql_proxy_fd + + ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) + proxy = KernelProxy(ql) + proxy.forward_syscall('close', arg_types=(FD,)) + + # capture what gets sent over IPC + sent = [] + original_syscall = proxy._client.syscall + + def spy(nr, args): + sent.append((nr, tuple(args))) + return 0 # pretend the kernel accepted + + proxy._client.syscall = spy + + # plant a ql_proxy_fd at guest_fd=42 with a chosen proxy_fd value + guest_fd = 42 + fake_proxy_fd = 9999 + ql.os.fd[guest_fd] = ql_proxy_fd(proxy._client, fake_proxy_fd) + + close_hook = ql.os.posix_syscall_hooks[QL_INTERCEPT.CALL]['ql_syscall_close'] + close_hook(ql, guest_fd) + + self.assertEqual(len(sent), 1) + _nr, args = sent[0] + self.assertEqual(args[0], fake_proxy_fd, + f"expected proxy_fd={fake_proxy_fd} forwarded, got args={args}") + + proxy._client.syscall = original_syscall + ql.os.fd[guest_fd] = None + proxy.stop() + del ql + + def test_fd_arg_rejects_non_proxy_fd(self): + """Forwarding with FD arg type rejects non-proxy guest FDs.""" + from qiling.os.posix.kernel_proxy import KernelProxy, FD + from qiling.exception import QlErrorSyscallError + + ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) + proxy = KernelProxy(ql) + proxy.forward_syscall('close', arg_types=(FD,)) + + close_hook = ql.os.posix_syscall_hooks[QL_INTERCEPT.CALL]['ql_syscall_close'] + + # stdin (fd 0) is a regular ql_pipe, not a proxy fd + with self.assertRaises(QlErrorSyscallError): + close_hook(ql, 0) + + proxy.stop() + del ql + + # ------------------------------------------------------------------------- + # Pointer marshaling (#2) + # ------------------------------------------------------------------------- + + def test_ptr_out_writes_back_to_guest_memory(self): + """PtrOut buffer is written back into guest memory after the syscall. + + Uses pipe2(int pipefd[2], int flags) — pipefd is an output buffer of + 2 * sizeof(int) = 8 bytes containing the read/write FDs created by + the kernel. + """ + from qiling.os.posix.kernel_proxy import KernelProxy, PtrOut + + ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) + proxy = KernelProxy(ql) + proxy.forward_syscall('pipe2', arg_types=(PtrOut(size=8), 'int')) + + # pick a free guest address and map it + addr = 0x800000 + ql.mem.map(addr, 0x1000) + ql.mem.write(addr, b'\xff' * 8) # poison so we can detect the writeback + + hook = ql.os.posix_syscall_hooks[QL_INTERCEPT.CALL]['ql_syscall_pipe2'] + retval = hook(ql, addr, 0) + self.assertEqual(retval, 0) + + raw = bytes(ql.mem.read(addr, 8)) + rfd, wfd = struct.unpack(' =0) and distinct + self.assertGreaterEqual(rfd, 0) + self.assertGreaterEqual(wfd, 0) + self.assertNotEqual(rfd, wfd) + + # clean up the proxy-side FDs the kernel just gave us + import os as _os + _os.close(rfd) + _os.close(wfd) + + proxy.stop() + del ql + + def test_ptr_in_reads_guest_memory(self): + """PtrIn buffer is copied from guest memory and the proxy sees the data. + + Uses write(int fd, const void *buf, size_t count) on stderr (fd 2). + We can't easily inspect proxy's stderr, but a successful return value + equal to count proves the data was forwarded — write would otherwise + return -EFAULT for a bad pointer or short for less data. + """ + from qiling.os.posix.kernel_proxy import KernelProxy, PtrIn + + ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) + proxy = KernelProxy(ql) + proxy.forward_syscall('write', arg_types=('int', PtrIn(size=lambda a: a[2]), 'int')) + + addr = 0x800000 + payload = b'kernel_proxy ptr_in roundtrip\n' + ql.mem.map(addr, 0x1000) + ql.mem.write(addr, payload) + + # forward stderr (fd 2 in the proxy is our subprocess's stderr, + # which goes to the test runner — harmless) + hook = ql.os.posix_syscall_hooks[QL_INTERCEPT.CALL]['ql_syscall_write'] + retval = hook(ql, 2, addr, len(payload)) + self.assertEqual(retval, len(payload)) + + proxy.stop() + del ql + + def test_ptr_size_callable(self): + """PtrIn/PtrOut accept a size callable that depends on other args.""" + from qiling.os.posix.kernel_proxy import PtrIn, PtrOut + + ptr = PtrIn(size=lambda args: args[2] * 4) + self.assertEqual(ptr.resolve((0, 0, 5)), 20) + + ptr2 = PtrOut(size=12) + self.assertEqual(ptr2.resolve((0, 0, 0)), 12) + + # ------------------------------------------------------------------------- + # Reference cycle (#4) + # ------------------------------------------------------------------------- + + def test_no_reference_cycle_via_hook(self): + """The forwarder closure holds only a weakref to KernelProxy, so the + proxy can be garbage-collected once the user drops their reference, + even though the hook is still registered on ql.os.""" + import gc + import weakref + from qiling.os.posix.kernel_proxy import KernelProxy + + ql = Qiling([self.HELLO_BIN], self.ROOTFS, verbose=QL_VERBOSE.OFF) + proxy = KernelProxy(ql) + proxy.forward_syscall('getpid') + proxy.forward_syscall('eventfd2', returns_fd=True) + + wref = weakref.ref(proxy) + proxy.stop() # tear down the subprocess but leave the hooks registered + del proxy + gc.collect() + + self.assertIsNone(wref(), + "KernelProxy survived after stop()+del — closure must hold " + "a strong ref (cycle), defeating the weakref design") + + del ql + + if __name__ == "__main__": unittest.main()