qemux 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qemux-1.0.0/LICENSE +6 -0
- qemux-1.0.0/PKG-INFO +39 -0
- qemux-1.0.0/README.md +13 -0
- qemux-1.0.0/pyproject.toml +40 -0
- qemux-1.0.0/setup.cfg +4 -0
- qemux-1.0.0/src/qemux/__init__.py +0 -0
- qemux-1.0.0/src/qemux/__main__.py +4 -0
- qemux-1.0.0/src/qemux/scripts/__init__.py +0 -0
- qemux-1.0.0/src/qemux/scripts/qxpy.py +24 -0
- qemux-1.0.0/src/qemux/targets/__init__.py +20 -0
- qemux-1.0.0/src/qemux/targets/i386.py +35 -0
- qemux-1.0.0/src/qemux/utils/__init__.py +0 -0
- qemux-1.0.0/src/qemux/utils/callable.py +36 -0
- qemux-1.0.0/src/qemux/utils/pattern.py +40 -0
- qemux-1.0.0/src/qemux/utils/pmmap.py +7 -0
- qemux-1.0.0/src/qemux/utils/prompt.py +10 -0
- qemux-1.0.0/src/qemux/utils/range.py +39 -0
- qemux-1.0.0/src/qemux/utils/slice.py +20 -0
- qemux-1.0.0/src/qemux/utils/wait.py +19 -0
- qemux-1.0.0/src/qemux.egg-info/PKG-INFO +39 -0
- qemux-1.0.0/src/qemux.egg-info/SOURCES.txt +24 -0
- qemux-1.0.0/src/qemux.egg-info/dependency_links.txt +1 -0
- qemux-1.0.0/src/qemux.egg-info/entry_points.txt +2 -0
- qemux-1.0.0/src/qemux.egg-info/requires.txt +5 -0
- qemux-1.0.0/src/qemux.egg-info/top_level.txt +1 -0
- qemux-1.0.0/tests/test_cli.py +10 -0
qemux-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Copyright (c) 2026 Elseware. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software and its associated documentation are proprietary and
|
|
4
|
+
confidential. No part of this software may be copied, modified,
|
|
5
|
+
distributed, sublicensed, or used without prior written permission from
|
|
6
|
+
the copyright holder.
|
qemux-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qemux
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Dynamic binary instrumentation platform
|
|
5
|
+
Author-email: Elseware <contact@elseware.io>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: homepage, https://elseware.io/qemux/
|
|
8
|
+
Project-URL: documentation, https://qemux.elseware.io/
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: typing_extensions
|
|
21
|
+
Requires-Dist: psutil
|
|
22
|
+
Requires-Dist: lief
|
|
23
|
+
Requires-Dist: pypcode
|
|
24
|
+
Requires-Dist: claripy
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[](https://github.com/johneiser/qemux/actions/workflows/test.yml)
|
|
29
|
+
|
|
30
|
+
# Qemux
|
|
31
|
+
|
|
32
|
+
Qemux is a dynamic binary instrumentation platform, based on Qemu.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from qemux import QxPopen, QxHarness
|
|
36
|
+
with QxPopen(["qemu-x86_64", "/usr/bin/true"]) as qp:
|
|
37
|
+
qx = QxHarness(qp)
|
|
38
|
+
qx.step()
|
|
39
|
+
```
|
qemux-1.0.0/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
[](https://github.com/johneiser/qemux/actions/workflows/test.yml)
|
|
3
|
+
|
|
4
|
+
# Qemux
|
|
5
|
+
|
|
6
|
+
Qemux is a dynamic binary instrumentation platform, based on Qemu.
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from qemux import QxPopen, QxHarness
|
|
10
|
+
with QxPopen(["qemu-x86_64", "/usr/bin/true"]) as qp:
|
|
11
|
+
qx = QxHarness(qp)
|
|
12
|
+
qx.step()
|
|
13
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qemux"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Dynamic binary instrumentation platform"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "LicenseRef-Proprietary"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Elseware", email = "contact@elseware.io" }]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"typing_extensions",
|
|
16
|
+
"psutil",
|
|
17
|
+
"lief",
|
|
18
|
+
"pypcode",
|
|
19
|
+
"claripy",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
qxpy = "qemux.scripts.qxpy:main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
homepage = "https://elseware.io/qemux/"
|
|
37
|
+
documentation = "https://qemux.elseware.io/"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
qemux-1.0.0/setup.cfg
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""A minimal python harness around a qemu process.
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
qxpy -- qemu-x86_64 /usr/bin/true
|
|
5
|
+
```
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
from ..utils.prompt import prompt
|
|
9
|
+
|
|
10
|
+
def run(argv):
|
|
11
|
+
print(argv)
|
|
12
|
+
qx = None
|
|
13
|
+
prompt(qx=qx)
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
description="A minimal python harness around a qemu process.",
|
|
18
|
+
epilog="example: qxpy -- qemu-x86_64 /usr/bin/true")
|
|
19
|
+
parser.add_argument("argv", nargs="*", type=str, help="qemu process arguments (qemu-* ...)")
|
|
20
|
+
args = parser.parse_args()
|
|
21
|
+
run(args.argv)
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Knowledge of target cpu registers."""
|
|
2
|
+
|
|
3
|
+
import os, pkgutil
|
|
4
|
+
from ctypes import Structure
|
|
5
|
+
from typing import Type
|
|
6
|
+
|
|
7
|
+
class QxRegisters(Structure):
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def __class_getitem__(cls, arch: str) -> Type["QxRegisters"]:
|
|
11
|
+
|
|
12
|
+
# Find subclass matching target architecture
|
|
13
|
+
for c in cls.__subclasses__():
|
|
14
|
+
if c.__name__ == arch:
|
|
15
|
+
return c
|
|
16
|
+
|
|
17
|
+
raise NotImplementedError(cls, arch)
|
|
18
|
+
|
|
19
|
+
# Automatically import residing architectures
|
|
20
|
+
__all__ = [m.name for m in pkgutil.iter_modules([os.path.dirname(__file__)])]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""i386 processor specification.
|
|
2
|
+
https://github.com/qemu/qemu/blob/master/target/i386/cpu.h
|
|
3
|
+
"""
|
|
4
|
+
__all__ = ["i386", "x86_64"]
|
|
5
|
+
|
|
6
|
+
from . import QxRegisters
|
|
7
|
+
from ctypes import Union, c_uint32, c_uint64
|
|
8
|
+
|
|
9
|
+
class c_uint32_esp(Union):
|
|
10
|
+
_fields_ = [("esp", c_uint32), ("sp", c_uint32)]
|
|
11
|
+
|
|
12
|
+
class i386(QxRegisters):
|
|
13
|
+
_anonymous_ = ("esp",)
|
|
14
|
+
_fields_ = [
|
|
15
|
+
("eax", c_uint32), ("ecx", c_uint32),
|
|
16
|
+
("edx", c_uint32), ("ebx", c_uint32),
|
|
17
|
+
("esp", c_uint32_esp), ("ebp", c_uint32),
|
|
18
|
+
("esi", c_uint32), ("edi", c_uint32),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
class c_uint64_rsp(Union):
|
|
22
|
+
_fields_ = [("rsp", c_uint64), ("sp", c_uint64)]
|
|
23
|
+
|
|
24
|
+
class x86_64(QxRegisters):
|
|
25
|
+
_anonymous_ = ("rsp",)
|
|
26
|
+
_fields_ = [
|
|
27
|
+
("rax", c_uint64), ("rcx", c_uint64),
|
|
28
|
+
("rdx", c_uint64), ("rbx", c_uint64),
|
|
29
|
+
("rsp", c_uint64_rsp), ("rbp", c_uint64),
|
|
30
|
+
("rsi", c_uint64), ("rdi", c_uint64),
|
|
31
|
+
("r8", c_uint64), ("r9", c_uint64),
|
|
32
|
+
("r10", c_uint64), ("r11", c_uint64),
|
|
33
|
+
("r12", c_uint64), ("r13", c_uint64),
|
|
34
|
+
("r14", c_uint64), ("r15", c_uint64),
|
|
35
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
__all__ = ["CallableType", "to_callable"]
|
|
2
|
+
|
|
3
|
+
from functools import singledispatch, partial
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Union, Any
|
|
6
|
+
|
|
7
|
+
CallableType = Union[None, bool, str, Callable]
|
|
8
|
+
|
|
9
|
+
def none(*args: Any, **kwargs: Any) -> None:
|
|
10
|
+
return None
|
|
11
|
+
|
|
12
|
+
def true(*args: Any, **kwargs: Any) -> bool:
|
|
13
|
+
return True
|
|
14
|
+
|
|
15
|
+
def false(*args: Any, **kwargs: Any) -> bool:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
@singledispatch
|
|
19
|
+
def to_callable(x: CallableType) -> Callable:
|
|
20
|
+
raise TypeError(type(x))
|
|
21
|
+
|
|
22
|
+
@to_callable.register
|
|
23
|
+
def _(x: None) -> Callable:
|
|
24
|
+
return none
|
|
25
|
+
|
|
26
|
+
@to_callable.register
|
|
27
|
+
def _(x: bool) -> Callable:
|
|
28
|
+
return true if x else false
|
|
29
|
+
|
|
30
|
+
@to_callable.register
|
|
31
|
+
def _(x: str) -> Callable:
|
|
32
|
+
return partial(print, x)
|
|
33
|
+
|
|
34
|
+
@to_callable.register
|
|
35
|
+
def _(x: Callable) -> Callable:
|
|
36
|
+
return x
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
__all__ = ["PatternType", "to_pattern", "PatternDict"]
|
|
2
|
+
|
|
3
|
+
import re, fnmatch
|
|
4
|
+
from typing import Union, Any, TypeVar
|
|
5
|
+
from functools import singledispatch
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
|
|
8
|
+
PatternType = Union[str, re.Pattern]
|
|
9
|
+
|
|
10
|
+
@singledispatch
|
|
11
|
+
def to_pattern(x: PatternType) -> re.Pattern:
|
|
12
|
+
raise TypeError(type(x))
|
|
13
|
+
|
|
14
|
+
@to_pattern.register
|
|
15
|
+
def _(x: str) -> re.Pattern:
|
|
16
|
+
return re.compile(fnmatch.translate(x), re.IGNORECASE)
|
|
17
|
+
|
|
18
|
+
@to_pattern.register
|
|
19
|
+
def _(x: re.Pattern) -> re.Pattern:
|
|
20
|
+
return x
|
|
21
|
+
|
|
22
|
+
K = TypeVar("K", bound=str)
|
|
23
|
+
V = TypeVar("V")
|
|
24
|
+
|
|
25
|
+
class PatternDict(dict[K, V]):
|
|
26
|
+
|
|
27
|
+
def _find(self, item: PatternType) -> Iterator[tuple[K, V]]:
|
|
28
|
+
p = to_pattern(item)
|
|
29
|
+
for k, v in self.items():
|
|
30
|
+
if p.match(k):
|
|
31
|
+
yield k, v
|
|
32
|
+
|
|
33
|
+
def find(self, item: PatternType) -> list[tuple[K, V]]:
|
|
34
|
+
"""Find items by pattern.
|
|
35
|
+
|
|
36
|
+
:param str item: item string or pattern
|
|
37
|
+
:return: items
|
|
38
|
+
:rtype: list[tuple[k, v]]
|
|
39
|
+
"""
|
|
40
|
+
return list(self._find(item))
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
__all__ = ["prompt"]
|
|
2
|
+
|
|
3
|
+
import code, readline, rlcompleter
|
|
4
|
+
from pprint import pformat
|
|
5
|
+
|
|
6
|
+
def prompt(**kwargs):
|
|
7
|
+
"""Spawn an interactive python prompt with the given keyword arguments."""
|
|
8
|
+
readline.set_completer(rlcompleter.Completer(kwargs).complete)
|
|
9
|
+
readline.parse_and_bind("tab: complete")
|
|
10
|
+
code.interact(banner=pformat(kwargs), local=kwargs)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
__all__ = ["RangeType", "to_range"]
|
|
2
|
+
|
|
3
|
+
from functools import singledispatch
|
|
4
|
+
from typing import Union
|
|
5
|
+
from .pmmap import pmmap_ext
|
|
6
|
+
|
|
7
|
+
RangeType = Union[int, slice, range, pmmap_ext]
|
|
8
|
+
|
|
9
|
+
@singledispatch
|
|
10
|
+
def to_range(x: RangeType, size: int = 1) -> range:
|
|
11
|
+
raise TypeError(type(x))
|
|
12
|
+
|
|
13
|
+
@to_range.register
|
|
14
|
+
def _(x: int, size: int = 1) -> range:
|
|
15
|
+
return range(x, x + size)
|
|
16
|
+
|
|
17
|
+
@to_range.register
|
|
18
|
+
def _(x: slice, size: int = 1) -> range:
|
|
19
|
+
if x.start is None or x.stop is None:
|
|
20
|
+
raise ValueError(x)
|
|
21
|
+
step = 1 if x.step is None else x.step
|
|
22
|
+
return range(x.start, x.stop, step)
|
|
23
|
+
|
|
24
|
+
@to_range.register
|
|
25
|
+
def _(x: range, size: int = 1) -> range:
|
|
26
|
+
return x
|
|
27
|
+
|
|
28
|
+
# @to_range.register
|
|
29
|
+
# def _(x: tuple[int, int]) -> range:
|
|
30
|
+
# if len(x) != 2:
|
|
31
|
+
# raise ValueError(x)
|
|
32
|
+
# return range(*x)
|
|
33
|
+
|
|
34
|
+
@to_range.register
|
|
35
|
+
def _(x: pmmap_ext) -> range:
|
|
36
|
+
a, b = x.addr.split("-")
|
|
37
|
+
start = int(a, 16)
|
|
38
|
+
end = int(b, 16)
|
|
39
|
+
return range(start, end)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
__all__ = ["SliceType", "to_slice"]
|
|
2
|
+
|
|
3
|
+
from functools import singledispatch
|
|
4
|
+
from typing import Union, Any
|
|
5
|
+
|
|
6
|
+
SliceType = Union[int, slice]
|
|
7
|
+
|
|
8
|
+
@singledispatch
|
|
9
|
+
def to_slice(x: SliceType, size: int = 1, step: int = 1) -> slice:
|
|
10
|
+
raise TypeError(type(x))
|
|
11
|
+
|
|
12
|
+
@to_slice.register
|
|
13
|
+
def _(x: int, size: int = 1, step: int = 1) -> slice:
|
|
14
|
+
return slice(x, x + size, step)
|
|
15
|
+
|
|
16
|
+
@to_slice.register
|
|
17
|
+
def _(x: slice, size: int = 1, step: int = 1) -> slice:
|
|
18
|
+
if x.start is None or x.stop is None:
|
|
19
|
+
raise ValueError(x)
|
|
20
|
+
return x
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
__all__ = ["WaitReadable"]
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
class WaitReadable:
|
|
6
|
+
"""Convert a blocking file descriptor into an awaitable object."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, fd):
|
|
9
|
+
self.fd = fd
|
|
10
|
+
|
|
11
|
+
def __enter__(self):
|
|
12
|
+
event = asyncio.Event()
|
|
13
|
+
loop = asyncio.get_running_loop()
|
|
14
|
+
loop.add_reader(self.fd, event.set)
|
|
15
|
+
return asyncio.create_task(event.wait())
|
|
16
|
+
|
|
17
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
18
|
+
loop = asyncio.get_running_loop()
|
|
19
|
+
loop.remove_reader(self.fd)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qemux
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Dynamic binary instrumentation platform
|
|
5
|
+
Author-email: Elseware <contact@elseware.io>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: homepage, https://elseware.io/qemux/
|
|
8
|
+
Project-URL: documentation, https://qemux.elseware.io/
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: typing_extensions
|
|
21
|
+
Requires-Dist: psutil
|
|
22
|
+
Requires-Dist: lief
|
|
23
|
+
Requires-Dist: pypcode
|
|
24
|
+
Requires-Dist: claripy
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[](https://github.com/johneiser/qemux/actions/workflows/test.yml)
|
|
29
|
+
|
|
30
|
+
# Qemux
|
|
31
|
+
|
|
32
|
+
Qemux is a dynamic binary instrumentation platform, based on Qemu.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from qemux import QxPopen, QxHarness
|
|
36
|
+
with QxPopen(["qemu-x86_64", "/usr/bin/true"]) as qp:
|
|
37
|
+
qx = QxHarness(qp)
|
|
38
|
+
qx.step()
|
|
39
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/qemux/__init__.py
|
|
5
|
+
src/qemux/__main__.py
|
|
6
|
+
src/qemux.egg-info/PKG-INFO
|
|
7
|
+
src/qemux.egg-info/SOURCES.txt
|
|
8
|
+
src/qemux.egg-info/dependency_links.txt
|
|
9
|
+
src/qemux.egg-info/entry_points.txt
|
|
10
|
+
src/qemux.egg-info/requires.txt
|
|
11
|
+
src/qemux.egg-info/top_level.txt
|
|
12
|
+
src/qemux/scripts/__init__.py
|
|
13
|
+
src/qemux/scripts/qxpy.py
|
|
14
|
+
src/qemux/targets/__init__.py
|
|
15
|
+
src/qemux/targets/i386.py
|
|
16
|
+
src/qemux/utils/__init__.py
|
|
17
|
+
src/qemux/utils/callable.py
|
|
18
|
+
src/qemux/utils/pattern.py
|
|
19
|
+
src/qemux/utils/pmmap.py
|
|
20
|
+
src/qemux/utils/prompt.py
|
|
21
|
+
src/qemux/utils/range.py
|
|
22
|
+
src/qemux/utils/slice.py
|
|
23
|
+
src/qemux/utils/wait.py
|
|
24
|
+
tests/test_cli.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
qemux
|