<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>

    協議分析實戰

    VSole2022-08-18 16:56:24

    協議分析是逆向技術中的一個重要技能,本篇文章先分享3個app。

    第一個app

    找到登錄界面,我輸入的手機號是13905376666,密碼是:666666666。

    先抓包:

    這個是請求包:

    http://m.xxxx.com.cn/v2/member?modules=cloudlogin%3A1&password=666666666&siteid=10001&sign=d01f5e4445af0a30d148d4dc451b41cf&clientid=1&system_name=android&type=android&time=1659366738640&ip=10.0.2.15&device_id=2a%3Aa5%3A33%3A2d%3Ade%3Ac9&account=13905376666 HTTP/1.1
    

    第一步,也是最重要的一步,得先定位到關鍵代碼的位置:

    (這里插一句,分析協議字段的時候一般都是最后分析sign

    字段,這個sign字段經過我這幾天的學習總結出來的一般是這樣生成的:

    先將其他字段加密之后,然后有的還可能帶一些鹽,最后將各個字段排序之后,然后加密,MD5一般是)

    回到分析過程中,這里發現,只有sign字段看著像加密的,其他的基本都是明文,然后找到兩個方法,分別下斷點,然后看看他走哪個就完事了。

    這里我打算搜索post請求中的v2/member,和"system_name"。

    最終定位到這個兩個方法中比較像:

    然后動態調試的時候我怕段不下來,就遇見sign都下了斷點(這也是一種方法吧哈哈,但是他會在一些奇奇怪怪的方法中段下來,但是只要不是你想要的斷點處,直接讓程序繼續運行就好了),然后發現我的判斷沒有錯:(當點擊登錄按鈕的時候程序從這里斷了下來)

    這個協議分析我也是剛剛開始自己動手,然后就打算把步驟寫的詳細一點:

    首先看這個函數的兩個參數:

    第一個參數表示一個集合,在這個集合中保存發往服務器的數據包中的各個參數,然后第二個參數的翻譯是上下文,所以這個參數應該就是連接上下函數的一個變量。

    然后看這個device_id就是表示手機的串號:

    是這樣聲明的:

    就是返回手機的型號,沒有什么好說的。

    然后解釋完第一個,其他的就不用過多解釋啦:

    這個clientid就是定值1:ClientID(客戶端標識號)用于標識連接到API服務的客戶端。

    然后下一個就是本機IP地址,還有時間timestamp,siteid站點標識符定值10001,系統名稱system_name,型號type是Android的。

    然后重點就是這個sign了,這個是個簽名校驗,這個校驗對于我們來說就是我們自己分析算出來的sign值和發往服務器的sign值一樣,就說明分析正確了,一般的校驗都是看這個發往服務器的sign值和服務器那邊計算出來的sign值是否一樣:

    所以接下來就是要分析這個函數了 m8098a(params.getURLHashMap(), timestamp + ""));

    他是這樣聲明的:

    然后就要分析這個關鍵函數了:

      public static String m8098a(HashMap<String, String> paramsMap, String time) {//傳入兩個參數,一個是map集合,一個是剛剛獲取的time        LinkedHashMap<String, String> sortParams = new LinkedHashMap<>();//聲名一個新的集合        Object[] key_arr = paramsMap.keySet().toArray();//將map中的屬性取出來,存放在key_arr數組里面        Arrays.sort(key_arr);//進行排序        for (Object key : key_arr) {            try {                sortParams.put(key.toString(), URLEncoder.encode(paramsMap.get(key).toString(), "UTF-8"));            } catch (UnsupportedEncodingException e) {                e.printStackTrace();            }        }//將剛剛key_arr數組里面的值轉化成string類型,然后將map中的數據進行url編碼,然后將這兩個數組組合成一個鍵值對放在新聲名的sortParams中        StringBuilder result = new StringBuilder();        for (Map.Entry<String, String> entry : sortParams.entrySet()) {//把剛剛處理完的sortParams中的值利用迭代器取出來放在entry變量中            if (result.length() > 0) {                result.append("&");            }//在每一個鍵值對后面用 & 連接            result.append(entry.getKey());            result.append("=");            result.append(entry.getValue());//將鍵和值之間用 = 連接        }//這樣就像發往服務器的包的結構了        String replace = result.toString().replace("*", "%2A").replace("%7E", "~").replace("+", "%20");//將處理完的結果result在to string之后進行字符替換為replace        String resultMD5 = MD5.md5(replace);//將replace進行md5加密之后為resultMD5        String str = resultMD5 + "1fa50ba25ed527f3fd1eb9467686f2bb" + time;//進行字符串拼接之后(加鹽)轉換為str        String md5Result = MD5.md5(str);//將str在進行md5加密之后作為函數的返回值md5Result        return md5Result;//返回的md5Result即為sign值    }
    

    這樣的話第一個app的協議字段到此就分析完了。

    第二個app:

    我輸入的用戶名是kanxue,密碼是kanxue123。

    data值明文傳輸:

    POST http://xxxx.xx8xx88xx.com/v2_2/user/login HTTP/1.1nonce=b104cc74ade1441a9c61759fe330883c&codeSign=2086B76A137CBA8B84DD1CBCAC3F7B45&timestamp=1659843481869&data=%7B%22params%22%3A%7B%22username%22%3A%22kanxue%22%2C%22password%22%3A%22kanxue123%22%7D%7D&version=2.2.1&product_version=220&platform=HD1910&network=1&device=864282012982996&access_token=62cb31ab6f2ffdaef382236aba9b98f4&screen_width=1280&screen_height=720&bbsnopic=0&system=2&system_version=19&theme=4
    

    根據上一個app的分析,這回需要重點分析的應該是codeSign,nonce,access_token(這個介紹一下吧):

    accesstoken一個訪問令牌包含了此登陸會話的安全信息。登錄一次,服務器生成一個token返回給你,你只需每次請求附帶這個token就能對網站標識自己的身份。換句話說,就是一個電子版的令牌。(這個字段可以通過解碼,變成人可以看的字段)
    

    然后剩下的字段看著直接random就好了。

    然后就該定位了,還是把看著像的地方都下上斷點,然后看看從哪里段下來就好了:(這個挺好的,就三處斷點,上一個app我下了9個,不要嫌麻煩,靜下心來就好):

    然后發現程序斷在了這里:

    這里我插一句,之前我說過JEB誰用誰說好...今天出bug了哈哈,這個JEB他有的時候不顯示寄存器的值:

    然后用readvar v0 string還報錯,這就得上Android stdio。

    定位到這個關鍵函數:

    直接上代碼分析過程了:

       public void mo17350a(String str, JSONObject jSONObject, AbstractC5417d<T> dVar) {        String replaceAll = UUID.randomUUID().toString().replaceAll("-", "");//這里就是隨機一個UUID,然后賦值給nonce字段        long currentTimeMillis = System.currentTimeMillis();//獲取系統時間,在計算codesign字段時會用到的·1        JSONObject a = mo17349a(jSONObject);//從函數的傳入的參數中取出的JSON_object中應該就是那個data字段的值        if (MyApplication.getInstance().isLogin()) {//如果點擊登錄按鈕,就走下面這個分支,就是進行鍵值對的拼接            C5445g.m20674a(str, new C5445g.C5461f[]{new C5445g.C5461f(ReportActivity.USER_ID, "" + MyApplication.getInstance().getUserDataEntity().getUid()), new C5445g.C5461f("login_token", "" + MyApplication.getInstance().getUserDataEntity().getLogin_token()), new C5445g.C5461f("nonce", replaceAll), new C5445g.C5461f("codeSign", C6485v.m23485a(replaceAll, a, MyApplication.getInstance().getUserDataEntity().getUid() + "", currentTimeMillis)), new C5445g.C5461f("timestamp", currentTimeMillis + ""), new C5445g.C5461f("data", a.toString()), new C5445g.C5461f("version", C5414a.f16227f + ""), new C5445g.C5461f("product_version", "220"), new C5445g.C5461f(C1380c.PLATFORM, Build.PRODUCT + ""), new C5445g.C5461f(CandidatePacketExtension.NETWORK_ATTR_NAME, MyApplication.getNetworkType() + ""), new C5445g.C5461f("device", "" + MyApplication.getDeviceId()), new C5445g.C5461f("access_token", "" + C5414a.f16229h), new C5445g.C5461f("screen_width", "" + C5414a.f16230i), new C5445g.C5461f("screen_height", "" + C5414a.f16231j), new C5445g.C5461f("bbsnopic", MyApplication.isForumNoIMG() + ""), new C5445g.C5461f(C7748d.C7755c.f24519a, MessageService.MSG_DB_NOTIFY_CLICK), new C5445g.C5461f("system_version", Build.VERSION.SDK_INT + ""), new C5445g.C5461f("theme", C5414a.f16223b + "")}, (ResultCallback) dVar);            return;        }        C5445g.m20674a(str, new C5445g.C5461f[]{new C5445g.C5461f("nonce", replaceAll), new C5445g.C5461f("codeSign", C6485v.m23484a(replaceAll, a, currentTimeMillis)), new C5445g.C5461f("timestamp", currentTimeMillis + ""), new C5445g.C5461f("data", a.toString()), new C5445g.C5461f("version", C5414a.f16227f + ""), new C5445g.C5461f("product_version", "220"), new C5445g.C5461f(C1380c.PLATFORM, Build.PRODUCT + ""), new C5445g.C5461f(CandidatePacketExtension.NETWORK_ATTR_NAME, MyApplication.getNetworkType() + ""), new C5445g.C5461f("device", "" + MyApplication.getDeviceId()), new C5445g.C5461f("access_token", "" + C5414a.f16229h), new C5445g.C5461f("screen_width", "" + C5414a.f16230i), new C5445g.C5461f("screen_height", "" + C5414a.f16231j), new C5445g.C5461f("bbsnopic", MyApplication.isForumNoIMG() + ""), new C5445g.C5461f(C7748d.C7755c.f24519a, MessageService.MSG_DB_NOTIFY_CLICK), new C5445g.C5461f("system_version", Build.VERSION.SDK_INT + ""), new C5445g.C5461f("theme", C5414a.f16223b + "")}, (ResultCallback) dVar);    }
    

    接下來這個函數new C5445g.C5461f()就是鍵值對的拼接了:

    然后就要一個個看看各個字段對應的值是怎么生成的了。

    nonce:先生成一個隨機數,然后進行字符的替換。

    然后這幾個就是明文,沒有加密過程:

    其他的幾個也是直接明文傳輸,就不一一展示了。

    然后來看看這個加密的字段access_token:

    然后就要分析這個東西了 C5414a.f16229h 跳過去看看:

    接著跟進去分析,終于找到了定義他的地方了:

    static {        String str = f16227f + Build.PRODUCT + MyApplication.getNetworkType() + MyApplication.getDeviceId();        crack.log(str);        f16229h = C6448r.m23455a(str);    }
    

    這個字段f16227f是獲取versionname:

    后面的幾個是deviceid,還有網絡信息:

    最后再看看這個函數C6448r.m23455a()干了什么:

    就是個md5加密就完了。

    最后重點看這個codesign字段:

    第一個參數relaceall是隨機生成的數字,然后進行了字符串的替換。

    第二個參數a是之前傳入的

    jsonobject中的data中的數據,就是明文的用戶名和密碼。

    第三個參數是當前的時間。

    然后就要跟進分析這個函數了:

    這個函數的三個參數都分析完了,只有這個參數我們還不知道是什么。m23483a():

    public static final int forum_key = 2131230990;
    

    然后就要看這個函數了C6420af.m23337b()。

    這個是獲取資源文件的函數。

    所以要想得到這個字段的值就要從資源文件中看看了:?

    <string name="forum_key">94ac5cfb69e87bd7</string>
    

    這樣所有字段的值都得到了,最后直接看看這個函數就好了。

    C6448r.m23455a()還是剛剛那個md5加密的函數:

    這樣這款app就分析完了。

    分析完上面兩款app大家也可以發現,這些工具沒有誰最好,只是都得使用,各有個的好處,上次說那個JEB yyds就打臉了哈哈,這個jadx也挺好的,一些代碼直接翻譯成人能看懂的了。

    第三個app

    這款app我用的真機,模擬器出了點問題,先登錄:

    然后抓包,登錄包中只有這兩個數據:

    這里如果只在key上面下斷點是段不下來程序的(分析到后面就知道了,這個傳輸的字段叫key_id),所以這里我打算從post字段上下手。

    POST http://passport.xxxx.com.cn/login_jsonp_active.do HTTP/1.1Content-Type: application/x-www-form-urlencoded; charset=utf-8Content-Length: 302Host: passport.xxxx.com.cnConnection: Keep-AliveAccept-Encoding: gzipCookie: JSESSIONID=aaapcYHe7KtGb7NDScYhyUser-Agent: okhttp/3.1.2 key=dTdlMmtGQjZIQk9CNmdudi95QURUbUNrZ2xKWFRNc0t0Z3g4NnpKRkZDYjRGc25RU05CL0wzSjQ2ZFYrMmxqd1ZaU2JtTVJvaURudWJhVnpFZGRsRmZGQldGQzBxbE0xVFVNVER5TDRpNkFpc1E4eVJVK0VnZWxBUUdaR0lvZ0Y5NTdFKzJKRVFtNlR0SDN5SGtCY1FOZnFIYnpNdmZqa3FPVnc2SkNGUzJ1SWUrb2xBby9wbGUrSUh1bU1wK2pTbW1XYkhzajNsYmM9P2tleUlkPTE
    

    這里我搜索的是login_jsonp_active這個字段,然后定位到這里:

    然后看那個API_LOGIN的交叉引用定位到了這里:

    然后向下找找看看有沒有突破點:這個函數中有解密字段,還有用戶名和結果,看著比較像:

    然后繼續看交叉引用,這里有解密的標志,所以這里看著比較像:

    最終定位到了這里,發現了是key_id字段,不是key字段.....難怪下這么多斷點都找不到它:

    然后還發現了這個:發現他是des加密:

    sb.append(DESedeCoder.encode(json, KEYS.get("1"))).append("?keyId=").append("1");
    

    然后就要分析這個函數是怎么加密key_id字段了:

    public String encrypt(Map params) {
    

    這個傳入的參數中的map數據,在fiddler中得數據包中可以發現。

    然后通過搜索sign字段也可以定位到在app中的位置:

    public static Map<String, String> m264a(Map<String, String> map) {      map.put("t", String.valueOf(System.currentTimeMillis()));      StringBuilder sb = new StringBuilder(128);      sb.append(m265b(map.get("appkey"))).append("&").append(m265b(map.get("domain"))).append("&").append(m265b(map.get("appName"))).append("&").append(m265b(map.get(SdkConstants.APP_VERSION))).append("&").append(m265b(map.get("bssid"))).append("&").append(m265b(map.get("channel"))).append("&").append(m265b(map.get(LeService.KEY_DEVICE_ID))).append("&").append(m265b(map.get("lat"))).append("&").append(m265b(map.get("lng"))).append("&").append(m265b(map.get("machine"))).append("&").append(m265b(map.get("netType"))).append("&").append(m265b(map.get("lng"))).append("&").append(m265b(map.get("platform"))).append("&").append(m265b(map.get("platformVersion"))).append("&").append(m265b(map.get("preIp"))).append("&").append(m265b(map.get("sid"))).append("&").append(m265b(map.get("t"))).append("&").append(m265b(map.get("v")));      map.put("sign", m263a(sb.toString()));      return map;  }
    

    分析完參數是啥,就要看看函數的具體操作了:

    先聲名一個stringbuffer變量,在將map中的數據轉化為string類型。

    然后就是對key字段進行的加密了:那個 KEYS.get("1")在這個可以找到了值:應該是密鑰。

    然后就要分析這個函數了:DESedeCoder.encode(json, KEYS.get("1"))

    第一個參數是map中的數據,第二個參數是密鑰。

    先將傳入的字符串轉化為byte類型,然后傳入encrypt函數:繼續跟進分析:

    這款app的加密字段就分析完了。總結一下:這個協議分析和找flag基本上是一樣的,就是定位到關鍵字段和函數,然后分析加密過程,這次就先分享三個java層的吧,下次再分析so的。


    stringsign
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    得益于Unicorn的強大的指令trace能力,可以很容易實現對cpu執行的每一條匯編指令的跟蹤,進而對ollvm保護的函數進行剪枝,去掉虛假塊,大大提高逆向分析效率。
    介紹實戰中由于各種情況,可能會對反序列化Payload的長度有所限制,因此研究反序列化Payload縮小技術是有意義且必要的本文以CommonsBeanutils1鏈為示例,
    本篇主要思路及方法來自于bit4woo和CC11001100兩位巨佬的成果。學習鏈接https://github.com/bit4woo/burp-api-drops
    JSP Webshell的檢測工具
    2021-12-13 12:04:53
    在11月初,我做了一些JSP Webshell的免殺研究,主要參考了三夢師傅開源的代碼。然后加入了一些代碼混淆手段,編寫了一個免殺馬生成器JSPHorse,沒想到在Github上已收獲500+的Star
    它們基于JSON格式,并包含一個令牌簽名以確保令牌的完整性。本文主要討論使用JSON Web令牌的安全隱患,以及攻擊者如何利用它們繞過訪問控制。有效載荷和簽名。
    frida復現某app算法
    2023-01-11 10:44:51
    獲取數據某app的登錄界面抓包,發現參數都加密了查驗一下殼,未加殼,可以進行反編譯使用jadx進行反編譯,嘗試搜索一下參數名”Encrypt”,發現兩個JsonRequest類的方法存在運用frida及其下面的代碼在java層進行hook,發現只調用了addRequestMap方法Java.perform(function (){. 跟進addRequestMap方法,了解其使用des加密,hook該方法可獲取明文及加密結果hook代碼Java.perform(function () {
    首先使用jadx對apk進行逆向。?搜索關鍵字 QDSign,可以直接找到對應的類,可以看到參數經過加密得到。??進一步跟蹤,發現了c類中有如下三個so方法,還有3個loadlibrary,分別進行了hook,發現c-lib動態注冊了sign,sos動態注冊了s,沒有發現crypto有動態注冊。
    買車報價APP sign分析
    2023-01-12 11:14:54
    數字企業殼懂得都懂不可能全部脫下來的只能用哪些類就脫哪些了,略微麻煩點。想著通過url的地址去找對應的發請求的位置是不是可以,于是通過搜索url路徑找到了一個類。這是具體的加密方法,沒啥難度了。
    在火線zone看到利用app歷史版本來進行app的逆向分析,可以降低一下分析難度,比較老版本的安全防護做的不是很到位。正好最近有一個app,嘗試利用一下老版本進行分析登錄簽名,沒想到效果非常好。
    FastJson結合二次反序列化繞過黑名單
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类