MadhuFarai
MadhuFarai
Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: accumulator_reconstruction, c_truncated_remainder_simulation, dot_product_targeting, static_analysis
Summary
Walkthrough of the CUSTOM Reversing MadhuFarai. Task: 32-bit PE32 console crackme (MadhuFarai.exe) reads N and N points, accumulates an even-index-gated dot product mod 32000 and checks Result == -17564. Solution: reverse main() with radarea2, reco
Description
Flag is your numbers separated by comma. Example:
4,1,1,2,2,3,3,4,4for N = 4 and points[#3] => (1,1) [#2] => (2,2) [#1] => (3,3) [#0] => (4,4). There are many correct flags.
English summary: We are given a 32-bit PE32 console executable MadhuFarai.exe (Intel 80386, MS Windows, linked against msvcrt). The program reads an integer N, then N points (x, y), computes a Result from them, and prints You are welcome! on success or Go out of here! on failure. The flag is the comma-joined list of the numbers we fed the program (first N, then the point coordinates in input order #(N-1) ... #0). Because the success condition is a single equation, many valid flags exist.
Analysis
The real main lives at 0x401000 (called from entry0). Reversed with radare2.
Control flow
-
scanf("%d", &N)followed by gate checks:N > 2elseNot enough!N <= 100elseToo many!N % 2 == 0elseNot even!- So
Nmust be even, in range4..100.
-
A loop reads points for index
i = N-1down to0(var_14h = i). For each point:- prints
[#%d] Input valid x and y: scanf("%d %d", &x, &y)
- prints
-
Input validation (re-prompts until satisfied): a point is accepted only if
|x| >= 3 AND |y| >= 3. The disassembly checks(x>=3 || x<=-3) AND (y>=3 || y<=-3); otherwise it loops back and re-asks. There is NO upper bound on coordinates — the key semantic trap is that the bound is|coord| >= 3, not<= 3. -
The accumulator
Result(var_8h) starts at0. Only for EVENiit adds:Result += (x_i * prev_x + y_i * prev_y) % 32000where
prev_x/prev_yare the previous iteration's coordinates.%32000is a C truncated remainder (the binary doesidivby0x7d00 = 32000and takesedx).prev_x/prev_yare updated to(x_i, y_i)on every iteration.Because
Nis even,istarts odd (N-1), so the first iteration only setsprevbefore any even-index term is computed → the uninitializedprevnever affectsResult. -
Success condition:
Result == 0xffffbb64(signed -17564) →You are welcome!.
So:
Result = sum over even i of ( dot(point_i, point_{i+1}) % 32000 ) == -17564
with every coordinate satisfying |x| >= 3 and |y| >= 3.
Solution
Pick the smallest legal N = 4. Input order is i = 3, 2, 1, 0, i.e. points p3, p2, p1, p0.
The even-i terms are:
i = 2:dot(p2, p3)i = 0:dot(p0, p1)
Choose two pairs:
-
Pair (p3, p2) — make it zero (orthogonal):
p3 = (3, -3),p2 = (3, 3)→dot = 3*3 + 3*(-3) = 0→ termi=2 = 0. -
Pair (p1, p0) — carry the full target via an unbounded large coordinate:
p1 = (4, 3),p0 = (-4388, -4)→dot = (-4388)*4 + (-4)*3 = -17552 - 12 = -17564→ since|-17564| < 32000the remainder is unchanged → termi=0 = -17564.
Total: Result = 0 + (-17564) = -17564 = target. All four points satisfy |x| >= 3 and |y| >= 3.
The flag is N followed by the point pairs in input order (#3, #2, #1, #0):
4, (3,-3), (3,3), (4,3), (-4388,-4)
→ REDACTED.
Verification script
python read-only • copy allowed#!/usr/bin/env python3 import math def crem(a, m): # C truncated-division remainder (sign of dividend), matching idiv/edx return int(math.fmod(a, m)) def simulate(N, points): # points are in input order: index i goes N-1 .. 0 # points[k] corresponds to i = N-1-k result = 0 prev_x = prev_y = 0 for k in range(N): i = N - 1 - k x, y = points[k] if i % 2 == 0: result += crem(x * prev_x + y * prev_y, 32000) prev_x, prev_y = x, y return result # Flag points in input order #3,#2,#1,#0 pts = [(3, -3), (3, 3), (4, 3), (-4388, -4)] print(simulate(4, pts)) # -> -17564 == target 0xffffbb64
Running it prints -17564, matching the success constant 0xffffbb64.
Note: the PE could not be executed on the ARM macOS host (wine-under-qemu crashes with an
anon_mmap_fixedassertion). Static analysis with radare2 plus the Python simulation were authoritative, and the server accepted the flag viaPOST flag.
Key Takeaways / Lessons
- Mastering accumulator_reconstruction, c_truncated_remainder_simulation, dot_product_targeting, static_analysis was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-06-15 • CUSTOM • MadhuFarai • Difficulty: Medium