CS2110 Reverse Engineering
This is not an official NUS module. It is an event organized and hosted by NUS Greyhats
crackme1
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int seed; // [rsp+0h] [rbp-10h] BYREF
int v5; // [rsp+4h] [rbp-Ch]
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
printf("Whats the secret key?\n>> ");
__isoc99_scanf("%d", &seed);
v5 = do_stuff(&seed);
srand(seed);
puts("Here's your flag. If you got the right key, the flag would output correctly!");
get_flag();
if ( !v5 )
puts("You did not get the right key, unfortunately. Try again!");
return 0;
}The program prompts the user for an integer, performs some transformations on it via do_stuff(), then uses the value as the seed to srand(). The flag is printed using get_flag(), which relies on rand().
If the original input does not satisfies the condition inside do_stuff(), the program prints a fail message.
Zooming into the function (do_stuff()):
Working the math backwards gives: 67.24979457682826
└─$ grey{cr4ck3d_an0th3r_one,nice!}
crackme
The program checks for the following condition: 13 * input == -559155996
But you can’t simply divide -559155996 by 13 because this value is stored in a 32-bit DWORD, and the number -559155996 doesn’t represent a true mathematical negative in this context. It’s actually the result of signed integer overflow.
To solve it properly, we must reinterpret -559155996 as an unsigned 32-bit value. Once we convert it to its unsigned form, then we can divide that value by 13 to recover the correct input.
└─$ grey{cr4ck3d_an0th3r_one,nice!}
puzzle
The program runs a Wordle game by selecting a random word from an encoded word list, decoding it, and then checking the player’s input against it.
func1: selects a random word from the list and deciphers it
func2 / func3: validate user input
func4: prints invalid
func5: generates the flag when the guess is correct
We can set a breakpoint right after the routine computes the target word and inspect the stack for the recovered word, then simply entered that value.
└─$ grey{cr4ck3d_an0th3r_one,nice!}
antidbg
The programs runs a password checker that uses the password as the key to decrypt the encrypted flag. The checker() function handles the validation of the password and this function is mapped dynamically at runtime.
Additionally, the binary includes anti-debugging checks that print "huh?" if a debugger is detected.
To bypass the debugger check, I simply patched the Zero Flag after the cmp instruction to 1.
Now we can obtain the mapped checker() function by tracing the address returned from the mapping routine.
└─$ grey{ur3_qu1t3_g00d_4h}
bomb
The program expects three integers, and checks each one against a different function:
Layer 1 : always return 227
Layer 2 : Fibonacci with
n = 21Layer 3 : Sieve of Eratosthenes withn = 1337
└─$ grey{k33p_t41k1ng_4nd_n0b0dy_expl0d35!}
packed
The program consists of two layers:
Mantle Layer : Memory-mapped blob thats XORed against a 4-byte key
Inner Layer : Flag checker
From the hint in the challenge, we can easily derive the XOR key 2110, which decrypts the memory‑mapped blob into a valid ELF.
After entering the key, we can set a breakpoint at ((void (*)(void))(addr + 4352))() to step into the decrypted code and inspect the Inner Layer logic. This inner function turns out to be a simple flag checker that validates the input by comparing it against a permuted version of a hard‑coded string. Reversing the permutation get us the flag:
└─$ grey{unp4ck1ng_grandmaster!}
Last updated