Description
The fourth assembly dump loads 0x9fe1a, then conditionally subtracts 0x65 (the value exceeds the 0x2710 threshold, so the SUB branch is taken instead of the ADD branch). Convert the result to decimal for the flag.
Setup
Fetch the dump and focus on the subtraction at <+31>.
wget https://artifacts.picoctf.net/c/511/disassembler-dump0_d.txtcat disassembler-dump0_d.txtSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Track the operationsObservationI noticed the dump contains a CMP against 0x2710 followed by a JLE and two arithmetic branches (ADD and SUB on EAX), which suggested I needed to evaluate the concrete value 0x9fe1a against the threshold to determine which branch actually executes before I could identify the final EAX value.EAX is assigned 0x9fe1a and then SUB 0x65 executes. No other arithmetic touches the register before return. Before the SUB executes, the dump compares [rbp-0x4] against 0x2710 and jumps to an ADD 0x65 branch if the value is less-than-or-equal; because 0x9fe1a (654874) exceeds 0x2710 (10000), the jle is not taken and the SUB path runs instead.Learn more
The SUB instruction subtracts a value from a register or memory location in place.
sub eax, 0x65means EAX = EAX - 0x65. Like ADD, it modifies flags (carry, overflow, zero, sign) that conditional branches can test - but since this function just returns after the subtract, the flags are irrelevant here.A key skill this challenge reinforces is verifying that no other instructions between the SUB and the RET touch EAX. In a longer function there might be other instructions that modify EAX - a PUSH/POP pair, a CALL that overwrites it, or a conditional MOV. Confirming that the final value is indeed the post-subtraction result requires scanning every subsequent instruction for EAX writes.
This kind of careful instruction-by-instruction analysis is exactly what reverse engineers do when tracing through license-key validation routines, cryptographic transformations, or anti-debugging checks. The habit of asking "does anything modify this value before it reaches the point I care about?" prevents mistakes that could waste hours during a real engagement.
Step 2
Compute the resultObservationI noticed the SUB branch is taken with EAX = 0x9fe1a and operand 0x65, which suggested using Python to evaluate 0x9fe1a - 0x65 directly so hex borrows are handled automatically and the decimal flag value is produced without manual error.Evaluate 0x9fe1a - 0x65 = 654,773 and wrap it with picoCTF{...}.pythonpython3 - <<'PY' print(0x9fe1a - 0x65) PYExpected output
654773
What didn't work first
Tried: Subtract in the other direction: 0x65 - 0x9fe1a, expecting the smaller constant to be the minuend.
Python returns a large negative number (-654773) because the operand order matters. The assembly reads 'sub eax, 0x65', which is EAX minus the constant, not the constant minus EAX. Reading the mnemonic carefully and keeping the destination register as the left operand gives the correct positive result.
Tried: Take the ADD branch result instead (0x9fe1a + 0x65 = 654,975) because the challenge description mentions both ADD and SUB paths.
The jle jump at the branch point is NOT taken because 0x9fe1a (654874) is greater than 0x2710 (10000), so control falls through to the SUB instruction. The ADD path only executes when the value is less than or equal to 0x2710. Using the ADD result produces the wrong flag.
Learn more
Subtraction in hex follows the same rules as in decimal, but carries propagate in powers of 16 instead of 10.
0x9fe1a - 0x65: the low byte1a(26 decimal) minus65(101 decimal) would go negative, triggering a borrow, givingb5(181 decimal) in the result's low byte. Letting Python handle this avoids manual borrow tracking.Notice that 0x65 is the ASCII code for 'e'. While this is coincidental here, it illustrates how the same number carries different meanings depending on context - always ask whether a constant is meant to be an integer, a character, a flag bitmask, or something else. In this series the constants are pure integers, but in real binaries this ambiguity appears constantly.
Completing all four Bit-O-Asm challenges builds the core loop of assembly reading: locate the relevant instructions, trace the data flow, apply any arithmetic, and convert to the required representation. This loop scales directly to reading real compiler output, whether you are auditing a closed-source binary, analyzing a firmware image, or studying how a C compiler optimizes a particular expression.
Interactive tools
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
- Number Base ConverterConvert numbers between binary, octal, decimal, and hexadecimal instantly. Enter any value and see all four bases update in real time.
Flag
Reveal flag
picoCTF{...}
Double-check the direction of the subtraction: EAX minus 0x65, not the other way around.