Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Good question. Obfuscation is more common on Android (10% of apps) than iOS (1%), but it's also pretty interesting where it's used most.

First, throw out Proguard on Android. That's not really obfuscation, it's an optimizer. Sure it renames variables and removes dead code, but that's only a problem if you have a naive system that relies on class & method names. Proguard is used in 20% of Android apps though.

The most common issue with Android is the malleability of its bytecode it inherited from Java. It's common to patch apps or repackage them for other app stores (after swapping advertising API keys). There's no work required to rebase addresses or anything after patching, unlike native code. This drives the need for obfuscation, but also provides a useful springboard for implementing it.

We've found that many of the obfuscation schemes on Android are custom. This is because of the liberal policy towards using native code via JNI and the ease of inserting a custom Java classloader. With iOS's code signing and memory protection, you can't write a custom loader in your app that decrypts and executes new code.

However, every other obfuscation measure is available in iOS. You can build opaque predicates, mangle control-flow, do arbitrary pointer arithmetic, and lots of things that are impossible in Dalvik. (However, you can just write a thin Java wrapper around your .so in Android and you've got the same capabilities and more.)

With LLVM bitcode distribution, you're really potentially losing only one thing in your obfuscation toolbox: self-checksumming. You couldn't due self-modifying code already due to code signing, so nothing changed there.

While I haven't implemented it myself, I believe you could still even do self-checksumming via clever use of intrinsics. That is, you carefully lay out the instructions and data and predict what you'll expect to see when it's translated to armv7, v7s, arm64, etc. That's the value you'll check for.

http://llvm.org/docs/ExtendingLLVM.html



Actually, before LLVM bitcode, self-checksumming was quite possible on iOS, and is widely used by many commercial obfuscators. Furthermore, self-checksumming is the root of all good anti-tamper schemes. It prevents someone from automatically patching out your license checks/anti debug/anti jailbreak/whatever. Without self-checksumming, someone can easily patch your app to pirate it, inject a backdoor, or modify it in some unwanted way. So it's really a big loss if you care about those kinds of things.

Self-checksumming via intrinsics and clever layouts is a very interesting idea, but it's impossible for you to checksum a program when you're not 100% sure what it will look like after compilation. For example, even if you manage to correctly 'guess' what machine code your LLVM bitcode would be translated to and adjusted your checksums accordingly, Apple could just update their LLVM bitcode compiler at some point in the future, and invalidate your checksums.

Also Android is a much more interesting platform than iOS, because it allows for self-modifying code, which various commercial obfuscators use in various interesting ways :) Also there are very interesting things you can do with reflection or the JNI in Java.


I agree re: checksumming.

Regarding Android's flexibility, I've written an obfuscator (on another Java-based platform) that took a DSL and generated half the code in Java and half in C, intertwining the computation between the two processors via IPC.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: