第36講:App 逆向的常見技巧
現在我們可以看到很多 App 在請求 API 的時候都有加密引數,前面我們也介紹了一種利用 mitmdump 來實時抓取資料的方法,但是這總歸還有些不方便的地方。
如果要想拿到 App 傳送的請求中包含哪些加密引數,就得剖析本源,深入到 App 內部去找到這些加密引數的構造邏輯,理清這些邏輯之後,我們就能自己用演算法實現出來了。這其中就需要一定的逆向操作,我們可能需要對 App 進行反編譯,然後通過分析原始碼的邏輯找到對應的加密位置。
所以,本課時我們來用一個示例介紹App 逆向相關操作。
1.案例介紹
這裡我們首先以一個 App 為例介紹這個 App 的抓包結果和加密情況,然後我們對這個 App 進行逆向分析,最後模擬實現其中的加密邏輯。
App 的下載地址為:https://app5.scrape.center/
我們先執行一下這個 App,上拉滑動,一些電影資料就會呈現出來了,介面如下:
這時候我們用 Charles 抓包來試一下,可以看到類似 API 的請求 URL 類似如下:https://app5.scrape.center/api/movie/?offset=0&limit=10&token=NDVjMTdjNjk5YWM2NWZkOGU5ZjFjNWEyN2MzNjhiYjIwMzRlZDU3ZiwxNTkxMjgyMzcz%0A,這裡我們可以發現有三個引數,分別為 offset、limit 還有 token,其中 token 是一個非常長的加密字串,我們也不好直觀地推測其生成邏輯。
本課時我們就來介紹一下逆向相關的操作,通過逆向操作獲得 apk 反編譯後的程式碼,然後追蹤這個 token 的生成邏輯是怎樣的,最後我們再用程式碼把這個邏輯實現出來。
App 逆向其實多數情況下就是反編譯得到 App 的原始碼,然後從原始碼裡面找尋特定的邏輯,本課時就來演示一下 App 的反編譯和入口點查詢操作。
2.環境準備
在這裡我們使用的逆向工具叫作 JEB。
JEB 是一款專業的安卓應用程式的反編譯工具,適用於逆向和審計工程,功能非常強大,可以幫助逆向人員節省很多逆向分析時間。利用這個工具我們能方便地獲取到 apk 的原始碼資訊,逆向一個 apk 不在話下。
JEB 支援 Windows、Linux、Mac 三大平臺,其官網地址為 https://www.pnfsoftware.com/,你可以在官網瞭解下其基本介紹,然後通過搜尋找到一些完整版安裝包下載。下載之後我們會看到一個 zip 壓縮包,解壓壓縮包之後會得到如下的內容:
在這裡我們直接執行不同平臺下的指令碼檔案即可啟動 JEB。比如我使用的是 Mac,那我就可以在此目錄下執行如下命令:
sh jeb_macos.sh
這樣我們就可以開啟 JEB 了。開啟 JEB 之後,我們把下載的 apk 檔案直接拖拽到 JEB 裡面,經過一段時間處理後,會發現 JEB 就已經將程式碼反編譯完成了,如圖所示:
這時候我們可以看到在左側 Bytecode 部分就是反編譯後的程式碼,在右側顯示的則是 Smali 程式碼,通過 Smali 程式碼我們大體能夠看出一些執行邏輯和資料操作等過程。
現在我們得到了這些反編譯的內容,該從哪個地方入手去找入口呢?
由於這裡我們需要找的是請求加密引數的位置,那麼最簡單的當然是通過 API 的一些標誌字串來查詢入口了。API 的 URL 裡面包含了關鍵字 /api/movie,那麼我們自然就可以通過這個來查詢了。
我們可以在 JEB 裡面開啟查詢視窗,查詢 /api/movie,如圖所示:
這時候我們發現就找到了一個對應的宣告如下:
.field public static final indexPath:String = "/api/movie"
這裡其實就是宣告瞭一個靜態不可變的字串,叫作 indexPath
。但這裡是 Smali 程式碼呀?我們怎麼去找到它的原始碼位置呢?
這時候我們可以右鍵該字串,選擇解析選項,這時 JEB 就可以成功幫我們定位到 Java 程式碼的宣告處了。
這時候我們便可以看到其跳轉到了如下頁面:
這裡我們就能看到 indexPath 的原始宣告,同時還看到了一個 index 方法的宣告,包含三個引數 offset、limit 還有 token,由此可以發現,這引數和宣告其實恰好和 API 的請求 URL 格式是相同的。
但這裡還觀察到這個是一個介面宣告,一定有某個類實現了這個介面。
我們這時候可以順著 index 方法來查詢是什麼類實現了這個 index 方法,在 index 方法上面右鍵選擇“交叉引用”,如圖所示:
這時候我們可以發現這裡彈出了一個視窗,找到了對應的位置,如圖所示:
我們選中它,點選確定,這時候就跳轉到了對應的 index 實現的位置了,如圖所示:
這裡 index 方法的實現如下:
public Observable index(int arg6, int arg7) {
ArrayList v2 = new ArrayList();
((List)v2).add("/api/movie");
return this.apiService.index((arg6 - 1) * arg7, arg7, Encrypt.encrypt(((List)v2)));
}
就能很輕易地發現一個類似 encrypt 的方法,代表加密的意思,其引數就是 v2,而 v2 就是一個 ArrayList,包含一個元素,就是 /api/movie 這個字串。
這時候我們再通過交叉引用找到 Encrypt 的定義,跳轉到如圖所示的位置:
這裡可以發現 encrypt 的方法實現如下:
public static String encrypt(List arg7) {
String v1 = String.valueOf(new Timestamp(System.currentTimeMillis()).getTime() / 1000);
arg7.add(v1);
String v2 = Encrypt.shaEncrypt(TextUtils.join(",", ((Iterable)arg7)));
ArrayList v3 = new ArrayList();
((List)v3).add(v2);
((List)v3).add(v1);
return Base64.encodeToString(TextUtils.join(",", ((Iterable)v3)).getBytes(), 0);
}
這裡我們分析一下,傳入的引數就是 arg7,剛才經過分析可知 arg7 其實就是一個長度為 1 的列表,其內容就是一個字串,即 ["/api/movie"]。
緊接著看邏輯,這裡又定義了一個 v1 的字串,其實就是獲取了時間戳資訊,然後把結果加入 arg7,現在 arg7 就有兩個內容了,一個是 /api/movie,另一個是時間戳。
接著又宣告瞭 v2,這裡經過分析可知是將 arg7 使用逗號拼接起來,然後呼叫了 shaEncrypt 操作,而 shaEncrypt 經過觀察其實就是 SHA1 演算法。
緊接著又宣告瞭一個 ArrayList,把 v2 和 v1 的結果加進去。最後把 v3 的內容使用逗號拼接起來,然後 Base64 編碼即可。
好,現在整體的 token 加密的邏輯就理清楚了。
3.模擬
瞭解了基本的演算法流程之後,我們可以用 Python 把這個流程實現出來,程式碼實現如下:
import hashlib
import time
import base64
from typing import List, Any
import requests
INDEX_URL = 'https://app5.scrape.cuiqingcai.com/api/movie?limit={limit}&offset={offset}&token={token}'
LIMIT = 10
OFFSET = 0
def get_token(args: List[Any]):
timestamp = str(int(time.time()))
args.append(timestamp)
sign = hashlib.sha1(','.join(args).encode('utf-8')).hexdigest()
return base64.b64encode(','.join([sign, timestamp]).encode('utf-8')).decode('utf-8')
args = ['/api/movie']
token = get_token(args=args)
index_url = INDEX_URL.format(limit=LIMIT, offset=OFFSET, token=token)
response = requests.get(index_url)
print('response', response.json())
這裡最關鍵的就是 token 的生成過程,我們定義了一個 get_token 方法來實現,整體上思路就是上面梳理的內容:
- 列表中加入當前時間戳;
- 將列表內容用逗號拼接;
- 將拼接的結果進行 SHA1 編碼;
- 將編碼的結果和時間戳再次拼接;
- 將拼接後的結果進行 Base64 編碼。
最後執行結果如下:
response {'count': 100, 'results': [{'id': 1, 'name': '霸王別姬', 'alias': 'Farewell My Concubine', 'cover': 'https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c', 'categories': ['劇情', '愛情'], 'published_at': '1993-07-26', 'minute': 171, 'score': 9.5, 'regions': ['中國大陸', '中國香港']}, {'id': 2, 'name': '這個殺手不太冷', 'alias': 'Léon', 'cover': 'https://p1.meituan.net/movie/6bea9af4524dfbd0b668eaa7e187c3df767253.jpg@464w_644h_1e_1c', 'categories': ['劇情', '動作', '犯罪'], 'published_at': '1994-09-14', 'minute': 110, 'score': 9.5, 'regions': ['法國']}, {'id': 3, 'name': '肖申克的救贖', 'alias': 'The Shawshank Redemption', 'cover': 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@464w_644h_1e_1c', 'categories': ['劇情', '犯罪'], 'published_at': '1994-09-10', 'minute': 142, 'score': 9.5, 'regions': ['美國']}, {'id': 4, 'name': '泰坦尼克號', 'alias': 'Titanic', 'cover': 'https://p1.meituan.net/movie/b607fba7513e7f15eab170aac1e1400d878112.jpg@464w_644h_1e_1c', 'categories': ['劇情', '愛情', '災難'], 'published_at': '1998-04-03', 'minute': 194, 'score': 9.5, 'regions': ['美國']}, {'id': 5, 'name': '羅馬假日', 'alias': 'Roman Holiday', 'cover': 'https://p0.meituan.net/movie/289f98ceaa8a0ae737d3dc01cd05ab052213631.jpg@464w_644h_1e_1c', 'categories': ['劇情', '喜劇', '愛情'], 'published_at': '1953-08-20', 'minute': 118, 'score': 9.5, 'regions': ['美國']}, {'id': 6, 'name': '唐伯虎點秋香', 'alias': 'Flirting Scholar', 'cover': 'https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@464w_644h_1e_1c', 'categories': ['喜劇', '愛情', '古裝'], 'published_at': '1993-07-01', 'minute': 102, 'score': 9.5, 'regions': ['中國香港']}, {'id': 7, 'name': '亂世佳人', 'alias': 'Gone with the Wind', 'cover': 'https://p0.meituan.net/movie/223c3e186db3ab4ea3bb14508c709400427933.jpg@464w_644h_1e_1c', 'categories': ['劇情', '愛情', '歷史', '戰爭'], 'published_at': '1939-12-15', 'minute': 238, 'score': 9.5, 'regions': ['美國']}, {'id': 8, 'name': '喜劇之王', 'alias': 'The King of Comedy', 'cover': 'https://p0.meituan.net/movie/1f0d671f6a37f9d7b015e4682b8b113e174332.jpg@464w_644h_1e_1c', 'categories': ['劇情', '喜劇', '愛情'], 'published_at': '1999-02-13', 'minute': 85, 'score': 9.5, 'regions': ['中國香港']}, {'id': 9, 'name': '楚門的世界', 'alias': 'The Truman Show', 'cover': 'https://p0.meituan.net/movie/8959888ee0c399b0fe53a714bc8a5a17460048.jpg@464w_644h_1e_1c', 'categories': ['劇情', '科幻'], 'published_at': None, 'minute': 103, 'score': 9.0, 'regions': ['美國']}, {'id': 10, 'name': '獅子王', 'alias': 'The Lion King', 'cover': 'https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c', 'categories': ['動畫', '歌舞', '冒險'], 'published_at': '1995-07-15', 'minute': 89, 'score': 9.0, 'regions': ['美國']}]}
這裡就以第一頁的資料為示例來演示了,其他的頁面我們通過修改 page 的值就可以拿到了。
4.總結
以上我們便通過一個樣例講解了一個比較基本的 App 的逆向過程,包括 JEB 的使用、追蹤程式碼的操作等等,最後通過分析程式碼理清了基本邏輯,最後模擬實現了 API 的引數構造和請求傳送,得到最終的資料。
相關文章
- (新)app逆向四(常見加密演算法)APP加密演算法
- 第07講:Flink 常見核心概念分析
- 常見邏輯語句逆向分析
- CSS常見佈局技巧CSS
- web效能常見優化技巧Web優化
- 逆向淺析常見病毒的注入方式系列之一-----WriteProcessMemorySSM
- 反爬蟲的四種常見方式-JS逆向方法論爬蟲JS
- PostgreSQL技術大講堂 - 第31講:SQL調優技巧SQL
- Go常見錯誤第15篇:interface使用的常見錯誤和最佳實踐Go
- uni-app常見問題APP
- Linux 常見命令小技巧集錦Linux
- postgresql從入門到精通教程 - 第36講:postgresql邏輯備份SQL
- 計算機基礎-常見的DOS命令講解計算機
- 淺析雲伺服器常見的維護技巧伺服器
- Gradle:你必須掌握的開發常見技巧Gradle
- 牛客堂常見面試題精講(一)1面試題
- GarageSale Mac版常見問題及提示技巧Mac
- 專案答辯常見問題和技巧
- 精華閱讀第 13 期 |常見的八種導致 APP 記憶體洩漏的問題APP記憶體
- 《HelloGitHub》第 36 期Github
- 每週分享第 36 期
- React + Typescript領域初學者的常見問題和技巧ReactTypeScript
- Mysql系列第四講 DDL常見操作彙總MySql
- 20個資料庫常見面試題講解資料庫面試題
- 常見演算法技巧之——雙指標思想演算法指標
- Github資料洩露事件處置常見技巧Github事件
- 日常爬蟲工作中一些常見的和不常見的偽裝技巧爬蟲
- 逆向中常見的加密演算法加密演算法
- 逆向破解MacOS AppMacAPP
- 【效能測試】常見的效能問題分析思路(二)案例&技巧
- uni-app開發 常見異常和解決辦法APP
- HTML5前端常見攻擊方式案例講解!HTML前端
- APP 中的 JS 加密逆向解析APPJS加密
- Vue開發必須知道的36個技巧Vue
- 提高PHP程式碼質量的36個技巧PHP
- Android Studio 中 Preview 常見問題和技巧AndroidView
- 技術管理實戰36講教程
- 常見Hybrid App框架優劣對比APP框架