mirror of
https://github.com/encounter/ghidra-cli.git
synced 2026-07-10 03:18:56 -07:00
Getting ready for v0.1.0
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Clippy
|
||||
run: cargo clippy -- -D warnings
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
archive: tar.gz
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-latest
|
||||
archive: tar.gz
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
archive: tar.gz
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
archive: zip
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Build release binary
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Create archive (Unix)
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
cd target/${{ matrix.target }}/release
|
||||
tar -czvf ../../../ghidra-cli-${{ github.ref_name }}-${{ matrix.target }}.tar.gz ghidra
|
||||
cd ../../..
|
||||
|
||||
- name: Create archive (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cd target/${{ matrix.target }}/release
|
||||
7z a ../../../ghidra-cli-${{ github.ref_name }}-${{ matrix.target }}.zip ghidra.exe
|
||||
cd ../../..
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ghidra-cli-${{ matrix.target }}
|
||||
path: ghidra-cli-${{ github.ref_name }}-${{ matrix.target }}.*
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
generate_release_notes: true
|
||||
files: artifacts/**/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish:
|
||||
name: Publish to crates.io
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Publish to crates.io
|
||||
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run unit tests
|
||||
run: cargo test --lib --verbose
|
||||
@@ -0,0 +1,46 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.0] - 2025-01-26
|
||||
|
||||
### Added
|
||||
|
||||
- Daemon-only architecture with persistent Ghidra connection
|
||||
- Auto-start daemon on import/analyze/quick commands
|
||||
- Comprehensive reverse engineering commands:
|
||||
- Function analysis (list, decompile, disassemble, calls, xrefs)
|
||||
- Symbol management (list, get, create, delete, rename)
|
||||
- String analysis and search
|
||||
- Type definitions and application
|
||||
- Comment management
|
||||
- Memory operations
|
||||
- Cross-reference analysis
|
||||
- Search capabilities:
|
||||
- String patterns
|
||||
- Byte sequences
|
||||
- Function names
|
||||
- Crypto constants
|
||||
- Interesting patterns
|
||||
- Call graph generation and export
|
||||
- Binary patching (bytes, NOP, export)
|
||||
- Script execution (Python and Java)
|
||||
- Batch operations
|
||||
- Flexible output formats:
|
||||
- Human-readable (default for TTY)
|
||||
- Compact JSON (default for pipes)
|
||||
- Pretty JSON (--pretty flag)
|
||||
- Expression-based filtering
|
||||
- AI agent integration support
|
||||
|
||||
### Security
|
||||
|
||||
- Local IPC communication only (Unix sockets / named pipes)
|
||||
|
||||
[unreleased]: https://github.com/akiselev/ghidra-cli/compare/v0.1.0...HEAD
|
||||
[0.1.0]: https://github.com/akiselev/ghidra-cli/releases/tag/v0.1.0
|
||||
@@ -25,5 +25,7 @@ See @AGENTS.md for agent-specific instructions.
|
||||
|
||||
| What | When |
|
||||
|------|------|
|
||||
| `CONTRIBUTING.md` | Setting up development environment, understanding PR process, test requirements |
|
||||
| `CHANGELOG.md` | Reviewing version history and release notes |
|
||||
| `src/daemon/README.md` | Understanding daemon architecture and IPC protocol |
|
||||
| `tests/README.md` | Understanding test structure and conventions |
|
||||
|
||||
@@ -262,4 +262,4 @@ Contributions are welcome! Please feel free to submit issues and pull requests.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See [LICENSE](LICENSE) for details.
|
||||
GPL-3.0 License - See [LICENSE](LICENSE) for details.
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
---
|
||||
name: ghidra-cli-skill
|
||||
description: >
|
||||
Use ghidra-cli for reverse engineering tasks: binary analysis, decompilation, function inspection, cross-reference analysis, pattern discovery, and binary patching.
|
||||
Activate when the user requests:
|
||||
- Binary analysis or reverse engineering
|
||||
- Decompilation or disassembly
|
||||
- Function listing, inspection, or renaming
|
||||
- Cross-reference or call graph analysis
|
||||
- String or byte pattern searches
|
||||
- Binary patching or modification
|
||||
- Ghidra project management
|
||||
---
|
||||
|
||||
# ghidra-cli
|
||||
|
||||
Use ghidra-cli for reverse engineering tasks: binary analysis, decompilation, function inspection, cross-reference analysis, pattern discovery, and binary patching.
|
||||
|
||||
## When to Use
|
||||
|
||||
Activate when the user requests:
|
||||
- Binary analysis or reverse engineering
|
||||
- Decompilation or disassembly
|
||||
- Function listing, inspection, or renaming
|
||||
- Cross-reference or call graph analysis
|
||||
- String or byte pattern searches
|
||||
- Binary patching or modification
|
||||
- Ghidra project management
|
||||
|
||||
## Workflow
|
||||
|
||||
### Pre-flight Check
|
||||
|
||||
Before running queries, verify the environment:
|
||||
|
||||
```bash
|
||||
# Check if daemon is running for fast queries
|
||||
ghidra daemon status --project <project>
|
||||
|
||||
# If not running, start it
|
||||
ghidra daemon start --project <project> --program <program>
|
||||
```
|
||||
|
||||
### Quick Start (New Binary)
|
||||
|
||||
For one-off analysis, use quick mode:
|
||||
|
||||
```bash
|
||||
ghidra quick ./binary
|
||||
ghidra daemon start --project quick-analysis --program binary
|
||||
```
|
||||
|
||||
### Full Project Setup
|
||||
|
||||
For sustained analysis:
|
||||
|
||||
```bash
|
||||
ghidra project create myproject
|
||||
ghidra import ./binary --project myproject
|
||||
ghidra analyze --project myproject --program binary
|
||||
ghidra daemon start --project myproject --program binary
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Querying Functions
|
||||
|
||||
```bash
|
||||
# List all functions
|
||||
ghidra function list --project <p> --program <prog>
|
||||
|
||||
# Filter functions by size or name
|
||||
ghidra function list --filter "size > 500"
|
||||
ghidra function list --filter "name contains 'crypt'"
|
||||
|
||||
# Get function details
|
||||
ghidra function get main
|
||||
|
||||
# Decompile to pseudocode
|
||||
ghidra function decompile main
|
||||
|
||||
# Disassemble
|
||||
ghidra function disasm main
|
||||
|
||||
# Cross-references
|
||||
ghidra function xrefs main
|
||||
ghidra function calls main
|
||||
```
|
||||
|
||||
### Search Operations
|
||||
|
||||
```bash
|
||||
# Find functions by pattern
|
||||
ghidra find function "*crypt*"
|
||||
|
||||
# Find strings
|
||||
ghidra find string "password"
|
||||
|
||||
# Find byte patterns (hex)
|
||||
ghidra find bytes "4883ec08"
|
||||
|
||||
# Find crypto constants
|
||||
ghidra find crypto
|
||||
|
||||
# Find suspicious patterns (anti-analysis, obfuscation)
|
||||
ghidra find interesting
|
||||
```
|
||||
|
||||
### Cross-References
|
||||
|
||||
```bash
|
||||
# References TO an address
|
||||
ghidra x-ref to 0x401000
|
||||
|
||||
# References FROM an address
|
||||
ghidra x-ref from 0x401000
|
||||
```
|
||||
|
||||
### Call Graphs
|
||||
|
||||
```bash
|
||||
# Full call graph
|
||||
ghidra graph calls
|
||||
|
||||
# Who calls this function (callers)
|
||||
ghidra graph callers main --depth 3
|
||||
|
||||
# What does this function call (callees)
|
||||
ghidra graph callees main --depth 3
|
||||
|
||||
# Export as DOT format
|
||||
ghidra graph export dot
|
||||
```
|
||||
|
||||
### Symbols and Strings
|
||||
|
||||
```bash
|
||||
# List symbols
|
||||
ghidra symbol list
|
||||
|
||||
# List strings
|
||||
ghidra strings list --limit 100
|
||||
|
||||
# References to a string
|
||||
ghidra strings refs "error"
|
||||
```
|
||||
|
||||
### Memory and Types
|
||||
|
||||
```bash
|
||||
# Memory map
|
||||
ghidra memory map
|
||||
|
||||
# Read memory at address
|
||||
ghidra memory read 0x401000 64
|
||||
|
||||
# List data types
|
||||
ghidra type list
|
||||
|
||||
# Apply type to address
|
||||
ghidra type apply 0x402000 "char[32]"
|
||||
```
|
||||
|
||||
### Modifications
|
||||
|
||||
```bash
|
||||
# Rename function
|
||||
ghidra function rename sub_401000 decrypt_password
|
||||
|
||||
# Add comment
|
||||
ghidra comment set 0x401000 "Key derivation starts here"
|
||||
|
||||
# Patch bytes
|
||||
ghidra patch bytes 0x401000 "90909090"
|
||||
|
||||
# NOP instructions
|
||||
ghidra patch nop 0x401010 --count 5
|
||||
|
||||
# Export patched binary
|
||||
ghidra patch export --output patched.bin
|
||||
```
|
||||
|
||||
### Scripting
|
||||
|
||||
```bash
|
||||
# Run Python script
|
||||
ghidra script run analysis.py
|
||||
|
||||
# Inline Python
|
||||
ghidra script python "print(currentProgram.getName())"
|
||||
|
||||
# Batch commands from file
|
||||
ghidra batch commands.txt
|
||||
```
|
||||
|
||||
## Output Handling
|
||||
|
||||
ghidra-cli outputs JSON by default. Parse the structured data:
|
||||
|
||||
```bash
|
||||
# JSON output (default)
|
||||
ghidra function list
|
||||
|
||||
# Table format for display
|
||||
ghidra function list --format table
|
||||
|
||||
# Count only
|
||||
ghidra function list --format count
|
||||
```
|
||||
|
||||
When processing results, extract relevant fields from JSON rather than displaying raw output.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Investigate a Function
|
||||
|
||||
```bash
|
||||
ghidra function get <name> # Overview
|
||||
ghidra function decompile <name> # Pseudocode
|
||||
ghidra function calls <name> # What it calls
|
||||
ghidra function xrefs <name> # Who calls it
|
||||
ghidra graph callers <name> --depth 2
|
||||
```
|
||||
|
||||
### Find Interesting Code
|
||||
|
||||
```bash
|
||||
ghidra find crypto # Crypto constants
|
||||
ghidra find interesting # Suspicious patterns
|
||||
ghidra find function "*alloc*" # Memory functions
|
||||
ghidra strings list --filter "length > 50"
|
||||
```
|
||||
|
||||
### Trace Data Flow
|
||||
|
||||
```bash
|
||||
ghidra x-ref to <address> # Who writes here
|
||||
ghidra x-ref from <address> # What this references
|
||||
ghidra graph callees <func> --depth 3
|
||||
```
|
||||
|
||||
## Error Recovery
|
||||
|
||||
| Situation | Resolution |
|
||||
| ------------------ | ------------------------------------------------------------- |
|
||||
| Daemon not running | `ghidra daemon start --project <p> --program <prog>` |
|
||||
| No project exists | `ghidra project create <name>` or use `ghidra quick <binary>` |
|
||||
| Function not found | Use `ghidra find function "*pattern*"` to search |
|
||||
| Address format | Use hex with 0x prefix: `0x401000` |
|
||||
| Slow queries | Start daemon for sub-second response times |
|
||||
|
||||
## Global Options
|
||||
|
||||
All commands accept:
|
||||
- `--project <name>` - Target project
|
||||
- `--program <name>` - Target program within project
|
||||
- `--format json|table|count` - Output format
|
||||
- `--filter <expr>` - Filter expression
|
||||
- `--limit <N>` - Max results
|
||||
Binary file not shown.
@@ -0,0 +1,199 @@
|
||||
|
||||
I started by searching for interesting strings:
|
||||
|
||||
```
|
||||
[Forge] > Enter Name:
|
||||
[Forge] > Enter Key:
|
||||
[*] Consult the Norns...
|
||||
[VM] Stack Overflow!
|
||||
The ribbon tightens! %s has forged Gleipnir!
|
||||
```
|
||||
|
||||
The `[VM] Stack Overflow!` string led me to the VM dispatcher at `0x140001850`.
|
||||
|
||||
### Program Flow
|
||||
|
||||
Tracing from `main` (`0x140001010`):
|
||||
|
||||
1. Display banner/story
|
||||
2. Read username into buffer
|
||||
3. Read serial key into buffer
|
||||
4. Call validation function `0x140001e10`
|
||||
5. Display success or failure message
|
||||
|
||||
---
|
||||
|
||||
## The Yggdrasil Virtual Machine
|
||||
|
||||
### VM Structure (0x140001850)
|
||||
|
||||
The VM uses a simple architecture:
|
||||
- **9 registers**: R0-R8 (64-bit each)
|
||||
- **Stack**: 1024 entries
|
||||
- **Instruction pointer** and **stack pointer**
|
||||
|
||||
### Opcode Table
|
||||
|
||||
| Opcode | Mnemonic | Format | Description |
|
||||
|--------|----------|--------|-------------|
|
||||
| 0x00 | HALT | `00` | Stop execution |
|
||||
| 0x01 | LOAD | `01 reg imm64` | Load 64-bit immediate into register |
|
||||
| 0x02 | MOV | `02 dst src` | Copy register to register |
|
||||
| 0x03 | ADD | `03 dst src` | dst += src |
|
||||
| 0x04 | SUB | `04 dst src` | dst -= src |
|
||||
| 0x05 | XOR | `05 dst src` | dst ^= src |
|
||||
| 0x06 | MUL | `06 dst src` | dst *= src |
|
||||
| 0x07 | PUSH | `07 reg` | Push register to stack |
|
||||
| 0x08 | POP | `08 reg` | Pop stack to register |
|
||||
| 0x09 | JMP | `09 addr64` | Unconditional jump |
|
||||
| 0x0A | JZ | `0A addr64` | Jump if R0 == 0 |
|
||||
| 0x0B | NOP | `0B` | No operation |
|
||||
|
||||
---
|
||||
|
||||
## Serial Validation (0x140001e10)
|
||||
|
||||
The validation function:
|
||||
|
||||
1. **Parses serial** as 4 hex values separated by non-alphanumeric characters
|
||||
- Example: `AAAA-BBBB-CCCC-DDDD`
|
||||
- Each part can be up to 16 hex digits (64-bit)
|
||||
|
||||
2. **Initializes VM context** via `0x140001b90`
|
||||
- R0-R8 set to 0
|
||||
- Serial parts loaded into R5-R8
|
||||
|
||||
3. **Generates bytecode** via `0x140001bf0` based on username
|
||||
|
||||
4. **Executes VM** via `0x140001850`
|
||||
|
||||
5. **Checks result**: Success if `R0 == 0x13371337CAFEBABE`
|
||||
|
||||
---
|
||||
|
||||
## Bytecode Generator (0x140001bf0)
|
||||
|
||||
### PRNG Seeding
|
||||
|
||||
The username is hashed using **FNV-1a**:
|
||||
|
||||
```c
|
||||
uint32_t fnv1a_hash(char *username) {
|
||||
uint32_t hash = 0x811c9dc5; // FNV offset basis
|
||||
while (*username) {
|
||||
hash = (*username ^ hash) * 0x1000193; // FNV prime
|
||||
username++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
```
|
||||
|
||||
### LCG PRNG
|
||||
|
||||
The hash seeds a Linear Congruential Generator:
|
||||
|
||||
```c
|
||||
uint32_t lcg_next(uint32_t state) {
|
||||
return state * 0x41c64e6d + 0x3039;
|
||||
}
|
||||
```
|
||||
|
||||
### Coefficient Extraction
|
||||
|
||||
Four coefficients (C1-C4) are generated, each from two LCG steps:
|
||||
|
||||
```c
|
||||
state1 = lcg_next(state);
|
||||
state2 = lcg_next(state1);
|
||||
coefficient = ((state1 >> 16) & 0x7fff) | (state2 & 0x7fff0000);
|
||||
```
|
||||
|
||||
This creates a 30-bit value with bit 15 always 0.
|
||||
|
||||
Four additional PRNG values (p1-p4) are extracted:
|
||||
```c
|
||||
state = lcg_next(state);
|
||||
p_value = (state >> 16) & 0x7fff;
|
||||
```
|
||||
|
||||
### Generated Bytecode Structure
|
||||
|
||||
The bytecode performs these operations:
|
||||
|
||||
```asm
|
||||
LOAD R1, C1 ; Load coefficient 1
|
||||
LOAD R2, C2 ; Load coefficient 2
|
||||
LOAD R3, C3 ; Load coefficient 3
|
||||
LOAD R4, C4 ; Load coefficient 4
|
||||
|
||||
XOR R1, R5 ; R1 = C1 ^ S1 (serial part 1)
|
||||
LOAD R0, p1 ; Load PRNG offset
|
||||
ADD R1, R0 ; R1 = (C1 ^ S1) + p1
|
||||
|
||||
ADD R2, R6 ; R2 = C2 + S2
|
||||
LOAD R0, p2
|
||||
XOR R2, R0 ; R2 = (C2 + S2) ^ p2
|
||||
|
||||
SUB R3, R7 ; R3 = C3 - S3
|
||||
LOAD R0, p3
|
||||
ADD R3, R0 ; R3 = (C3 - S3) + p3
|
||||
|
||||
XOR R4, R8 ; R4 = C4 ^ S4
|
||||
LOAD R0, p4
|
||||
XOR R4, R0 ; R4 = (C4 ^ S4) ^ p4
|
||||
|
||||
MOV R0, R1 ; Start accumulation
|
||||
ADD R0, R2 ; R0 = R1 + R2
|
||||
ADD R0, R3 ; R0 = R0 + R3
|
||||
ADD R0, R4 ; R0 = R0 + R4
|
||||
HALT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The Equation
|
||||
|
||||
From the bytecode analysis, the final equation is:
|
||||
|
||||
```
|
||||
R0 = (C1 ^ S1 + p1) + ((C2 + S2) ^ p2) + (C3 - S3 + p3) + ((C4 ^ S4) ^ p4)
|
||||
```
|
||||
|
||||
Where:
|
||||
- **C1-C4**: PRNG-derived coefficients (from username)
|
||||
- **S1-S4**: Serial parts (user input)
|
||||
- **p1-p4**: PRNG-derived offsets
|
||||
- **Target**: `R0 == 0x13371337CAFEBABE`
|
||||
|
||||
### Solving for S4
|
||||
|
||||
Since we have 4 unknowns and 1 equation, we can fix S1, S2, S3 and solve for S4:
|
||||
|
||||
```
|
||||
TARGET = partial + ((C4 ^ S4) ^ p4)
|
||||
|
||||
where partial = (C1 ^ S1 + p1) + ((C2 + S2) ^ p2) + (C3 - S3 + p3)
|
||||
|
||||
Solving:
|
||||
(C4 ^ S4) ^ p4 = TARGET - partial
|
||||
C4 ^ S4 = (TARGET - partial) ^ p4
|
||||
S4 = C4 ^ ((TARGET - partial) ^ p4)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Heimdall Anti-Debug (0x140001fc0)
|
||||
|
||||
The anti-debug system uses three checks:
|
||||
|
||||
1. **IsDebuggerPresent()** - Windows API
|
||||
2. **Timing check** - rdtsc before/after a loop, fails if > 100000 cycles
|
||||
3. **PEB.BeingDebugged** - Direct PEB flag check
|
||||
|
||||
If any check triggers, Heimdall injects additional bytecode:
|
||||
```asm
|
||||
LOAD R5, 0xBADF00D
|
||||
ADD R0, R5
|
||||
```
|
||||
|
||||
This corrupts the calculation, making the serial fail even if mathematically correct.
|
||||
Binary file not shown.
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Keygen for Ragnarok.exe crackme based on bytecode analysis."""
|
||||
|
||||
import argparse
|
||||
|
||||
FNV_INIT = 0x811c9dc5
|
||||
FNV_PRIME = 0x1000193
|
||||
LCG_MULT = 0x41c64e6d
|
||||
LCG_ADD = 0x3039
|
||||
TARGET = 0x13371337cafebabe
|
||||
MASK_64 = 0xFFFFFFFFFFFFFFFF
|
||||
MASK_32 = 0xFFFFFFFF
|
||||
|
||||
|
||||
def fnv1a_hash(username: str) -> int:
|
||||
"""Compute FNV-1a hash of username.
|
||||
|
||||
Binary does: hash = (byte ^ hash) * prime
|
||||
Note the XOR happens BEFORE multiply.
|
||||
"""
|
||||
hash_value = FNV_INIT
|
||||
for char in username:
|
||||
hash_value = ((ord(char) ^ hash_value) * FNV_PRIME) & MASK_32
|
||||
return hash_value
|
||||
|
||||
|
||||
def lcg_next(state: int) -> int:
|
||||
"""Perform one LCG step."""
|
||||
return (state * LCG_MULT + LCG_ADD) & MASK_32
|
||||
|
||||
|
||||
def extract_coefficients(username: str) -> tuple:
|
||||
"""Generate C1-C4 and PRNG offsets from username hash.
|
||||
|
||||
From FUN_140001bf0 decompilation:
|
||||
- Two LCG steps per coefficient
|
||||
- Coefficient = (state1 >> 16 & 0x7fff) | (state2 & 0x7fff0000)
|
||||
- This creates a 30-bit value with bit 15 always 0
|
||||
"""
|
||||
state = fnv1a_hash(username)
|
||||
|
||||
# C1: two LCG steps, combine bits
|
||||
state1 = lcg_next(state)
|
||||
state2 = lcg_next(state1)
|
||||
c1 = ((state1 >> 16) & 0x7fff) | (state2 & 0x7fff0000)
|
||||
state = state2
|
||||
|
||||
# C2
|
||||
state1 = lcg_next(state)
|
||||
state2 = lcg_next(state1)
|
||||
c2 = ((state1 >> 16) & 0x7fff) | (state2 & 0x7fff0000)
|
||||
state = state2
|
||||
|
||||
# C3
|
||||
state1 = lcg_next(state)
|
||||
state2 = lcg_next(state1)
|
||||
c3 = ((state1 >> 16) & 0x7fff) | (state2 & 0x7fff0000)
|
||||
state = state2
|
||||
|
||||
# C4
|
||||
state1 = lcg_next(state)
|
||||
state2 = lcg_next(state1)
|
||||
c4 = ((state1 >> 16) & 0x7fff) | (state2 & 0x7fff0000)
|
||||
state = state2
|
||||
|
||||
# Additional PRNG values used as offsets in bytecode (p1-p4)
|
||||
state = lcg_next(state)
|
||||
p1 = (state >> 16) & 0x7fff
|
||||
|
||||
state = lcg_next(state)
|
||||
p2 = (state >> 16) & 0x7fff
|
||||
|
||||
state = lcg_next(state)
|
||||
p3 = (state >> 16) & 0x7fff
|
||||
|
||||
state = lcg_next(state)
|
||||
p4 = (state >> 16) & 0x7fff
|
||||
|
||||
return (c1, c2, c3, c4, p1, p2, p3, p4)
|
||||
|
||||
|
||||
def solve_serial(coeffs: tuple, s1: int, s2: int, s3: int) -> int:
|
||||
"""Solve for S4 given coefficients and S1, S2, S3.
|
||||
|
||||
From VM trace, the actual equation is:
|
||||
R0 = (C1 ^ S1 + p1) + ((C2 + S2) ^ p2) + (C3 - S3 + p3) + ((C4 ^ S4) ^ p4)
|
||||
|
||||
Each serial part is processed differently:
|
||||
- S1: XOR with C1, then ADD p1
|
||||
- S2: ADD to C2, then XOR with p2
|
||||
- S3: SUB from C3, then ADD p3
|
||||
- S4: XOR with C4, then XOR with p4
|
||||
|
||||
Solving for S4:
|
||||
TARGET = partial + ((C4 ^ S4) ^ p4)
|
||||
(C4 ^ S4) ^ p4 = TARGET - partial
|
||||
C4 ^ S4 = (TARGET - partial) ^ p4
|
||||
S4 = C4 ^ ((TARGET - partial) ^ p4)
|
||||
"""
|
||||
c1, c2, c3, c4, p1, p2, p3, p4 = coeffs
|
||||
|
||||
# Compute known contributions from S1, S2, S3
|
||||
r1 = (c1 ^ s1) + p1
|
||||
r2 = (c2 + s2) ^ p2
|
||||
r3 = (c3 - s3 + p3)
|
||||
|
||||
partial = (r1 + r2 + r3) & MASK_64
|
||||
|
||||
# Solve for S4
|
||||
needed = (TARGET - partial) & MASK_64
|
||||
s4 = c4 ^ (needed ^ p4)
|
||||
|
||||
return s4
|
||||
|
||||
|
||||
def verify_serial(username: str, s1: int, s2: int, s3: int, s4: int) -> tuple:
|
||||
"""Verify serial produces target value. Returns (result, matches)."""
|
||||
coeffs = extract_coefficients(username)
|
||||
c1, c2, c3, c4, p1, p2, p3, p4 = coeffs
|
||||
|
||||
# Emulate actual VM computation
|
||||
r1 = (c1 ^ s1) + p1
|
||||
r2 = (c2 + s2) ^ p2
|
||||
r3 = (c3 - s3 + p3)
|
||||
r4 = (c4 ^ s4) ^ p4
|
||||
|
||||
r0 = (r1 + r2 + r3 + r4) & MASK_64
|
||||
|
||||
return r0, r0 == TARGET
|
||||
|
||||
|
||||
def generate_serial(username: str) -> str:
|
||||
"""Generate complete serial for given username."""
|
||||
coeffs = extract_coefficients(username)
|
||||
s1 = s2 = s3 = 0x1337
|
||||
s4 = solve_serial(coeffs, s1, s2, s3)
|
||||
return f"{s1:X}-{s2:X}-{s3:X}-{s4:X}"
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point."""
|
||||
parser = argparse.ArgumentParser(description='Ragnarok.exe Keygen')
|
||||
parser.add_argument('username', nargs='?', help='Username to generate serial for')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Show debug info')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.username:
|
||||
username = args.username
|
||||
else:
|
||||
username = input("Enter username: ").strip()
|
||||
|
||||
if not username:
|
||||
print("Error: Username cannot be empty")
|
||||
return 1
|
||||
|
||||
serial = generate_serial(username)
|
||||
print(f"\n{'='*50}")
|
||||
print(f" Username: {username}")
|
||||
print(f" Serial: {serial}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
# Verify the generated serial
|
||||
s1 = s2 = s3 = 0x1337
|
||||
coeffs = extract_coefficients(username)
|
||||
s4 = solve_serial(coeffs, s1, s2, s3)
|
||||
result, matches = verify_serial(username, s1, s2, s3, s4)
|
||||
|
||||
if args.verbose:
|
||||
c1, c2, c3, c4, p1, p2, p3, p4 = coeffs
|
||||
print(f"\n[Debug] FNV-1a hash: 0x{fnv1a_hash(username):08X}")
|
||||
print(f"[Debug] C1=0x{c1:08X} C2=0x{c2:08X} C3=0x{c3:08X} C4=0x{c4:08X}")
|
||||
print(f"[Debug] p1=0x{p1:04X} p2=0x{p2:04X} p3=0x{p3:04X} p4=0x{p4:04X}")
|
||||
print(f"[Debug] S4=0x{s4:016X}")
|
||||
print(f"[Debug] Result: 0x{result:016X}")
|
||||
print(f"[Debug] Target: 0x{TARGET:016X}")
|
||||
|
||||
if matches:
|
||||
print("\n[OK] Serial verified!")
|
||||
else:
|
||||
print(f"\n[FAIL] Serial verification failed!")
|
||||
print(f" Expected: 0x{TARGET:016X}")
|
||||
print(f" Got: 0x{result:016X}")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main() or 0)
|
||||
@@ -0,0 +1,259 @@
|
||||
---
|
||||
name: ghidra-cli-skill
|
||||
description: >
|
||||
Use ghidra-cli for reverse engineering tasks: binary analysis, decompilation, function inspection, cross-reference analysis, pattern discovery, and binary patching.
|
||||
Activate when the user requests:
|
||||
- Binary analysis or reverse engineering
|
||||
- Decompilation or disassembly
|
||||
- Function listing, inspection, or renaming
|
||||
- Cross-reference or call graph analysis
|
||||
- String or byte pattern searches
|
||||
- Binary patching or modification
|
||||
- Ghidra project management
|
||||
---
|
||||
|
||||
# ghidra-cli
|
||||
|
||||
Use ghidra-cli for reverse engineering tasks: binary analysis, decompilation, function inspection, cross-reference analysis, pattern discovery, and binary patching.
|
||||
|
||||
## When to Use
|
||||
|
||||
Activate when the user requests:
|
||||
- Binary analysis or reverse engineering
|
||||
- Decompilation or disassembly
|
||||
- Function listing, inspection, or renaming
|
||||
- Cross-reference or call graph analysis
|
||||
- String or byte pattern searches
|
||||
- Binary patching or modification
|
||||
- Ghidra project management
|
||||
|
||||
## Workflow
|
||||
|
||||
### Pre-flight Check
|
||||
|
||||
Before running queries, verify the environment:
|
||||
|
||||
```bash
|
||||
# Check if daemon is running for fast queries
|
||||
ghidra daemon status --project <project>
|
||||
|
||||
# If not running, start it
|
||||
ghidra daemon start --project <project> --program <program>
|
||||
```
|
||||
|
||||
### Quick Start (New Binary)
|
||||
|
||||
For one-off analysis, use quick mode:
|
||||
|
||||
```bash
|
||||
ghidra quick ./binary
|
||||
ghidra daemon start --project quick-analysis --program binary
|
||||
```
|
||||
|
||||
### Full Project Setup
|
||||
|
||||
For sustained analysis:
|
||||
|
||||
```bash
|
||||
ghidra project create myproject
|
||||
ghidra import ./binary --project myproject
|
||||
ghidra analyze --project myproject --program binary
|
||||
ghidra daemon start --project myproject --program binary
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Querying Functions
|
||||
|
||||
```bash
|
||||
# List all functions
|
||||
ghidra function list --project <p> --program <prog>
|
||||
|
||||
# Filter functions by size or name
|
||||
ghidra function list --filter "size > 500"
|
||||
ghidra function list --filter "name contains 'crypt'"
|
||||
|
||||
# Get function details
|
||||
ghidra function get main
|
||||
|
||||
# Decompile to pseudocode
|
||||
ghidra function decompile main
|
||||
|
||||
# Disassemble
|
||||
ghidra function disasm main
|
||||
|
||||
# Cross-references
|
||||
ghidra function xrefs main
|
||||
ghidra function calls main
|
||||
```
|
||||
|
||||
### Search Operations
|
||||
|
||||
```bash
|
||||
# Find functions by pattern
|
||||
ghidra find function "*crypt*"
|
||||
|
||||
# Find strings
|
||||
ghidra find string "password"
|
||||
|
||||
# Find byte patterns (hex)
|
||||
ghidra find bytes "4883ec08"
|
||||
|
||||
# Find crypto constants
|
||||
ghidra find crypto
|
||||
|
||||
# Find suspicious patterns (anti-analysis, obfuscation)
|
||||
ghidra find interesting
|
||||
```
|
||||
|
||||
### Cross-References
|
||||
|
||||
```bash
|
||||
# References TO an address
|
||||
ghidra x-ref to 0x401000
|
||||
|
||||
# References FROM an address
|
||||
ghidra x-ref from 0x401000
|
||||
```
|
||||
|
||||
### Call Graphs
|
||||
|
||||
```bash
|
||||
# Full call graph
|
||||
ghidra graph calls
|
||||
|
||||
# Who calls this function (callers)
|
||||
ghidra graph callers main --depth 3
|
||||
|
||||
# What does this function call (callees)
|
||||
ghidra graph callees main --depth 3
|
||||
|
||||
# Export as DOT format
|
||||
ghidra graph export dot
|
||||
```
|
||||
|
||||
### Symbols and Strings
|
||||
|
||||
```bash
|
||||
# List symbols
|
||||
ghidra symbol list
|
||||
|
||||
# List strings
|
||||
ghidra strings list --limit 100
|
||||
|
||||
# References to a string
|
||||
ghidra strings refs "error"
|
||||
```
|
||||
|
||||
### Memory and Types
|
||||
|
||||
```bash
|
||||
# Memory map
|
||||
ghidra memory map
|
||||
|
||||
# Read memory at address
|
||||
ghidra memory read 0x401000 64
|
||||
|
||||
# List data types
|
||||
ghidra type list
|
||||
|
||||
# Apply type to address
|
||||
ghidra type apply 0x402000 "char[32]"
|
||||
```
|
||||
|
||||
### Modifications
|
||||
|
||||
```bash
|
||||
# Rename function
|
||||
ghidra function rename sub_401000 decrypt_password
|
||||
|
||||
# Add comment
|
||||
ghidra comment set 0x401000 "Key derivation starts here"
|
||||
|
||||
# Patch bytes
|
||||
ghidra patch bytes 0x401000 "90909090"
|
||||
|
||||
# NOP instructions
|
||||
ghidra patch nop 0x401010 --count 5
|
||||
|
||||
# Export patched binary
|
||||
ghidra patch export --output patched.bin
|
||||
```
|
||||
|
||||
### Scripting
|
||||
|
||||
```bash
|
||||
# Run Python script
|
||||
ghidra script run analysis.py
|
||||
|
||||
# Inline Python
|
||||
ghidra script python "print(currentProgram.getName())"
|
||||
|
||||
# Batch commands from file
|
||||
ghidra batch commands.txt
|
||||
```
|
||||
|
||||
## Output Handling
|
||||
|
||||
ghidra-cli outputs JSON by default. Parse the structured data:
|
||||
|
||||
```bash
|
||||
# JSON output (default)
|
||||
ghidra function list
|
||||
|
||||
# Table format for display
|
||||
ghidra function list --format table
|
||||
|
||||
# Count only
|
||||
ghidra function list --format count
|
||||
```
|
||||
|
||||
When processing results, extract relevant fields from JSON rather than displaying raw output.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Investigate a Function
|
||||
|
||||
```bash
|
||||
ghidra function get <name> # Overview
|
||||
ghidra function decompile <name> # Pseudocode
|
||||
ghidra function calls <name> # What it calls
|
||||
ghidra function xrefs <name> # Who calls it
|
||||
ghidra graph callers <name> --depth 2
|
||||
```
|
||||
|
||||
### Find Interesting Code
|
||||
|
||||
```bash
|
||||
ghidra find crypto # Crypto constants
|
||||
ghidra find interesting # Suspicious patterns
|
||||
ghidra find function "*alloc*" # Memory functions
|
||||
ghidra strings list --filter "length > 50"
|
||||
```
|
||||
|
||||
### Trace Data Flow
|
||||
|
||||
```bash
|
||||
ghidra x-ref to <address> # Who writes here
|
||||
ghidra x-ref from <address> # What this references
|
||||
ghidra graph callees <func> --depth 3
|
||||
```
|
||||
|
||||
## Error Recovery
|
||||
|
||||
| Situation | Resolution |
|
||||
| ------------------ | ------------------------------------------------------------- |
|
||||
| Daemon not running | `ghidra daemon start --project <p> --program <prog>` |
|
||||
| No project exists | `ghidra project create <name>` or use `ghidra quick <binary>` |
|
||||
| Function not found | Use `ghidra find function "*pattern*"` to search |
|
||||
| Address format | Use hex with 0x prefix: `0x401000` |
|
||||
| Slow queries | Start daemon for sub-second response times |
|
||||
|
||||
## Global Options
|
||||
|
||||
All commands accept:
|
||||
- `--project <name>` - Target project
|
||||
- `--program <name>` - Target program within project
|
||||
- `--format json|table|count` - Output format
|
||||
- `--filter <expr>` - Filter expression
|
||||
- `--limit <N>` - Max results
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FILE_INFO>
|
||||
<BASIC_INFO>
|
||||
<STATE NAME="CONTENT_TYPE" TYPE="string" VALUE="Program" />
|
||||
<STATE NAME="PARENT" TYPE="string" VALUE="/" />
|
||||
<STATE NAME="FILE_ID" TYPE="string" VALUE="7f01199e7609371500416946" />
|
||||
<STATE NAME="FILE_TYPE" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="READ_ONLY" TYPE="boolean" VALUE="false" />
|
||||
<STATE NAME="NAME" TYPE="string" VALUE="thevoid.exe" />
|
||||
</BASIC_INFO>
|
||||
</FILE_INFO>
|
||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
VERSION=1
|
||||
/
|
||||
00000000:thevoid.exe:7f01199e7609371500416946
|
||||
NEXT-ID:1
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
@@ -0,0 +1,5 @@
|
||||
VERSION=1
|
||||
/
|
||||
00000000:thevoid.exe:7f01199e7609371500416946
|
||||
NEXT-ID:1
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FILE_INFO>
|
||||
<BASIC_INFO>
|
||||
<STATE NAME="OWNER" TYPE="string" VALUE="kiselev" />
|
||||
</BASIC_INFO>
|
||||
</FILE_INFO>
|
||||
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user