Skip to content

HMKPM Kernel Protocol Specification

HMKPM (HuntMemory KernelPatch Module) is a kernel-space extension designed for the KernelPatch framework on ARM64 Android. It provides direct, high-speed virtual memory access into target processes by traversing the kernel MMU structures directly.


📡 1. Communication Channel

HMKPM intercepts the Linux SYS_GETRESUID syscall (Syscall ID 148 on aarch64):

long syscall(148, unsigned long magic, void *request_buffer, unsigned long buffer_len);

Commands are dispatched using specific 64-bit magic operation identifiers.

Magic Command Identifiers

Identifier Value (Hex) Purpose
HMKPM_MAGIC 0x0048_4D4B_504D Module Probe / Version & Feature Discovery
HMKPM_MAGIC_READ 0x0048_4D4B_504E Single Virtual Address Memory Read
HMKPM_MAGIC_WRITE 0x0048_4D4B_504F Single Virtual Address Memory Write
HMKPM_MAGIC_READ_BATCH 0x0048_4D4B_5050 Batch Memory Read
HMKPM_MAGIC_WRITE_BATCH 0x0048_4D4B_5051 Batch Memory Write
HMKPM_MAGIC_V2P_BATCH 0x0048_4D4B_5052 Batch Virtual-to-Physical & Page Flags Resolution
HMKPM_MAGIC_SCAN_KERNEL 0x0048_4D4B_5053 In-Kernel Direct MMU Vectorized Memory Scan

🏗️ 2. Memory Struct Layouts & Alignment

All structures crossing the userspace ↔ kernel boundary are strictly aligned to 8-byte boundaries using #[repr(C, align(8))] to avoid unaligned access penalties on ARM64.

1. Version & Feature Info (HmkpmVersionInfo) — 48 Bytes

#[repr(C, align(8))]
pub struct HmkpmVersionInfo {
    pub magic: u64,             // Offset 0x00: HMKPM magic identifier
    pub version_code: u32,      // Offset 0x08: Packed version code (e.g. 0x00020800)
    pub version_major: u16,     // Offset 0x0C: Major version
    pub version_minor: u16,     // Offset 0x0E: Minor version
    pub version_patch: u16,     // Offset 0x10: Patch version
    pub is_lockless: u8,        // Offset 0x12: Flag indicating lockless MMU page walk
    pub _pad: u8,               // Offset 0x13: 1-byte padding
    pub mmap_lock_offset: i32,  // Offset 0x14: mm_struct lock offset
    pub features: u64,          // Offset 0x18: Bitmask of supported features
    pub version_str: [u8; 16],  // Offset 0x20: Null-terminated version string ("2.8.0")
}

2. Single Request Header (HmkpmReq) — 24 Bytes

#[repr(C, align(8))]
pub struct HmkpmReq {
    pub pid: i32,    // Offset 0x00: Target process PID
    pub _pad: u32,   // Offset 0x04: 4-byte explicit padding
    pub addr: u64,   // Offset 0x08: 64-bit Target virtual address
    pub size: u64,   // Offset 0x10: Number of bytes to read/write
}

3. Batch Request Header (HmkpmBatchHdr) — 24 Bytes

#[repr(C, align(8))]
pub struct HmkpmBatchHdr {
    pub pid: i32,        // Offset 0x00: Target process PID
    pub _pad: u32,       // Offset 0x04: 4-byte explicit padding
    pub count: u64,      // Offset 0x08: Number of batch entries
    pub data_total: u64, // Offset 0x10: Total payload data length in bytes
}

4. Batch Entry Descriptor (HmkpmBatchEntry) — 16 Bytes

#[repr(C, align(8))]
pub struct HmkpmBatchEntry {
    pub addr: u64,   // Offset 0x00: 64-bit Target virtual address
    pub size: u64,   // Offset 0x08: Requested size / actual size returned
}

5. Virtual-to-Physical Batch Header & Entry (HmkpmV2pHdr & HmkpmV2pEntry)

#[repr(C, align(8))]
pub struct HmkpmV2pHdr {
    pub pid: i32,        // Offset 0x00: Target process PID
    pub _pad: u32,       // Offset 0x04: 4-byte explicit padding
    pub count: u64,      // Offset 0x08: Number of V2P entries to translate
}

#[repr(C, align(8))]
pub struct HmkpmV2pEntry {
    pub va: u64,         // Offset 0x00: Target virtual address
    pub pa: u64,         // Offset 0x08: Resolved physical address (output)
    pub flags: u32,      // Offset 0x10: Page flags (PAGE_FLAG_PRESENT, RO, DIRTY, BLOCK)
    pub page_size: u32,  // Offset 0x14: Underlying hardware page size (e.g. 4096)
}

📦 3. Batch Payload Buffer Memory Layout

For batch operations (HMKPM_MAGIC_READ_BATCH and HMKPM_MAGIC_WRITE_BATCH), requests and data are packed into a single contiguous buffer:

+-------------------------------------------------------------------------+
| HmkpmBatchHdr (24 bytes)                                                |
+-------------------------------------------------------------------------+
| HmkpmBatchEntry[0] (16 bytes)                                           |
| HmkpmBatchEntry[1] (16 bytes)                                           |
| ...                                                                     |
| HmkpmBatchEntry[count - 1] (16 bytes)                                   |
+-------------------------------------------------------------------------+
| Data Payload Area (contiguous byte streams for entry 0 .. count - 1)    |
+-------------------------------------------------------------------------+

Buffer Size Calculation

\[\text{Total Buffer Size} = \text{sizeof}(\text{HmkpmBatchHdr}) + (\text{count} \times \text{sizeof}(\text{HmkpmBatchEntry})) + \text{data\_total}\]

⚡ 4. Protocol Limits

Constraint Maximum Value Description
HMKPM_MAX_SINGLE_SIZE 64 MB (0x0400_0000) Maximum single read/write chunk
HMKPM_MAX_ENTRY_SIZE 64 MB (0x0400_0000) Maximum individual entry size in a batch
HMKPM_MAX_BATCH_ENTRIES 65,536 (0x10000) Maximum entries per batch call
HMKPM_MAX_BATCH_TOTAL_SIZE 128 MB (0x0800_0000) Maximum combined payload across a batch

🛡️ 5. MMU Resolution & Memory Safety

  1. Kernel Page Table Walking:
  2. The kernel resolves addresses by inspecting the target task's memory descriptor: task->mm->pgd.
  3. Bypasses userspace process_vm_readv / ptrace limitations and security hooks.

  4. Stack Allocation Optimization (Small I/O):

  5. For transfers \(\le 256\) bytes, kpm.rs allocates an 8-byte aligned stack buffer (AlignedStackBuf), avoiding heap allocator overhead for high-frequency freeze and read operations.

  6. Userspace Memory Locking (mlock):

  7. Buffers passed across the syscall interface during heavy batch scans are locked in RAM to prevent the Linux kernel page-reclaim subsystem from swapping out the receiving buffers mid-execution.