# CTF Reverse Engineering Cheatsheet
## Prompts
```!
I want you to act as an expert Reverse Engineering Assistant.
Our goal is to collaboratively reverse engineer a program. Your role is to guide the analysis by building an increasingly accurate theory of the program's purpose based on the information I provide. Our workflow will follow a strict, iterative process I call the "Hypothesis Loop":
Analyze: I will provide you with a snippet of decompiled code or runtime data. You will first analyze its immediate, local logic—explaining what the code does, what data it manipulates, and how.
Hypothesize & Score: Based on all the information gathered so far, you will update (or create) a primary hypothesis about the program's overall purpose. You must state this hypothesis clearly and attach a confidence level (as a percentage) to it. Example: "Hypothesis: This program is a custom file packer. Confidence: 35%."
Prioritize & Request: To improve your confidence, you must identify and ask for the single most critical piece of information we need next. This will be your "Priority Request." You must explain why this specific piece of information (e.g., the code for a specific function, a variable's value at a certain breakpoint) is the most important for verifying or refuting your current hypothesis.
Example: "Priority Request: Please provide the decompiled code for sub_401180. My confidence will increase significantly if it performs compression algorithms, or decrease if it contains network code."
Repeat: I will provide the information from your Priority Request, and the loop begins again. You will integrate the new information, update your analysis, and provide a revised hypothesis, confidence score, and a new Priority Request. You must follow these guiding principles throughout our entire conversation:
No Unfounded Assumptions: Your analysis must be strictly grounded in the code and data I provide. Never assume what an unseen function does based on its name or common patterns. State what is known and what is unknown.
Evidence-Based Reasoning: You must always justify your hypothesis, confidence score, and Priority Request by explicitly referencing specific lines of code or data points we have already discussed.
Strategic Inquiry: Your Priority Request should always be aimed at the "highest value" target—the single piece of information that will give us the most clarity and move our investigation forward the fastest. If you understand and agree to this methodology, please confirm, and I will provide the first function for analysis.
The binary that will be reversing is ...
More details that you should know about:
- ...
```
## GDB
### Break and Scrape Register
```python=
import gdb
class PrintRDI(gdb.Command):
def __init__(self):
super().__init__("solp", gdb.COMMAND_OBSCURE)
def invoke(self,arg,from_tty):
gdb.execute('b*0x401933')
gdb.execute('r')
a = ''
for _ in range(100):
try:
res = gdb.selected_frame().read_register('rdi')
gdb.execute('c')
a += chr(res)
except : break
print(a)
PrintRDI()
```
## IDA
### Break and Scrape Register
```python=
import idaapi
import idc
def solp():
target_addr = 0x401933
max_reads = 100
# Set breakpoint
idc.AddBpt(target_addr)
# Start the debugger
if not idaapi.run_to(target_addr):
print("Failed to run to the breakpoint")
return
idaapi.get_debugger().wait_for_next_event(idaapi.WFNE_SUSP, -1)
result = ""
for _ in range(max_reads):
try:
rdi_val = idc.GetRegValue("RDI")
result += chr(rdi_val)
# Continue until next breakpoint
if idaapi.get_debugger().run_requests_pending():
idaapi.get_debugger().wait_for_next_event(idaapi.WFNE_CONT, -1)
else:
idaapi.continue_process()
idaapi.get_debugger().wait_for_next_event(idaapi.WFNE_SUSP, -1)
# Check if we are still at the breakpoint
if idc.here() != target_addr:
break
except Exception as e:
print("Exception occurred:", e)
break
print("Result:", result)
# Run the function
solp()
```
## Lief
Takes an ELF executable and convert it to .so
[Documentation](https://lief.re/doc/latest/tutorials/08_elf_bin2lib.html)
[Write-Up](https://keymoon.hatenablog.com/entry/2024/10/07/230734#ctypesCDLL%E3%82%92%E7%94%A8%E3%81%84%E3%81%A6Python%E3%81%8B%E3%82%89update%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)
### AlpacaHack - Simple Flag Checker
C compiled binary
```python=
import lief
bin = lief.parse("./checker")
bin.add_exported_function(bin.get_function_address("update"), "update")
bin[lief.ELF.DynamicEntry.TAG.FLAGS_1].remove(lief.ELF.DynamicEntryFlags.FLAG.PIE)
bin.write("checker.so")
```
```python=
#!/usr/bin/env python3
import ctypes
from pwn import *
flag_len = 49
lib = ctypes.CDLL('./checker.so')
exe = ELF('./checker', checksec=0)
def update(state, char):
state_arr = ctypes.create_string_buffer(state)
lib.update(state_arr, ctypes.c_char(char))
return bytes(state_arr)
cur_state = b'\x00' * 36
# put table from ELF to list
table = []
for i in range(flag_len):
ti = exe.read(exe.sym.table + 16 * i, 16)
table.append(ti)
flag = ""
state = b"\x00" * 36
for i in range(flag_len):
for c in range(0x20, 0xff):
next_state = update(state, c)
if next_state[:16] == table[i]:
flag += chr(c)
state = next_state
break
print(flag)
```
### ImaginaryCTF 2025 - nimrod
Nim compiled binary
```python=
import lief
bin = lief.parse("./nimrod")
bin.add_exported_function(bin.get_function_address("xorEncrypt__nimrod_46"), "xorEncrypt__nimrod_46")
bin.add_exported_function(bin.get_function_address("NimMain"), "NimMain")
# bin.add_exported_function(bin.get_function_address("NimMainModule"), "NimMainModule")
bin[lief.ELF.DynamicEntry.TAG.FLAGS_1].remove(lief.ELF.DynamicEntryFlags.FLAG.PIE)
bin.write("nimrod.so")
```
```python=
#!/usr/bin/env python3
import ctypes
lib = ctypes.CDLL('./nimrod.so')
# CRUCIAL: Run NimMain
lib.NimMain()
NI = ctypes.c_longlong
encrypted_flag = bytes.fromhex('28F83EE63E2F430CB996D15CD6BF36D820790E8E5221B250E398B5C9B8A08830D90A0000000000000000000000000000').strip(b'\x00')
lib.xorEncrypt__nimrod_46.argtypes = [ctypes.c_void_p, NI]
lib.xorEncrypt__nimrod_46.restype = ctypes.c_void_p
class NimStr(ctypes.Structure):
_fields_ = [('length', NI), ('cap', NI)]
HDR_SZ = ctypes.sizeof(NimStr)
def make_nim_string(data: bytes) -> ctypes.Array:
n = len(data)
cap = n
buf = ctypes.create_string_buffer(HDR_SZ + n + 1)
hdr = NimStr.from_buffer(buf)
hdr.length = n
hdr.cap = cap
ctypes.memmove(ctypes.addressof(buf) + HDR_SZ, data, n)
buf[HDR_SZ + n] = 0
return buf
def read_nim_string(ptr: int) -> bytes:
hdr = NimStr.from_address(ptr)
start = ptr + HDR_SZ
return ctypes.string_at(start, hdr.length)
def xorEncrypt_py(b: str) -> bytes:
a = b.encode()
inbuf = make_nim_string(a)
res_ptr = lib.xorEncrypt__nimrod_46(ctypes.addressof(inbuf), 0x13371337)
return read_nim_string(res_ptr)
flag = 'ictf{'
from string import printable
while True:
for ch1 in printable:
guess = flag + ch1
res = xorEncrypt_py(guess)
if encrypted_flag.startswith(res):
flag = guess
break
if flag[-1] == '}':
print(f'Flag: {flag}')
break
```