我如何差点感染试图出售靴子的病毒



我是秋天来临时试图减少在街头花费时间的人之一。 在莫斯科,这并不困难:您仅限于从家到办公室再返回的路线。 但是,潮湿的天气会导致房间不舒服,尤其是当您的工作场所(例如我的工作场所)在窗户旁边,并且每隔一个同事抱怨闷闷的时候,就要求给办公室通风。 为了不让脾脏掉落,今年秋天我更新了衣橱。

考虑到不必要的事情的命运,我想知道如何处理这些事情:扔掉,切成碎片,交给我弟弟带走? 但有一件事情,这些方法都不适合:它们是大小合适的皮靴44,但它们打扰了我。 我决定在Avito上出售它们。 我上传了照片,并指出了一个错误的名字(信息安全性是相同的),穿上了靴子,还有其他几件东西去睡觉了。 我怎么知道这将导致对隐藏威胁的应用程序进行长时间分析?



惊喜


经过几次可疑的通话的第二天,我收到了一条有趣的SMS消息,内容如下:



几天后,我收到了另一条类似的消息:



我惊讶地发现有人可以通过互联网将钱转给我(显然,我是唯一的一个老人-我仍在使用纸质储蓄书),我单击了SMS中的链接。

之后,我被要求下载一个Android应用程序(apk文件)。 愉快地下载文件,我看到了以下内容:



这是可信的! 我热切希望快速安装并结束所有内容。
但是在这里,像往常一样,由于某种原因,恼人的Android操作系统不允许我运行该文件。 “已经把钱给我!” 我很愤慨。 我必须进入设置并打开“未知来源”选项,2018年的手机真的那么笨吗? 顺便说一句,我的手机是Andoid 6.0.1的Xiaomi Remdi(技术人员注意事项)。



随后是一系列奇怪的事件。 电话继续报告不可靠来源。 但是Avito是可靠的来源 ! 我必须用谷歌搜索,弄清楚如何解决这个问题,然后在设置中将其关闭。 很快出现了某种我没有安装的防病毒软件。



最终,我看到了梦co以求的标准安装窗口- 这些天来查看权限毫无意义 ,现在,即使您没有完全授权,也无法启动笔记本电脑。 应用程序安装完成并开始的快乐时刻! 我很期待承诺的钱。 此外,该应用程序还请求管理员特权,而我很高兴地同意这一点。 不幸的是,该应用程序的行为很奇怪,不想付款,很快它就从通用屏幕上的应用程序列表中完全消失了。





扰流板
后来我检查了另一部手机-搭载Android 4.4.2的联想。 事实证明,安装过程中的权限列表要大得多。 而且,Play Protection和Anti-Virus不会相互干扰,您只需要允许从不可靠的来源进行安装。





利他主义



因此,我们得出以下结论:

  • 安装花费了20分钟。
  • 我还没有收到钱。

我认为问题是代码错误,就像程序员经常遇到的那样。 我决定确定在手机上运行该应用程序时发生什么错误,并将有关该错误的报告发送给开发人员。

扰流板
现在剩下的无私人士很少,其中一个是我。

显然,当连接Internet时,这种类型的应用程序应该可以工作,因此一开始我尝试分析电话和应用程序服务器之间的流量。

代理配置技术指南
您可以在从电话到服务器的路径上的任何位置收听Internet流量,无论它是家庭路由器,本地Internet提供商,Internet主干网还是应用程序服务器。

问题:

  • 需要使用此设备。
  • 需要将所需应用程序的流量与其余流量分开。
  • 在加密的情况下(并且在2018年,所有内容均已加密)-必须知道密钥。

我决定采用一种更为经典的方式,并在我自己的笔记本电脑上使用代理服务器在电话上配置代理。 为了减轻加密问题,我决定导入我的证书,并且没有启动其他应用程序来分隔应用程序流量。
我选择burp suite作为代理服务器程序 。 我设置了代理服务器并将证书导出到我的手机。





