📜 ⬆️ ⬇️

How I almost caught a virus trying to sell my boots



I am one of those people who with the onset of autumn tries to spend less time on the street. In Moscow, this is not difficult: you limit yourself to the route from home to office and back. However, the wet weather can cause discomfort in the room, especially if your workplace, like mine, is at the window, and every second colleague, complaining of stuffiness, asks to air the office. In order not to fall into a blues, I updated my wardrobe this fall.

Reflecting on the fate of unnecessary things, I wondered what to do with them: throw it away, cut it into rags, give it to my younger brother? But for one thing, none of these methods did not suit: they were leather boots of the 44th size of a decent look, but I was rather tired of order. I decided to sell them on Avito. I uploaded photos, indicated a fake name (information security), put on boots, a couple of other things and went to sleep. How could I know that this would result in a lengthy analysis of the application for hidden threats?


')

A pleasant surprise


The next day, after a couple of dubious calls, I received an interesting SMS message with the following content:



After a couple of days, I received another similar message:



Surprised that someone somehow was able to transfer money to me via the Internet (apparently, I’m the only one - I still use paper savings books), I followed the link in SMS.

After that I was offered to download an application for Android (apk-file). Happily downloading the file, I saw the following:



Causes trust! I eagerly wanted to quickly install everything and get it over with.
But here, as usual, the annoying Android operating system for some reason prevented me from running the file. "Yes, give the money already!" - I was indignant. I had to go to the settings and turn on some option "Unknown sources", is the phone really that stupid in 2018? By the way, my phone is Xiaomi Remdi with Andoid 6.0.1 (note for techies).



This was followed by a chain of strange events. The phone continued to report inaccurate sources. But Avito is a reliable source ! I had to google, figure out how to get around this, and then disable it in the settings. Soon a certain antivirus appeared that I did not install.



Finally, I saw the cherished standard installation window - it makes no sense to look at permissions in our time , now even the notebook will not start until you give it full access to the phone. Happy moment of the end of the application installation and START! I was looking forward to the promised money. Additionally, the application requested administrator privileges, with which I happily agreed. Unfortunately, the application behaved strangely and did not want to pay, and soon disappeared completely from the list of applications on the general screen.





Spoiler
Later I checked on another phone - Lenovo with Android 4.4.2 on board. The list of permissions during installation turned out to be much larger. And no Play Protection and Antivirus do not interfere, you only need to allow the installation from unreliable sources.





Altruism



So, what we have come to:


I thought that the problem is in the error code, as is often the case with programmers. I decided to identify the errors that occur when the application is running on my phone, and send a report about this to the developer.

Spoiler
Now there are so few unselfish people left, one of them is me.

It is clear that an application of this type should work when the Internet is connected, so for a start I tried to analyze the traffic between the phone and the application server.

Technical instructions for setting up a proxy
You can listen to Internet traffic at any point along the path from the phone to the server, be it a home router, a local Internet provider, a trunk Internet provider or an application server.

Problems:

  • Access to this equipment is required.
  • It is required to separate the traffic of the desired application from the rest.
  • In the case of encryption (and everything is already encrypted in 2018), you need to know the key.

I decided to go a more classic way and set up a proxy on the phone with a proxy server on my own laptop. In order to level the problem of encryption, I decided to import my certificate, but to separate the traffic of the application I did not launch other applications.
I chose the burp suite as the proxy server program . I set up a proxy server and exported the certificate to my phone.





After setting up specialized software, I was able to see which requests the application sends to the server:



As you can see, the data is not sent (in the IP column the value is “unknown host”), moreover, they cannot be analyzed due to the presence of additional encryption at the application level. Judging by the error, the phone could not determine the IP address of the server and its subdomains https: //*.sky-sync.pw by its domain name.

This could only mean the following options:


To check the assumption about the problem with the DNS server, I tried to make requests from various large DNS servers: Google, Yandex, OpenDNS (local DNS is usually censored):



Here you can see that none of them knows anything about such a name. Then I looked at the whois-domain registration information:



