Note: A more complete writeup on Kony was published at Analyzing Kony Mobile Applications
What is Kony?
Analyzing an Android APK file built with Kony requires a different approach as
normal tools such as
apktool do not work. This is because the application
We will go briefly take a look at how to obtain the source files for each major Kony revision. For more details, reading through the slide deck is highly recommended.
Kony versions < 6.0
In older versions of Kony, a Lua bytecode interpreter is used instead of a
We can recognize APKs built with this version of Kony if we find a
konyappluabytecode.o.mp3 file in the unzipped APK directory.
If we encounter APKs built with this version of Kony, we can use
unluac.jar to decompile the Lua bytecode.
Kony version 6.0+ (Unencrypted)
introduced. We can recognize APKs built with the newer versions of Kony if we
lib/libkonyjsvm.so file in the unzipped APK directory.
archive. If we see that
assets/js/startup.js is a ZIP archive, we can simply
Kony version 6.0+ (Encrypted)
At some point in the Kony 6 lifecycle (the BlackHat talk mentions 6.3), the
ZIP file is now encrypted. We can recognize APKs built with this version of
assets/js/startup.js is seen as a data file (instead of a ZIP
archive). The encryption scheme used is
AES_256_CBC with a hardcoded key
that undergoes a series of transformations that is described in the BlackHat
We can use the method described in the talk to decrypt the ZIP archive without
reverse engineering the key transformation logic. This method takes advantage
of the fact that the
kony_loadfile.exe binary which is used in the build
process to encrypt the ZIP archive calls out to OpenSSL's
function that has an integer parameter to indicate whether the function is
used for encryption or decryption. If we patch that parameter in the binary,
we can change
kony_loadfile.exe from an encryptor to a decryptor.
We can call the patched binary (
kony_decryptfile.exe) like below to decrypt
the ZIP archive.
$ kony_decryptfile.exe <infile> <outfile> <app id> <package name> <timestamp>
package name parameter can be found in the
app id and
timestamp parameters can be found in the
assets/application.properties file as the values of the
Kony version 7.0+
In yet another update to Kony,
kony_loadfile.exe has now been changed to use
EVP_EncryptInit function. This means that the method of
patching a byte in
kony_loadfile.exe to turn it into a decryptor no longer
works. It also appears that the hardcoded key has been changed so using an old
kony_loadfile.exe will not work.
However, since the encryption process still uses a hardcoded key, we can use
a debugger to pull the encryption key (after the
performs the key transformation logic) from process memory.
- Set a breakpoint right before the call to
EVP_EncryptInit. In the binary I am working with, this is address
- Run the binary with the same parameters.
kony_loadfile.exe <infile> <outfile> <app id> <package name> <timestamp>.
- Step through the program until we hit the breakpoint.
- Pull the 32 bytes of key material from the stack. In the binary I am working with, this is the stack address pointed at by the
Once we have the key material from the stack, we can write a Python script to
decrypt the file we want. After a little reverse engineering of the code, we
see that a static IV,
abcd1234efgh5678, is used for the AES encryption step.
import binascii import sys from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend def main(): if len(sys.argv) != 4: print("Usage: ./decrypt.py <infile> <outfile> <keyfile>") return IN_FILE = sys.argv OUT_FILE = sys.argv KEY_FILE = sys.argv backend = default_backend() with open(KEY_FILE, "rb") as f: key = f.read() iv = b"abcd1234efgh5678" cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) decryptor = cipher.decryptor() with open(IN_FILE, "rb") as f: ciphertext = f.read() plaintext = decryptor.update(ciphertext) + decryptor.finalize() with open(OUT_FILE, "wb") as f: f.write(plaintext) if __name__ == "__main__": main()
After we decrypt the file, we should see a ZIP archive.
$ python3 decrypt.py app/assets/js/startup.js startup.js.zip key.bin $ file startup.js.zip startup.js.zip: Zip archive data, at least v1.0 to extract