8

I was trying to reverse engineer an android ndk (arm) using ida pro on a static analysis. However, it appears there are a lot of useless jumps to the same place and with random values set to R1 to compare and jump again. Like the following picture. Nearly every function is doing something similar -- Initialization first (stuffs like push registers), then it jumps to code somewhere (like the right up corner of the picture), load random values to R1 register and compare it with R0 and do the jumps, but it will always jump back to the up corner again

Code graph

Here is another case, it seems loc_FFBA and it's following branches serve as a big switch, but what doesn't make sense is nearly every branch will eventually jump back to loc_FFBA again. There can be a flow red->green->blue->red, but it doesn't even make sense... You can see the jump in blue is even useless because if it takes jump to blue (R0>0x6fe5e521), it will definitely take the jump to the red (R0>0x661cf941)...

enter image description here

My question is, is this done intentionally by the author to prevent the reverse engineering? If so how is it achieved? For me, it sounds like the author will need a lot of if-else and goto clause in his C++ code, but that will also slow down the development because he may confuse himself... And it doesn't seem the .so ndk is packed, since by using a debugger, I found generally what it actually does is just to decode a resource file to an apk and load it dynamically, but all the decryption is done by calling JNI functions to do an AES decryption to load the APK, rather than do it in C++, so the messed up jumps seems not to be something related to the decryption either...

Maybe there's some kind of compiler that will generate the junks if this is really an intentional obfuscation?

5

Unfortunately it is not enough code to say exactly what it is, but, as @0xec said, it looks very much like control flow flattening. Usually such kind of code transformations are done automatically with obfuscators.

There are some obfuscating compilers, and one of them does CFG flattening. As far as I know this compiler is not one-of-a-kind, there are a lot of others. This specific obfuscating compiler is based on LLVM framework and if you want to see how it can be implemented - the code for the CFG flattening is here. Also, you can find an example on how to deobfuscate small programs here.

  • Very interesting, but it sounds to me the obfuscation is more resistant than packing in static analysis because for packing, the original machine code can nearly always be recovered natively, but for obfuscation, it is just what it is... – Y M Jul 6 '17 at 18:01
4

Here's a blog post describing this technique in a little more detail as well as some hints on how to undo it:

https://blog.quarkslab.com/deobfuscation-recovering-an-ollvm-protected-program.html

EDIT

Another article from 2017:

https://blog.rpis.ec/2017/12/dissection-llvm-obfuscator-p1.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.