Curiously: the domain is registered, that is, most likely, it is not local, but since the domain is not resolved, it may have been blocked by the registrar on the complaint (abuse). But for what? What did he do wrong?

To find out what this is all about the application and how I can take my money, I decided to use the magic of reverse engineering.

Dive into the abyss


If you are a humanist and have read this far, then this is already good - for mastering the subsequent, you are worthy of the award posthumously .

Tools


To find out what is under the hood of the application, we need to download specialized tools. You can download them separately:


Or you can use the product, usually paid, where there is everything at once. I prefer JebDecompiler : it can simply send an apk-application to the input, and he will carefully arrange everything on tabs, plus it is convenient to switch between smali and decompiled Java code.

Separately, I want to note:


Only old men go to battle


Overview


When opening the decompiled code, it becomes immediately clear that it is obfuscated.



How do I understand this?


It is unlikely that the programmer has gone crazy enough to initially develop the code. Most likely, he used one of the public obfuscators. This step is quite common for complicating the analysis of the code in order to protect intellectual property, for example, but what kind of “outlook” it is for the researcher.

Pay attention to the basic encryption function:



The encryption function itself accepts 3 lines as input (if more, then the rest do not make sense):

  1. ciphertext
  2. key
  3. initialization vector for CBC - AES

This function is referred to in the program at least 213 times:



I note that it is an important key for normal code analysis. Next you need to think we have the following ways to analyze the program:

  1. Restore the logic of the function, collect all calls in a static analysis, decrypt strings. It may be difficult and long, but it will give a 100% result.
  2. Make changes to the application smali-code, re-compile, run the application and catch the decrypted lines in the logs. It is done easily, but how the application behaves in a particular launch is unknown, and you can not see the whole picture (not to receive calls for all functions). In addition, there may be problems with self-checking certificate and / or integrity.
  3. If it is difficult to restore the logic of the function, you can collect all the function calls and pull these functions themselves with the necessary parameters directly in the dynamics (using, for example, Frida software).

We will choose the method number 1 as the most severe reliable.

Deobfuscation


Immediately make a reservation, deobfuscation is often a long and tedious process, so you need to properly evaluate your time frame. For our analysis, it is enough to decipher all the lines in at least some way and do it in the shortest time, even if in a crutch way, to mess around for a vague ideal option.

High-quality de-obfuscation makes sense in the case of a complete reverse engineering, for example, this has to be done to intellectual property thieves when they try to copy a competitor's solution, or if you often have to analyze programs processed by one obfuscator, but this is not our case.

Source Code after JEB Decompiler v.1.4 Decompiler

Spoiler
 public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; String v7 = args[1]; String v0 = args[2]; if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; label_11: IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); goto label_15; } catch(NoSuchPaddingException v3) { } catch(NoSuchAlgorithmException v3_1) { } String v11 = ""; goto label_10; label_15: SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); if(v2.length <= 0) { goto label_48; } v4 = 0; v6 = v2.length - 1; label_29: if(v6 < 0) { goto label_38; } if(v2[v6] != 0) { goto label_33; } } catch(Exception v3_2) { goto label_51; } ++v4; label_33: --v6; goto label_29; label_38: if(v4 <= 0) { goto label_48; } try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch(Exception v3_2) { label_51: v11 = ""; goto label_10; } label_48: v11 = new String(v2); goto label_10; label_9: v11 = ""; label_10: return v11; } } 

Note about decompilers
By the way, dex2jar often crashes. So, in the figure below you can see that dex2jar version 2.0 could not cope and just issued an smali-code.



Its latest version, compiled from source, produced the decompiled code of this function, but was unable to decompile many others (tricks like this).





Bottom line: pay attention to the choice of decompiler - it will save you a lot of time and be easier than analyzing smali code.


So, if we now just insert this code into the IDE, then it will not work due to errors.

It is important to remember: The decompiler is not required to issue a valid code written by the developer. He is just a magic wand in the analysis and makes assumptions how the code could be written. In most cases, after optimization by the compiler, the task of restoring the original code ceases to be trivial.

