Tags: rev arm32 

Rating:

# DEF CON CTF Qualifier 2022

## not-so-advanced

[`not-so-advanced.gba`](not-so-advanced.gba)

Tags: _arm_ _arm32_ _gba_ _rom_ _rev_

## Summary

GBA, _groan..._

> I spend more time tooling up for these type of challenges than actually solving them.

With the help of teammate [dobs](https://github.com/dobsonj), I didn't have to do too much researching on tooling; [dobs](https://github.com/dobsonj) set me up with the Ghidra plugin [GhidraGBA](https://github.com/SiD3W4y/GhidraGBA) and found the functions of interest. However, we still lost time finding GBA emulators and getting debuggers working with them. I guess we could have just _Read the code, should be easy_, but I prefer to noodle my way through with a debugger to check my assumptions. I ended up using [mGBA](https://mgba.io/) with its internal debugger (GDB had numerous problems with a stable and useful remote connection).

## Analysis

### Give it a go

![](pic1.png)

From here you up/down/left/right to input a string of lowercase letters and underscores then enter with _start_ (or _return_ if not using a controller).

Then a check function, _checks it out_:

![](pic2.png)

### Decompile with Ghidra

```c
longlong main(void)
{
int iVar1;
uint in_lr;
undefined auStack268 [256];
undefined uStack12;

FUN_080009cc();
FUN_08000e74("Read the code, should be easy\n");
uStack12 = 0;
FUN_08000340(auStack268);
FUN_08000e74("Checking it out\n");
iVar1 = checkit(auStack268);
if (iVar1 == 0x12e1) {
FUN_08000e74("That works");
}
else {
FUN_08000e74(&DAT_08005ce4);
}
return (ulonglong)in_lr << 0x20;
}
```

`main` does very little, just gets your input and passes to `checkit`, if `checkit` returns `0x12e1`, then you've solve it.

```c
undefined8 checkit(int param_1)
{
int iVar1;
ushort extraout_r1;
ushort extraout_r1_00;
uint uVar2;
undefined4 in_lr;
uint local_18;
ushort local_14;
ushort local_12;

iVar1 = FUN_08001148(param_1);
local_12 = 1;
local_14 = 0;
if (iVar1 == 9) {
for (local_18 = 0; local_18 < 9; local_18 = local_18 + 1) {
thunk_FUN_08000cc8((uint)local_12 + (uint)*(byte *)(param_1 + local_18),0xfff1);
thunk_FUN_08000cc8((uint)local_14 + (uint)extraout_r1,0xfff1);
local_14 = extraout_r1_00;
local_12 = extraout_r1;
}
uVar2 = (uint)(local_14 ^ local_12);
}
else {
uVar2 = 0xffffffff;
}
return CONCAT44(in_lr,uVar2);
}
```

Clearly we can tell from the loop that the input must be `9` in length, but then ...

_I got lazy_

`thunk_FUN_08000cc8` calls `FUN_08000ba0` (not shown) and that looked like a lot of work, so instead I set two break points with mGBA's debugger where `local_12` and `local_14` were being updated:

```
b 0x80002FA
b 0x800030C
```

Then looked at the values on stack on each loop iteration:

```
r/2 0x03007DDE
r/2 0x03007DDC
```

It was easy to see a pattern:

```c
local_12 += (uint)*(byte *)(param_1 + local_18) // input byte
local_14 += local_12
```

## Solve

```python3
#!/usr/bin/env python3

from itertools import product

for i in product('abcdefghijklmnopqrstuvwxyz_', repeat = 9):
local_12 = 1
local_14 = 0
for j in i:
local_12 += ord(j)
local_14 += local_12
if local_12 ^ local_14 == 0x12e1:
print(''.join(i))
break
```

Output:

```
aaaaaanzb
```

Check:

![](pic3.png)

BTW, this is just one of 1000s that works, just remove the `break` from the solve above and see for yourself.

Original writeup (https://github.com/datajerk/ctf-write-ups/tree/master/dc2022q/not-so-advanced).