Solving ZCrackme#2: A Custom Emulator Approach

Solving ZCrackme#2: A Custom Emulator Approach

Due to my non-existent experience with using gdb under ARMv7, I decided to
solve this challenge (the ZCrackme #2 Challenge) using a minimal
ARMv7 emulator
based on my ARMv7 disassembler. (The original
challenge can be found here.)

ZCrackme#2 Challenge

The binary itself is fairly interesting. It has a similar structure as the
first ZCrackme challenge (not sure if there’s a blogpost about this one
though.) Basically the ELF header is messed up, sections are missing, and the
Entry Point points to a page filled with zeroes.

INIT_ARRAY

Upon further inspection, using readelf -a zcrackme2, we find that the binary
features the so-called preinit-array, init-array, and fini-array dynamic
sections. These dynamic sections in fact represent a table of function
addresses which are being called right before calling the real Entry
Point (in the case of preinit-array and init-array) and called after
calling the real Entry Point (in the case of fini-array.)

Looking up the various virtual offsets using IDA Pro, we find that only the
init-array points to a real address, loc_B0D8. (The other table arrays,
preinit-array and fini-array, are filled with zeroes and -1′s, which are
nops – as in, these are not really called.)

We conclude that the actual entry point, or, the code that will be executed
first, is located at this (loc_B0D8) address. From analyzing this routine
in IDA Pro, we see
some sort of decryption loop which overwrites some memory. Finally, after
executing said decryption loop, an interesting system call is performed,
namely #0xf0002. We find that this system call represents
__clear_cache.

__clear_cache

Basically, before the decrypted code can be executed, the code cache first has
to be cleared for the particular address range in order to make sure that,
when it is being executed, the new code will be executed, rather than any
remaining code in the cache.

Similar tricks to this (decrypting code and clearing the cache) are performed
a total of five times in this crackme.

So, having cleared the cache, the execution flow of the crackme now ends up in
the decrypted code. Which, in turn, does some more rounds of decryption.

Code Decryption

As mentioned, the crackme overwrites memory of the ELF file a total of five
times. One of these “decryptions” in fact zeroes the first 100 bytes of the
ELF header. As our goal is to dump a decrypted version of the crackme binary,
this ELF header corruption does not help us (as IDA Pro wouldn’t understand
the binary anymore.)

Reconstructing a Decrypted Binary

As decryption is being followed by clearing the cache each time, we dump a new
binary during each time the cache is cleared. We do this by applying the
changes to a copy of the original binary. That is, read the decrypted data
from emulator memory, and overwriting it to our original binaries buffer. We
do this for each decryption, except for the one iteration where the ELF header
is zeroed out. (Note: the __clear_cache system call takes the starting
address as first parameter and the end address as second parameter, hence it
is trivial for us to find out which chunks of memory have been decrypted.)

Scripting the Emulator

The following script, although a bit messy, represents the code to dump
the binary a couple of times, which results in the final binary we’re
interested in. The unpacked binary can be found here. (Note that this
unpacked binary may be inaccurate, with regards to global variables etc that
have been updated during runtime but are not reflected in this version of the
binary.)

Having successfully dumped the unpacked binary, it is now time for some static
analysis on this binary. Do note that our emulator should run fine on
Windows and Linux (with a 32bit Python installed, that is.)

The actual Crackme

Looking through the dumped binary, we find ourselves looking at sub_87B4,
which is the function where the real stuff is happening (argc/argv parsing,
that is.)

There are a couple of odd text messages which will be printed whenever
incorrect information is entered on the commandline. Finally, we find some
interesting function calls, of which one to sub_8638, which seems to decrypt
the string buffer that can be found at byte_9C35, and another function which
does a custom strcmp() against the argument on the commandline.

The string at byte_9C35 is decrypted by xor’ing with 0x0d (decrypted to a
buffer on the stack by sub_8638), resulting in the
string ZenCracking. That said, we’ve solved the challenge..

Conclusion

In case somebody has a working gdb for ARMv7 setup, this challenge is probably
pretty easy (i.e., step through the various decryption iterations, and try to
find the custom strcmp.) However, I had fun implementing the simple ARMv7
emulator, which is in fact pretty tricky, with all the conditional stuff going
on.

Now a harder crackme? Let’s hope the next one does not involve xor
“encryption” :p Zimperium’s response was, however, that having gotten to the
xor-decryption part already shows enough knowledge and understanding of ARMv7,
to which I agree :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>