Rating:
Since we first blooded this challenge, I feel like we have to write it up.
The file we were given called `not-so-advanced.gba`. This tells us that we are looking at a Game Boy Advance rom.
We also were told that the flag doesn't use the standard `flag{...}` format.
I don't have any experience with hacking the GBA, but a quick search tells me that it has an ARM7TDMI in it.
I'm pretty familiar with this architecture from my previous experience with the BPMP cpu on the Nintendo Switch.
Loading the binary into IDA at address 0 the first function we see is this:
```
int sub_0()
{
int _R0; // r0
int _R0; // r0
int v6; // r0
int v8; // r0
int v9; // r0
int v10; // r0
int v11; // r0
MEMORY[0x4000208] = 0x4000000;
_R0 = 18;
__asm { MSR CPSR_cf, R0 }
_R0 = 31;
__asm { MSR CPSR_cf, R0 }
if ( __CFSHL__(0x8000000, 5) )
{
sub_186(0x2000000);
}
else if ( __CFSHL__(274, 5) )
{
v6 = sub_19C(8768, 0x8000000, 0x2000000);
return MEMORY[0x2000000](v6);
}
sub_186(0x3000000);
v8 = sub_186(0x2000000);
v9 = sub_19A(v8, 0x800D2EC, 50331808);
v10 = sub_19A(v9, 0x800D2EC, 50331648);
sub_19A(v10, 0x800F1A4, 0x2000000);
MEMORY[0x3000098] = 0x2040000;
((void (*)(void))nullsub_1)();
v11 = nullsub_1(0, 0);
return sub_186(v11);
}
```
This tells me that I should load it to 0x8000000.
```
_DWORD *sub_8000000()
{
int v6; // r0
_DWORD *v8; // r0
int v9; // r0
int v10; // r0
int v11; // r2
int v12; // r2
_DWORD *v13; // r0
int v14; // r1
MEMORY[0x4000208] = 0x4000000;
_R0 = 18;
__asm { MSR CPSR_cf, R0 }
_R0 = 31;
__asm { MSR CPSR_cf, R0 }
if ( __CFSHL__(sub_8000000, 5) )
{
sub_8000186((_DWORD *)0x2000000, 0x40000);
}
else if ( __CFSHL__(134218002, 5) )
{
v6 = sub_800019C(8768, 0x8000000, 0x2000000);
return (_DWORD *)MEMORY[0x2000000](v6);
}
sub_8000186((_DWORD *)0x3000000, 160);
v8 = sub_8000186((_DWORD *)0x2000000, 0);
v9 = sub_800019A(v8, "abcdefghijklmnopqrstuvwxyz_", 0x30000A0);
v10 = sub_800019A(v9, "abcdefghijklmnopqrstuvwxyz_", 0x3000000);
sub_800019A(v10, "dkARM", 0x2000000);
MEMORY[0x3000098] = 0x2040000;
nullsub_1(0x2040000, 50331800, v11, (int (__fastcall *)(int, int))((char *)sub_8000CEC + 1));
v13 = (_DWORD *)nullsub_1(0, 0, v12, (int (__fastcall *)(int, int))((char *)sub_8000568 + 1));
return sub_8000186(v13, v14);
}
```
It already looks a lot more promising. Keep `abcdefghijklmnopqrstuvwxyz_` in mind for later!
Looking at the functions it calls, one of them stands out:
```
int sub_8000568()
{
_BYTE v1[260]; // [sp+4h] [bp+4h] BYREF
sub_80009CC();
sub_8000E74("Read the code, should be easy\n");
v1[256] = 0;
sub_8000340(v1);
sub_8000E74("Checking it out\n");
if ( sub_8000298(v1) == 0x12E1 )
sub_8000E74("That works");
else
sub_8000E74("Nope");
return 0;
}
```
So `sub_8000298` checks the flag!
```
int __fastcall sub_8000298(int a1)
{
unsigned __int16 v2; // r1
unsigned __int16 v3; // r1
unsigned int i; // [sp+10h] [bp+10h]
unsigned __int16 v7; // [sp+14h] [bp+14h]
unsigned __int16 v8; // [sp+16h] [bp+16h]
v8 = 1;
v7 = 0;
if ( sub_8001148(a1) != 9 )
return -1;
for ( i = 0; i < 9; ++i )
{
sub_8005C60(v8 + *(unsigned __int8 *)(a1 + i), 0xFFF1);
v8 = v2;
sub_8005C60(v7 + v2, 0xFFF1);
v7 = v3;
}
return v7 ^ v8;
}
```
What are we looking at? This looks pretty broken. Lets dig further!
```
int __fastcall sub_8001148(int a1)
{
int v1; // r3
int v3; // r2
int v4; // r2
if ( a1 << 30 )
{
v1 = a1;
while ( *(_BYTE *)v1 )
{
if ( (++v1 & 3) == 0 )
goto LABEL_7;
}
}
else
{
v1 = a1;
LABEL_7:
if ( ((*(_DWORD *)v1 - 16843009) & ~*(_DWORD *)v1 & 0x80808080) == 0 )
{
do
{
v3 = (*(_DWORD *)(v1 + 4) - 16843009) & ~*(_DWORD *)(v1 + 4);
v1 += 4;
if ( (v3 & 0x80808080) != 0 )
break;
v4 = (*(_DWORD *)(v1 + 4) - 16843009) & ~*(_DWORD *)(v1 + 4);
v1 += 4;
}
while ( (v4 & 0x80808080) == 0 );
}
while ( *(_BYTE *)v1 )
++v1;
}
return v1 - a1;
}
```
This instantly looks like strlen to me. `sub_8001148` is `strlen`. Cool!
What does `sub_8005C60` do?
After a bunch of calls we arrive to:
```
int __fastcall sub_8000BA0(int result, unsigned int a2)
{
char v2; // nf
int v3; // r12
unsigned int v4; // r3
unsigned int v5; // r2
bool v6; // cf
bool v7; // cf
bool v8; // zf
int v9; // r2
v3 = result ^ a2;
if ( v2 )
a2 = -a2;
if ( a2 == 1 )
{
if ( (v3 ^ result) < 0 )
return -result;
}
else
{
v4 = result;
if ( result < 0 )
v4 = -result;
if ( v4 <= a2 )
{
if ( v4 < a2 )
result = 0;
if ( v4 == a2 )
return (v3 >> 31) | 1;
}
else if ( (a2 & (a2 - 1)) != 0 )
{
if ( (a2 & 0xE0000000) != 0 )
{
v5 = 1;
}
else
{
a2 *= 8;
v5 = 8;
}
while ( 1 )
{
v6 = a2 >= 0x10000000;
if ( a2 < 0x10000000 )
v6 = a2 >= v4;
if ( v6 )
break;
a2 *= 16;
v5 *= 16;
}
while ( 1 )
{
v7 = a2 >= 0x80000000;
if ( a2 < 0x80000000 )
v7 = a2 >= v4;
if ( v7 )
break;
a2 *= 2;
v5 *= 2;
}
result = 0;
while ( 1 )
{
if ( v4 >= a2 )
{
v4 -= a2;
result |= v5;
}
if ( v4 >= a2 >> 1 )
{
v4 -= a2 >> 1;
result |= v5 >> 1;
}
if ( v4 >= a2 >> 2 )
{
v4 -= a2 >> 2;
result |= v5 >> 2;
}
if ( v4 >= a2 >> 3 )
{
v4 -= a2 >> 3;
result |= v5 >> 3;
}
v8 = v4 == 0;
if ( v4 )
{
v5 >>= 4;
v8 = v5 == 0;
}
if ( v8 )
break;
a2 >>= 4;
}
if ( v3 < 0 )
return -result;
}
else
{
if ( a2 < 0x10000 )
{
v9 = 0;
}
else
{
a2 >>= 16;
v9 = 16;
}
if ( a2 >= 0x100 )
{
a2 >>= 8;
v9 += 8;
}
if ( a2 >= 0x10 )
{
a2 >>= 4;
v9 += 4;
}
if ( a2 <= 4 )
v9 += a2 >> 1;
else
LOBYTE(v9) = v9 + 3;
result = v4 >> v9;
if ( v3 < 0 )
return -result;
}
}
return result;
}
```
This reminds me a lot about divmod, where it takes two numbers to R0 and R1 and returns quotient into R0 and remainder to R1.
In `sub_8000298` we see that v2 and v3 is indeed R1.
This tells me that the original function is something like that:
```
int sub_8000298(char *text)
{
uint16_t a = 1;
uint16_t b = 0;
if (strlen(text) != 9)
return -1;
for (int i = 0; i < 9; ++i )
{
a = (a + text[i]) % 0xFFF1;
b = (b + a) % 0xFFF1;
}
return b ^ a;
}
```
Ok, but what is text?
We know that it's 9 characters long.
I took a guess and said `abcdefghijklmnopqrstuvwxyz_` must he the charset used tor making the input.
From `sub_8000568` we know that `sub_8000298` should return 0x12E1.
Let's write a really dumb brute force for that:
```
int main()
{
char *chars = "abcdefghijklmnopqrstuvwxyz_";
int clen = strlen(chars);
for (int a = 0; a < clen; a++)
{
for (int b = 0; b < clen; b++)
{
for (int c = 0; c < clen; c++)
{
for (int d = 0; d < clen; d++)
{
for (int e = 0; e < clen; e++)
{
for (int f = 0; f < clen; f++)
{
for (int g = 0; g < clen; g++)
{
for (int h = 0; h < clen; h++)
{
for (int i = 0; i < clen; i++)
{
char sol[10] = { chars[a], chars[b], chars[c], chars[d], chars[e], chars[f], chars[g], chars[h], chars[i], 0 };
if (sub_8000298(sol) == 0x12E1)
{
printf("%s\n", sol);
exit(0);
}
}
}
}
}
}
}
}
}
}
return 0;
}
```
It instantly comes back with `aaaaaanzb`.
That's it. We got the flag.