An example of a bad decompile:

 if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; … label_9: v11 = ""; return v11; 

We see that it turned out bad and inoperable. Rewrite:

 if ((v10 == null) || (v10.length() == 0)) { return ""; } 

Now it has become much clearer, here is the usual input data check. In this case, we need:


As a result, we obtain:

 package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu"})); package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } 

This code successfully fulfills. Once his work becomes clear, it can be simplified and simplified, leading to the originally intended laconic look written by the programmer (if this, of course, his hands were not crooked initially).

Note
By the way, in this case, the decryption of strings can be demonstrated using a bunch of online resources. An example of calling an encrypted string within a program:



Here, the initialization vector must first be converted to Hex-format:



Substitute all values:



And at the end we decode from base64:



As a result, we get the usual string, and the call takes on a meaningful form.

Thus, you need to go through the whole code and collect all the encrypted strings, now we can independently decrypt them. The important point here is that at the stage of “modifying and commenting on code” we can work both at the smali level and at the Java level (decompiled smali).

Pluses modificationsDownsides modification
SmaliYou can make changes, reassemble in dex and decompile with new lines.It is not always easy to work on smali-code. If the change is incorrect, the application will not build
JavaMore often, it is much easier to extract data from high-level operations.Edit in most Java-code viewers can not.

Another example string

 vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

From here it is very easy to pick up parameters using a regular expression, than to write a regular code on the following code:

