# 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 ```