安卓Native逆向之MOO音乐解密( .bkcflac,bkcmp3文件解密)

安卓Native逆向之MOO\QQ音乐解密( .bkcflac,bkcmp3文件解密)

1、背景

本文写于2021年1月5日,解密算法适用于目前最新版的MOO加密和QQ音乐加密。之前加密方式和目前加密方式并不完全相同

支持的格式

MOO音乐 :.bkcflac,bkcmp3

QQ音乐:.qmcflac,.qmc0,qmc2,qmc3,

2018年鹅厂推出了一款名为MOO的音乐APP,设计风格独特,采用QQ音乐曲库,可以看作是QQ音乐的轻奢款。可以是因为设计风格太过独特,MOO音乐只在一些小圈子里流行,一直不温不火。

不过从2019年末开始,MOO音乐就开始免费送VIP,到现在持续了一年多。由于MOO音乐曲库的曲库和QQ音乐的曲库一致,所以,相当于免费送绿钻。

条件也很容易达到,每日听够一个小时的歌,就送一天的VIP,有了VIP后,就可以将喜欢的音乐下载下来。

但不幸的是,MOO音乐下载的文件是加密的,目前最新的加密格式是

.bkcflac,bkcmp3

在这里插入图片描述

2、Java层逆向

用jadx打开MOO音乐,经过一番分析,确定了文件的解密是在这里做的(搜decrypt分析一下)

在这里插入图片描述
具体来说,在这个函数里面读取了本地加密文件,然后在

PayProcessor.d(i, bArr, read);

这个函数里面解密。追到内部,发现是个native函数,可以知道加密函数在
libpay_encrypt.so里面实现

public class PayProcessor {
   
   
    private static final String TAG = "PayProcessor";
    private static boolean hXR = true;

    private static native int native_decrypt(int i, byte[] bArr, int i2);

    private static native int native_encrypt(int i, byte[] bArr, int i2);

    static {
   
   
        try {
   
   
            System.loadLibrary("pay_encrypt");
            b.a.i(TAG, "[static initializer] load success", new Object[0]);
        } catch (Throwable th) {
   
   
            b.a.e(TAG, "static initializer", th);
        }
    }

    public static int c(int i, byte[] bArr, int i2) {
   
   
        if (hXR) {
   
   
            return native_encrypt(i, bArr, i2);
        }
        b.a.e(TAG, "[encrypt] sLoad = false", new Object[0]);
        return -1;
    }

    public static int d(int i, byte[] bArr, int i2) {
   
   
        if (hXR) {
   
   
            return native_decrypt(i, bArr, i2);
        }
        b.a.e(TAG, "[encrypt] sLoad = false", new Object[0]);
        return -1;
    }
}

3、Native层逆向

把MOO安装包解压后 ,我们可以在lib\armeabi-v7a目录下找到这个so文件

在这里插入图片描述
用IDA Pro打开这个so文件,我们可以定位带加密函数

在这里插入图片描述

最终可以定位到这里,加密函数的逻辑很简单,对整个文件的每个字节与一个字典进行异或操作,解密也是用的同一个函数。例如 假设某个数据为a,字典里的值为b,加密后的数据为c

那么加密的过程就是 c=a^b
解密的过程就是a=c^b

当然,在MOO音乐这里,还是做了一些额外的操作,IDA的伪代码里已经写得很清楚,不多作介绍了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


4、Java实现

将上面我们逆向后得到的数据,翻译成Java代码

