← Back to Writeups
CUSTOMMediumReversing

Крестики (Krestiki)

XESXOR9/2/20264 min read
#reversing#custom#medium#mathematical_equation_reversal#pe64_static_analysis#utf16_string_extraction#xor_add_sub_chain_solving#windows

Крестики (Krestiki)

Platform: hackerlab | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Windows | Author: XESXOR | Date: 2025-12-27 | Status: Solved Techniques: mathematical_equation_reversal, pe64_static_analysis, utf16_string_extraction, xor_add_sub_chain_solving

Summary

Task: Windows Forms crackme with mathematical validation. Solution: Reverse XOR/ADD/SUB equation chains to find 4 numbers for the flag format CODEBY{NNNN-NNNN-NNNN-NNNN}.

Крестики (Krestiki) — HackerLab

Description

"Сможешь решить классический крякми на крестиках и формочках? :)"

Classic C++ crackme with Windows Forms. The name "krestiki i formochki" (crosses and forms) is a hint at C++ (crosses/plus signs) and Windows Forms.

Analysis

File Identification

bash
file krestiki.exe
# PE32+ executable (GUI) x86-64, for MS Windows

64-bit Windows GUI application — typical crackme.

String Extraction

UTF-16 strings found in .rdata section:

bash
strings -el krestiki.exe | head -20

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key findings:

  • Крестики — window title
  • Имя: — name field
  • Флаг: — flag field
  • Проверить — check button
  • CODEBY{ — flag prefix
  • MASTER_OF_CODEBY — expected username
  • Success/failure messages in Russian

Validation Logic

From disassembly (0x1400013a0):

  1. Flag length check: exactly 27 characters (0x1b)
  2. Format check:
    • Starts with CODEBY{
    • Ends with }
    • Contains 4 groups of 4 digits, separated by -
    • Format: CODEBY{NNNN-NNNN-NNNN-NNNN}
  3. Name check: must be MASTER_OF_CODEBY ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Mathematical Checks

At address 0x1400015a1, found 4 equations for each number:

Number 1 (esi): (n1 ^ 0xdfaf7 + 0x151336e) ^ 0x23b97b == 0x17cab7e
Number 2 (ebx): ((n2 ^ 0x378) + 0x551) ^ 0xb84c == 0x9e14
Number 3 (ebp): (n3 - 0x2443) ^ 0xa808 == 0xffff4681
Number 4 (edx): ((n4 ^ 0xfd836) - 0x3338) ^ 0xbc3f == 0xf1584

Solution

Reverse Solving the Equations

For each number, perform operations in reverse order:

python
#!/usr/bin/env python3
"""
Крестики (Krestiki) - HackerLab Reverse Engineering Challenge
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Reverse XOR/ADD/SUB equations to find the 4 numbers in the flag.
Format: CODEBY{NNNN-NNNN-NNNN-NNNN}
"""

# Number 1: (n1 ^ 0xdfaf7 + 0x151336e) ^ 0x23b97b == 0x17cab7e
# Reverse: n1 = ((0x17cab7e ^ 0x23b97b) - 0x151336e) ^ 0xdfaf7
result1 = 0x17cab7e ^ 0x23b97b  # Undo final XOR
result1 -= 0x151336e            # Undo ADD
result1 ^= 0xdfaf7              # Undo first XOR
n1 = result1
print(f"Number 1: {n1}")  # 9312

# Number 2: ((n2 ^ 0x378) + 0x551) ^ 0xb84c == 0x9e14
# Reverse: n2 = ((0x9e14 ^ 0xb84c) - 0x551) ^ 0x378
result2 = 0x9e14 ^ 0xb84c      # Undo final XOR
result2 -= 0x551               # Undo ADD
result2 ^= 0x378               # Undo first XOR
n2 = result2
print(f"Number 2: {n2}")  # 8831
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Number 3: (n3 - 0x2443) ^ 0xa808 == 0xffff4681
# Reverse: n3 = (0xffff4681 ^ 0xa808) + 0x2443
# Note: 0xffff4681 is a 32-bit signed negative representation
result3 = 0xffff4681 ^ 0xa808  # Undo XOR
result3 = (result3 & 0xffffffff)  # Keep 32-bit
result3 += 0x2443              # Undo SUB
n3 = result3 & 0xffff          # Take lower 16 bits
print(f"Number 3: {n3}")  # 4812

# Number 4: ((n4 ^ 0xfd836) - 0x3338) ^ 0xbc3f == 0xf1584
# Reverse: n4 = ((0xf1584 ^ 0xbc3f) + 0x3338) ^ 0xfd836
result4 = 0xf1584 ^ 0xbc3f     # Undo final XOR
result4 += 0x3338              # Undo SUB
result4 ^= 0xfd836             # Undo first XOR
n4 = result4
print(f"Number 4: {n4}")  # 1221
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Construct the flag
flag = f"CODEBY{{{n1}-{n2}-{n3}-{n4}}}"
print(f"\nFlag: {flag}")

Result

Number 1: 9312
Number 2: 8831
Number 3: 4812
Number 4: 1221

Flag: CODEBY{REDACTED}

Verification

To verify, substitute the numbers back into the equations:

python
# Verification
def verify(n1, n2, n3, n4):
    check1 = ((n1 ^ 0xdfaf7) + 0x151336e) ^ 0x23b97b
    check2 = ((n2 ^ 0x378) + 0x551) ^ 0xb84c
    check3 = (n3 - 0x2443) ^ 0xa808
    check4 = ((n4 ^ 0xfd836) - 0x3338) ^ 0xbc3f
    
    print(f"Check 1: {hex(check1)} == 0x17cab7e: {check1 == 0x17cab7e}")
    print(f"Check 2: {hex(check2)} == 0x9e14: {check2 == 0x9e14}")
    print(f"Check 3: {hex(check3 & 0xffffffff)} == 0xffff4681: {(check3 & 0xffffffff) == 0xffff4681}")
    print(f"Check 4: {hex(check4)} == 0xf1584: {check4 == 0xf1584}")
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

verify(9312, 8831, 4812, 1221)

Notes

  • Task name: "Крестики и формочки" (crosses and forms) — wordplay: "кресты" (crosses) = C++, "формочки" (forms) = Windows Forms
  • MASTER_OF_CODEBY: Username that must be entered to pass validation
  • Signed vs Unsigned: Number 3 uses 32-bit signed representation (0xffff4681), requiring careful handling
  • Reverse Engineering Flow: XOR → ADD/SUB → XOR — typical obfuscation chain, solved in reverse order
  • Static Analysis: Task is solved entirely statically, without running the binary ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