设置专用软件后,我能够看到应用程序向服务器发送的请求:



如您所见,数据没有被发送(未知主机值在IP列中指示),而且由于在应用程序级别存在附加加密,因此无法进行分析。 错误地判断,电话无法通过域名来确定服务器及其子域https://*.sky-sync.pw的IP地址。

这仅意味着以下选项:

  • 域名已不存在
    • 它被所有者本人阻止。
    • 他因投诉而被注册服务商阻止。
  • DNS服务器问题
    • DNS服务器不知道该地址,因为生产中的开发人员推出了本地DNS地址。
    • DNS服务器专门阻止了此请求,这在Internet审查时代并不奇怪。

为了检查DNS服务器是否存在问题,我尝试从其他大型DNS服务器发出请求:Google,Yandex,OpenDNS(通常对本地DNS进行审查):



可以看出,没有一个人知道这个名字。 接下来,我查看了有关域名注册的whois信息:



好奇:该域名已注册,即很可能不是本地域名,但是由于该域名无法解析,因此它可能已被注册服务商阻止滥用。 但是为了什么呢? 他做错了什么?

为了弄清楚这个应用程序是什么,以及如何赚钱,我决定使用反向工程的魔力

深渊


如果您是一位人道主义者并且已经读过这个地方,那么这很好-对于后续的发展,您应该值得追授

工具包


要了解应用程序的“内幕”,我们需要下载专用工具。 您可以单独下载它们:

  • 打开APK容器包装
    • 经典-ApkTool
    • 您可以使用常规的存档程序将其解压缩,但是所有二进制资源(包括应用程序和Manifest文件)将无法读取。
  • Smali代码反编译器
    • 标准是Dex2Jar ,但是了解到该程序经常运行不正常。
    • 您需要非常仔细地处理此问题,因为反编译器与反编译器是不同的,我们将在以后考虑。
  • 一个用于查看反编译代码的程序 ,我建议使用jd-gui

或者,您可以使用通常付费的产品,一次拥有所有东西。 我更喜欢JebDecompiler :他只需提交一个apk应用程序到他的输入中,他就可以将所有内容整齐地排列在选项卡中,而且可以方便地在smali和反编译的Java代码之间进行切换。

另外,我要注意:

  • 如果存在本机lib(应用程序结构中的/ libs文件夹),则需要反汇编程序。
  • 您可能需要使用一组实用程序来处理smali代码(dex-to-smali,smali-to-dex)。
  • 记事本++中的语法高亮显示 ,以免引起您的注意。

只有老年人参加战斗


复习


当您打开反编译的代码时,可以立即清楚地看到它已被混淆。



我怎么理解?

  • 易于理解的类名
    isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq.wukovicq;
  • 无法访问的代码
     if(0 != 0) {</li> String v1 = "flnwznvh";</li> if(v1.length() != 661 && v1.charAt(0) == 104) {</li> v1.length();</li> } 
  • 字符串加密
     vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

程序员不太可能疯狂地开始开发代码。 他极有可能使用了一种公开的混淆器。 例如,为了保护知识产权而使代码分析复杂化,这一步骤非常普遍,但是对于研究人员来说,这是一种“扭曲”。

让我们注意主要的加密功能:



加密功能本身接受3行输入(如果更多,则其余的行都没有意义):

  1. 密文
  2. 关键
  3. CBC的初始化向量-AES

该函数在程序中至少被引用了213次:



我注意到,这是正常代码分析的重要关键。 接下来, 您需要考虑以下几种分析程序的方法:

  1. 恢复功能的逻辑,收集静态分析中的所有调用,解密线路。 这可能很困难而且很长,但是它将给出100%的结果。
  2. 更改应用程序的smali代码,再次进行编译,运行应用程序并在日志中捕获解密的行。 这很容易做到,但是在特定的启动中应用程序的行为是未知的,并且您可能看不到整个画面(无法获得对所有函数的调用)。 另外,通过应用证书和(或)完整性进行自我检查可能存在问题。
  3. 如果很难恢复功能的逻辑,则可以收集所有功能调用,并直接在动力学中使用必要的参数自己提取这些功能(例如,使用Frida软件。

我们将选择方法1作为最严格的方法。

去混淆


立即进行预订,去模糊处理通常是一个漫长而乏味的过程,因此您需要正确评估时间范围。 在我们的分析中,以某种方式解密所有行并在最短的时间内(甚至以拐杖的方式)解密就足够了,而不是为了一个模糊的理想选择而花一个月左右。

在完全逆向工程的情况下,定性去混淆是有意义的,例如,知识产权小偷在尝试复制竞争对手的解决方案时必须这样做,或者如果您经常必须分析由一个混淆器处理的程序,但这不是我们的情况。

JEB Decompiler v.1.4反编译器之后的源代码

扰流板
 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; } } 

