<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    一個QQ音樂源無損音質下載軟件逆向淺要分析

    VSole2022-08-10 14:45:58

    逆向一個QMD QQ音樂源下載軟件

    這個Apk主要是用來下載QQ音樂的無損數字音頻文件,我為了把我iMac上的mp3音質音樂替換為flac或者HiRes無損,一個個去網上找文件。偶然間在網上發現了這個app,有些好奇怎么實現的,于是做本篇分析文章。

    樣本APK:QMD 1.7.2.apk https://wwb.lanzoub.com/iX5Lr08oc3be

    第一步 反射大師脫殼

    對于類似于這種神奇的軟件作者總會加個殼加加固保護一下源代碼,我有種直覺這玩意應該也是加了固的,果然打開zip文件一看:

    libjiagu.so赫然在目。

    得,直接打開反射大師先來扒一層皮看看能不能看到里面。

    反射大師脫殼過程不再贅述,直接導出內存dex即可。

    第二步 JEB靜態分析

    可喜可賀,2021年初還只有Jeb 3.24,坐了一年牢出來發現竟然有4.x的版本更新了,好,很有精神!

    首先我們打開app開始下載高解析度音頻,看看加密如何。

    我們關注一下上圖的/api/Download請求。

    這個包是用來獲取QQ音樂的文件實際下載地址,我們來看看這個請求:

    一個迷之數據,一個樸實無華的http請求,再無其他。

    只有下面的一個http://ws.stream.qqmusic.qq.com/RS01003zLSuX07Z0LB.flac

    接口關聯他。那么為了得到這個qqmusic的源數據,我們要批量下載這些音樂就需要逆向出這個迷之數據到底是什么東西,我們如何生成它。

     復制代碼 隱藏代碼
    接口表
    獲取音樂的高解析度下載地址
    /api/MusicLink/link
    

    那么話不多說,jeb直接打開導出的dex看函數,搜索這個字符串。

    直接可以看到這個搜索結果了,很好,看來不需要再去找其他的dex文件了。

    tab一下看看。

    關注以下函數:

     復制代碼 隱藏代碼
    public String getMusicLink(String arg4) {
        String v4 = EncryptAndDecrypt.encryptText(arg4);
        String v4_1 = new HttpManager("http://8.136.185.193/api/MusicLink/link").postDataWithResult("\"" + v4 + "\"");
        Logger.e(v4_1, new Object[0]);
        return v4_1;
    }
    

    v4=arg4,arg4則是一個String不足為懼,先看看這個寫的非常漂亮的Encryption函數:

     復制代碼 隱藏代碼
        public static String encryptText(String arg1) {
            return EncryptAndDecrypt.encryptText(arg1, Cookie.getQQ());
        }
        public static String encryptText(String arg4, String arg5) {
            if(!TextUtils.isEmpty(arg4) && !TextUtils.isEmpty(arg5)) {
                int v1 = 0;
                StringBuilder v5 = new StringBuilder(EncryptAndDecrypt.encryptDES(arg4, "QMD" + arg5.substring(0, 8)));
                Random v4 = new Random(((long)Calendar.getInstance().get(5)));
                int v0 = v4.nextInt(4) + 1;
                while(v1 < v0) {
                    v5.insert(v4.nextInt(v5.length()), "-");
                    ++v1;
                }
                return v5.toString();
            }
            return "";
        }
    

    他調用了encryptText(String arg4, String arg5)函數,那么我們就可知道arg5是作為密碼而存在的,arg4則是String的原文,那么Cookie.getQQ()這個代碼則就相當的可疑。

    跳轉一下看看:

     復制代碼 隱藏代碼
    public class Cookie {
        private static String Mkey;
        private static String QQ;
        public static String getMkey() { return Cookie.Mkey; }
        public static String getQQ() { return Cookie.QQ; }
        public static void setCookie(String arg0, String arg1) {
            Cookie.Mkey = arg0;
            Cookie.QQ = arg1; 
        }
    }
    

    一個靜態實體類,直接看setCookie的交叉引用看看是從decryptAndSetCookie函數設置密碼的:

     復制代碼 隱藏代碼
        public static boolean decryptAndSetCookie(String arg5) {
            String v5 = arg5.replace("-", "").replace("|", "");
            if(v5.length() >= 10 && (v5.contains("%"))) {
                String[] v5_1 = v5.split("%");
                String v0 = v5_1[0];
                String v5_2 = EncryptAndDecrypt.decryptDES(v5_1[1], v0.substring(0, 8));
                if(v5_2.length() < 8) {
                    v5_2 = v5_2 + "QMD";
                }
                Cookie.setCookie(EncryptAndDecrypt.decryptDES(v0, v5_2.substring(0, 8)), v5_2);//v5_2就是密碼,由arg5參數分解而來。            return true;
            }
            return false;
        }
    

    繼續跟蹤交叉引用:

     復制代碼 隱藏代碼
        public boolean getCookie() {
            String v0 = new HttpManager("http://8.136.185.193/api/Cookies").postDataWithResult(new Gson().toJson(SystemInfoUtil.getDeviceInfo()));
            return TextUtils.isEmpty(v0) ? false : EncryptAndDecrypt.decryptAndSetCookie(v0);
        }
    

    找到了,看來是從這個接口獲取的數據,但是這個接口居然是POST提交,那么我們就有必要看看這個提交的數據SystemInfoUtil.getDeviceInfo()到底是什么東西:

     復制代碼 隱藏代碼
        public static final DeviceInfo getDeviceInfo() {
            DeviceInfo v10 = new DeviceInfo(SystemInfoUtil.getUID(), SystemInfoUtil.getSystemModel(), SystemInfoUtil.getDeviceBrand(), SystemInfoUtil.getAppVersionName(), SystemInfoUtil.getSystemVersion(), SystemInfoUtil.getAppVersionCode() + "", null, 0x40, null);
            v10.setIp(EncryptAndDecrypt.encryptText(v10.getUid() + v10.getDeviceModel() + v10.getDeviceBrand() + v10.getSystemVersion() + v10.getAppVersion() + v10.getVersionCode(), "F*ckYou!"));//密碼是F*ckYou!,emmmm....        return v10;
        }
    

    獲取了設備的一些信息,然后調用了一個setIp函數,這個加密看起來像是一個接口簽名防止被抓包調用接口,提高逆向成本。

    直接抓包看數據,可以看出來其實就是幾個字符串appand一起后加上密碼des,下面我們就開始先用python實現一下。

    不過在此之前我們還要看一下EncryptAndDecrypt.encryptText函數,里面是如何處理的:

     復制代碼 隱藏代碼
        public static String encryptText(String arg4, String arg5) {
            if(!TextUtils.isEmpty(arg4) && !TextUtils.isEmpty(arg5)) {
                int v1 = 0;
                StringBuilder v5 = new StringBuilder(EncryptAndDecrypt.encryptDES(arg4, ("QMD" + arg5).substring(0, 8)));
                Random v4 = new Random(((long)Calendar.getInstance().get(5)));
                int v0 = v4.nextInt(4) + 1;
                while(v1 < v0) {
                    v5.insert(v4.nextInt(v5.length()), "-");
                    ++v1;
                }
                return v5.toString();
            }
            return "";
        }
    

    清晰地看到又調用了EncryptAndDecrypt.encryptDES函數,還加上了“QMD”作為密碼前置字符串,我們繼續跟蹤:

     復制代碼 隱藏代碼
        public static String encryptDES(String arg5, String arg6) {
            if(arg5 != null && arg6 != null) {
                try {
                    Cipher v0 = Cipher.getInstance("DES/CBC/PKCS5Padding");
                    v0.init(1, new SecretKeySpec(arg6.getBytes(), "DES"), new IvParameterSpec(arg6.getBytes()));
                    return Base64.encodeToString(v0.doFinal(arg5.getBytes()), 0).trim();
                }
                catch(Exception v5) {
                    return v5.getMessage();
                }
            }
            return null;
        }
    

    到這里我們已經很清晰了,密碼作為iv,加密方式為des/cbc/pkcs5padding方式填充結果,那么我們用python實現一下這個加密函數:

    到這里我們就用python寫出了加密算法,接下來就可以用這個算法生成數據去請求http數據了。

    第三步 測試接口訪問

    于是為了下載flac,我直接寫了一個python腳本。

    項目地址

    https://github.com/QiuChenly/python_down_jaychou
    
    stringqq音樂
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    逆向一個QMD QQ音樂源下載軟件這個Apk主要是用來下載QQ音樂的無損數字音頻文件,我為了把我iMac上的mp3音質音樂替換為flac或者HiRes無損,一個個去網上找文件。反射大師脫殼過程不再贅述,直接導出內存dex即可。
    crawlergo是一個使用chrome headless模式進行URL收集的瀏覽器爬蟲。它對整個網頁的關鍵位置與DOM渲染階段進行HOOK,自動進行表單填充并提交,配合智能的JS事件觸發,盡可能的收集網站暴露出的入口。內置URL去重模塊,過濾掉了大量偽靜態URL,對于大型網站仍保持較快的解析與抓取速度,最后得到高質量的請求結果集合。調研1.
    冰蝎流量免殺初探
    2022-12-20 09:11:06
    本文僅針對冰蝎流量改造進行初步探討,熟悉一下整個流程,真的要繞流量設備,估計還需要其他的技巧。
    可想而知,如果 Spring 城門失火,Java 必定遭殃。根據官方文檔,Spring Cloud Function 是基于 Spring Boot 的函數計算框架,它可以:通過函數促進業務邏輯的實現。
    Java Agent到內存馬
    2021-12-14 14:52:22
    前言今天看到一篇文章,寫的是關于JAVA Agent相關的資料(附1),里面提到了Java Agent的兩種實現方法:實現premain方法,在JVM啟動前加載實現agentmain方法,在JVM啟動后attach加載因為最近流行破解CobaltStrike不再直接使用反編譯打包源碼了,而是使用JAVA Agent進行提前字節碼修改。
    本文來自“白帽子社區”知識星球作者:想看云飛卻沒風01環境搭建composer create-project topthink/think=6.0.x-dev thinkphp-v6.0. 在 ThinkPHP5.x 的POP鏈中,入口都是 think\process\pipes\Windows 類,通過該類觸發任意類的__toString 方法。
    對這段時間做的一次攻防演練做一個記錄,這次給我們分了三個目標,一個目標是甲方單位自己的一個自建系統,其余兩個是甲方的下級單位的系統。開始之前覺得不好做,因為攻防演練跟HW有些差別,HW可以不限制攻擊手法,可以從上游供應鏈,社工、釣魚多種角度出發來挖掘漏洞。這次攻防演練給我們三個目標、兩個web系統、一個app,可以利用的點非常少,不可以攻擊其他的系統,只能搞這幾個目標,要不是這次運氣好真的就拉垮了
    由于測試過程中很多系統我們能接觸到的只有一個登陸界面,所以要充分挖掘漏洞,進行深入操作登錄 注冊萬能密碼繞過登錄存在 SQL 注入的情況下,有可能使用萬能密碼直接登錄admin' or '1'='1'--. 有超級多登錄口 SQL 注入存在 SQL 注入的話直接注出賬密有些可以構造用戶注入登錄,比如熊海 cms 后臺登錄處存在 sql 注入$login=$_POST['login'];
    Redis系列漏洞總結
    2023-06-19 10:29:18
    前言Redis的未授權漏洞一直都是一個很火的漏洞,最近看許多前輩的文章自己復現后,根據自己的實踐再次總結一下,為日后復習方便回顧。Redis簡介redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string、list、set、zset和hash。這些數據類型都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。
    burp0_data = {"name": username, "pw": password, "repw": password, "email": email, "submit": ''}
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类