Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ on:

jobs:
test:

runs-on: windows-latest
strategy:
fail-fast: false
Expand All @@ -27,13 +26,39 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Restore cached test binaries
id: restore-cache-test
uses: actions/cache/restore@v4
with:
path: tests/manual/test_inject/target
key: ${{ runner.os }}-${{ hashFiles('tests/manual/test_inject/src/**') }}

- name: Setup rust
if: steps.restore-cache-test.outputs.cache-hit != 'true'
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Build test binaries
if: steps.restore-cache-test.outputs.cache-hit != 'true'
run: cargo build --release --manifest-path tests/manual/test_inject/Cargo.toml

- name: Cache test binaries
if: steps.restore-cache-test.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: tests/manual/test_inject/target
key: ${{ runner.os }}-${{ hashFiles('tests/manual/test_inject/src/**') }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: uv sync --all-groups
run: uv sync

- name: Run tests
run: uv run pytest
155 changes: 155 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[workspace]
members = [
"tests/manual/test_inject"
]
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ This project uses uv as the build backend and package manager.
- isort . && black .

Optional: A Nix flake provides a dev shell with Python 3.11, just, black, isort, and more:

- nix develop

## Support

discord: https://discord.gg/wcftyYm6qe
discord: <https://discord.gg/wcftyYm6qe>
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
python
just
alejandra
rustc
cargo
python.pkgs.black
python.pkgs.isort
python.pkgs.vulture
Expand Down
15 changes: 15 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,18 @@ format:
isort .
black .
alejandra .

# build test dll
build-test-dll:
if (!(Test-Path "tests/manual/test_inject/target/release/test_inject.dll")) { cargo build --release --manifest-path tests/manual/test_inject/Cargo.toml }

# build test exe
build-test-exe:
if (!(Test-Path "tests/manual/test_inject/target/release/inject_target.dll")) { cargo build --release --manifest-path tests/manual/test_inject/Cargo.toml --bin inject_target }

# run manual tests
manual-test: build-test-dll build-test-exe
uv run pytest -rs --run-manual tests/manual/

# run all tests
all-tests: test manual-test
8 changes: 4 additions & 4 deletions memobj/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import logging

from .allocation import Allocation, Allocator
from .object import MemoryObject
from .process import Process, WindowsProcess
from .property import *
from .object import MemoryObject
from .allocation import Allocator, Allocation

import logging

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
Expand Down
46 changes: 43 additions & 3 deletions memobj/allocation.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from typing import TYPE_CHECKING, Self
from typing import TYPE_CHECKING, Any, Self

from memobj.utils import Type, get_type_size

if TYPE_CHECKING:
from memobj.process import Process


class Allocation:
def __init__(self, address: int, process: "Process"):
def __init__(self, address: int, process: "Process", size: int):
self.address = address
self.process = process
self.size = size

self._is_closed: bool = False

Expand All @@ -32,6 +34,22 @@ def free(self):
self.process.free_memory(self.address)
self._is_closed = True

def read_typed(self, read_type: Type) -> Any:
if (type_size := get_type_size(read_type)) != self.size:
raise ValueError(
f"{read_type} size ({type_size}) does not match allocation size ({self.size})"
)

return self.process.read_typed(self.address, read_type)

def write_typed(self, write_type: Type, value: Any) -> None:
if (type_size := get_type_size(write_type)) != self.size:
raise ValueError(
f"Write type ({type_size}) does not match allocation size ({self.size})"
)

return self.process.write_typed(self.address, write_type, value)


class Allocator:
"""
Expand Down Expand Up @@ -61,12 +79,34 @@ def closed(self) -> bool:
return self._is_closed

def allocate(self, size: int) -> Allocation:
"""
Allocates a block of memory for the process.

Allocates a specified block of memory for the associated process, keeping
track of the allocation for management purposes. The allocated memory
is represented as an `Allocation` object and is appended to the list of
current allocations.

Args:
size (int): The size of the memory block to allocate in bytes.

Returns:
Allocation: An object representing the allocated memory block.
"""
address = self.process.allocate_memory(size)
allocation = Allocation(address, self.process)
allocation = Allocation(address, self.process, size)
self.allocations.append(allocation)
return allocation

def close(self):
"""
Closes the allocator, ensuring all current allocations are properly freed and the allocator
is set to a closed state. This method prevents further use of the allocator by marking it
as closed. If the allocator is already closed, an error will be raised.

Raises:
ValueError: If the allocator is already closed before the invocation of this method.
"""
if self._is_closed:
raise ValueError("Cannot close an already closed allocator")

Expand Down
Loading