SMS Spy ZertSecurity with decompiled, analyzed Java sources

The implementation of this Android malware is strong and clean. Nothing really innovative though.


Here’s a short summary:

  • Masquerades as a German certificate installer app. The fake login/pin is used to uniquely identify the phone (on top of the phone number and IMEI.)
  • Achieves SMS interception, most likely to break 2-factor authentication. There are 3 modes of operation: do nothing, intercept and proceed, intercept and cancel SMS broadcast.
  • Multiple command-and-control servers. Two hard-coded domains are hxxp://app-smartsystem.(com|net)/sms/d_m009.php
  • The C&C urls can be updated, either by contacting the existing the existing C&C, or via SMS sent by the master. Such SMS contain the string “&Sign28tepXXX”.
  • The communication with the C&C is encrypted using AES-ECB, with the static key “0523850789a8cfed”. The server also base64-encodes its payloads. (The client does not.)
  • The malware will try to start at boot time (BOOT_COMPLETED receiver), and also registers a 15-minute timer to query the server for updated C&C urls.



The APK was run through a name obfuscator. I’m attaching the sources decompiled using JEB 1.1, and with most of the refactoring/renaming/commenting work done. For JEB users, here is the JDB file. Enjoy.

Sample MD5: 1cf41bdc0fdd409774eb755031a6f49d

Android app assets encryption

Continuing part 1, let’s have a look at another feature, assets encryption.

Let’s have a look at an app containing a protected asset, 1.png. The hexdump below shows the encrypted 1.png. The PNG header is gone, as this is the encrypted version of the file.


The decryption routine is easy to spot: it appears the call was replaced by inline code that uses encrypted strings and reflection to instantiate a Cipher class and decrypt the asset file. We will use the script from the previous post to decode the strings and determine exactly what is happening. The screenshot below shows the decompiled routine in question, after it was marked up and commented using JEB (try-catch have been disabled for improved clarity, as they do not help understand how the routine works):


The references to the AssetManager class, open method, asset filename 1.png as well as cipher type AES-CFB have been encrypted using the string encryption feature. The decryption is achieved using standard Android classes, and the scheme used is AES with a random key and IV (outlined in red in.)

The final call to InputStream.available() is actually what the original method was doing. Therefore, the protection mechanism replaced by the following section of code:


We conclude that manually restoring protected assets is not difficult, although it cannot be easily automated. One way to improve that protection scheme would be to use custom decryption routines instead of SDK-provided classes, and/or generalize the use of reflection to reference the decryption code.

A look inside an Android app protector

Let’s have a quick look at one feature of this well-known Android application protector, the string encryption mechanism. The app in question is a version of Cyanide.

First, access to strings inside a protected class are replaced by calls to a static private decryption routine. That routine accesses a static private array containing all protected strings within the class. That array is created by the class initializer upon loading:


The decryption routine usually takes 3 arguments, although in some simpler cases, it may only take two arguments. The arguments may also be shuffled. (Note: from a Dalvik standpoint, that routine is oddly structured. Although JEB’s decompiled output is structurally fine, a for-loop construct cannot be created, mainly because of the irregular jump inside the first if-cond branch.)

The screenshot below represents that decryption routine. The name was obfuscated by the app protector. Notice the constant numerals that have been circled in that code snippet: they are the decryption routine characteristics.


The following screenshot shows the same piece of code after marking it up. As you can see, the encryption algorithm is fairly simple: the current output character is derived from the previous character as well as the current byte of the encryptedStrings array. A constant (-8 in this case) is added to yield the final character. The initial length (length) as well as initial character (curChar) are also offset by hard-coded constants. These three constants (0x3E, 0x199, -8) characterize the decryption routine, and are unique for each routine. The curChar parameter acts as the decryption key.


You may customize this Python script to decrypt the strings:

# decryptor

# customize
strings = [0x4C, 0xE, 2, 9, -7, 0x10, -54, 0x3E, 0x17, -9, -44, 0x4C, 0xA, ...]
c0, c1, c2 = (0x199, 0x3E, -8)

def decrypt(length, curChar, pos):
  length += c0
  curChar += c1
  r = ''
  for i in range(length):
    r += chr(curChar & 0xFF)
    curEncodedChar = strings[pos]
    pos += 1
    curChar = curChar + curEncodedChar + c2
  return r

Combined with reflection (another feature of the protector), the protection can be pretty effective.