9

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?

1

3 Answers 3

7

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.

1
  • 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
    Commented Jul 6, 2017 at 18:01
5

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

1
  • Isn't this technically a link-only answer? I think a mention of OLLVM in the answer would be in order, no?
    – 0xC0000022L
    Commented Jan 9, 2023 at 9:00
2

There are several method of obfuscation, including:

  • obfuscation based on source code obfuscation.
  • meta-level based code obfuscation (compile-time level).
  • IR level obfuscation (like OLLVM and etc).
  • binary level obfuscation (instructions morphing, shuffling and etc).
  • packing/protection level obfuscation (which involves using external tools to protect the binary code and restore it while executing).
  • compilation of these methods.

Each of these methods has its own advantages and disadvantages: For example

  • Compile-time level obfuscation can be difficult to control and debug.
  • While Binary level of obfuscation can leave many signs of obfuscation such as changed opcode sequences and abnormal constructions.
  • IR level obfuscators can also leave many signs of obfuscation and can be easily deobfuscated with automatic or semi-automatic tools.
  • Packing/protection level obfuscation may easily be deobfuscated\unpacked with automatic tools too and protected binary will be restored as original one.

Here I want to stop on code level obfuscation and transformation.

Yes, when it comes to code level obfuscation and transformation, parsing and handling the code of all C/C++ languages (including ISO versions such as C17+) is a complex task.

Processing transformation passes at the source code level can also be challenging. However, if done correctly, code level obfuscation can provide many benefits. For example, mixing dummy generated code with the original code can protect against automatic deobfuscation.

Adding powerful opaque predicates with complex precalculation can also protect against auto tracing and code simplify tools. Protecting strings, values, and expressions at the source level is also valuable. Additionally, the control flow of the code can be shuffled without changing the logic through techniques such as while/goto jumps or conditional if blocks. But we should remember about code optimization by complier phase and prepare code obfuscation passes with mind of how to avoid optimization of protected code.

So, code level obfuscation and transformation can be a powerful tool for protecting code against reverse engineering and tampering attacks. However, it is important to carefully consider the pros and cons of each method and to design the obfuscation passes with an understanding of how they will be affected by compiler optimization. By doing so, it is possible to effectively protect code and safeguard intellectual property.

At RandomBlocks Lab, we use code level obfuscation in our CodeMorpher C/C++ Code Obfuscator to protect our customers' code. We leverage the power of the Clang parser and our own techniques and transformation passes to ensure the highest level of protection. Our team of experts has extensive experience in code obfuscation and is committed to helping our customers safeguard their intellectual property. If you are looking to protect your C/C++ code, we encourage you to give our CodeMorpher Obfuscator a try. We believe it is the most effective solution available for code level obfuscation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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