# 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!}`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://x44ylan.gitbook.io/write-ups/cs2110-reverse-engineering.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
