Skip to main content

Low-level physical memory access via Linpmem kernel driver

Project description

LinPyMem

Read physical memory from user-space using a kernel-mode driver (linpmem.ko) and virtual-to-physical address translation with CR3 walking.

License


🔍 What is LinPyMem?

LinPyMem is a Python wrapper around the linpmem kernel module, enabling:

  • Translation of virtual memory addresses to physical addresses
  • Reading raw physical memory (primitives, vectors, strings)
  • Inspecting memory mapped regions of another Linux process

⚠️ Requirements

  • Linux system with root privileges
  • linpmem.ko built from source
  • Module must be signed and enrolled via MOK if Secure Boot is enabled
  • Python 3.7+
  • Dependencies: psutil, ctypes, fcntl, struct, subprocess

🔧 Installation

pip install linpymem

🔐 Secure Boot Considerations

If Secure Boot is enabled:

Sign the linpmem.ko using your own key:

/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 MOK.key MOK.crt linpmem.ko

Enroll the public key:

sudo mokutil --import MOK.crt

Reboot and follow the on-screen instructions

✅ Usage with context manager (auto-loads and removes driver)

from linpymem import LinPyMem

some_offset = 0xcafebabe
with LinPyMem(ko_module_path="/path/to/linpmem.ko", process_name="firefox", vm_pathname="/usr/lib/x86_64-linux-gnu/libc.so.6") as reader:
    print(f"[+] pid: {reader.pid} vm_range: {hex(reader.process_vm_start_addr)}-{hex(reader.process_vm_end_addr)} size: {hex(reader.process_vm_size)} cr3: {hex(reader.cr3)}")
    regions = reader.pathname_vm_regions
    for base_addr, size in regions:
        data = reader.read_ptr(base_addr + some_offset)

⚙️ Manual Usage (driver is already loaded and should remain loaded)

from linpymem import LinPyMem, PhysAccessMode

some_offset = 0xcafebabe
reader = LinPyMem(process_name="firefox", vm_pathname="/usr/lib/x86_64-linux-gnu/libc.so.6")
print(f"[+] pid: {reader.pid} vm_range: {hex(reader.process_vm_start_addr)}-{hex(reader.process_vm_end_addr)} size: {hex(reader.process_vm_size)} cr3: {hex(reader.cr3)}")
for base_addr, size in regions:
    # using conveinience function
    data = reader.read_bytes(base_addr + some_offset, 0x1337)

    # or equivalently
    physical_address, pte_virt_addr = reader.virtual_to_physical(base_addr + some_offset, reader.cr3)
    data, num_bytes_read, num_expected_bytes = reader.read_physical_memory(physical_address, PhysAccessMode.PHYS_BUFFER_READ, 0x1337)

🧭 Available class properties and functions

reader = LinPyMem(...)
reader.pid
reader.process_vm_start_addr
reader.process_vm_end_addr
reader.process_vm_size
reader.pathname_vm_regions
reader.cr3

# optional driver setup/teardown helpers
def insert_kernel_module(self, module_path: str):
def get_driver_major_number(self, driver_name: str) -> int:
def create_device_node(self, major_number: int, device_path: str):
def setup_driver(self, module_path: str, device_path: str):
def remove_driver(self, device_path: str):

# process inspection helpers
def get_pid_by_process_name(self, process_name: str) -> int:
def get_process_virtual_memory_bounds(self, pid: int) -> tuple[int, int, int]:
def get_pathname_virtual_address_range(self, pid: int, pathname: str) -> list[tuple[int, int]]:

# linpmem kernel IOCTL calls
def read_physical_memory(self, phys_addr: int, mode: PhysAccessMode, readbuffer_size: int = 0) -> tuple:
def virtual_to_physical(self, virt_addr: int, cr3: int = 0) -> tuple[int, int]:
def get_cr3_for_process(self, pid: int) -> int:

# convenince functions (constructed with IOCTL primitaves above)
def read_bytes(self, addr: int, size: int) -> bytes:
def read_ptr(self, addr: int) -> int:
def read_short(self, addr: int) -> int:
def read_int(self, addr: int) -> int:
def read_float(self, addr: int) -> float:
def read_double(self, addr: int) -> float:
def read_vec3_float(self, addr: int) -> tuple[float, float, float]:
def read_vec3_double(self, addr: int) -> tuple[float, float, float]:
def read_utf_string(self, addr: int, max_len: int = 1024) -> str:
def view_memory_region(self, start_address: int, size: int, row_size: int = 16):

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

linpymem-0.3.0.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

linpymem-0.3.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file linpymem-0.3.0.tar.gz.

File metadata

  • Download URL: linpymem-0.3.0.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.10.12

File hashes

Hashes for linpymem-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7bf5b41992ca59b5d857328a1d30d6b05f2b89aad2f014d9931b68c51c208641
MD5 bbfa8ff514dc462115f6eb715e725ba4
BLAKE2b-256 e08ec18736166805c7227528d8b0f7235621bda046fc7d47db9bd46c9c4c7a50

See more details on using hashes here.

File details

Details for the file linpymem-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: linpymem-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.10.12

File hashes

Hashes for linpymem-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 155af1c82924806758e967a59cd2b5dab310474667f91623249ae078b7da3717
MD5 1e996672d64fa86ed920ab3530dd15f7
BLAKE2b-256 79ce3d99cc5c9f23d86af04bfd6911420bec515a10ec5745f13e3e5e6e59a0e7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page