需求分析
- 拿到每套房子房價
- 拿到所有戶型介紹
抓關鍵包
- 房價請求包
- 工具 Charles
抓包如下
看這樣子內容是加密後在app內不進行解密完成的, 那麼我們要獲取到通過api請求到的真實數據就需要拆解app!獲取其加解密方法才行。
殼檢測
- 可以用工具查看是否有殼
- 或者查看特徵
- 檢測混淆
百度加固特徵明顯所以判斷爲百度加固
[外鏈圖片轉存失敗(img-jYM2EbCz-1563893153505)(https://www.zhangkunzhi.com/images/20190720/查看到百度加固.png)]
脫殼
- 此處省略 500 字(發這些會被舉報)
- 利用反編譯工具
jadx
對 apk 進行反編譯
跟蹤代碼
[外鏈圖片轉存失敗(img-K1a0rCFr-1563893153506)(https://www.zhangkunzhi.com/images/20190720/房價查詢目錄.png)]
-
查看到 cmdDHP 就是我們請求的 url
-
查看調用 cmdDHP 到位置
public static void getHousePrice(String houseId, String bId, String unitId, RequestResultI requestResultI) { RequestParams params = new RequestParams(); StringBuffer sb = new StringBuffer(); String sp = "$$@$$"; for (int i = 0; i < 30; i++) { if (i == 3) { sb.append(houseId); } else if (i == 4) { sb.append(bId); } else if (i == 9) { sb.append(unitId); } else { sb.append(new Random().nextInt(100) + ""); } sb.append(sp); } String str = ""; params.put("key", Utils.encodeKey(sb.toString())); SendRequest.getInstance().get(112, params, cmdDHP, requestResultI); }
檢查參數
方法參數 房屋id bID? unitId 開放商id? 然後對其拼接發送的 get 請求
跟蹤解密方法
搜索 response
查找找到基類
ResponseBaseModel 或 ResponseBaseDomain
private static final String key_Code = "Code";
private static final String key_Data = "Data";
private static final String key_Remark = "Remark";
private static final String key_Total = "Total";
// 找到json關鍵字樣
public ResponseBaseModel(JSONObject response) throws Exception {
if (response.has(key_Code)) {
this.Code = response.getString(key_Code);
}
if (response.has(key_Remark)) {
this.Remark = response.getString(key_Remark);
}
if (response.has(key_Data)) {
this.Data = response.getString(key_Data);
}
if (response.has(key_Total)) {
this.Total = response.getInt(key_Total);
}
}
跟蹤到解密模塊
// 解密工作模塊
public static String decodeB(String encryptText) {
try {
checkKeyAndIv();
Key deskey = SecretKeyFactory.getInstance("desede").generateSecret(new DESedeKeySpec(secretKey.getBytes())); // key 處理
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
cipher.init(2, deskey, new IvParameterSpec(iv.getBytes()));
return new String(cipher.doFinal(Base64.decodeBase64(encryptText)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static String decodeA(String en) {
String content = "";
try {
checkKeyAndIv();
String den = decodeB(en);
String content2 = den.substring(0, den.length() - 6);
String[] hIndex = decodeBase64(den.substring(den.length() - 6, den.length()).replace("==", ""), "UTF-8").split("_");
String content3 = content2.substring(Integer.valueOf(hIndex[0]).intValue(), content2.length());
return decodeBase64(new StringBuffer(content3.substring(0, content3.length() - Integer.valueOf(hIndex[1]).intValue())).reverse().toString(), "UTF-8").replace("##", "").replace("{@mk7}", "");
} catch (Exception e) {
e.printStackTrace();
return content;
}
}
檢查 key 與 Iv
發現每次解密都會調用 checkKeyAndIv()
// 每次加解密都會調用check 函數來看是否有key
private static void checkKeyAndIv() {
if (stringIsNull(secretKey) || stringIsNull(iv)) {
secretKey = FuniSecret.getSecretKey();
iv = FuniSecret.getSecretIv();
}
}
跟蹤獲取 Key 與 Iv 處
// 再次跟蹤到 getSecretIv
package com.funi.cloudcode.util;
public class FuniSecret {
public static native String getSecretIv();
public static native String getSecretKey();
static {
System.loadLibrary("FuniSecret");
}
}
SO 層跟蹤
在上面看到了
native
, 與System.loadLibrary("FuniSecret")
發現 他們是將 key 與 Iv 在 SO 層(C/C++)處理好, 交給 java 處理的
靜態調試 SO 層
他們用的是 DES 加密
最終找到 Key 與 Iv
再利用 python 實現 DES 解密過程即可
此處省略
感謝
以上兩位的支持與幫忙