反编译器说明
顺便说一句,dex2jar经常崩溃。 因此,在下图中可以看到dex2jar 2.0版无法应对,只能发出一个smali代码。



从源代码编译的最新版本为此功能生成了反编译的代码,但无法反编译许多其他代码(这就是技巧)。





底线:仔细考虑选择反编译器-这样可以节省大量时间,并且比分析smali代码更容易。


因此,如果我们现在仅将此代码粘贴到IDE中,则由于错误它将无法工作。

重要的是要记住:不需要反编译器产生由开发人员编写的有效代码。 他只是分析工作中的救星,他对如何编写代码进行了假设。 在大多数情况下,在由编译器进行优化之后,完全恢复原始代码的任务就变得微不足道了。

反编译错误示例:

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

我们看到结果很糟糕而且无效。 我们重写:

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

现在更加清楚了,这是通常的输入检查。 在这种情况下,我们需要:

  • 用其他语言构造替换所有“ goto”,例如 “ Goto”长期以来一直是无效的运算符。
  • 用Java库调用替换Android库调用(如果我们尝试在Java IDE中执行代码)。
  • 插入我们的代码引用的依赖类。
  • 自己想想什么是错的。

结果,我们得到:

 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" })); } } 

此代码成功实现。 在他的工作变得清晰之后,就可以对其进行简化和简化,从而导致程序员编写所谓的简单字体(当然,除非最初他的手没有弯曲)。

注意事项
顺便说一下,在这种情况下,可以使用大量在线资源来演示字符串的解密。 在程序内部调用加密字符串的示例:



在这里,必须首先将初始化向量转换为十六进制格式:



替换所有值:



最后,从base64解码:



结果,我们得到了通常的字符串,并且该调用具有有意义的外观。

因此,您需要遍历整个代码并收集所有加密的字符串,现在我们可以独立解密它们了。 这里的重点是,在“修改和注释代码”阶段,我们可以在smali级别和Java级别(反编译的smali)上工作。

修改的加号缺点模组
斯马里语您可以进行更改,在dex中重新编译并使用新行进行反编译通过smali代码工作并不总是那么容易。 如果更改不正确,则应用程序将无法编译
爪哇从更高级别的操作中提取数据通常要容易得多。大多数Java代码查看器无法编辑。

另一行示例

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

从这里开始,使用正则表达式拾取参数要比在以下代码上编写正则表达式容易得多:

Smali代码示例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代码示例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代码示例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 


可以看到,内部变量发生变化,命令顺序发生变化,参数数量也发生变化,此外,在程序中,解密函数不是直接调用,而是通过层的函数调用。 尝试自己编写一条规则来搜索此构造,避免在捕获其他函数的字符串时出错,并迅速进行所有操作( 祝您好运 )。

