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 it 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 have 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 44 sizes of a decent look, but I was pretty tired. 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:



    A couple of days later I received another similar message:



    Surprised that someone somehow could transfer me money via the Internet (apparently, I’m the old one I still use paper savings books), I clicked on 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 confidence! 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 - there is no point in looking at permissions in our time, now even the notebook will not start until you give him full access to the phone. Joyful moment of the end of the installation of the application and the 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 Anti-Virus do not interfere, it is only necessary to allow installation from unreliable sources.





    Altruism



    So, what we have come to:

    • The installation took 20 minutes of time.
    • I did not get the money.

    I thought that the problem was 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 this type of application 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
    Можно прослушать интернет-трафик в любой точке, проходящей по пути от телефона до сервера, будь то домашний роутер, местный интернет-провайдер, магистральный интернет-провайдер или сервер приложения.

    Проблемы:

    • Необходим доступ к данному оборудованию.
    • Требуется отделить трафик нужного приложения от остального.
    • В случае шифрования (а в 2018 шифруется уже всё) — необходимо знание ключа.

    Я задумал пройти более классическим путём и настроить прокси на телефоне с прокси сервером на собственном ноутбуке. Чтобы нивелировать проблему шифрования, решил импортировать свой сертификат, а для отделения трафика приложения не стал запускать другие приложения.
    В качестве программы прокси-сервера выбрал burp suite. Настроил прокси-сервер и экспортировал сертификат на телефон.





    After setting up the specialized software, I was able to see which requests the application sends to the server: As you can see, the data is not sent (the IP 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:







    • Domain name has ceased to exist
      • It was blocked by the owner himself.
      • He was blocked by the registrar of the complaint.
    • DNS server issue
      • The DNS server does not know the address, as the developer in production rolled out the local DNS address.
      • The DNS server specifically blocked this request, which is not surprising in the era of Internet censorship.

    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:

    • Apk-container unpacker
      • Classic - ApkTool .
      • You can unpack with a regular archiver, but then all binary resources, including applications and the Manifest file, will be unreadable.
    • Smali code decompiler
      • The standard is Dex2Jar , but learn that this program often performs crookedly.
      • It is necessary to approach the matter very carefully, because the decompiler to the decompiler is different, consider it later.
    • A program to view the decompiled code , I would recommend jd-gui

    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:

    • In the presence of native libs (the / libs folder in the application structure), a disassembler would be needed .
    • You may need a set of utilities for working with smali-code (dex-to-smali, smali-to-dex).
    • Syntax highlighting in Notepad ++, so as not to break the eyes.

    Only old men go to battle


    Overview


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



    How do I understand this?

    • Unreadable class names
      isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq.wukovicq;
    • Unreachable code
      if(0 != 0) {</li>
                  String v1 = "flnwznvh";</li>
                  if(v1.length() != 661 && v1.charAt(0) == 104) {</li>
                      v1.length();</li>
                  }
    • String Encryption
      vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"})

    It is unlikely that the programmer is crazy enough to initially develop the code. Most likely, he used one of the public obfuscators. This step is quite usual 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.

    Let's pay attention to the basic encryption function:



    The encryption function itself accepts 3 lines as input (if more, 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 (do not get 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 minimum 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 the JEB Decompiler v.1.4 decompiler

    Spoiler
    publicstatic 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 = newbyte[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
    Кстати, dex2jar часто даёт сбои. Так, на рисунке ниже видно, что dex2jar версии 2.0 не смог справиться и просто выдал smali-код.



    Его свежая версия, собранная из исходников, выдала декомпилированный код данной функции, но не смогла декомпилировать многие другие (вот так фокусы).





    Итог: внимательно отнеситесь к выбору декомпилятора — это сэкономит вам кучу времени и будет проще, чем анализ smali-кода.


    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 when analyzing 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 at all.

    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 validation. In this case, we need:

    • Replace all “goto” with other language constructs, since “Goto” has long been an invalid operator.
    • Replace calls to Android libraries with calls to Java libraries (if we are trying to execute code in the Java IDE).
    • Insert dependent classes referenced by our code.
    • Think for yourself what is wrong.

    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;
    publicabstractclassMain{
        publicMain(){
            super();
        }
        //hex to asciipublicstaticbyte[] xkvasepi(String str) {
            byte[] v0 = null;
            if(str != null && str.length() >= 2) {
                int v2 = str.length() / 2;
                v0 = newbyte[v2];
                int v1;
                for(v1 = 0; v1 < v2; ++v1) {
                    v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16));
                }
            }
            return v0;
        }
        publicstatic 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//checkif ((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));
                //checkif(v2.length <= 0) {
                    returnnew 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 = newbyte[v2.length - v4];
                    System.arraycopy(v2, 0, v8, 0, v2.length - v4);
                    v2 = v8;
                } catch (Exception v3_2) {
                    return"";
                }
            }
            v2 = Base64.getDecoder().decode(v2);
            returnnew String(v2);
        }
        publicstaticvoidmain(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, of course, his hands were not crooked initially).

    Note
    Кстати, в данном случае дешифрование строк можно продемонстрировать с помощью связки онлайн-ресурсов. Пример вызова шифрованной строки внутри программы:



    Здесь вектор инициализации нужно сначала перевести в Hex-формат:



    Подставляем все значения:



    И в конце декодируем из base64:



    В результате получаем обычную строку, и вызов приобретает осмысленный вид.

    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 be collected.
    JavaMore often, it is much easier to extract data from high-level operations.Edit in most Java-code viewers can not.

    Another string example

    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 for 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, the 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 functions of the interlayer. 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. We substitute, for example, in exchange for 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 black sheep . Example:



    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 compile 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 have 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 away from the Manifest file.

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



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



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



    In the Java code, the processing is performed:



    Further, all this is encrypted in the class mcrypt:



    Inside the function, data is encrypted 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"
    }

    If I had a window for entering bank data in dynamics, then the data would have been in traffic. Thus, we can conclude that this is a “phishing” application.

    Those who were attentive noticed that there are quite a few permissions in the Manifest file, and the application has more rich functionality. We will carry out a deep analysis of the functional in another article. In the meantime, success!

    findings


    I am disappointed that I did not sell the boots. And the conclusions are:

    • Do not sell boots on Avito
    • Do not follow obscure links (even if from friends and even if “you need to urgently borrow 100 rubles - a matter of life and death”)
    • Do not download applications, except from Google Play or AppStore
      • Disable the installation from "unreliable sources", if you really do not understand what's what.
      • Do not disable Play Protect.
      • Remember, there may be malware on Google Play
    • Install antivirus on the phone (really works).
    • If you are a developer, don't obfuscate the code, let people see your good intentions (joke)
    • If you are a researcher, do not work on food and analyze applications in your free time and publish reports. Together we will make the world a better place.

    PS I tried to write an article in a slightly humorous format and submit as simply as possible, because even I wouldn’t want to read a serious longrid called “Reverse engineering of an obfuscated malicious application on the Android OS” on Friday.

    Also popular now: