> 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/reverse-engineering-bugs.md).

# Reverse Engineering & Bug Hunting — OSED Study Reference

> **EXP-301 / OSED** | Offensive Security Exploit Developer\
> Static Analysis · Dynamic Analysis · Vulnerability Discovery · Crash Triage

***

## Table of Contents

1. [Overview & Mindset](#overview--mindset)
2. [Tools Setup](#tools-setup)
3. [x86 Disassembly Reading](#x86-disassembly-reading)
4. [Static Analysis Fundamentals](#static-analysis-fundamentals)
5. [Dynamic Analysis Fundamentals](#dynamic-analysis-fundamentals)
6. [Identifying Vulnerability Classes](#identifying-vulnerability-classes)
7. [Stack Buffer Overflows](#stack-buffer-overflows)
8. [SEH (Structured Exception Handling) Overflows](#seh-structured-exception-handling-overflows)
9. [Heap Overflows & Use-After-Free](#heap-overflows--use-after-free)
10. [Format String Bugs](#format-string-bugs)
11. [Integer Overflows / Truncation](#integer-overflows--truncation)
12. [Off-by-One Bugs](#off-by-one-bugs)
13. [Type Confusion](#type-confusion)
14. [Fuzzing for Bug Discovery](#fuzzing-for-bug-discovery)
15. [Crash Triage & Root Cause Analysis](#crash-triage--root-cause-analysis)
16. [Reversing Custom Protocols](#reversing-custom-protocols)
17. [Reversing Parsers & File Format Handlers](#reversing-parsers--file-format-handlers)
18. [Patch Diffing](#patch-diffing)
19. [IDA Pro & Ghidra Quick Reference](#ida-pro--ghidra-quick-reference)
20. [WinDbg for RE & Triage](#windbg-for-re--triage)
21. [Python Tooling for RE](#python-tooling-for-re)
22. [Common OSED RE Patterns Cheatsheet](#common-osed-re-patterns-cheatsheet)
23. [Resources](#resources)

***

## Overview & Mindset

Reverse engineering in the OSED context is about **finding bugs in compiled Windows binaries** — no source code. The workflow is:

```
Target Binary
    │
    ├─── Static Analysis ──► Understand code flow, data structures,
    │    (IDA / Ghidra)       find interesting attack surface
    │
    ├─── Dynamic Analysis ──► Observe runtime behavior, memory state,
    │    (WinDbg / x64dbg)    register values, heap layout
    │
    ├─── Fuzzing ───────────► Trigger unexpected code paths with
    │    (custom / Spike)     malformed input
    │
    └─── Crash Triage ──────► Determine exploitability, root cause,
         (WinDbg + scripts)   offset to control flow
```

### The RE Loop

1. **Identify attack surface** — what input does the program accept?
2. **Trace input handling** — follow data from entry point into parsing code
3. **Find dangerous operations** — copy, format, arithmetic on attacker data
4. **Trigger the bug** — craft input that reaches the dangerous code
5. **Triage the crash** — determine what you control and how

***

## Tools Setup

### Core Tools

| Tool                  | Purpose                                | Download                  |
| --------------------- | -------------------------------------- | ------------------------- |
| **IDA Free / Pro**    | Industry-standard disassembler         | hex-rays.com              |
| **Ghidra**            | Free NSA disassembler/decompiler       | ghidra-sre.org            |
| **x64dbg / x32dbg**   | Modern Windows debugger                | x64dbg.com                |
| **WinDbg**            | Microsoft kernel+user debugger         | via Windows SDK           |
| **Immunity Debugger** | Old but stable, mona.py support        | immunityinc.com           |
| **mona.py**           | Exploit dev plugin for Immunity/WinDbg | github/corelan            |
| **PE-bear**           | PE file analysis                       | github/hasherezade        |
| **CFF Explorer**      | PE header viewer                       | ntcore.com                |
| **Process Monitor**   | File/registry/network tracing          | sysinternals              |
| **Process Hacker**    | Runtime memory/handle inspection       | processhacker.sf.io       |
| **Wireshark**         | Network protocol reversing             | wireshark.org             |
| **pwntools**          | Python exploit dev framework           | github/Gallopsled         |
| **Spike**             | Network fuzzer                         | kali: `apt install spike` |

### WinDbg Setup

```
; Install via Windows SDK or winget:
winget install Microsoft.WinDbg

; Symbols — set _NT_SYMBOL_PATH:
SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols

; In WinDbg:
.sympath SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols
.reload
```

### mona.py Setup (Immunity)

```
; Copy mona.py to Immunity's PyCommands folder:
C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands\

; Usage inside Immunity:
!mona help
!mona config -set workingfolder C:\mona\%p
```

***

## x86 Disassembly Reading

### Key Patterns to Recognize

#### Function Prologue / Epilogue

```asm
; Prologue — saves caller's EBP, sets up stack frame
push    ebp
mov     ebp, esp
sub     esp, 0x50       ; allocate 80 bytes for locals

; Epilogue — restores frame
mov     esp, ebp        ; or: leave
pop     ebp
ret     [N]             ; stdcall: ret 8 cleans 2 DWORD args
```

#### Local Variable Access

```asm
; [ebp - N]  = local variables (below frame)
; [ebp + N]  = function arguments (above frame)

mov     eax, [ebp-4]    ; local var 1
mov     ecx, [ebp+8]    ; 1st argument (after ret addr + saved EBP)
mov     edx, [ebp+0xC]  ; 2nd argument
```

#### Loops

```asm
; for (i=0; i<10; i++)
    xor     ecx, ecx
.loop:
    cmp     ecx, 0x0A
    jge     .done
    ; ... body ...
    inc     ecx
    jmp     .loop
.done:
```

```asm
; rep movsb — copy ECX bytes from ESI to EDI
cld
mov     ecx, count
mov     esi, src
mov     edi, dst
rep     movsb
```

#### Conditional Patterns

```asm
cmp     eax, 0          ; sets ZF if eax == 0
jz      .null_ptr       ; jump if zero

test    eax, eax        ; same effect as cmp eax,0 but 1 byte shorter
jnz     .not_null

; Signed vs unsigned:
jl / jle                ; signed less-than (uses SF, OF)
jb / jbe                ; unsigned below (uses CF)
```

#### String Operations

```asm
; strcpy equivalent:
.copy_loop:
    mov     al, [esi]
    mov     [edi], al
    inc     esi
    inc     edi
    test    al, al
    jnz     .copy_loop  ; until null byte

; strlen equivalent:
    mov     edi, str_ptr
    xor     al, al
    mov     ecx, 0xFFFFFFFF
    repne   scasb        ; scan until AL found
    not     ecx
    dec     ecx          ; ECX = length
```

#### Switch Statements

```asm
; Jump table pattern:
    cmp     eax, MAX_CASE
    ja      .default
    jmp     [eax*4 + jump_table]

jump_table:
    dd      case_0
    dd      case_1
    dd      case_2
```

***

## Static Analysis Fundamentals

### Approach — Top Down

1. **Find `main` / `WinMain`** — IDA names it, or look for `GetCommandLine`, `ExitProcess`
2. **Identify input entry points** — `recv`, `ReadFile`, `fread`, `scanf`, `gets`, `WSARecv`
3. **Follow data flow** — trace the buffer from input function into processing
4. **Look for dangerous functions** — see list below
5. **Check size calculations** — anywhere arithmetic is done on attacker-controlled lengths

### Dangerous Functions — Immediate Red Flags

| Function             | Risk                               | Why                    |
| -------------------- | ---------------------------------- | ---------------------- |
| `strcpy`             | Stack/heap BOF                     | No bounds check        |
| `strcat`             | Stack/heap BOF                     | No bounds check        |
| `sprintf`            | Stack BOF                          | Format + no bounds     |
| `gets`               | Stack BOF                          | No length limit        |
| `scanf("%s")`        | Stack BOF                          | No width specifier     |
| `memcpy`             | BOF if size attacker-controlled    | Size not validated     |
| `memmove`            | BOF                                | Same as memcpy         |
| `strncpy`            | Off-by-one                         | May not null-terminate |
| `snprintf`           | Format string if fmt is user input |                        |
| `printf(user_input)` | Format string                      | Direct user control    |
| `malloc(user_size)`  | Integer overflow → heap BOF        | Size unchecked         |
| `realloc`            | Use-after-free if ptr reused       |                        |

### IDA Workflow

```
1. File → Open → load PE (select x86 if asked)
2. Wait for auto-analysis
3. Functions window (Ctrl+F) → search for dangerous functions
4. Cross-references (X key on function name) → see all call sites
5. At each call site:
   - Trace where arguments come from
   - Is the source/size attacker-controlled?
6. Use Structures window to define custom structs
7. Rename variables (N key) to track data flow
8. Add comments (: key) at important points
```

### Ghidra Workflow

```
1. File → New Project → Import File
2. Run auto-analysis (accept defaults)
3. Symbol Tree → Functions → navigate
4. Decompiler window: view pseudo-C on right
5. Search → For Strings → find interesting strings
6. References → Show References to → trace callers
7. Right-click → Rename Variable for clarity
```

### Finding Attack Surface — Input Functions

```
IDA: Search → Text → type "recv" → find all recv calls
     View → Open Subviews → Imports → look for:
       ws2_32: recv, WSARecv, recvfrom
       kernel32: ReadFile, ReadConsole
       msvcrt: fread, fgets, scanf, gets
       user32: GetDlgItemText
```

### Data Flow Tracking — Manual Example

```c
// Decompiled (Ghidra/IDA pseudo-C):
int handle_request(SOCKET s) {
    char buf[512];                    // ← fixed-size stack buffer
    int n = recv(s, buf, 4096, 0);   // ← reads up to 4096 bytes!
    // ...                            // BUG: 4096 > 512 → stack BOF
    process_data(buf, n);
}
```

Key observation: **recv size (4096) > buffer size (512)** → classic stack BOF.

***

## Dynamic Analysis Fundamentals

### Attach / Launch in Debugger

```
; WinDbg — launch:
windbg.exe -g target.exe arg1 arg2

; WinDbg — attach to running process:
windbg.exe -p <PID>

; x64dbg — File → Open or Attach
```

### Essential WinDbg Commands

```
; Execution control
g               go (run)
p               step over (F10)
t               step into (F11)
gu              go up (step out of function)
q               quit

; Breakpoints
bp 0x401234     break at address
bp recv         break at symbol
bl              list breakpoints
bc 0            clear breakpoint 0
bpx             break on access (ba r4 addr)
ba r4 0x12345   hardware read breakpoint (4 bytes)
ba w1 0x12345   hardware write breakpoint (1 byte)

; Registers & Memory
r               show all registers
r eax           show EAX
r eax=0x41      set EAX
dd esp          dump stack (DWORDs)
dd esp L8       dump 8 DWORDs
db 0x12345      dump bytes
da 0x12345      dump ASCII string
du 0x12345      dump Unicode string
dq              dump QWORDs
dp              dump pointers

; Disassembly
u eip           unassemble at EIP
u eip L20       unassemble 20 instructions
ub eip          unassemble backward

; Search memory
s -a 0 L?80000000 "password"    ; search ASCII string in whole range
s -b 0 L?80000000 41 41 41      ; search byte pattern

; Stack
k               stack backtrace (call stack)
kv              with frame info
kb              with args

; Modules
lm              list loaded modules
lm m kernel32   info on kernel32
!dh -a kernel32 dump PE header

; Process info
!peb            dump PEB
!teb            dump TEB
!address esp    show memory attributes at ESP

; Heap
!heap           list heaps
!heap -s        heap summary
!heap -a 0x...  analyze specific heap block
```

### Setting Up Logging

```
; WinDbg — log to file:
.logopen C:\windbg.log
; ... reproduce crash ...
.logclose
```

### x64dbg Tips

```
F2      — toggle breakpoint
F7      — step into
F8      — step over
F9      — run
Ctrl+G  — go to address
Ctrl+F  — find pattern in disassembly
Space   — assemble at cursor (patch)
; Right-click → Follow in Dump
; Right-click → Set breakpoint → Hardware (for write/access BPs)
```

***

## Identifying Vulnerability Classes

### Decision Tree

```
Program crashes with your input?
    │
    ├─ EIP overwritten with your bytes?
    │       └─► Classic Stack Buffer Overflow
    │
    ├─ EIP = 0x00000000 or wild pointer?
    │       └─► NULL deref / bad pointer dereference
    │
    ├─ SEH chain overwritten?
    │       └─► SEH-based Stack Overflow
    │
    ├─ Crash in heap allocator (ntdll!RtlAllocateHeap)?
    │       └─► Heap Corruption (overflow or UAF)
    │
    ├─ Crash after format string input (%n, %x...)?
    │       └─► Format String Bug
    │
    ├─ Crash with very large integer / wrapped size?
    │       └─► Integer Overflow / Truncation
    │
    └─ Crash at off-by-one boundary?
            └─► Off-by-One Bug
```

***

## Stack Buffer Overflows

### Concept

A fixed-size stack buffer is written beyond its end, overwriting:

* Saved frame pointer (EBP)
* Return address (EIP)
* Potentially SEH records

```
High address
┌─────────────────┐
│   arg2          │ [ebp+0xC]
│   arg1          │ [ebp+0x8]
│   ret address   │ ← overwrite this to control EIP
│   saved EBP     │
│   local buf[N]  │ ← overflow starts here
│   ...           │
Low address
```

### RE Checklist for Stack BOF

* [ ] Find recv/read call — note the **max bytes read**
* [ ] Find the destination buffer — note its **declared size**
* [ ] Check: max\_read > buffer\_size?
* [ ] Is there a length check **before** the copy?
* [ ] Is the buffer on the stack (local variable) or heap (malloc)?
* [ ] What is the offset from buffer start to saved EIP?

### Calculating Offset

```python
# Send cyclic pattern
from pwn import cyclic
pattern = cyclic(1000)
# → crash → note EIP value in debugger
# → find offset:
from pwn import cyclic_find
offset = cyclic_find(0x61616166)  # EIP value
print(f"EIP offset: {offset}")
```

```
; mona.py:
!mona pattern_create 1000
!mona pattern_offset -q 61616166   ; value from EIP register
```

### Identifying the Overflow in IDA

```asm
; Pattern: fixed local buffer + copy without size check
sub     esp, 0x200          ; char buf[512]
lea     eax, [ebp-0x200]    ; &buf
push    eax                 ; dst for strcpy
push    [ebp+8]             ; src = user input (attacker controlled)
call    strcpy              ; ← BUG: no length check
```

### Verifying EIP Control

```python
# Confirm control:
payload = b"A" * offset + b"B" * 4 + b"C" * 100
# EIP = 0x42424242 (BBBB)
# After EIP: C's visible on stack
```

***

## SEH (Structured Exception Handling) Overflows

### SEH Chain Structure

On x86 Windows, SEH records live on the **stack** in a linked list:

```
FS:[0] → SEH record N  (nearest)
           ├─ +0x00: Next SEH record ptr
           └─ +0x04: Exception Handler ptr

           → SEH record N-1
           ...
           → EXCEPTION_REGISTRATION_RECORD { -1, default_handler }
```

### Why SEH Overflows Differ

A large overflow can overwrite the SEH record:

* `nSEH` (+0) → `\xeb\x06\x90\x90` (short jmp over handler + 2 NOPs)
* `SEH handler` (+4) → address of a `POP POP RET` gadget

When the exception fires:

1. Windows walks SEH chain
2. Calls your fake handler (POP POP RET)
3. ESP is pointing at the SEH record
4. POP POP RET → EIP = nSEH = your short JMP
5. JMP jumps into shellcode

### RE Checklist for SEH Overflow

* [ ] Crash occurs while handling SEH exception?
* [ ] `!exchain` in WinDbg shows overwritten SEH?
* [ ] `nSEH` contains your pattern bytes?
* [ ] Module lacks SafeSEH (`!mona modules` → SafeSEH: False)?

### WinDbg SEH Commands

```
; When crash happens, check SEH chain:
!exchain

; Example output:
; 0012f734: 41414141   ← overwritten handler (your A's)
; 0012f798: kernel32!_except_handler3+0

; Also:
dt nt!_EXCEPTION_REGISTRATION_RECORD @$exr
```

### Finding POP POP RET Gadgets

```
; mona.py:
!mona seh              ; finds POP POP RET in non-SafeSEH modules

; Manual in WinDbg:
s -b 0x10000000 L?10000000 5B 5B C3    ; POP EBX, POP EBX, RET
; Any two POPs + RET — registers don't matter
```

### SEH Exploit Structure

```python
nseh    = b"\xeb\x06\x90\x90"   # short JMP +6, 2 NOPs
seh     = p32(pop_pop_ret)       # address of PPR gadget

payload = b"A" * offset_to_nseh
payload += nseh
payload += seh
payload += b"\x90" * 16         # NOP sled after jump
payload += shellcode
```

***

## Heap Overflows & Use-After-Free

### Heap Basics (Windows)

* `HeapAlloc` / `malloc` → allocate chunk
* `HeapFree` / `free` → return chunk
* Each chunk has a **header** with size/flags
* Overflow: write past end of chunk → corrupt next chunk's header or data
* UAF: free a chunk, keep a pointer, use it again

### RE: Spotting Heap Overflow

```c
// Pseudo-decompiled:
buf = HeapAlloc(heap, 0, user_size);    // allocate user_size bytes
memcpy(buf, user_data, actual_size);    // copy actual_size bytes
// BUG: if actual_size > user_size → heap overflow
```

```asm
; In IDA — look for:
; HeapAlloc with one size, then memcpy/strcpy with different (larger) size
push    [controlled_len]
push    [buf]
push    [src]
call    memcpy          ; len comes from attacker, not from alloc size
```

### RE: Spotting Use-After-Free

```c
// Pattern:
obj = malloc(sizeof(OBJECT));
// ... later:
free(obj);             // freed!
// ... still later:
obj->vtable->method(); // ← UAF: obj pointer still used
```

In IDA/Ghidra: look for `free()` followed by later dereferences of the same pointer variable, often through a virtual call (vtable dispatch).

### Heap Analysis in WinDbg

```
!heap -s                    ; list all heaps and sizes
!heap -a 0x00390000         ; analyze heap (start address)
!heap -flt s 0x100          ; find all blocks of size 0x100

; After UAF crash:
; Look for: access violation reading/writing freed memory
; Use gflags to enable full heap validation:
gflags.exe /p /enable target.exe /full
```

### Heap Spray Concept (for UAF exploitation)

Allocate many same-size objects to land controlled data at a predictable address:

```python
# Spray the heap with 0x1000-byte chunks containing fake vtables
spray_chunk = b"\x41\x41\x41\x41" * (0x1000 // 4)
spray = spray_chunk * 0x1000   # 4096 chunks × 4096 bytes
```

***

## Format String Bugs

### The Bug

When user input is passed **directly** as the format string to `printf`/`sprintf`:

```c
// VULNERABLE:
printf(user_input);         // user controls format string

// SAFE:
printf("%s", user_input);   // user input is just data
```

### What an Attacker Can Do

| Format Specifier | Effect                                                    |
| ---------------- | --------------------------------------------------------- |
| `%x`             | Read 4 bytes from stack (hex)                             |
| `%s`             | Read memory at address on stack                           |
| `%n`             | **Write** the number of printed chars to address on stack |
| `%100x`          | Advance stack read by printing 100 chars                  |
| `%7$x`           | Read 7th argument directly (direct parameter access)      |

### RE: Identifying Format String Bugs

```asm
; Look for:
push    [user_controlled_ptr]   ; this becomes the format arg
call    printf                  ; or sprintf, fprintf, snprintf

; Safe pattern would be:
push    [user_data]
push    offset fmt_str          ; "%s" — hardcoded format
call    printf
```

In IDA: find all `printf` / `sprintf` calls → check if the first pushed argument is a hardcoded format string or user data.

### Testing for Format String

```python
# Send format specifiers as input:
payload = b"%x.%x.%x.%x.%x.%x.%x.%x"
# If server echoes something like: f7abc123.0.41414141.0...
# → confirmed format string bug

# Find your offset (where your input appears on stack):
payload = b"AAAA.%x.%x.%x.%x.%x.%x.%x"
# When output shows "41414141" → that position is your offset

# Read arbitrary memory:
payload = struct.pack("<I", target_addr) + b"%6$s"
# (if offset is 6)
```

***

## Integer Overflows / Truncation

### Types

| Type                  | Description                        | Example                                                       |
| --------------------- | ---------------------------------- | ------------------------------------------------------------- |
| **Integer overflow**  | Signed value wraps past MAX\_INT   | `0x7FFFFFFF + 1 = -2147483648`                                |
| **Integer underflow** | Wraps below 0                      | `0 - 1 = 0xFFFFFFFF`                                          |
| **Truncation**        | Wide value assigned to narrow type | `int n = recv(...); unsigned short s = n; // if n=65537, s=1` |
| **Sign confusion**    | Signed vs unsigned comparison      | `-1 < 10` is true but `(unsigned)-1 > 10`                     |

### RE: Finding Integer Issues

```c
// Decompiled pattern 1 — truncation:
int  recvd  = recv(s, buf, 0x10000, 0);  // int: up to 65536
short count = (short)recvd;               // truncated to 16-bit!
char *out   = malloc(count);              // if recvd=65537, count=1
memcpy(out, buf, recvd);                  // copy 65537 into 1-byte alloc!

// Decompiled pattern 2 — size arithmetic overflow:
size_t total = header->count * sizeof(ENTRY);  // if count is large, wraps!
buf = malloc(total);                            // tiny alloc
// then loop copies count*sizeof(ENTRY) bytes → heap overflow

// Decompiled pattern 3 — sign confusion:
int len = get_length_from_input();     // attacker controls → can be negative
if (len > MAX_BUF) return ERR;        // -1 passes this check (signed)!
memcpy(dst, src, len);                 // but memcpy(len as unsigned) = HUGE!
```

### In IDA — Spotting Dangerous Casts

Look for:

* `MOVSX` — sign-extend (small → large, preserving sign)
* `MOVZX` — zero-extend (small → large, unsigned)
* `MOV AL, [large_reg]` — truncating to 8-bit
* `MOV AX, [large_reg]` — truncating to 16-bit
* Implicit truncation when `mov [word ptr]` is used

```asm
; Truncation example in assembly:
mov     eax, [ebp+recvd]     ; EAX = full 32-bit value
mov     [ebp-2], ax          ; ← TRUNCATION: only low 16 bits stored
movsx   ecx, [ebp-2]         ; sign-extend the truncated value
```

***

## Off-by-One Bugs

### The Bug

A loop or copy runs **one iteration too many** (or size check is `<` vs `<=`):

```c
char buf[256];
for (int i = 0; i <= 256; i++) {  // <= should be <
    buf[i] = input[i];             // writes buf[256] → 1 byte past end!
}
```

### What Gets Overwritten

With a single byte past the end:

* If it overwrites the **low byte of saved EBP** → influences frame pointer → may redirect execution
* If it overwrites a **heap chunk header** → potentially exploitable heap corruption

### RE: Spotting Off-by-One in IDA

```asm
; Count carefully:
mov     ecx, size          ; ECX = N
add     ecx, 1             ; ← suspicious: N+1 iterations?
lea     edi, [ebp-N]       ; buffer of N bytes
rep     movsb              ; copies N+1 bytes!

; Or in loop:
.loop:
    ; ...
    inc     eax
    cmp     eax, size
    jle     .loop           ; jle = ≤ : runs size+1 times!
                            ; should be jl (strictly less)
```

***

## Type Confusion

### Concept

An object of type A is treated as type B — often due to incorrect casting or misuse of a union. Common in C++ code with virtual dispatch.

```c
// Example:
BASE *obj = factory(user_type);   // user controls which type is created
obj->vtable->method();             // if wrong type, vtable ptr is wrong
```

### RE: Finding Type Confusion

1. Identify factory/allocation functions that branch on user-controlled type ID
2. Trace what type is returned vs what type the caller assumes
3. In IDA: look for `cmp [type_field], expected_type` guards that can be bypassed

***

## Fuzzing for Bug Discovery

### Network Fuzzing with Spike

```
# Define a Spike template (.spk file):
# Example for HTTP-like protocol:
s_readline();            # read server banner
s_string("GET ");
s_string_variable("A"); # fuzz this field
s_string(" HTTP/1.0\r\n\r\n");

# Run:
generic_send_tcp TARGET_IP PORT request.spk 0 0
```

### Python Fuzzer Skeleton

```python
#!/usr/bin/env python3
"""Simple length-based network fuzzer."""

import socket
import time
import sys

TARGET = "192.168.1.100"
PORT   = 9999

# Build payloads of increasing size
for size in range(100, 10000, 100):
    payload = b"FUZZ " + b"A" * size + b"\r\n"
    try:
        s = socket.socket()
        s.settimeout(3)
        s.connect((TARGET, PORT))
        banner = s.recv(1024)
        s.send(payload)
        resp = s.recv(1024)
        s.close()
        print(f"[*] Sent {size} bytes — OK")
        time.sleep(0.05)
    except Exception as e:
        print(f"[!] CRASH at {size} bytes: {e}")
        sys.exit(0)
```

### Mutation Fuzzer

```python
import os, socket, time

def mutate(seed: bytes) -> bytes:
    """Simple bit-flip mutation."""
    import random
    data = bytearray(seed)
    for _ in range(random.randint(1, 10)):
        idx = random.randrange(len(data))
        data[idx] ^= random.randint(1, 255)
    return bytes(data)

with open("sample_request.bin", "rb") as f:
    seed = f.read()

for i in range(10000):
    candidate = mutate(seed)
    try:
        s = socket.socket()
        s.settimeout(2)
        s.connect(("TARGET", PORT))
        s.sendall(candidate)
        s.close()
    except:
        print(f"[!] Possible crash on iteration {i}")
        with open(f"crash_{i}.bin", "wb") as f:
            f.write(candidate)
        time.sleep(1)
```

### WinDbg Automated Crash Logging

```
; Run target under WinDbg with crash logging:
windbg -g -G -c ".logopen crash.log; g; .logclose; q" target.exe

; Or use a script:
; After .exr -1 on crash:
.exr -1              ; exception record
.cxr                 ; context record
kb                   ; call stack
r                    ; registers
dd esp L10           ; stack dump
```

***

## Crash Triage & Root Cause Analysis

### Step-by-Step Triage

```
1. Reproduce the crash consistently
2. Note the crash address and exception type
3. Check register values — which registers contain your input?
4. Determine offset to EIP (or nSEH)
5. Identify what lies after EIP on the stack
6. Check memory attributes at EIP
7. Determine exploitability
```

### WinDbg Crash Analysis Commands

```
; When debugger breaks on crash:
.exr -1              ; show exception info
.ecxr               ; switch to exception context
r                    ; registers at crash
u eip               ; what was about to execute
kb                   ; call stack
dd esp              ; stack contents
!analyze -v         ; automated crash analysis

; Check memory permissions at various addresses:
!address eip        ; is EIP in executable memory?
!address esp        ; stack info
!address eax        ; is EAX a valid pointer?
```

### Exploitability Assessment

| Scenario                 | Exploitability                         |
| ------------------------ | -------------------------------------- |
| EIP = 0x41414141         | Highly likely — full control           |
| EIP = 0x00414141         | Partial control (null byte constraint) |
| SEH overwritten          | Likely with POP POP RET                |
| Access violation writing | Depends on what's being written        |
| Access violation reading | Harder, may be info leak               |
| Stack exhaustion         | Likely DoS only                        |
| Heap corruption          | Possible with heap feng shui           |

### !analyze -v Output — Key Fields

```
EXCEPTION_CODE: c0000005 (Access Violation)
EXCEPTION_ADDRESS: 41414141      ← EIP you control
WRITE_ADDRESS: 41414141          ← or crash on write
STACK_COMMAND: kb                ← run this
FOLLOWUP_NAME: ...

; Check:
FAULTING_IP — where the crash happened
CONTEXT — all registers
EXCEPTION_RECORD — exception parameters
```

### Determining Offsets Without Pattern

```python
# Binary search approach — bisect to find exact offset:
# Send: "A"*N + "B"*4 + "C"*remaining
# If EIP = 0x42424242 → offset is exactly N
# If EIP = 0x41414141 → need N+1 or more
```

***

## Reversing Custom Protocols

### Step 1 — Capture Traffic

```
; Use Wireshark or:
netsh trace start capture=yes tracefile=C:\cap.etl

; Or use Python socket proxy to log:
import socket
def proxy(client, server):
    while True:
        data = client.recv(4096)
        if not data: break
        print("[C→S]", data.hex())
        server.sendall(data)
        resp = server.recv(4096)
        print("[S→C]", resp.hex())
        client.sendall(resp)
```

### Step 2 — Find Protocol Parser in Binary

```
; In IDA:
; 1. Find recv/WSARecv — trace buffer into parsing code
; 2. Look for comparisons against magic bytes:
;    cmp [buf], 0x1234   → "if first 2 bytes == 0x1234"
; 3. Look for length extraction:
;    movzx eax, [buf+2]  → "length field at offset 2"
; 4. Look for command dispatch (switch or if-chain on command byte)
```

### Step 3 — Map Protocol Fields

```python
# Document your findings:
# Offset 0: magic (2 bytes) = 0x5353
# Offset 2: command (1 byte): 0x01=login, 0x02=get, 0x03=put
# Offset 3: length (2 bytes, big-endian): payload length
# Offset 5: payload (length bytes)

import struct

def build_packet(cmd: int, payload: bytes) -> bytes:
    magic  = b"\x53\x53"
    header = struct.pack(">BH", cmd, len(payload))
    return magic + header + payload
```

### Step 4 — Find Length Validation Gaps

```c
// RE finding:
int cmd_len = ntohs(*(short*)(buf+3));     // attacker-controlled!
char *dst   = malloc(0x200);               // fixed 512-byte buffer
memcpy(dst, buf+5, cmd_len);              // ← no check: cmd_len > 0x200?
```

***

## Reversing Parsers & File Format Handlers

### Approach

1. **Identify the file format** — magic bytes, known extension
2. **Find the parsing entry point** — follow `ReadFile` / `fread` / `mmap`
3. **Identify field extraction** — size fields, offsets, counts
4. **Check arithmetic on size fields** — overflow potential
5. **Check copy destinations** — stack vs heap, declared size

### File Format Mapping in IDA

```asm
; Pattern: parse a TLV (Type-Length-Value) structure
mov     eax, [esi]          ; type field (4 bytes)
cmp     eax, TYPE_A
je      .handle_a
cmp     eax, TYPE_B
je      .handle_b

.handle_a:
    mov     ecx, [esi+4]    ; length field — IS THIS VALIDATED?
    add     esi, 8          ; advance past header
    ; copy ecx bytes from esi into fixed buffer ← check size!
```

### Common File Format Bugs

| Pattern                                 | Bug                                 |
| --------------------------------------- | ----------------------------------- |
| Count field × element size unchecked    | Integer overflow → undersized alloc |
| Offset field used directly as pointer   | Out-of-bounds read/write            |
| Nested length fields unchecked          | Buffer overread                     |
| String length from file, no null check  | Stack BOF                           |
| Decompression without output size check | Heap BOF                            |

***

## Patch Diffing

Used to find what a security patch changed — reveals the vulnerability.

### BinDiff (IDA plugin)

```
1. Disassemble patched and unpatched versions in IDA
2. Save .idb files for both
3. BinDiff → Diff → select both .idb files
4. Review "Unmatched functions" and "Changed functions"
5. Changed functions → compare side-by-side
6. Look for: added bounds checks, changed comparison operators,
             replaced dangerous function with safe equivalent
```

### Manual Diffing with Ghidra + Git

```bash
# Export decompiled pseudo-C from both versions:
# Ghidra: File → Export → C/C++ (per function)

# Diff:
diff -u unpatched_func.c patched_func.c

# Or use diaphora (free BinDiff alternative):
# github.com/joxeankoret/diaphora
```

### What to Look For in Diffs

```c
// UNPATCHED:
memcpy(dst, src, user_len);

// PATCHED:
if (user_len > sizeof(dst)) return ERROR;  // ← this reveals the bug
memcpy(dst, src, user_len);
```

```c
// UNPATCHED:
printf(user_input);                     // ← format string

// PATCHED:
printf("%s", user_input);              // ← safe
```

***

## IDA Pro & Ghidra Quick Reference

### IDA Keyboard Shortcuts

| Key      | Action                           |
| -------- | -------------------------------- |
| `Space`  | Toggle graph/linear view         |
| `F5`     | Decompile (Hex-Rays)             |
| `X`      | Cross-references to current item |
| `N`      | Rename identifier                |
| `:`      | Add line comment                 |
| `;`      | Add repeatable comment           |
| `G`      | Jump to address                  |
| `Ctrl+F` | Search text                      |
| `Alt+T`  | Search text (again)              |
| `Ctrl+X` | Cross-refs to                    |
| `H`      | Convert to hex                   |
| `R`      | Convert to character             |
| `D`      | Convert to data                  |
| `C`      | Convert to code                  |
| `P`      | Create function                  |
| `U`      | Undefine                         |
| `A`      | Convert to ASCII string          |
| `Ctrl+W` | Save                             |
| `Esc`    | Go back                          |
| `Enter`  | Follow jump/call                 |
| `Alt+←`  | Navigate back                    |

### IDA — Useful Scripts (IDAPython)

```python
import idc, idaapi, idautils

# Find all calls to a function by name:
target = idc.get_name_ea_simple("strcpy")
for xref in idautils.CodeRefsTo(target, True):
    func = idc.get_func_name(xref)
    print(f"strcpy called from {func} at {hex(xref)}")

# Rename variables automatically:
idc.set_name(0x401234, "parse_packet", idc.SN_NOWARN)

# Find all strings:
for s in idautils.Strings():
    if b"password" in str(s).encode():
        print(hex(s.ea), str(s))

# Dump function bytes:
start = idc.get_func_attr(here(), idc.FUNCATTR_START)
end   = idc.get_func_attr(here(), idc.FUNCATTR_END)
data  = idc.get_bytes(start, end - start)
print(data.hex())
```

### Ghidra Shortcuts

| Key                      | Action              |
| ------------------------ | ------------------- |
| `G`                      | Go to address       |
| `L`                      | Rename label        |
| `T`                      | Retype variable     |
| `Ctrl+F`                 | Find                |
| `Alt+←`                  | Navigate back       |
| `Ctrl+Shift+F`           | Search for scalars  |
| Right-click → References | Show all references |

***

## WinDbg for RE & Triage

### Useful Analysis Commands

```
; Walk SEH chain:
!exchain

; Show exception handlers for a function:
.fnent address

; Find string in all modules:
s -a 0 L?80000000 "ERROR"

; Disassemble function from symbol:
uf kernel32!CreateFileA

; Walk heap looking for pattern:
!heap -srch 41414141

; Show all threads:
~*k

; Switch to thread N:
~Ns

; Module info:
!dh -f kernel32            ; PE flags
lmvm kernel32              ; version info

; Check ASLR / DEP:
!dh -a ntdll | findstr "DLL characteristics"
; 0x140 = ASLR + NX (DEP)
; 0x100 = NX only
; 0x040 = ASLR only

; Find ROP gadgets (simple):
s -b 00000000 L?7fffffff c3          ; find all RET instructions
s -b 00000000 L?7fffffff ff e4       ; JMP ESP
s -b 00000000 L?7fffffff ff d4       ; CALL ESP
```

### WinDbg Script for Crash Triage

```
; Save as triage.wds, run: windbg -c "$$><C:\triage.wds" target.exe
.logopen C:\crash_log.txt
g
.if (@$exr != 0) {
    .echo "=== EXCEPTION ==="
    .exr -1
    .echo "=== REGISTERS ==="
    r
    .echo "=== STACK ==="
    kb 20
    .echo "=== DISASM ==="
    u @eip L10
    .echo "=== MEMORY at EIP ==="
    !address @eip
}
.logclose
q
```

***

## Python Tooling for RE

### Parse PE File (pefile)

```python
import pefile

pe = pefile.PE("target.exe")

# Show imports:
for entry in pe.DIRECTORY_ENTRY_IMPORT:
    print(entry.dll.decode())
    for imp in entry.imports:
        print(f"  {hex(imp.address)}  {imp.name}")

# Show exports:
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
    print(hex(pe.OPTIONAL_HEADER.ImageBase + exp.address), exp.name)

# Sections:
for s in pe.sections:
    print(s.Name, hex(s.VirtualAddress), hex(s.Misc_VirtualSize))
    flags = []
    if s.Characteristics & 0x20000000: flags.append("EXEC")
    if s.Characteristics & 0x40000000: flags.append("READ")
    if s.Characteristics & 0x80000000: flags.append("WRITE")
    print("  Flags:", flags)
```

### Disassemble with Capstone

```python
from capstone import *

md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True

code = bytes.fromhex("558bec83ec20")  # your bytes

for insn in md.disasm(code, 0x401000):
    print(f"0x{insn.address:08x}:  {insn.mnemonic:10s} {insn.op_str}")
```

### Assemble with Keystone

```python
from keystone import *

ks = Ks(KS_ARCH_X86, KS_MODE_32)
asm = "push ebp; mov ebp, esp; sub esp, 0x20"
encoding, _ = ks.asm(asm)
print(bytes(encoding).hex())
```

### Extract Strings from Binary

```python
import re

with open("target.exe", "rb") as f:
    data = f.read()

# ASCII strings ≥ 6 chars:
for m in re.finditer(rb"[ -~]{6,}", data):
    print(hex(m.start()), m.group().decode())

# Unicode strings:
for m in re.finditer(rb"(?:[ -~]\x00){6,}", data):
    print(hex(m.start()), m.group().decode("utf-16-le"))
```

### Network Protocol RE Helper

```python
import socket, struct, hexdump

def send_recv(host, port, data, verbose=True):
    s = socket.socket()
    s.settimeout(5)
    s.connect((host, port))
    banner = s.recv(4096)
    if verbose:
        print("[BANNER]")
        hexdump.hexdump(banner)
    s.sendall(data)
    resp = s.recv(4096)
    if verbose:
        print("[RESPONSE]")
        hexdump.hexdump(resp)
    s.close()
    return resp
```

***

## Common OSED RE Patterns Cheatsheet

### Vulnerability Indicators in Pseudocode

```
INDICATOR                          LIKELY BUG
─────────────────────────────────────────────────────
recv(s, buf, BIG, 0) into buf[SMALL]  → Stack BOF
strcpy(fixed_buf, user_str)           → Stack BOF
sprintf(fixed_buf, user_fmt, ...)     → Stack BOF / FmtStr
memcpy(dst, src, user_len)            → BOF if len unchecked
user_count * fixed_size → alloc size  → Integer overflow → heap BOF
(short)large_int → used as size       → Truncation → BOF
printf(user_input)                    → Format string
free(p); ...; p->field                → Use-after-free
loop: buf[i++] until '\0' (no bound)  → Stack BOF
loop: i <= N (should be i < N)        → Off-by-one
```

### Assembly Anti-patterns

```asm
; No size check before copy:
push    [user_data]
push    [local_buf]
call    strcpy          ; ← no cmp/jl guard before this

; Length from user data used in rep movsb:
mov     ecx, [user_controlled_len]
rep     movsb           ; ← is ECX validated against buffer size?

; Signed/unsigned confusion:
movsx   ecx, word ptr [user_len]   ; sign-extend: if 0x8000 → 0xFFFF8000
; ECX is now -32768 as signed — passes "if (len < 0x200)" check
; but used as unsigned in memcpy → HUGE copy
```

### Questions to Ask at Every Copy Operation

1. Where does the **source** come from? (user input? file? network?)
2. Where does the **destination** live? (stack? heap? global?)
3. What is the **destination size**? (declared in bytes)
4. What is the **copy length**? (fixed? from header? from attacker?)
5. Is there a **length check** before the copy?
6. Is the length check **before or after** the copy? (order matters)
7. Are there **type mismatches** (signed/unsigned, short/int)?

***

## Resources

### Books

* *The Art of Software Security Assessment* — Dowd, McDonald, Schuh (the bible)
* *Hacking: The Art of Exploitation* — Jon Erickson
* *Practical Malware Analysis* — Sikorski & Honig
* *Windows Internals* (Parts 1 & 2) — Yosifovich et al.
* *Shellcoder's Handbook* — Anley et al.

### Online References

| Resource                | URL                                |
| ----------------------- | ---------------------------------- |
| OSED Course Page        | offensive-security.com/exp301-osed |
| corelan exploit writing | corelan.be/index.php/articles      |
| WinDbg cheatsheet       | windbg.info                        |
| IDA Pro docs            | hex-rays.com/documentation         |
| Ghidra docs             | ghidra-sre.org                     |
| OALabs YouTube          | youtube.com/@OALabs                |
| LiveOverflow            | youtube.com/@LiveOverflow          |
| Connor McGarr blog      | connormcgarr.github.io             |
| j00ru Windows internals | j00ru.vexillium.org                |

### Tool Repos

| Tool                  | Repo                                |
| --------------------- | ----------------------------------- |
| mona.py               | github.com/corelan/mona             |
| pwntools              | github.com/Gallopsled/pwntools      |
| ROPgadget             | github.com/JonathanSalwan/ROPgadget |
| diaphora (patch diff) | github.com/joxeankoret/diaphora     |
| pefile                | github.com/erocarrera/pefile        |
| capstone              | github.com/aquynh/capstone          |
| keystone              | github.com/keystone-engine/keystone |

***

## RE Workflow Summary

```
1. STATIC ANALYSIS
   ├─ Load in IDA / Ghidra
   ├─ Find input functions (recv, ReadFile, scanf...)
   ├─ Trace data flow into parsing/processing code
   ├─ Flag dangerous function calls
   └─ Note size discrepancies and unchecked lengths

2. DYNAMIC ANALYSIS
   ├─ Run under WinDbg / x64dbg
   ├─ Set BPs at interesting functions
   ├─ Observe register/memory values with real input
   └─ Confirm static findings at runtime

3. FUZZING
   ├─ Identify protocol structure
   ├─ Build fuzzer targeting each field
   └─ Monitor for crashes

4. TRIAGE
   ├─ Reproduce crash consistently
   ├─ Determine what you control (EIP? ESP? EAX?)
   ├─ Calculate offsets
   ├─ Identify constraints (bad chars, DEP, ASLR)
   └─ Classify vulnerability type

5. EXPLOITATION
   └─ (Separate topic — shellcode, ROP, bypass techniques)
```

***

*Read the disassembly like a story. Every instruction is a sentence.*\
*Follow the data. The bug is where validation ends before the data does.*


---

# 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/reverse-engineering-bugs.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.
