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 # only set when vm_pathname is provided in constuctor
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.1.0.tar.gz (12.1 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.1.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for linpymem-0.1.0.tar.gz
Algorithm Hash digest
SHA256 93d360da3d211fdf8016408859391ee70a6705bca4989826aa74fb99598f2e7e
MD5 ee73b615a40d518b60d05492f759f265
BLAKE2b-256 4ef4c49e7dcd6fd2673e83bb1771ba31db29f651c56352796cd70762390514ca

See more details on using hashes here.

File details

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

File metadata

  • Download URL: linpymem-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.6 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cbf529e916f52f8402c6641ebfbfa23d32463def33c11c86f4854ac70d5bfde5
MD5 54f957c4cdd61929f6b28bc043662f8d
BLAKE2b-256 79c791d1d5daf751822c5a6256f9a2e6d4349dc6a93b15c7616e7d4b5aae0a58

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