安卓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];
    }

最後

在這裏插入圖片描述

歡迎關注我的微信公衆號:從來不想

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