>> Pokémon GO - REVISITING THE "HACKING" SCENE (PART 4)
Some would say I am addicted to Pokémon GO - but I think I have
found a new passion.
Once upon a time it was about filling the Pokédex (aka: "catch them all")
which eventually moved onto finding Pokémon with perfect
individual values; heck, 142/143 and the majority of
my Pokémon with 80+% IV (all caught manually, no bots), not to
mention winter in Europe - it is time to continue the #re series of blog
posts to share the knowledge to get others up-to speed.
Starting with 0.41 - Niantic introduced a new level of obfuscation
and between each release since, simply change a series of magic numbers
that relate directly to the release version. The 0.45.0 magic
numbers were found pretty quickly; however, only a limited number of people
have the ability to extract these values from the thousands of ARM opcodes
that are executed.
// pogo 0.45.0
uint64_t magic_table[16] = {
0x2dd7caaefcf073eb, 0xa9209937349cfe9c,
0xb84bfc934b0e60ef, 0xff709c157b26e477,
0x3936fd8735455112, 0xca141bf22338d331,
0xdd40e749cb64fd02, 0x5e268f564b0deb26,
0x658239596bdea9ec, 0x31cedf33ac38c624,
0x12f56816481b0cfd, 0x94e9de155f40f095,
0x5089c907844c6325, 0xdf887e97d73c50e3,
0xae8870787ce3c11d, 0xa6767d18c58d2117,
};
#define ROUND_MAGIC_HI 0xe3f0d44988bcdfab
#define ROUND_MAGIC_LO 0x081570afdd535ec3
#define FINAL_MAGIC0 0xce7c4801d683e824
#define FINAL_MAGIC1 0x6823775b1daad522
#define ROUND_MAGIC U128(ROUND_MAGIC_HI,ROUND_MAGIC_LO)
It is my prediction that Niantic will throw something new at the
hacking community, they surely know that if they keep changing the
magic numbers, they will never win the battle - in fact, a lot of the
developers are anxious to see what challenge will come their way next.
The bottom line is that tool development continues, waiting for the next
update from Niantic to make the next step.
While the
Unicorn CPU
emulator has been used as a temporary solution to generate hash values
- it is also a great tool for understanding what happens in the code.
I spent the weekend cleaning up pogohash and including the
Capstone disassembler - in addition to monitoring CPU registers.
With TRACE_DEBUG enabled; the code will now give a lot more
information about what is going on with the CPU emulation. In addition;
it will be possible to implement tracing and pattern detection to assist
in finding the magic numbers as the application updates (of course,
still must find function).
address: 0x00002000
R00* 0xe0001000 R01* 0x00000080 R02* 0x01b175c1 R03* 0x40000000
R04: 0x00000000 R05: 0x00000000 R06: 0x00000000 R07: 0x00000000
R08: 0x00000000 R09: 0x00000000 R10: 0x00000000 R11: 0x00000000
R12: 0x00000000 SP* 0xe0002000 LR: 0x00000000 PC* 0x00002000
opcodes: 90 47 :: blx r2
---
address: 0x01b175c0
R00: 0xe0001000 R01: 0x00000080 R02: 0x01b175c1 R03: 0x40000000
R04: 0x00000000 R05: 0x00000000 R06: 0x00000000 R07: 0x00000000
R08: 0x00000000 R09: 0x00000000 R10: 0x00000000 R11: 0x00000000
R12: 0x00000000 SP: 0xe0002000 LR* 0x00002003 PC* 0x01b175c0
opcodes: f0 b5 :: push {r4, r5, r6, r7, lr}
I have highlighted changes registers in bold; it is obvious that the
PC (program counter) register will change for every instruction;
but you can also see that R0..R3 relate to the code that
we used before calling uc_emu_start. The LR register
also contains the address to return to when done.
uint64_t Hash(uint8_t* buffer, int size)
{
uint32_t r0 = 0xE0001000; // buffer
uint32_t r1 = size; // size
uint32_t r2 = HashFunctionAddr + 1;
uint64_t ret;
uc_err err;
uc_reg_write(uc, UC_ARM_REG_R0, &r0);
uc_reg_write(uc, UC_ARM_REG_R1, &r1);
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
Ok; so, what does all this mean - why are we bothering with making such
a pretty output of what is going on. Well; one of the flaws Niantic did
with the latest update (actually, the compiler does it) - is that all the
magic numbers are loaded into registers as hard coded values. Let's look
at the lower 32-bits of the FINAL_MAGIC1 value, which is
0x1daad522, simply find where this value was loaded.
address: 0x01d8d2c6
R00: 0x00000000 R01: 0xe9c69562 R02* 0xddbb933d R03: 0xe0001f40
R04: 0x8e4338e2 R05: 0xddbb933d R06: 0xe0001c60 R07: 0xe0001cf4
R08: 0x02ea9800 R09: 0x2a6cabbf R10: 0xffffffff R11: 0xe0001c7c
R12: 0xe0001ed0 SP: 0xe0001c5c LR: 0x01b1842d PC* 0x01d8d2c6
opcodes: 4d f2 22 55 :: movw r5, #0xd522
---
address: 0x01d8d2ca
R00: 0x00000000 R01: 0xe9c69562 R02: 0xddbb933d R03: 0xe0001f40
R04: 0x8e4338e2 R05* 0x0000d522 R06: 0xe0001c60 R07: 0xe0001cf4
R08: 0x02ea9800 R09: 0x2a6cabbf R10: 0xffffffff R11: 0xe0001c7c
R12: 0xe0001ed0 SP: 0xe0001c5c LR: 0x01b1842d PC* 0x01d8d2ca
opcodes: c1 f6 aa 55 :: movt r5, #0x1daa
---
address: 0x01d8d2ce
R00: 0x00000000 R01: 0xe9c69562 R02: 0xddbb933d R03: 0xe0001f40
R04: 0x8e4338e2 R05* 0x1daad522 R06: 0xe0001c60 R07: 0xe0001cf4
R08: 0x02ea9800 R09: 0x2a6cabbf R10: 0xffffffff R11: 0xe0001c7c
R12: 0xe0001ed0 SP: 0xe0001c5c LR: 0x01b1842d PC* 0x01d8d2ce
opcodes: c3 e9 00 12 :: strd r1, r2, [r3]
The movt and movw instructions are typically used to
load a 32-bit value into a register; as someone has documented on
stackoverflow.com. With all known magic numbers for 0.45.0 you
can find exactly where in the flow of execution these numbers are loaded.
Unfortunately; the code-flow will definitely change with the next
release, the obfuscation engine being used will definitely want to make
it more difficult to trace - but, de-obfuscating the code will solve this.
I have put together a bare-bones version that you can extend to your
content - either to trace the branching, or try to put some auto-detect
in the code to know when registers are loaded in this manner et al.
You will depend on the Unicorn and Capstone libraries - check the
README.