Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:17:15 +08:00
commit 31df8711f2
13 changed files with 6197 additions and 0 deletions

532
skills/ctf-pwn/SKILL.md Normal file
View File

@@ -0,0 +1,532 @@
---
name: ctf-pwn
description: Solve CTF binary exploitation challenges by discovering and exploiting memory corruption vulnerabilities to read flags. Use for buffer overflows, format strings, heap exploits, ROP challenges, or any pwn/exploitation task.
---
# CTF Binary Exploitation (Pwn)
## Purpose
You are a CTF binary exploitation specialist. Your goal is to **discover memory corruption vulnerabilities** and **exploit them to read flags** through systematic vulnerability analysis and creative exploitation thinking.
This is a **generic exploitation framework** - adapt these concepts to any vulnerability type you encounter. Focus on understanding **why** memory corruption happens and **how** to manipulate it, not just recognizing specific bug classes.
## Conceptual Framework
### The Exploitation Mindset
**Think in three layers:**
1. **Data Flow Layer**: Where does attacker-controlled data go?
- Input sources: stdin, network, files, environment, arguments
- Data destinations: stack buffers, heap allocations, global variables
- Transformations: parsing, copying, formatting, decoding
2. **Memory Safety Layer**: What assumptions does the program make?
- Buffer boundaries: Fixed-size arrays, allocation sizes
- Type safety: Integer types, pointer validity, structure layouts
- Control flow integrity: Return addresses, function pointers, vtables
3. **Exploitation Layer**: How can we violate trust boundaries?
- Memory writes: Overwrite critical data (return addresses, function pointers, flags)
- Memory reads: Leak information (addresses, canaries, pointer values)
- Control flow hijacking: Redirect execution to attacker-controlled locations
- Logic manipulation: Change program state to skip checks or trigger unintended paths
### Core Question Sequence
For every CTF pwn challenge, ask these questions **in order**:
1. **What data do I control?**
- Function parameters, user input, file contents, environment variables
- How much data? What format? Any restrictions (printable chars, null bytes)?
2. **Where does my data go in memory?**
- Stack buffers? Heap allocations? Global variables?
- What's the size of the destination? Is it checked?
3. **What interesting data is nearby in memory?**
- Return addresses (stack)
- Function pointers (heap, GOT/PLT, vtables)
- Security flags or permission variables
- Other buffers (to leak or corrupt)
4. **What happens if I send more data than expected?**
- Buffer overflow: Overwrite adjacent memory
- Identify what gets overwritten (use pattern generation)
- Determine offset to critical data
5. **What can I overwrite to change program behavior?**
- Return address → redirect execution on function return
- Function pointer → redirect execution on indirect call
- GOT/PLT entry → redirect library function calls
- Variable value → bypass checks, unlock features
6. **Where can I redirect execution?**
- Existing code: system(), exec(), one_gadget
- Leaked addresses: libc functions
- Injected code: shellcode (if DEP/NX disabled)
- ROP chains: reuse existing code fragments
7. **How do I read the flag?**
- Direct: Call system("/bin/cat flag.txt") or open()/read()/write()
- Shell: Call system("/bin/sh") and interact
- Leak: Read flag into buffer, leak buffer contents
## Core Methodologies
### Vulnerability Discovery
**Unsafe API Pattern Recognition:**
Identify dangerous functions that don't enforce bounds:
- **Unbounded copies**: strcpy, strcat, sprintf, gets
- **Underspecified bounds**: read(), recv(), scanf("%s"), strncpy (no null termination)
- **Format string bugs**: printf(user_input), fprintf(fp, user_input)
- **Integer overflows**: malloc(user_size), buffer[user_index], length calculations
**Investigation strategy:**
1. `get-symbols` includeExternal=true → Find unsafe API imports
2. `find-cross-references` to unsafe functions → Locate usage points
3. `get-decompilation` with includeContext=true → Analyze calling context
4. Trace data flow from input to unsafe operation
**Stack Layout Analysis:**
Understand memory organization:
```
High addresses
├── Function arguments
├── Return address ← Critical target for overflow
├── Saved frame pointer
├── Local variables ← Vulnerable buffers here
├── Compiler canaries ← Stack protection (if enabled)
└── Padding/alignment
Low addresses
```
**Investigation strategy:**
1. `get-decompilation` of vulnerable function → See local variable layout
2. Estimate offsets: buffer → saved registers → return address
3. `set-bookmark` type="Analysis" category="Vulnerability" at overflow site
4. `set-decompilation-comment` documenting buffer size and adjacent targets
**Heap Exploitation Patterns:**
Heap vulnerabilities differ from stack:
- **Use-after-free**: Access freed memory (dangling pointers)
- **Double-free**: Free same memory twice (corrupt allocator metadata)
- **Heap overflow**: Overflow into adjacent heap chunk (overwrite metadata/data)
- **Type confusion**: Use object as wrong type after reallocation
**Investigation strategy:**
1. `search-decompilation` pattern="(malloc|free|realloc)" → Find heap operations
2. Trace pointer lifecycle: allocation → use → free
3. Look for dangling pointer usage after free
4. Identify adjacent allocations (overflow targets)
### Memory Layout Understanding
**Address Space Discovery:**
Map the binary's memory:
1. `get-memory-blocks` → See sections (.text, .data, .bss, heap, stack)
2. Note executable sections (shellcode candidates if NX disabled)
3. Note writable sections (data corruption targets)
4. Identify ASLR status (addresses randomized each run?)
**Offsets and Distances:**
Calculate critical distances:
- Buffer to return address: For stack overflow payload sizing
- GOT to PLT: For GOT overwrite attacks
- Heap chunk to chunk: For heap overflow targeting
- libc base to useful functions: For address calculation after leak
**Investigation strategy:**
1. `get-data` or `read-memory` at known addresses → Sample memory layout
2. `find-cross-references` direction="both" → Map relationships
3. Calculate offsets manually from decompilation
4. `set-comment` at key offsets documenting distances
### Exploitation Planning
**Constraint Analysis:**
Identify exploitation constraints:
- **Bad bytes**: Null bytes (\x00) terminate C strings → avoid in address/payload
- **Input size limits**: Truncation, buffering, network MTU
- **Character restrictions**: Printable-only, alphanumeric, no special chars
- **Protection mechanisms**: Detect via `search-decompilation` pattern="(canary|__stack_chk)"
**Bypass Strategies:**
Common protections and bypass techniques:
- **Stack canaries**: Leak canary value, brute-force (fork servers), overwrite without corrupting
- **ASLR**: Leak addresses (format strings, uninitialized data), partial overwrite (last byte randomization)
- **NX/DEP**: ROP (Return-Oriented Programming), ret2libc, JOP (Jump-Oriented Programming)
- **PIE**: Leak code addresses, relative offsets within binary, partial overwrites
**Exploitation Primitives:**
Build these fundamental capabilities:
- **Arbitrary write**: Write controlled data to chosen address (format string, heap overflow)
- **Arbitrary read**: Read from chosen address (format string, uninitialized data, overflow into pointer)
- **Control flow hijack**: Redirect execution (overwrite return address, function pointer, GOT entry)
- **Information leak**: Obtain addresses, canaries, pointers (uninitialized variables, format strings)
**Chain multiple primitives when needed:**
- Leak → Calculate addresses → Overwrite function pointer → Exploit
- Partial overwrite → Leak full address → Calculate libc base → ret2libc
- Heap overflow → Overwrite function pointer → Arbitrary write → GOT overwrite → Shell
## Flexible Workflow
This is a **thinking framework**, not a rigid checklist. Adapt to the challenge:
### Phase 1: Binary Reconnaissance (5-10 tool calls)
**Understand the challenge:**
1. `get-current-program` or `list-project-files` → Identify target binary
2. `get-memory-blocks` → Map sections, identify protections
3. `get-functions` filterDefaultNames=false → Count functions (stripped vs. symbolic)
4. `search-strings-regex` pattern="flag" → Find flag-related strings
5. `get-symbols` includeExternal=true → List imported functions
**Identify entry points and input vectors:**
6. `get-decompilation` functionNameOrAddress="main" limit=50 → See program flow
7. Look for input functions: read(), recv(), gets(), scanf(), fgets()
8. `find-cross-references` to input functions → Map input flow
9. `set-bookmark` type="TODO" category="Input Vector" at each input point
**Flag suspicious patterns:**
- Unsafe functions (strcpy, sprintf, gets)
- Large stack buffers with small read operations
- Format string vulnerabilities (user-controlled format)
- Unbounded loops or recursion
### Phase 2: Vulnerability Analysis (10-15 tool calls)
**Trace data flow from input to vulnerability:**
1. `get-decompilation` of input-handling function with includeReferenceContext=true
2. Identify buffer sizes: char buf[64], malloc(size), etc.
3. Identify write operations: strcpy(dest, src), read(fd, buf, 1024)
4. **Calculate vulnerability**: Write size > buffer size?
**Analyze vulnerable function context:**
5. `rename-variables` → Clarify data flow (user_input, buffer, size, etc.)
6. `change-variable-datatypes` → Fix types for clarity
7. `set-decompilation-comment` → Document vulnerability location and type
**Map memory layout around vulnerability:**
8. Identify local variables and their stack positions
9. Calculate offset from buffer start to return address
10. `read-memory` at nearby addresses → Sample stack layout (if debugging available)
11. `set-bookmark` type="Warning" category="Overflow" → Mark vulnerability
**Cross-reference analysis:**
12. `find-cross-references` to vulnerable function → How is it called?
13. Check for exploitation helpers: system(), exec(), "/bin/sh" string
14. `search-strings-regex` pattern="/bin/(sh|bash)" → Find shell strings
15. `search-decompilation` pattern="system|exec" → Find execution functions
### Phase 3: Exploitation Strategy (5-10 tool calls)
**Determine exploitation approach:**
Based on protections and available primitives:
**If no protections (NX disabled, no canary, no ASLR):**
- Stack overflow → overwrite return address → jump to shellcode
- Inject shellcode in buffer, jump to buffer address
**If NX enabled but no ASLR:**
- ret2libc: Overwrite return address → chain to system() with "/bin/sh"
- ROP chain: Chain gadgets to build system("/bin/sh") call
- GOT overwrite: Overwrite GOT entry to redirect library call
**If ASLR enabled:**
- Leak addresses first (format string, uninitialized data)
- Calculate libc base from leaked address
- Use leak to build ROP chain or ret2libc with correct addresses
**If stack canary present:**
- Leak canary value (format string, sequential overflow)
- Preserve canary in overflow payload
- Or use heap exploitation instead
**Investigation for each strategy:**
1. `search-strings-regex` pattern="(\\x2f|/)bin/(sh|bash)" → Find shell strings
2. `find-cross-references` to "/bin/sh" → Get string address
3. `get-symbols` includeExternal=true → Find system/exec imports
4. `get-decompilation` of system → Get address (if not PIE)
**For ROP:**
5. `search-decompilation` pattern="(pop|ret)" → Find gadget candidates
6. Manual ROP gadget discovery (use external tools like ROPgadget)
7. Document gadget addresses with `set-bookmark` type="Note" category="ROP Gadget"
**For format string exploitation:**
8. `get-decompilation` of printf call → Analyze format string control
9. Test format string primitives: %x (leak), %n (write), %s (arbitrary read)
10. `set-comment` documenting exploitation primitive
### Phase 4: Payload Construction (Conceptual)
**Build the exploit payload:**
This happens **outside Ghidra** using Python/pwntools, but plan it here:
1. **Document payload structure** using `set-comment`:
```
Payload structure:
[padding: 64 bytes] + [saved rbp: 8 bytes] + [return addr: 8 bytes] + [args]
```
2. **Record critical addresses** with `set-bookmark`:
- Buffer address: 0x7fffffffdd00
- Return address location: 0x7fffffffdd40 (offset +64)
- system() address: 0x7ffff7e14410
- "/bin/sh" string: 0x00404030
3. **Document exploitation steps** with `set-bookmark` type="Analysis" category="Exploit Plan":
```
Step 1: Send 64 bytes padding
Step 2: Overwrite return address with system() address
Step 3: Inject "/bin/sh" pointer as argument
Step 4: Trigger return to execute system("/bin/sh")
```
4. **Track assumptions** with `set-bookmark` type="Warning" category="Assumption":
- "Assuming stack addresses are stable (no ASLR)"
- "Assuming no canary based on decompilation (verify runtime)"
### Phase 5: Exploitation Validation (Iterative)
**This phase happens outside Ghidra**, but document findings:
1. Test exploit against local binary
2. Adjust offsets based on crash analysis
3. Handle bad bytes or character restrictions
4. Refine payload until successful
**Update Ghidra database with findings:**
- `set-comment` with actual working offsets
- `set-bookmark` documenting successful exploitation
- `checkin-program` message="Documented successful exploitation of buffer overflow in function_X"
## Pattern Recognition
See `patterns.md` for detailed vulnerability patterns:
- Unsafe API usage patterns
- Buffer overflow indicators
- Format string vulnerability signatures
- Heap exploitation patterns
- Integer overflow scenarios
- Control flow hijacking opportunities
## Exploitation Techniques Reference
### Stack Buffer Overflow
**Concept**: Write beyond buffer bounds to overwrite return address or function pointers on stack.
**Discovery**:
1. Find unsafe copy: strcpy, gets, scanf("%s"), read with large size
2. Identify buffer size from decompilation
3. Compare buffer size to maximum input size
4. Calculate offset to return address (buffer size + saved registers)
**Exploitation**:
- Payload: [padding to return address] + [new return address] + [optional arguments/ROP chain]
- Target: Overwrite return address to redirect execution
### Format String Vulnerability
**Concept**: User-controlled format string allows arbitrary memory read/write.
**Discovery**:
1. `search-decompilation` pattern="printf|fprintf|sprintf"
2. Check if format string comes from user input: printf(user_buffer)
3. Vulnerable pattern: printf(input) instead of printf("%s", input)
**Exploitation**:
- Read: %x, %p (leak stack values), %s (arbitrary read via pointer on stack)
- Write: %n (write number of bytes printed to pointer on stack)
- Position: %N$x (access Nth argument directly)
**Investigation**:
4. `get-decompilation` with includeReferenceContext → See printf call context
5. `set-decompilation-comment` documenting format string control
6. `set-bookmark` type="Warning" category="Format String"
### Return-Oriented Programming (ROP)
**Concept**: Chain existing code fragments (gadgets) ending in 'ret' to build arbitrary computation without injecting code.
**Discovery**:
1. Find gadgets: `pop reg; ret`, `mov [addr], reg; ret`, `syscall; ret`
2. External tool: ROPgadget, ropper (Ghidra doesn't have built-in gadget search)
3. Document gadgets in Ghidra with `set-bookmark` type="Note" category="ROP Gadget"
**Exploitation**:
- Chain gadgets by placing addresses on stack
- Each gadget executes, then 'ret' pops next gadget address
- Build syscall with proper registers: execve("/bin/sh", NULL, NULL)
**Workflow**:
4. Identify required gadgets for goal (e.g., execve syscall)
5. `set-comment` at gadget addresses documenting purpose
6. Plan ROP chain structure with `set-bookmark` type="Analysis" category="ROP Chain"
### ret2libc
**Concept**: Redirect execution to libc functions (system, exec, one_gadget) instead of shellcode.
**Discovery**:
1. `get-symbols` includeExternal=true → Find libc imports
2. `find-cross-references` to system, execve → Get addresses
3. `search-strings-regex` pattern="/bin/sh" → Find shell string
**Exploitation** (no ASLR):
- Overwrite return address → system function address
- Set first argument → pointer to "/bin/sh" string
- Calling convention: x86-64 uses RDI for first arg, x86 uses stack
**Exploitation** (with ASLR):
- Leak libc address (format string, uninitialized pointer)
- Calculate system/exec address = libc_base + offset
- Build ROP chain with calculated addresses
**Investigation**:
4. `get-data` at GOT entries → See libc function addresses
5. Calculate libc base from known offset
6. `set-bookmark` documenting calculated addresses
### Heap Exploitation
**Concept**: Corrupt heap metadata or overflow between heap chunks to achieve arbitrary write or control flow hijack.
**Discovery**:
1. `search-decompilation` pattern="malloc|free|realloc"
2. Trace allocation and free patterns
3. Look for use-after-free: pointer used after free()
4. Look for heap overflow: write beyond allocated size
**Exploitation techniques**:
- **Use-after-free**: Free object, allocate new object in same slot, use old pointer to access new object (type confusion)
- **Double-free**: Free same pointer twice, corrupt allocator metadata
- **Heap overflow**: Overflow into next chunk, overwrite metadata (size, pointers) or data (function pointers)
- **Fastbin/tcache poisoning**: Corrupt freelist pointers to allocate arbitrary memory
**Investigation**:
5. `rename-variables` for heap pointers (heap_ptr, freed_ptr, chunk1, chunk2)
6. `set-decompilation-comment` at allocation/free sites
7. `set-bookmark` type="Warning" category="Use-After-Free"
### Integer Overflow
**Concept**: Integer overflow/underflow leads to incorrect buffer size calculation or bounds check bypass.
**Discovery**:
1. Find size calculations: size = user_input * sizeof(element)
2. Check for overflow: What if user_input is very large?
3. Find bounds checks: if (index < size) → What if index is large unsigned?
**Exploitation**:
- Overflow allocation size → heap buffer too small → heap overflow
- Underflow size check → negative check bypassed → buffer overflow
- Wrap-around arithmetic → bypass length checks
**Investigation**:
4. `change-variable-datatypes` to proper integer types (uint32_t, size_t)
5. Identify overflow scenarios in comments
6. `set-bookmark` type="Warning" category="Integer Overflow"
## Tool Integration
**Use ReVa tools systematically:**
### Discovery Tools
- `get-symbols` → Find unsafe API imports
- `search-strings-regex` → Find interesting strings (flag, shell, paths)
- `search-decompilation` → Find vulnerability patterns (unsafe functions)
- `get-functions-by-similarity` → Find functions similar to known vulnerable pattern
### Analysis Tools
- `get-decompilation` with `includeIncomingReferences=true` and `includeReferenceContext=true`
- `find-cross-references` with `includeContext=true` → Trace data flow
- `get-data` → Examine global variables, GOT entries, constant data
- `read-memory` → Sample memory layout
### Database Improvement Tools
- `rename-variables` → Clarify exploitation-relevant variables (buffer, user_input, return_addr)
- `change-variable-datatypes` → Fix types for proper understanding
- `set-decompilation-comment` → Document vulnerabilities inline
- `set-comment` → Document exploitation strategy at key addresses
- `set-bookmark` → Track vulnerabilities, gadgets, exploit plan
### Organization Tools
- `set-bookmark` type="Warning" category="Vulnerability" → Mark vulnerabilities
- `set-bookmark` type="Note" category="ROP Gadget" → Track gadgets
- `set-bookmark` type="Analysis" category="Exploit Plan" → Document strategy
- `set-bookmark` type="TODO" category="Verify" → Track assumptions to verify
- `checkin-program` → Save progress
## Success Criteria
You've successfully completed the challenge when:
1. **Vulnerability identified**: Specific function, line, and vulnerability type documented
2. **Memory layout understood**: Buffer sizes, offsets, adjacent data mapped
3. **Exploitation strategy planned**: Clear path from vulnerability to flag documented
4. **Critical addresses recorded**: All addresses needed for exploit payload documented
5. **Assumptions tracked**: All assumptions documented with confidence levels
6. **Database improved**: Renamed variables, added comments, set bookmarks for clarity
7. **Exploit plan ready**: Sufficient information to write exploit code outside Ghidra
**Return to user:**
- Vulnerability description with evidence
- Exploitation approach explanation
- Critical addresses and offsets
- Payload structure plan
- Assumptions and verification needs
- Follow-up tasks if needed (e.g., "Test exploit against binary")
## Anti-Patterns
**Don't**:
- Assume vulnerability without evidence (check buffer sizes!)
- Forget about protections (canaries, NX, ASLR, PIE)
- Overlook input restrictions (bad bytes, size limits)
- Get stuck on one approach (try different exploitation techniques)
- Ignore calling conventions (x86 vs x64 argument passing)
- Forget null byte termination (C string functions)
**Do**:
- Verify buffer sizes from decompilation
- Check for stack canaries: `__stack_chk_fail` references
- Calculate offsets precisely (buffer to return address)
- Document all assumptions with `set-bookmark` type="Warning"
- Adapt exploitation technique to protections present
- Think creatively (chain primitives, use unconventional targets)
## Remember
Binary exploitation is **creative problem-solving**:
- Understand **why** vulnerabilities exist (unsafe assumptions)
- Think **how** to manipulate memory (data flow analysis)
- Plan **what** to overwrite (control flow, data, pointers)
- Determine **where** to redirect (existing code, injected code, ROP)
- Execute **step-by-step** (leak, calculate, overwrite, trigger)
Every CTF challenge is different. Use this framework to **think** about exploitation, not as a checklist to blindly follow.
**Your goal**: Document enough information in Ghidra to write the exploit script. The actual exploitation happens outside, but the analysis happens here.

948
skills/ctf-pwn/patterns.md Normal file
View File

@@ -0,0 +1,948 @@
# CTF Binary Exploitation Patterns
This document contains patterns for recognizing common vulnerability classes and exploitation primitives in CTF challenges. Focus on **conceptual understanding** rather than specific exploits.
## Vulnerability Recognition Patterns
### Unsafe String Operation Patterns
**Conceptual characteristics**:
- Functions that don't check destination buffer size
- Unbounded copying from source to destination
- Reliance on null terminator without size validation
- No length parameter or ignored length parameter
**Dangerous API patterns**:
```c
// Unbounded copy (no size checking)
strcpy(dest, user_input); // Copies until null byte
strcat(dest, user_input); // Appends until null byte
sprintf(dest, "%s", user_input); // Formats without bounds
gets(buffer); // Reads unlimited from stdin
// Underspecified bounds
strncpy(dest, src, sizeof(dest)); // Doesn't guarantee null termination
scanf("%s", buffer); // No size limit specified
read(fd, buffer, 1024); // May exceed buffer size if buffer < 1024
recv(sock, buffer, MAX, 0); // May exceed buffer capacity
```
**What to look for in decompiled code**:
```
Buffer declaration:
char buffer[64]; // Fixed-size local array
Unsafe operation on same buffer:
strcpy(buffer, user_input); // No size check
read(fd, buffer, 256); // Reads more than buffer holds
Distance to critical data:
buffer[64] // Local variable at stack offset
saved_rbp // Usually at buffer + buffer_size
return_address // Usually at buffer + buffer_size + 8
```
**Investigation strategy**:
1. `get-symbols` includeExternal=true → Find strcpy, strcat, gets, scanf, sprintf imports
2. `find-cross-references` to unsafe functions → Locate call sites
3. `get-decompilation` with includeContext=true → Analyze buffer size vs. input size
4. Calculate: input_max_size > buffer_size? → Buffer overflow exists
5. `set-bookmark` type="Warning" category="Buffer Overflow" at vulnerability
**Telltale signs**:
- Local char arrays with small sizes (64, 128, 256 bytes)
- Unbounded string functions called on those arrays
- User input directly passed to unsafe function
- No explicit size checking before copy operation
### Format String Vulnerability Patterns
**Conceptual characteristics**:
- User controls the format string parameter
- Format specifiers allow memory read (%x, %s, %p) and write (%n)
- Stack-based exploitation (format string reads stack arguments)
- Arbitrary read/write primitive when exploited
**Vulnerable patterns**:
```c
// VULNERABLE: User input as format string
printf(user_input);
fprintf(fp, user_input);
sprintf(buffer, user_input);
snprintf(buffer, size, user_input);
syslog(priority, user_input);
// SAFE: Format string is literal
printf("%s", user_input);
fprintf(fp, "Input: %s\n", user_input);
sprintf(buffer, "Data: %s", user_input);
```
**What to look for in decompiled code**:
```
Direct user input to format function:
read(0, buffer, 256);
printf(buffer); // VULNERABLE
Variable format string:
char* fmt = get_format_string(); // Source from user
printf(fmt, args); // VULNERABLE if fmt user-controlled
Missing format string:
fprintf(stderr, error_msg); // VULNERABLE if error_msg from user
```
**Exploitation primitives**:
```
%x or %p → Leak stack values (addresses, canaries, pointers)
%s → Arbitrary read (if pointer on stack)
%n → Arbitrary write (writes byte count to pointer)
%N$x → Direct parameter access (Nth argument)
%N$n → Write to Nth argument pointer
Example attack:
printf("AAAA%10$x"); → Leak 10th stack parameter
printf("AAAA%7$n"); → Write to pointer at 7th stack position
```
**Investigation strategy**:
1. `search-decompilation` pattern="printf|fprintf|sprintf|snprintf|syslog"
2. `get-decompilation` at each match with includeContext=true
3. Check format string argument: Is it a constant string or variable?
4. If variable, trace source: Does it come from user input?
5. `set-bookmark` type="Warning" category="Format String" at vulnerability
**Telltale signs**:
- printf/fprintf with single argument (no format string literal)
- Format string stored in writable buffer
- User input copied into format string variable
- Error message formatted with user-supplied data
### Buffer Size vs. Operation Mismatch Patterns
**Conceptual characteristics**:
- Buffer allocated with one size
- Operation assumes different (larger) size
- Off-by-one errors
- Mismatched size calculations
**Common mismatch patterns**:
```c
// Wrong size constant
char buffer[64];
read(fd, buffer, 128); // Reads 128 into 64-byte buffer
// Off-by-one
char buffer[64];
for (i = 0; i <= 64; i++) // Loop goes to 64 (65 iterations)
buffer[i] = input[i]; // Writes one byte past end
// Null terminator forgotten
char buffer[64];
strncpy(buffer, input, 64); // May not null-terminate
printf("%s", buffer); // Reads past end if not terminated
// Size calculation error
char buffer[64];
memcpy(buffer, src, strlen(src)); // strlen doesn't include null byte
// But may overflow if strlen(src) >= 64
```
**What to look for in decompiled code**:
```
Size declaration:
local_48 = buffer (char array, size 64)
Operation size:
read(0, local_48, 0x80); // 0x80 = 128 > 64
Offset calculation:
local_48[iVar1] = input[iVar1]; // Check iVar1 bounds
Loop bounds:
for (i = 0; i < size; i++) // Is size validated?
buffer[i] = input[i]; // Does size match buffer capacity?
```
**Investigation strategy**:
1. `get-decompilation` → Identify buffer size from local variable declaration
2. Find operations on buffer (read, memcpy, strcpy, loops)
3. Compare buffer size to operation size
4. `rename-variables` → buffer, buffer_size, read_size for clarity
5. `set-decompilation-comment` → "Buffer overflow: reads 128 into 64-byte buffer"
6. `set-bookmark` type="Warning" category="Size Mismatch"
**Telltale signs**:
- Magic constants in read/copy operations that don't match buffer size
- sizeof() used incorrectly (sizeof(pointer) vs. sizeof(array))
- Off-by-one in loop bounds (<= instead of <)
- Missing null terminator checks
### Integer Overflow Leading to Memory Corruption
**Conceptual characteristics**:
- Integer arithmetic wraps around at type bounds
- Overflow in size calculation leads to small allocation
- Small allocation leads to buffer overflow
- Underflow in bounds check bypasses security
**Vulnerable patterns**:
```c
// Allocation size overflow
uint32_t count = user_input; // User controls this
uint32_t size = count * sizeof(element); // May overflow
buffer = malloc(size); // Allocates small buffer due to overflow
for (i = 0; i < count; i++) // Loop uses original count
buffer[i] = data[i]; // Heap overflow
// Bounds check underflow
size_t len = user_input;
if (len - 1 < MAX_SIZE) { // Underflows if len == 0 (unsigned)
memcpy(buffer, src, len); // Large len bypasses check
}
// Sign confusion
int size = user_size; // User controls, may be negative
if (size < MAX_SIZE) { // Passes check if negative
memcpy(buffer, src, size); // Casted to size_t (huge number)
}
```
**What to look for in decompiled code**:
```
Size calculation:
size = user_count * 16; // Multiplication may overflow
Wraparound check missing:
if (user_count < 1000) { // Doesn't check for overflow
size = user_count * 16;
buf = malloc(size);
}
Unsigned underflow:
if (len - 1 < 1024) { // What if len == 0?
Sign conversion:
int signed_size = user_input; // Signed integer
malloc(signed_size); // Casted to size_t (unsigned)
// Negative becomes huge positive
```
**Investigation strategy**:
1. `search-decompilation` pattern="malloc|calloc|realloc"
2. Trace size parameter back to source
3. Check for multiplication/addition in size calculation
4. `change-variable-datatypes` to proper types (uint32_t, size_t, ssize_t)
5. Look for overflow checks (or lack thereof)
6. `set-decompilation-comment` → "Integer overflow: count * size may wrap"
7. `set-bookmark` type="Warning" category="Integer Overflow"
**Telltale signs**:
- Multiplication in allocation size without overflow check
- Unsigned subtraction in bounds check
- Signed/unsigned type confusion
- Missing validation for very large user-supplied sizes
### Use-After-Free Patterns
**Conceptual characteristics**:
- Memory freed but pointer still accessible (dangling pointer)
- Dangling pointer dereferenced (use after free)
- Heap allocator may reuse freed memory for new allocation
- Type confusion when old pointer accesses new object
**Vulnerable patterns**:
```c
// Classic use-after-free
object* ptr = malloc(sizeof(object));
use_object(ptr);
free(ptr);
// ... later in code ...
use_object(ptr); // Use after free!
// Double-free (special case)
free(ptr);
free(ptr); // Corrupts heap metadata
// Use-after-free via aliasing
object* ptr1 = malloc(sizeof(object));
object* ptr2 = ptr1; // Aliased pointer
free(ptr1);
use_object(ptr2); // Use after free via alias
```
**What to look for in decompiled code**:
```
Allocation and free:
heap_ptr = malloc(0x40);
// ... use heap_ptr ...
free(heap_ptr);
Later usage (use-after-free):
// ... some code ...
*heap_ptr = value; // Write to freed memory
function(heap_ptr); // Pass freed pointer
Conditional free (double-free risk):
if (condition1) free(ptr);
if (condition2) free(ptr); // May free twice if both true
No pointer nulling:
free(ptr);
// ptr not set to NULL, can be reused
```
**Investigation strategy**:
1. `search-decompilation` pattern="free"
2. For each free(), trace pointer usage after free
3. `find-cross-references` to pointer variable → See all uses
4. Check if pointer is nulled after free (ptr = NULL)
5. Check if pointer is checked before use (if (ptr != NULL))
6. `rename-variables` → freed_ptr, dangling_ptr for clarity
7. `set-decompilation-comment` at use site → "Use-after-free"
8. `set-bookmark` type="Warning" category="Use-After-Free"
**Telltale signs**:
- free() call without setting pointer to NULL
- Pointer dereferenced after free() in any code path
- Multiple free() calls on same pointer
- Pointer used in different contexts (freed as type A, used as type B)
### Heap Overflow Patterns
**Conceptual characteristics**:
- Allocation with one size
- Write operation exceeds allocated size
- Overflows into adjacent heap chunk
- Can corrupt heap metadata or adjacent object data
**Vulnerable patterns**:
```c
// Allocation too small
buffer = malloc(64);
read(fd, buffer, 128); // Heap overflow
// Calculation error
buffer = malloc(count * sizeof(element));
for (i = 0; i <= count; i++) // Off-by-one (should be <, not <=)
buffer[i] = data[i]; // Overflows by one element
// Unchecked string operation on heap
buffer = malloc(64);
strcpy(buffer, user_input); // Overflow if user_input > 63 bytes
```
**What to look for in decompiled code**:
```
Heap allocation:
heap_buf = malloc(0x40); // Allocates 64 bytes
Write operation:
read(0, heap_buf, 0x100); // Reads 256 bytes → overflow
Adjacent allocations:
buf1 = malloc(0x40);
buf2 = malloc(0x40); // buf2 likely adjacent to buf1
strcpy(buf1, user_input); // May overflow into buf2
Metadata corruption risk:
chunk = malloc(size);
overflow_write(chunk, large_size); // May corrupt next chunk's metadata
```
**Investigation strategy**:
1. `search-decompilation` pattern="malloc"
2. Trace allocated buffer through code
3. Find write operations on buffer (strcpy, memcpy, read, loops)
4. Compare allocation size to write size
5. Check for adjacent allocations (exploitation targets)
6. `set-decompilation-comment` → "Heap overflow: writes 256 into 64-byte allocation"
7. `set-bookmark` type="Warning" category="Heap Overflow"
**Telltale signs**:
- Small malloc() followed by large read/write
- String operations on heap buffers without bounds
- Loop writing to heap array without bounds check
- Multiple sequential allocations (heap layout predictable)
---
## Exploitation Primitive Patterns
### Arbitrary Memory Write Primitives
**Conceptual characteristics**:
- Ability to write controlled data to chosen address
- Achieved through various vulnerability classes
- Foundation for control flow hijacking and data corruption
**Primitive construction patterns**:
**Format string arbitrary write**:
```
// Concept: %n writes byte count to pointer argument
printf("AAAA%7$n");
// If stack[7] is controlled pointer, writes to *stack[7]
Technique:
1. Place target address on stack
2. Position format string to access it (%N$n)
3. Adjust byte count with padding to write desired value
4. Use width specifiers: %200c%7$n → writes 200+4=204
```
**Buffer overflow arbitrary write**:
```
// Concept: Overflow to overwrite pointer, then use pointer
Step 1: Overflow to corrupt pointer
[buffer overflow] → [overwrite ptr variable]
Step 2: Trigger write through pointer
*ptr = value; // Writes to attacker-controlled address
```
**Heap overflow arbitrary write**:
```
// Concept: Overflow heap chunk to corrupt adjacent chunk's pointers
Chunk layout:
[chunk1 metadata][chunk1 data][chunk2 metadata][chunk2 data]
Overflow chunk1 data → overwrite chunk2 metadata → corrupt pointers
When chunk2 used, writes to attacker-controlled addresses
```
**Investigation strategy**:
1. Identify vulnerability (format string, overflow, use-after-free)
2. Analyze what can be overwritten
3. Trace pointer dereferencing after corruption
4. `set-bookmark` type="Analysis" category="Arbitrary Write" → Document primitive
**What enables arbitrary write**:
- Controlled pointer value (overflow, format string)
- Dereference of controlled pointer (assignment, function call)
- Heap metadata corruption (unlink exploitation)
### Arbitrary Memory Read Primitives
**Conceptual characteristics**:
- Ability to read from chosen memory address
- Used to leak addresses, canaries, code/data
- Critical for defeating ASLR and other protections
**Primitive construction patterns**:
**Format string arbitrary read**:
```
// Concept: %s reads string from pointer argument
printf("AAAA%10$s");
// If stack[10] is controlled pointer, prints string at *stack[10]
Technique:
1. Place target address on stack
2. Position format string to access it (%N$s)
3. Read output to obtain memory contents
```
**Uninitialized data read**:
```
// Concept: Uninitialized variables contain previous stack/heap data
Pattern in decompiled code:
char buffer[64];
// No initialization
send(socket, buffer, 64, 0); // Leaks stack contents
Investigation:
Look for send/write without initialization
Check if data used before written
```
**Buffer over-read**:
```
// Concept: Read past end of buffer into adjacent memory
Pattern:
char buffer[64];
strncpy(buffer, input, 64); // No null termination
printf("%s", buffer); // Reads past end until null byte
Result: Leaks adjacent stack data
```
**Investigation strategy**:
1. Find format string vulnerabilities (user-controlled format)
2. Find uninitialized variables sent to output
3. Find string operations missing null termination
4. `set-bookmark` type="Analysis" category="Info Leak" → Document primitive
5. Calculate what can be leaked (addresses, canaries, pointers)
**What enables arbitrary read**:
- Format string with %s and controlled pointer
- Uninitialized buffer sent to network/file
- Missing null terminator allows over-read
- Heap use-after-free with read operations
### Control Flow Hijack Primitives
**Conceptual characteristics**:
- Redirect program execution to attacker-controlled location
- Achieved by overwriting function pointers or return addresses
- Goal: Execute shellcode, ROP chain, or existing functions
**Hijack target patterns**:
**Return address overwrite (stack overflow)**:
```
Stack layout:
[buffer][saved rbp][return address]
Overflow buffer → overwrite return address → redirect on function return
What to look for:
Local buffer vulnerable to overflow
Return address at predictable offset (buffer_size + 8 on x64)
Calculate offset: buffer start to return address location
```
**Function pointer overwrite**:
```
// Global or heap-allocated function pointer
void (*callback)(void) = default_handler;
// Overflow to overwrite callback
buffer_overflow → overwrite callback pointer
// Trigger hijack
callback(); // Calls attacker-controlled address
```
**GOT/PLT overwrite**:
```
// Global Offset Table contains addresses of library functions
// Overwrite GOT entry to redirect library call
Example:
Overwrite GOT[puts] with system address
Next call to puts() actually calls system()
Requirement: Arbitrary write primitive to GOT address
```
**Virtual table (vtable) overwrite**:
```
// C++ objects have vtable pointers
// Overwrite vtable pointer to fake vtable
Object layout:
[vtable ptr][member1][member2]...
Overflow → overwrite vtable ptr → point to attacker-controlled memory
Virtual function call → uses fake vtable → hijacks control flow
```
**Investigation strategy**:
1. Identify overflow vulnerability
2. Determine what's adjacent in memory (return address, function pointer, vtable)
3. Calculate offset from buffer to target
4. `get-data` at GOT/PLT addresses → Get function pointer locations
5. `set-bookmark` type="Analysis" category="Control Flow Hijack"
6. Document target address and offset
**Telltale signs**:
- Function pointers in global variables or structures
- Indirect calls through function pointers
- Virtual function calls (C++ code)
- GOT/PLT entries for library functions
### Information Leak Primitives (Defeating ASLR)
**Conceptual characteristics**:
- Leak address from memory to defeat address randomization
- Calculate base addresses from leaked pointers
- Use leaked addresses in subsequent exploitation
**Leak source patterns**:
**Stack address leak**:
```
// Stack addresses often present on stack itself
Format string: printf("%p %p %p %p") // Leak stack pointers
Uninitialized: Stack variable contains previous stack frame address
Use: Calculate stack layout, predict buffer addresses
```
**Code address leak (PIE bypass)**:
```
// Return addresses on stack point to code section
Format string leak of return address → code address
Calculate code base: leaked_addr & ~0xFFF (page alignment)
Use: Calculate gadget addresses, function addresses
```
**Libc address leak (ASLR bypass)**:
```
// GOT contains resolved libc function addresses
Arbitrary read of GOT entry → libc function address
Calculate libc base: leaked_addr - function_offset
Use: Calculate system(), one_gadget, useful function addresses
```
**Heap address leak**:
```
// Heap pointers often in freed chunks or stack
Use-after-free leak: Read freed chunk (contains fwd/bck pointers)
Format string: Leak heap pointer from stack
Use: Predict heap layout, target heap objects
```
**Investigation strategy**:
1. Identify leak primitive (format string, uninitialized data, over-read)
2. Determine what's leaked (stack, code, heap, libc addresses)
3. Calculate offsets to useful addresses
4. `set-bookmark` type="Note" category="Address Leak" → Document leak
5. `set-comment` → "Leaks libc address, calculate system() as libc_base + 0x4f4e0"
**Telltale signs**:
- printf with user-controlled format string
- Send/write with uninitialized buffer
- String operations without null termination
- Heap metadata visible to program (freed chunks)
---
## Common Exploitation Workflows
### Stack Overflow to Shell
**Attack flow**:
```
1. Find buffer overflow on stack
2. Calculate offset to return address
3. Identify target for hijack:
a. Shellcode address (if NX disabled)
b. system() address (if no ASLR)
c. ROP chain address (if protections enabled)
4. Construct payload: [padding][return address][arguments/ROP]
5. Trigger overflow, return redirects to attacker code
6. Execute shellcode/system("/bin/sh") to get shell
```
**Investigation steps**:
1. `get-decompilation` of vulnerable function → Find buffer overflow
2. `rename-variables` → buffer, user_input, size
3. Calculate offset: buffer to return address (usually buffer_size + 8)
4. `search-strings-regex` pattern="/bin/sh" → Find shell string
5. `get-symbols` includeExternal=true → Find system() import
6. `set-bookmark` type="Analysis" category="Exploit Plan"
7. Document payload structure in comment
### Format String to Arbitrary Write
**Attack flow**:
```
1. Find printf(user_input) vulnerability
2. Test format string: Send "%x %x %x" → leak stack values
3. Find offset to controlled data on stack
4. Construct format string to write to arbitrary address:
- Place target address on stack
- Use %N$n to write to address at stack[N]
5. Target: Overwrite GOT entry, return address, or function pointer
6. Redirect execution to attacker code
```
**Investigation steps**:
1. `search-decompilation` pattern="printf|sprintf" → Find format string calls
2. `get-decompilation` with includeContext → Verify format string from user
3. `get-data` at GOT addresses → Identify targets for overwrite
4. Calculate stack offset to controlled buffer
5. `set-bookmark` type="Warning" category="Format String"
6. Document exploitation: "%7$n writes to address at stack[7]"
### Heap Exploitation to Code Execution
**Attack flow**:
```
1. Find heap vulnerability (use-after-free, heap overflow, double-free)
2. Understand heap layout (chunk sizes, allocation order)
3. Exploit heap corruption:
a. Use-after-free: Free object, allocate new, use old pointer (type confusion)
b. Heap overflow: Overflow chunk to corrupt adjacent chunk metadata
c. Double-free: Corrupt freelist to allocate arbitrary address
4. Gain arbitrary write or control flow hijack primitive
5. Overwrite function pointer, GOT entry, or return address
6. Execute attacker code
```
**Investigation steps**:
1. `search-decompilation` pattern="malloc|free"
2. Trace allocation and free patterns
3. Identify vulnerability (use-after-free, overflow, double-free)
4. `rename-variables` → chunk1, chunk2, freed_ptr, size
5. Analyze adjacent allocations (overflow targets)
6. `set-bookmark` type="Warning" category="Heap Vulnerability"
7. Document exploitation primitive achieved
### Ret2libc (Return-to-libc)
**Attack flow**:
```
1. Find stack overflow vulnerability
2. Cannot use shellcode (NX enabled)
3. Redirect to existing libc function: system()
4. Set up arguments: First arg points to "/bin/sh"
5. Payload structure:
- Overflow to return address
- Overwrite return address → system() address
- Set first argument → pointer to "/bin/sh" string
6. Function returns, calls system("/bin/sh"), spawns shell
```
**Investigation steps**:
1. `get-decompilation` → Find buffer overflow
2. `search-strings-regex` pattern="/bin/sh" → Get shell string address
3. `get-symbols` includeExternal=true → Find system import
4. Check calling convention (x86: stack args, x64: RDI register)
5. Calculate ROP gadgets if needed: pop rdi; ret
6. `set-bookmark` type="Note" category="Ret2libc Plan"
7. Document payload: [padding][system_addr][ret_addr]["/bin/sh"_ptr]
### ROP Chain Construction
**Attack flow**:
```
1. Find code execution vulnerability (overflow, etc.)
2. Protections prevent direct shellcode/ret2libc
3. Build ROP chain: Sequence of gadget addresses
4. Each gadget: Small code fragment ending in 'ret'
5. Chain gadgets to build desired operation (e.g., execve syscall)
6. Place chain on stack, trigger vulnerability
7. Execution flows through gadgets, performs desired operation
```
**Investigation steps**:
1. Identify required gadgets (pop rdi; ret, pop rsi; ret, syscall; ret, etc.)
2. Use external tool (ROPgadget) to find gadgets in binary/libc
3. `set-bookmark` type="Note" category="ROP Gadget" at each gadget address
4. `set-comment` at gadget address → "pop rdi; ret"
5. Document ROP chain structure:
- [gadget1_addr] → pop rdi; ret
- ["/bin/sh"_ptr] → argument for rdi
- [gadget2_addr] → pop rsi; ret
- [NULL] → argument for rsi
- [syscall_addr] → execve syscall
6. `set-bookmark` type="Analysis" category="ROP Chain Plan"
---
## Protection Mechanism Bypass Patterns
### Stack Canary Bypass
**Canary mechanism**:
```
Stack layout with canary:
[buffer][stack canary][saved rbp][return address]
On function return:
if (canary != expected_canary)
__stack_chk_fail(); // Abort on corruption
```
**Bypass techniques**:
**1. Leak canary value (format string, uninitialized data)**:
```
printf(user_input); // Format string leak
Send "%7$p" → leak canary from stack position 7
Include leaked canary in overflow payload to preserve it
```
**2. Brute-force canary (fork server)**:
```
If server forks instead of exiting:
Canary same across fork
Brute-force one byte at a time
256 attempts per byte, 1024 total for 32-bit canary
```
**3. Overwrite without corrupting canary**:
```
Partial overwrite: Overflow only up to return address
Don't touch canary if it's not in the way
Or overwrite saved rbp and return address precisely
```
**Investigation**:
1. `search-decompilation` pattern="__stack_chk_fail" → Detect canary presence
2. `get-decompilation` → See canary check in code
3. Identify canary position on stack
4. `set-bookmark` type="Note" category="Stack Canary" → Document location
5. Plan bypass: leak, brute-force, or avoid
### NX/DEP Bypass (No Execute)
**Protection mechanism**:
```
Stack/heap marked non-executable
Shellcode injection doesn't work (causes segfault)
```
**Bypass techniques**:
**1. Return-to-libc (ret2libc)**:
```
Don't inject code, reuse existing code
Redirect to system(), execve(), etc.
Set up arguments properly
```
**2. Return-Oriented Programming (ROP)**:
```
Chain existing code fragments (gadgets)
Build complex operations from simple gadgets
No new code introduced
```
**3. mprotect/VirtualProtect ROP**:
```
Use ROP to call mprotect(shellcode_addr, RWX)
Change shellcode memory to executable
Jump to now-executable shellcode
```
**Investigation**:
1. `get-memory-blocks` → Check stack/heap permissions (look for 'x' flag)
2. If NX enabled, plan ROP or ret2libc
3. `get-symbols` includeExternal=true → Find usable functions
4. `set-bookmark` type="Analysis" category="NX Bypass"
### ASLR Bypass (Address Space Layout Randomization)
**Protection mechanism**:
```
Addresses randomized each execution
Code base, libc base, stack base, heap base all randomized
Exploit addresses must be dynamically calculated
```
**Bypass techniques**:
**1. Information leak**:
```
Leak address from memory (format string, uninitialized data)
Calculate base address from leaked pointer
Use base + offset to find desired functions
```
**2. Partial overwrite**:
```
Only lowest 12 bits (page offset) are not randomized
Overwrite only last byte of address
Reduces entropy, enables brute-force or partial redirect
```
**3. Heap spraying (rarely applicable in CTF)**:
```
Fill heap with controlled data
Increase probability of hitting controlled memory
```
**Investigation**:
1. Identify leak primitive (format string, over-read, uninitialized)
2. Calculate what's leaked (code, stack, heap, libc)
3. Determine offsets: leaked_addr to target_addr
4. `set-comment` → "Leak libc: system = libc_base + 0x4f4e0"
5. `set-bookmark` type="Analysis" category="ASLR Bypass"
### PIE Bypass (Position Independent Executable)
**Protection mechanism**:
```
Code section randomized (in addition to ASLR)
Function addresses, gadget addresses randomized
Cannot hardcode code addresses
```
**Bypass techniques**:
**1. Leak code address**:
```
Leak return address from stack → points to code
Calculate code base: leaked_addr & ~0xFFF
Calculate function/gadget addresses: code_base + offset
```
**2. Partial overwrite**:
```
Overwrite only last byte of return address
Redirect within same function or nearby functions
Useful for redirecting to existing win() function
```
**Investigation**:
1. Identify if PIE enabled (check binary properties)
2. Find code address leak (stack return address)
3. Calculate offsets from code base to targets
4. `set-bookmark` type="Analysis" category="PIE Bypass"
---
## Using This Reference
### Pattern Recognition Workflow
1. **Identify vulnerability class** → Match decompiled code to vulnerability patterns
2. **Determine exploitation primitive** → What capability does vulnerability provide?
3. **Check protections** → What bypass techniques are needed?
4. **Plan exploitation workflow** → Chain primitives to achieve goal
5. **Document in Ghidra** → Bookmarks, comments, renamed variables
### Investigation Priority
**Start with:**
1. Unsafe API recognition (strcpy, printf, etc.)
2. Buffer size vs. operation size comparison
3. Input flow tracing (where does user data go?)
**Then analyze:**
4. Memory layout (what's adjacent to vulnerable buffer?)
5. Available exploitation targets (return address, function pointers, GOT)
6. Protection mechanisms (canary, NX, ASLR, PIE)
**Finally plan:**
7. Exploitation primitive construction
8. Protection bypass strategy
9. Payload structure
10. Exploit execution plan
### Progressive Understanding
**First pass**: "Unsafe strcpy in main() on buffer[64]"
**Second pass**: "Overflow of 64 bytes to reach return address at offset +72"
**Third pass**: "Can redirect to system@plt, need '/bin/sh' string address"
**Fourth pass**: "Full ret2libc: overflow → system('/bin/sh') → shell"
Each iteration refines the exploitation plan.
### Evidence-Based Exploitation
Every claim needs evidence:
- "Buffer overflow exists" → Show buffer size < input size
- "Return address at offset 72" → Show stack layout calculation
- "Can call system()" → Show system@plt address or import
- "ASLR bypass possible" → Show leak primitive and calculation
Document all evidence with bookmarks and comments in Ghidra.