> For the complete documentation index, see [llms.txt](https://x44ylan.gitbook.io/write-ups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://x44ylan.gitbook.io/write-ups/cs2110-reverse-engineering.md).

# CS2110 Reverse Engineering

> This is not an official NUS module. It is an event organized and hosted by NUS Greyhats

## crackme1

```c
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()`.&#x20;

If the original input does not satisfies the condition inside `do_stuff()`, the program prints a fail message.

Zooming into the function (`do_stuff()`):

```c
*seed *= 1217;
*seed += 1111;
*seed -= 333;
*seed ^= 0x3213u;
return *seed == 95134;
```

Working the math backwards gives: 67.24979457682826

```
Whats the secret key?
>> 67.24979457682826
Here's your flag. If you got the right key, the flag would output correctly!
grey{cr4ck3d_an0th3r_one,nice!}
```

`└─$ grey{cr4ck3d_an0th3r_one,nice!}`&#x20;

## crackme

```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
  _DWORD v4[3]; // [rsp+Ch] [rbp-14h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("Whats the secret password?\n>> ");
  __isoc99_scanf("%d", v4);
  v4[2] = 116025;
  v4[1] = 13 * v4[0] + 117259;
  if ( 13 * v4[0] == -559155996 )
  {
    puts("Correct!");
    gtflg(28, v4);
  }
  else
  {
    puts("Wrong :c");
  }
  return 0;
}
```

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.

```
Whats the secret password?
>> 287370100
Correct!
grey{y0u_r_cr4ck3d_at_th1s!}
```

`└─$ grey{cr4ck3d_an0th3r_one,nice!}`

## puzzle

```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // ebp
  char secret[5]; // [rsp+9h] [rbp-2Fh] BYREF
  char guess[5]; // [rsp+Eh] [rbp-2Ah] BYREF
  char out[5]; // [rsp+13h] [rbp-25h] BYREF
  unsigned __int64 v8; // [rsp+18h] [rbp-20h]

  v8 = __readfsqword(0x28u);
  func1(secret);
  for ( i = 0; i <= 5; ++i )
  {
    if ( func2(guess) || !func3(guess) )
      func4();
    func5(secret, guess, out);
    printf("%.5s\n", out);
    if ( !memcmp(out, "=====", 5u) )
      win();
  }
  return 0;
}
```

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&#x20;
* **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.

```
choir
=====
grey{taken_aback_edams}
```

`└─$ grey{cr4ck3d_an0th3r_one,nice!}`

## antidbg

```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  const char *v5; // [rsp+8h] [rbp-38h]
  char s[28]; // [rsp+10h] [rbp-30h] BYREF
  int v7; // [rsp+2Ch] [rbp-14h]
  const char **v8; // [rsp+30h] [rbp-10h]
  int v9; // [rsp+38h] [rbp-8h]
  int v10; // [rsp+3Ch] [rbp-4h]

  v10 = 0;
  v9 = argc;
  v8 = argv;
  if ( argc == 2 )
  {
    if ( strlen(v8[1]) == 12 )
    {
      v7 = checker(v8[1]);
      if ( v7 == 1 )
      {
        memset(s, 0, 0x18u);
        v5 = v8[1];
        v3 = strlen(v5);
        RC4(&flag_enc, 23, v5, v3, s);
        printf("%s\n", s);
      }
      else
      {
        printf("boooo\n");
      }
      return 0;
    }
    else
    {
      printf("booo\n");
      return 1;
    }
  }
  else
  {
    printf("Usage: %s <password>\n", *v8);
    return 1;
  }
}
```

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.

```c
int what()
{
  FILE *v0; // rax
  char s[10]; // [rsp+10h] [rbp-110h] BYREF
  char v3[254]; // [rsp+1Ah] [rbp-106h] BYREF
  FILE *stream; // [rsp+118h] [rbp-8h]

  v0 = fopen("/proc/self/status", "r");
  stream = v0;
  if ( v0 )
  {
    while ( fgets(s, 256, stream) )
    {
      if ( !strncmp(s, "TracerPid:", 0xAu) )
      {
        if ( atoi(v3) )
        {
          fclose(stream);
          printf("huh?\n");
          exit(1);
        }
        break;
      }
    }
    LODWORD(v0) = fclose(stream);
  }
  return (int)v0;
```

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.&#x20;

```c
if ((input[0] ^ 0) != 103) return false;
if ((input[1] ^ 1) != 0x73) return false;
if ((input[2] ^ 2) != 0x31) return false;
if ((input[3] ^ 3) != 0x7A) return false;
if ((input[4] ^ 4) != 0x6C) return false;
if ((input[5] ^ 5) != 0x31) return false;
if ((input[6] ^ 6) != 0x72) return false;
if ((input[7] ^ 7) != 0x32) return false;
if ((input[8] ^ 8) != 0x37) return false;
if ((input[9] ^ 9) != 0x36) return false;
if ((input[10] ^ 0xA) != 0x35) return false;
if ((input[11] ^ 0xB) != 0x34) return false;
```

```
./workshop_antidbg "gr3yh4t5????"
grey{ur3_qu1t3_g00d_4h}
```

`└─$ grey{ur3_qu1t3_g00d_4h}`

## bomb

```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
  v19 = __readfsqword(0x28u);
  printf("The bomb is about to explode! Quick, enter the passcode to defuse it!!!\n>> ");
  v10 = __isoc99_scanf("%d %d %d", &v6, &v7, &v8);
  if ( v10 == 3 )
  {
    v4 = v6;
    if ( v4 == mysterious_function_0() )
    {
      v5 = v7;
      if ( v5 == mysterious_function_1(21) )
      {
        if ( (unsigned int)mysterious_function_2(1337) == v8 )
        {
          puts("All layers passed. Bomb defused!");
          v11 = v6 * v7 - v8;
          *(_QWORD *)s = 0x8DC04EC58A40CC94LL;
          v13 = 0x9D14D5C211CAAC55LL;
          v14 = 0x15D0AC41D0C77AD9LL;
          v15 = 0xCE8B40E18A418E91LL;
          v16 = 0xF3589FC616DAC349LL;
          v17 = 0;
          v18 = 0;
          for ( i = 0; i <= 39; i += 3 )
          {
            s[i] ^= v11 % 256;
            s[i + 1] ^= BYTE1(v11);
            s[i + 2] ^= BYTE2(v11);
          }
          puts(s);
          return 0;
        }
        else
        {
          puts("BOOM! Layer 3 detonated.");
          return 4;
        }
      }
      else
      {
        puts("BOOM! Layer 2 detonated.");
        return 3;
      }
    }
    else
    {
      puts("BOOM! Layer 1 detonated.");
      return 2;
    }
  }
  else
  {
    fprintf(stderr, "Input error: expected 3 tokens separated by ' '. Got %d tokens.\n", v10);
    return 1;
  }
}
```

The program expects three integers, and checks each one against a different function:

* **Layer 1 :** always return 227
* **Layer 2** : Fibonacci with `n = 21`
* &#x20;**Layer 3** : Sieve of Eratosthenes with `n = 1337`

```
./workshop_bomb
The bomb is about to explode! Quick, enter the passcode to defuse it!!!
>> 227 10946 11027
All layers passed. Bomb defused!
grey{k33p_t41k1ng_4nd_n0b0dy_expl0d35!}
```

`└─$ grey{k33p_t41k1ng_4nd_n0b0dy_expl0d35!}`

## packed

```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+Ch] [rbp-34h]
  _BYTE *addr; // [rsp+18h] [rbp-28h]
  int v6; // [rsp+34h] [rbp-Ch] BYREF
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  v6 = 0;
  addr = mmap(0, 0x24F8u, 3, 34, -1, 0);
  if ( mprotect(addr, 0x24F8u, 7) )
  {
    printf("mprotect error");
    munmap(addr, 0x24F8u);
    return 0;
  }
  else
  {
    memset(addr, 0, 0x24F8u);
    printf("Activating Mantle Layer\nEnter key:\n>> ");
    __isoc99_scanf("%4s", &v6);
    for ( i = 0; i < 9464; ++i )
      addr[i] = blob[i] ^ *((_BYTE *)&v6 + i % 4);
    if ( *addr == 127 && addr[1] == 69 && addr[2] == 76 && addr[3] == 70 )
    {
      ((void (*)(void))(addr + 4352))();
      munmap(addr, 0x24F8u);
      return 0;
    }
    else
    {
      puts("ERROR. Expected header to start with 0x7F E L F");
      return 0;
    }
  }
}
```

The program consists of two layers:

1. **Mantle Layer :** Memory-mapped blob thats XORed against a 4-byte key
2. **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.&#x20;

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:            &#x20;

```c
v12 = "1mrnpng}{k_raynaegud!4cesgtr"
v11 = [11, 20, 25, 12, 7, 6, 15, 27, 4, 10, 14, 1, 21, 3, 18, 17,
       2, 13, 5, 19, 26, 8, 9, 24, 22, 0, 23, 16]

v9 = [''] * 28

for i in range(28):
    v9[v11[i]] = v12[i]

flag = ''.join(v9)
print(flag)

grey{unp4ck1ng_grandmaster!}
```

`└─$ grey{unp4ck1ng_grandmaster!}`