public class FileDecrypt {
   
   
    private static final int[] map={
   
   0x77,0x48,0x32,0x73,0xDE,0xF2,0xC0,0xC8,0x95,0xEC,0x30,0xB2,0x51,0xC3,0xE1,0xA0,0x9E,0xE6,0x9D,0xCF,0xFA,0x7F,0x14,0xD1,0xCE,0xB8,0xDC,0xC3,0x4A,0x67,0x93,0xD6,0x28,0xC2,0x91,0x70,0xCA,0x8D,0xA2,0xA4,0xF0,8,0x61,0x90,0x7E,0x6F,0xA2,0xE0,0xEB,0xAE,0x3E,0xB6,0x67,0xC7,0x92,0xF4,0x91,0xB5,0xF6,0x6C,0x5E,0x84,0x40,0xF7,0xF3,0x1B,2,0x7F,0xD5,0xAB,0x41,0x89,0x28,0xF4,0x25,0xCC,0x52,0x11,0xAD,0x43,0x68,0xA6,0x41,0x8B,0x84,0xB5,0xFF,0x2C,0x92,0x4A,0x26,0xD8,0x47,0x6A,0x7C,0x95,0x61,0xCC,0xE6,0xCB,0xBB,0x3F,0x47,0x58,0x89,0x75,0xC3,0x75,0xA1,0xD9,0xAF,0xCC,8,0x73,0x17,0xDC,0xAA,0x9A,0xA2,0x16,0x41,0xD8,0xA2,6,0xC6,0x8B,0xFC,0x66,0x34,0x9F,0xCF,0x18,0x23,0xA0,0xA,0x74,0xE7,0x2B,0x27,0x70,0x92,0xE9,0xAF,0x37,0xE6,0x8C,0xA7,0xBC,0x62,0x65,0x9C,0xC2,8,0xC9,0x88,0xB3,0xF3,0x43,0xAC,0x74,0x2C,0xF,0xD4,0xAF,0xA1,0xC3,1,0x64,0x95,0x4E,0x48,0x9F,0xF4,0x35,0x78,0x95,0x7A,0x39,0xD6,0x6A,0xA0,0x6D,0x40,0xE8,0x4F,0xA8,0xEF,0x11,0x1D,0xF3,0x1B,0x3F,0x3F,7,0xDD,0x6F,0x5B,0x19,0x30,0x19,0xFB,0xEF,0xE,0x37,0xF0,0xE,0xCD,0x16,0x49,0xFE,0x53,0x47,0x13,0x1A,0xBD,0xA4,0xF1,0x40,0x19,0x60,0xE,0xED,0x68,9,6,0x5F,0x4D,0xCF,0x3D,0x1A,0xFE,0x20,0x77,0xE4,0xD9,0xDA,0xF9,0xA4,0x2B,0x76,0x1C,0x71,0xDB,0,0xBC,0xFD,0xC,0x6C,0xA5,0x47,0xF7,0xF6,0,0x79,0x4A,0x11};

    public static boolean decrypt(String fullPath){
   
   
        try {
   
   
            String sign="";
            if(fullPath.contains(".bkc")){
   
   
                sign=".bkc";
            }else if(fullPath.contains(".qmc")){
   
   
                sign=".qmc";
            }else {
   
   
                return false;
            }
            String decryptFilePath=fullPath.replace(sign,".");
            String postFix=decryptFilePath.substring(decryptFilePath.indexOf("."));
            if(postFix.equals(".0")||postFix.equals(".2")||postFix.equals(".3")){
   
   
                decryptFilePath.replace(postFix,".mp3");
            }
            File file=new File(fullPath);
            FileInputStream is=new FileInputStream(file);
            FileOutputStream fout=new FileOutputStream(decryptFilePath);
            byte[] b=new byte[8192];
            int len=0;
            long total=0;
            while ((len=is.read(b))!=-1){
   
   
                for (int i=0;i<len;i++){
   
   
                    b[i]^=encMap(total+i);
                }
                fout.write(b,0,len);
                total+=len;
            }
            is.close();
            fout.close();
            return true;
        }catch (Exception e){
   
   
            e.printStackTrace();
        }

        return false;
    }

    private static int encMap(long len){
   
   
        int pos= (int) (len%0x7fff);
        return map[(pos*pos+80923)%256];
    }

最后

在这里插入图片描述

欢迎关注我的微信公众号:从来不想

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章