破解某小說App(一)
背景
想實現一個支援多渠道的小說App,因此對該小說App進行分析獲取它的介面呼叫方式
該App詳情頁截圖:

詳情頁截圖
抓包
Charles抓取飛盧詳情頁面的介面

charles抓包
發現該小說App的域名是一個固定IP(試過幾次退出登陸發現每次IP不一樣,猜測是客戶端寫死的或者服務端下發,應該是防止域名被劫持吧)
上傳的引數是一堆亂碼,token也是亂碼,

token
一般末尾是‘=’,應該是base64編碼, 解密失敗

base64解密
返回內容居然是XML,author_name居然也是亂碼,猜測是base64編碼,使用站長工具Base64解密

base64解密
感覺該App對資料加密做的很嚴謹,怪不得市場沒有破解版
現在模擬它的請求只要破解它的token和引數就可以了(至於收費內容暫時不考慮)
反編譯
使用Android Studio開啟該App,看到tencent_stub,應該是做個樂固加固了。

App
開始反編譯
Mac上使用 Android CrackTool 進行反編譯處理

Android CrackTool
反編譯結果的確是樂固加固

結果
加固包反編譯
那開始對這個加固包進行反編譯
參照這個文件 Android APK脫殼--騰訊樂固、360加固一鍵脫殼
使用脫殼工具FDex2
VirtualXposed:無需root手機即可使用xp框架。
dex進行dex2jar
這個步驟可以用Android Crack Tool上執行,執行成功之後,使用jdgui開啟
搜尋關鍵字 Xml4Android_relevantPage
,獲取詳情頁

詳情頁
重新整理詳情頁發現Logcat中有日誌

image
contetn 原文
進行搜尋,發現token和引數加密都是native實現的RSA加密,
public <T extends BaseModel> T a(Object paramObject, String paramString1, UserInfoDto paramUserInfoDto, String paramString2, b<T> paramb) { try { paramString1 = paramString1.split("\\?"); String str2 = paramString1[1]; String str1 = a(paramString1[0]); paramString1 = new java/lang/StringBuilder; paramString1.<init>(); paramString1.append(str1); paramString1.append("?appversion="); paramString1.append(AppUtils.getAppversion()); paramString1.append("&Type=Android"); paramString1 = paramString1.toString(); str1 = paramString1; if (!TextUtils.isEmpty(paramString1)) { str1 = paramString1; if (paramString1.contains("http://client4ip")) { str1 = paramString1.replace("http://client4ip", "https://client4ip"); } } if (!TextUtils.isEmpty(paramString2)) { paramString1 = paramb.d(paramString2); } else { if (!TextUtils.isEmpty(paramUserInfoDto.getUsername())) { paramUserInfoDto.setUsername(paramUserInfoDto.getUsername().toLowerCase()); } if (!TextUtils.isEmpty(paramUserInfoDto.getPassword())) { paramUserInfoDto.setPassword(paramUserInfoDto.getPassword().toLowerCase()); } if (!TextUtils.isEmpty(paramUserInfoDto.getVerifyCode())) { paramUserInfoDto.setVerifyCode(paramUserInfoDto.getVerifyCode().toLowerCase()); } if (!TextUtils.isEmpty(paramUserInfoDto.getUsername())) { paramString2 = paramb.a(paramUserInfoDto); paramString1 = paramb.d(paramString2); } else { paramString1 = new java/lang/StringBuilder; paramString1.<init>(); paramString1.append(System.currentTimeMillis()); paramString1.append(""); paramString2 = MD5.MD5(paramString1.toString()); paramString1 = paramb.d(paramString2); } } paramUserInfoDto = m.a().b("SP_UID"); if (!TextUtils.isEmpty(m.a().b("username"))) { paramUserInfoDto = m.a().b("username"); } Object localObject = paramUserInfoDto; if (TextUtils.isEmpty(paramUserInfoDto)) { localObject = j.a(); m.a().a("SP_UID", (String)localObject); } paramUserInfoDto = new java/lang/StringBuilder; paramUserInfoDto.<init>(); paramUserInfoDto.append(str2); paramUserInfoDto.append("&uuid="); paramUserInfoDto.append((String)localObject); localObject = paramUserInfoDto.toString(); paramUserInfoDto = (UserInfoDto)localObject; if (!TextUtils.isEmpty((CharSequence)localObject)) { paramUserInfoDto = (UserInfoDto)localObject; if (!((String)localObject).contains("time=")) { paramUserInfoDto = new java/lang/StringBuilder; paramUserInfoDto.<init>(); paramUserInfoDto.append((String)localObject); paramUserInfoDto.append("&time="); paramUserInfoDto.append(URLEncoder.encode(TimeUtils.getNowString(), "gb2312")); paramUserInfoDto = paramUserInfoDto.toString(); } } localObject = paramUserInfoDto; if (!TextUtils.isEmpty(paramUserInfoDto)) { localObject = paramUserInfoDto; if (!paramUserInfoDto.contains("appversion=")) { localObject = new java/lang/StringBuilder; ((StringBuilder)localObject).<init>(); ((StringBuilder)localObject).append(paramUserInfoDto); ((StringBuilder)localObject).append("&appversion="); ((StringBuilder)localObject).append(AppUtils.getAppversion()); ((StringBuilder)localObject).append("&Type=Android"); localObject = ((StringBuilder)localObject).toString(); } } paramUserInfoDto = new java/lang/StringBuilder; paramUserInfoDto.<init>(); paramUserInfoDto.append("content 原文 "); paramUserInfoDto.append((String)localObject); g.a(new Object[] { paramUserInfoDto.toString() }); paramUserInfoDto = paramb.a((String)localObject, paramString2); paramString2 = new com/lzy/okgo/model/HttpParams; paramString2.<init>(); paramString2.put("", paramUserInfoDto, new boolean[0]); paramObject = ((PostRequest)((PostRequest)((PostRequest)((PostRequest)((PostRequest)com.lzy.okgo.a.b(str1).tag(paramObject)).headers("token", paramString1)).headers("mobileType", "Android")).headers("appversion", AppUtils.getAppversion())).params(paramString2)).execute(); if (paramObject == null) { return null; } if (((Response)paramObject).isSuccessful()) { paramUserInfoDto = ((Response)paramObject).body().byteStream(); paramObject = new java/io/BufferedReader; paramString1 = new java/io/InputStreamReader; paramString1.<init>(paramUserInfoDto, "gb2312"); ((BufferedReader)paramObject).<init>(paramString1); paramUserInfoDto = new java/lang/StringBuffer; paramUserInfoDto.<init>(); for (;;) { paramString1 = ((BufferedReader)paramObject).readLine(); if (paramString1 == null) { break; } paramUserInfoDto.append(paramString1); } paramObject = (BaseModel)paramb.b(paramUserInfoDto.toString()); return (T)paramObject; } }
先記錄這些,後續有進展在繼續