Post

9. Writing ARM Shellcode

9. Writing ARM Shellcode

Avoid NULL bytes in Shellcode

1. Use Registers to Zero Values (Instead of mov reg, 0)

1
2
3
eor r0, r0, r0   // ARM (32-bit)  
xor eax, eax     // x86  
sub r1, r1, r1   // ARM Thumb  

2. Use PC-Relative Addressing (Avoid Hardcoded Addresses)

Problem: Direct memory references (ldr r0, =0x00010000) introduce nulls.
Solution: Use adr (ARM) or lea (x86) to calculate addresses dynamically.

3. Increment/Decrement to Avoid 0 in Immediates

Problem: mov r7, #11 (syscall) may encode as 0B 70 A0 E3 (safe), but mov r7, #255 could introduce nulls.

Solutions:

  • Use shifts/adds:
    1
    2
    
    mov r7, #10  
    add r7, #1      // r7 = 11 (no nulls)  
    
  • 8-bit rotations (ARM):
    1
    
    mov r7, #0x1F00 >> 8  // Encoded as non-null  
    

4. String Termination Without Nulls

Problem: "/bin/sh\0" has an implicit null.

Solutions:

  • Overwrite at runtime:
    1
    2
    
    strb r2, [r0, #7]   ; Replace 'X' with null (r2=0)  
    .ascii "/bin/shX"    ; Non-null placeholder  
    
  • Stack-based strings (push in reverse):
    1
    2
    3
    4
    5
    6
    7
    
    mov r0, #'h'  
    push {r0}  
    mov r0, #'/sh'  
    push {r0}  
    mov r0, #'/bin'  
    push {r0}  
    mov r0, sp      // r0 points to "/bin/sh"  
    

5. Avoid svc #0 (Syscall Triggers)

Problem: svc #0 encodes as 00 00 00 EF (ARM)

Use svc #1 (ARM):

1
svc #1   // Encodes as `01 00 00 EF` (null-free)  

6. Thumb Mode (ARM) for Smaller, Null-Free Code

Problem: ARM mode instructions are 4 bytes (often nulls).
Solution: Switch to Thumb mode (2-byte instructions):

1
2
3
4
5
6
7
8
.section .text  
.global _start  
.ARM  
_start:  
    add r3, pc, #1   // Set LSB=1 for Thumb  
    bx r3            // Switch to Thumb  
.THUMB  
    // Null-free 16-bit code here  

8. XOR Obfuscation (For Payloads)

Problem: Raw payloads (e.g., "/bin/sh") may contain nulls.
Solution: XOR-encode and decode at runtime.

9. Avoid str/ldr with 0 Offsets

Problem: str r0, [r1, #0] may encode as null.
Solution: Use register offsets:

1
2
add r2, r1, #0   // r2 = r1 + 0 (no nulls)  
str r0, [r2]     // Store without null offset  

PIC (Position Independent Code)

“PIC is like writing a letter that works no matter which mailbox it’s dropped into.”

This post is licensed under CC BY 4.0 by the author.