Smali code example 1

 00000280 new-instance v13, Ljava/lang/StringBuilder; 00000284 invoke-direct {v13}, Ljava/lang/StringBuilder;-><init>()V 0000028A const/4 v14, 0x6 0000028C new-array v14, v14, [Ljava/lang/String; 00000290 const/4 v15, 0x0 00000292 const-string v16, "f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6" 00000296 aput-object v16, v14, v15 0000029A const/4 v15, 0x1 0000029C const-string v16, "378f40211b6e32a5406cd97e85bcf9ad" 000002A0 aput-object v16, v14, v15 000002A4 const/4 v15, 0x2 000002A6 const-string v16, "6378a459b1c20edf" 000002AA aput-object v16, v14, v15 000002AE const/4 v15, 0x3 000002B0 const-string v16, "gexnfwok" 000002B4 aput-object v16, v14, v15 000002B8 const/4 v15, 0x4 000002BA const-string v16, "meazfhdp" 000002BE aput-object v16, v14, v15 000002C2 const/4 v15, 0x5 000002C4 const-string v16, "bsmotaxn" 000002C8 aput-object v16, v14, v15 


Smali code example 2

 0000008E new-array v0, v0, [Ljava/lang/String; 00000092 move-object/from16 v89, v0 00000096 const/16 v90, 0x0 0000009A const-string v91, "4500b5e2e2ad26b7545eb54d70ab360ae28c9d031e2afcc3f6a2b2ac488ea440" 0000009E aput-object v91, v89, v90 000000A2 const/16 v90, 0x1 000000A6 const-string v91, "da96f678922d4b07350b3a184ecc1f5e" 000000AA aput-object v91, v89, v90 000000AE const/16 v90, 0x2 000000B2 const-string v91, "0cf69e3d2745a1b8" 000000B6 aput-object v91, v89, v90 000000BA const/16 v90, 0x3 000000BE const-string v91, "jhiqsaoe" 000000C2 aput-object v91, v89, v90 000000C6 const/16 v90, 0x4 000000CA const-string v91, "khbqxurn" 000000CE aput-object v91, v89, v90 


Smali code example 3

 00000D3E new-array v0, v0, [Ljava/lang/String; 00000D42 move-object/16 v298, v0 00000D48 const/4 v0, 0x0 00000D4A move/16 v299, v0 00000D50 const-string v0, "b286945744e085f4d5c19916fd261481" 00000D54 move-object/16 v300, v0 00000D5A move-object/from16 v0, v300 00000D5E move-object/from16 v1, v298 00000D62 move/from16 v2, v299 00000D66 aput-object v0, v1, v2 00000D6A const/4 v0, 0x1 00000D6C move/16 v299, v0 00000D72 const-string v0, "df6883742b2911ac5ac7b4dee065390f" 00000D76 move-object/16 v300, v0 00000D7C move-object/from16 v0, v300 00000D80 move-object/from16 v1, v298 00000D84 move/from16 v2, v299 00000D88 aput-object v0, v1, v2 00000D8C const/4 v0, 0x2 00000D8E move/16 v299, v0 00000D94 const-string v0, "90a463ce2df17b58" 00000D98 move-object/16 v300, v0 00000D9E move-object/from16 v0, v300 00000DA2 move-object/from16 v1, v298 00000DA6 move/from16 v2, v299 00000DAA aput-object v0, v1, v2 00000DAE const/4 v0, 0x3 00000DB0 move/16 v299, v0 00000DB6 const-string v0, "cupyzsgq" 00000DBA move-object/16 v300, v0 00000DC0 move-object/from16 v0, v300 00000DC4 move-object/from16 v1, v298 00000DC8 move/from16 v2, v299 00000DCC aput-object v0, v1, v2 


As we see, internal variables change, the sequence of commands differs, the number of arguments also varies, in addition to the program, the decryption function is not called directly, but through the layer functions. Try to write a rule yourself to search for this construct, not to make mistakes on capturing strings from other functions, and do it all quickly ( good luck ).

Plan-trap:

  1. Extract all values ​​from decompiled code.
  2. We decrypt.
  3. Let's replace the ciphertext with the one opened in smali-code. Let's substitute, for example, instead of the first operator. (It will be more professional to cut out the entire function call and leave the returned decrypted string, but then again there is a big risk to break the program).
  4. We collect the smali-code in a dex-file.
  5. We will continue to look further in the code analyzer, from which we started.

If you compile the entire decompiled code into one file, you will get about 20,000 lines, which for manual analysis takes a lot of time, which is clearly more expensive than the boots I have put up for sale. To begin with we will collect all strings with a regular expression.



We see 593 coincidences, plus about a dozen that did not fall under this rule, in a family with a freak . Example:



We sort, weed out, total 422 unique lines:



We skip through the decryption function that we restored earlier. Result:



Replace the ciphertext with the one opened in smali-code using Python:

 import os words_replace=dict() words_replace["0018aacad3d146266317d8d8c51785fd"]="imei" words_replace["016d15e4d0a72667c61428e736a6f3b8"]="WakeLock" words_replace["032c534efb6c9990cd845a08c5a08b95"]="inbox" #…  .. # smali- #      def change(path): print("file="+path) file_handle = open(path, 'r') context_full = file_handle.read() file_handle.close() for i in words_replace: context_full=context_full.replace(i, words_replace[i]) #print (i+""+words_replace[i]) file_handle = open(path, 'w') context_full = file_handle.write(context_full) file_handle.close() #      smali- for top, dirs, files in os.walk('C:\\work\\test'): for nm in files: path=os.path.join(top, nm) print (path) change(path) 

We collect smali-files in dex:



Now it can be somehow analyzed (reading the first argument from the whole construction):



Analysis


So, we have 20,000 more or less readable lines of code, we don’t need to do a complete analysis before us. It is necessary to understand the functionality as a whole. Here, in essence, only the skill of reading Java source code is required. Walk through the code, look at cross-references, rename variables and functions.

How is it better to analyze the Android application, especially large?

Option 1: You can move from the file Manifest

For example, consistently from LAUNCHER try to unleash the entire chain of calls. By the way, do not forget that there is “Receiver” and “Service”, which can change the linear execution of the program.



Full Manifest File
 <?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"> <uses-permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.QUICKBOOT_POWERON"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE" android:protectionLevel="signature"/> <application android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/tgiwmpqy" android:noHistory="true"> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:label="@string/tgiwmpqy" android:launchMode="singleTop" android:name="zemquyog.csrtmnak.xrkfygen.wkahrnjd.acnfunjh.rgipxbuf.lruiwxeg.blqndche.dcjihbou" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="xbfrscou.hxrvwnoi.djvpcqri.enlnrfio.aoegxbiu.heywzmnb.znfnxcht.nazcxobq" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="hcfkagds.timkagsd.oetvghzr.fcioynvl.psynofdj.slcghdjz.tapnwsdk.gzvwnban.htenafdb.qwebhzgy" android:noHistory="true" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:launchMode="singleTop" android:name="njfbwmre.voefarqx.ftuxvngl.wrmshxqj.zdenywgn.eiwyunlg.jysgkbam.yrijthab.vstqxpuo.iplamgxf" android:priority="2147483647" android:screenOrientation="portrait"/> <receiver android:name="gfbaznoc.asyoqtnm.kbetoqca.mqysobzu.gqwfibrv.dorxijuk.wgzkmiep.ywnnurzv.csfpqhrn" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="@string/pkzrlscm" android:resource="@xml/ynqukvnb"/> <intent-filter android:priority="2147483646"> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="ykwbodxc.gymjhibn.kgmdfqor.hbasvmfz.yegkmaif.ortzknvm.quplincn.cuxytvhs.fqonzuts.cyuoxgqi.znumwyct" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE"/> <action android:name="com.google.android.c2dm.intent.REGISTRATION"/> <action android:name="com.google.android.c2dm.intent.UNREGISTRATION"/> <category android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"/> </intent-filter> </receiver> <receiver android:enabled="true" android:exported="true" android:name="kqwihjot.nvkqjloc.grjnyknm.owydvckh.mugknwdx.enhcyvja.mhvbpcue.ztbwjhfo"> <intent-filter android:priority="2147483646"> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.BATTERY_OKAY"/> <action android:name="android.intent.action.BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> <action android:name="android.intent.action.APP_ERROR"/> <action android:name="android.intent.action.HEADSET_PLUG"/> <action android:name="android.intent.action.PHONE_STATE"/> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> <action android:name="android.intent.action.TIME_TICK"/> <action android:name="android.intent.action.SCREEN_ON"/> <action android:name="android.intent.action.SCREEN_OFF"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/> <action android:name="android.intent.action.DREAMING_STOPPED"/> <category android:name="android.intent.category.HOME"/> </intent-filter> </receiver> <receiver android:name="btnsxnuz.wmjizbky.lynvjxqz.zinomjuv.yizlgcnf.qwoikgnc.wnrskjea.wfqgmeny.lcgvqrms.ocwkgblp"> <intent-filter android:priority="2147483646"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> <service android:name="ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn"/> <service android:name="rbnakfzo.qsreiubk.pwvlnngs.twoxnhfv.mftarcnd.pfioxcub.xjlaftqr.nxrqvlwh"/> <service android:enabled="true" android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.ugshpjvo"/> </application> </manifest> 


Option 2: You can move from interesting lines



Part of the decrypted lines
 system_update.apk () () , error = , unregistered = ,  .permission.C2D_MESSAGE //sky-sync.pw/ //sms/inbox /system_update.apk ALLCONTACTS ALLMSG AUTHENTICATION_FAILED Acquiring wakelock Application BLOCKER_BANKING_START BLOCKER_EXTORTIONIST_START BLOCKER_STOP BLOCKER_UPDATE_START Banking CHANGE_GCM_ID CONTACTS CONTACTS_PRO CREATE TABLE IF NOT EXISTS END Error|No process list|No access Extortionist Foreground GCM returned invalid number of GCMBaseIntentService GCMBroadcastReceiver GCMIntentService- GCMRegistrar GCM_LIB GET MESSAGE Mobile Network NEWMSG Not retrying failed operation ONLINE PAGE POST Process finished with exit code 0 RESTART Received deleted messages Registering receiver Releasing wakelock SERVICE_NOT_AVAILABLE SSL START STOP Saving regId on app version Scheduling registration retry, backoff = Setting registeredOnServer status as Stop System UNBLOCK UPDATE UPDATE_PATTERNS URL UTF-8 Update WakeLock Wakelock reference is null Wi-Fi WiMax _success add_msg_ok address android.intent.action.QUICKBOOT_POWERON answer_text answer_to api_url app appVersion application application/vnd.android.package-archive apps_list ask backoff_ms blocker blocker_banking blocker_banking_autolock blocker_banking_forced_access blocker_banking_success blocker_extortionist blocker_extortionist_autolock blocker_extortionist_forced_access blocker_extortionist_success blocker_update blocker_update_forced_access blocker_update_success body build callback cardSuccess check com.android.settings com.google.android.c2dm.intent.RECEIVE com.google.android.c2dm.intent.REGISTER com.google.android.c2dm.intent.REGISTRATION com.google.android.c2dm.intent.UNREGISTER com.google.android.gcm com.google.android.gcm.intent.RETRY com.google.android.gsf com.htc.intent.action.QUICKBOOT_POWERON command command_receive contactslist country data date delete deleted_messages device_block disableDataConnectivity enableDataConnectivity error failure file deleted. first_start force-locked gafzpjxb.cix gcm gcm_id gcm_register gcm_register_ok getITelephony get_message_list id integer primary key autoincrement, id=? imei immunity inbox init_bootable init_imei is_admin is_awake_display is_imunnity is_locked is_network_type is_top_activity job job_date job_id komgejif.hqr locked message message_delivered message_type method model msg msg_id msglist name not nypjtinq.nvp ok onServer onServerExpirationTime onServerLifeSpan operator org.android.sys.admin.disabled org.android.sys.admin.enabled org.android.sys.admin.request org.android.sys.command.receive org.android.sys.launch.first org.android.sys.sms.pro.sent org.android.sys.sms.push org.android.sys.sms.sent outbox page params pattern patterns personal phone phone_list privet process_list protocol qwertyuiopasdfghjklzxcvbnm receive regId regex register register_ok registrationId = registration_id repeat resetting backoff for ru save_contacts_list save_message_history sender sent sent_status sid ss status stop_blocker text text, text/html time token total_deleted type unknown unregistered until url useragent utf-8 value version xpls yes   !     ...                   !  ?                ! 

Option 3: You can move from interesting resources (assets, libs)

In this case, option 3 turned out to be preferable. In the folder / assets (apk-container) are three interesting html-files. Here is their view in the browser:





It looks doubtful for the official Avito payment transfer program, don't you think so? Track what happens when you press the key to send bank data on a page with the Sberbank logo. JavaScript calls the sendCardData() function:



And then it is transmitted to the Java code via the ok.performClick() call:



In Java code, processing is performed:



Further, all this is encrypted in the mcrypt class:



Inside the function, data is encrypted in the same way as previously discussed:



But for everything else, the keys are hard-wired:



We try to decrypt through an online-resource:



And convert from base64. Success! We can decrypt all application data: checked on traffic captured earlier.

The application reports all events to the server.
 { "sid":15, "imei":"861117030537111", "phone":"System", "message":"     22.10.2018 23:30:47", "time":"1540240247", "msg_id":1, "status":"unknown", "type":"inbox", "method":"message" } 

And also periodically transfers all running applications.
 { "sid": 15, "imei": "861117030537111", "country": "ru", "operator": "MTS RUS", "phone": "", "model": "Xiaomi Redmi 3X", "version": "6.0.1", "application": "", "build": "30.0.2", "process_list": [ "Background|com.android.bluetooth|com.android.bluetooth.hid.HidService", "Background|com.android.settings:remote|com.android.settings.wifi.MiuiWifiService", "Background|com.android.phone|org.codeaurora.ims.ImsService", "Background|system|com.qualcomm.location.LocationService", ..., "Background|xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft|ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn" ], "apps_list": [ "com.introspy.config", "com.google.android.youtube", "com.google.android.googlequicksearchbox", "org.telegram.messenger", ..., "com.google.android.inputmethod.latin", "jakhar.aseem.diva" ], "method": "register" } 

, . , , «» .

, , , Manifest , . . !

findings


, . :


PS , , , «- Android».

Source: https://habr.com/ru/post/430074/


All Articles