陷阱计划:

  1. 我们将从反编译代码中提取所有值。
  2. 解密。
  3. 将密文替换为smali代码中打开的密文。 例如,我们替代了第一个运算符。 (切出整个函数调用并保留返回的解密字符串会更加专业,但是再次有很大的风险来破坏程序)
  4. 让我们将smali代码收集到dex文件中。
  5. 进一步研究我们开始的代码分析器将很方便。

如果将所有反编译的代码收集到一个文件中,则将获得大约20,000行,用于手动分析需要很多时间,这显然比我准备出售的靴子还要贵。 首先,使用正则表达式收集所有行。



我们看到有593场比赛,再加上12场不属于这个规则的比赛, 全家都有其败类 。 一个例子:



排序,过滤总共422条唯一行:



我们通过了先前恢复的解密功能。 结果:



使用Python在smali代码中用打开的密文替换密文:

 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) 

我们在dex中收集smali文件:



现在可以通过某种方式进行分析(通过阅读整个构造的第一个论点):



分析方法


因此,我们有20,000行或多或少的可读代码行,我们不需要进行完整的分析。 有必要从整体上了解功能。 实际上,这里仅需要读取Java源代码的功能。 遍历代码,查看交叉引用,重命名变量和函数。

分析Android应用程序(尤其是大型应用程序)的最佳方法是什么?

选项1:您可以从清单文件中移出

例如,依次从LAUNCHER尝试展开整个呼叫链。 顺便说一句,不要忘记还有“接收器”和“服务”可以改变程序的线性执行。



完整清单文件
 <?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> 


选项2:您可以移开有趣的行



解密字符串的一部分
 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   !     ...                   !  ?                ! 

选项3:您可以移出有趣的资源(资产,库)

在这种情况下,选项3是更可取的。 / asset文件夹(apk容器)中有三个有趣的html文件。 这是他们在浏览器中的视图:





对于官方的Avito付款转账计划,它似乎值得怀疑,对您来说不是吗? 让我们追踪一下您按下该键在带有Sberbank徽标的页面上发送银行业务数据时发生的情况。 JavaScript调用sendCardData()函数:



然后通过调用ok.performClick()将其传输到Java代码:



在Java代码中,执行处理:



此外,所有这些都在mcrypt类中加密:



在该函数内部,以与先前考虑的相同方式对数据进行加密:



但是对于其余部分,这些密钥是硬连线的:



我们尝试通过在线资源解密:



并从base64转换。 成功! 我们可以解密所有应用程序数据:对先前捕获的流量进行了测试。

该应用程序向服务器报告所有事件
 { "sid":15, "imei":"861117030537111", "phone":"System", "message":"     22.10.2018 23:30:47", "time":"1540240247", "msg_id":1, "status":"unknown", "type":"inbox", "method":"message" } 

它还会定期传输所有正在运行的应用程序
 { "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文件具有相当多的权限,并且该应用程序具有更丰富的功能。我们将在另一篇文章中对功能进行深入分析。同时,成功!

结论


我没有出售靴子感到很失望。结论如下:

  • 不要在Avito上出售靴子
  • 不要点击晦涩的链接(即使是从朋友那里,甚至“您需要紧急借用100卢布-生死攸关的事情”)
  • 请勿从Google Play或AppStore下载其他应用程序
    • 如果您真的不知道是什么,请断开安装与“不可靠来源”的连接。
    • 不要断开“播放保护”。
    • 请记住,Google Play上可能存在恶意软件
  • 在手机上安装防病毒软件(确实有效)。
  • 如果您是开发人员,请不要混淆代码,让人们确定您的良好意图(开个玩笑
  • 如果您是研究员,请不要从事食品工作;在空闲时间分析应用程序并发布报告。我们将携手使世界变得更美好。

PS:我试图以幽默的形式来写这篇文章,并提交尽可能简单的文章,因为即使在星期五,我也不想读一本严肃的长篇小说,名为《模糊恶意Android应用程序的逆向工程》。

Source: https://habr.com/ru/post/zh-CN430074/


All Articles