refunc
refunc
Platform: spbctf | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-06-24 | Status: Solved Techniques: computed_goto_vm_recognition, dead_code_discovery, docker_cross_arch_run, rel32_call_patching, string_xref_analysis
Summary
Task: stripped x86-64 PIE ELF (kids/junior rev track) whose normal control flow never reaches the flag-printing code. Solution: locate the dead 'secret' function via string cross-references, then patch main's rel32 call to reroute control flow into it (avoiding a full reverse of the embedded bytecode VM) and run the patched binary under Docker linux/amd64.
refunc — spbctf
Description
File:
https://rev-kids20.forkbomb.ru/files/rev/new7/refunc.elfJunior / kids rev track ("rev-kids20").
A stripped x86-64 PIE ELF is provided. Running it normally prompts for a name and
only ever prints Hello, world. The flag-printing logic exists in the binary but
is never reached. The task name refunc ("re-func" / reconstruct the function
call path) is the hint: we must reroute execution into a dead "secret" function.
Analysis
Recon
file reports a stripped x86-64 LSB PIE executable, dynamically linked for
GNU/Linux 3.2.0, BuildID sha1 bb42a3a68414d1e912b58880f9bdb18b695acd1a, ~102 KB.
strings reveals the interesting rodata:
Enter your name:Hello, worldHello, admin! Do you remember secret function?World, hello, its secret!
Notable imports: strncmp, sscanf, getchar, fopen/fclose, malloc,
fprintf, syscall.
main() — 0x1286
main calls a name-reading helper fcn.0x120b (prints Enter your name:,
mallocs 0x64 bytes, reads chars with getchar until 0/newline/0xff). It then
checks cmp al, 0x61 ('a') on the first character. Both branches eventually
fall into fcn.0x11e5, which only prints Hello, world. So the normal execution
path can never print the flag — no input bypasses it.
Finding the secret function
Cross-referencing the rodata strings (0x9018 = "Hello, admin!..." and
0x9058 = "World, hello, its secret!") shows they are referenced only by two
functions that nothing in the program ever calls:
fcn.0x11f8— printsHello, admin! Do you remember secret function?fcn.0x12c4— the secret function: printsWorld, hello, its secret!thencall 0x5b2d.
These are pure dead code; no edge in the normal control-flow graph reaches them. The challenge name refunc points directly at reconstructing the missing call.
The VM at fcn.0x5b2d
fcn.0x5b2d (~3951 bytes) is a stack-based bytecode interpreter:
- Initializes via
fcn.0x509d, setting the instruction pointer (var_640h) to the bytecode program at 0xe4c0 plus a constant-table pointer. - Main dispatch: reads a byte at IP,
sub eax, 5,cmp eax, 0xf9(bounds check), then indexes a 250-entry jump table at 0x9cb8 (jmp rax) — a classic computed-goto dispatch compiled from a largeswitch. - Opcode handlers implement stack ops: push immediate, add, xor, pop
(e.g.
xor edx, ecx; mov [rax], edxthensub var_638h, 8). - A nested sub-dispatch (switch table at
0xa0a0, 10 cases) implements I/O opcodes:printf,puts, and rawwritevia syscall wrappers (fcn.0x1872= write wrapper,fcn.0x6d2e= output helper). This is what emits the computed flag. - The bytecode at
0xe4c0shows a clear pattern: a descending data byte (0x68='h',0x67='g',0x66='f',0x65='e', ...) interleaved with opcodes0x31, 0x1d, 0x05, 0x95, 0x7b, 0x56, 0x2c, 0xad.
Rather than fully emulate the VM, we just need to reach fcn.0x12c4, which runs
the VM that prints the flag.
Solution
Patch main so that, instead of calling the name-reading helper, it calls the
secret function fcn.0x12c4.
At file offset 0x1293, main contains call fcn.0x120b, encoded as the rel32
near-call e8 73 ff ff ff. We rewrite the target to 0x12c4:
rel32 = 0x12c4 - (0x1293 + 5) = 0x2c
new bytes: e8 2c 00 00 00
Patch script:
python read-only • copy allowed#!/usr/bin/env python3 data = bytearray(open('refunc.elf', 'rb').read()) off = 0x1293 assert data[off:off+5] == bytes.fromhex('e873ffffff') # call fcn.0x120b data[off:off+5] = bytes.fromhex('e82c000000') # call 0x12c4 open('refunc_patched.elf', 'wb').write(data)
The host is macOS, which can't exec a Linux ELF, so run the patched binary under
Docker (--platform linux/amd64):
bash read-only • copy alloweddocker run --rm --platform linux/amd64 -v "$PWD":/w -w /w debian:bullseye-slim \ bash -c "echo test | ./refunc_patched.elf"
Output:
World, hello, its secret!
start...
spbctf{REDACTED}
Hello, world
The patched call routes execution into fcn.0x12c4, which prints the banner and
invokes the VM at 0x5b2d; the VM's bytecode program at 0xe4c0 computes and
writes the flag — no full VM reversing required.