免責聲明
- 本博客所涉及的 爬蟲技術、逆向分析方法 僅用于 學習、研究和技術交流。
- 文中所有示例代碼、工具和方法,均不得用于以下行為:
- 未經授權的數據采集
- 侵犯他人知識產權
- 干擾或破壞正常業務系統
- 任何違反國家法律法規的行為
- 因讀者將本教程內容用于 非法用途 而產生的一切后果,由讀者本人承擔,本博客作者不承擔任何法律及連帶責任。
- 請在學習和實踐過程中 遵守相關法律法規、網站/平臺的服務協議及 robots 協議,合理合規地使用所學技術。
device-id
這其實就是設備id,可以直接隨機生成就行,這里還是帶著看一下
String a4 = com.bilibili.api.e.a();
if (!TextUtils.isEmpty(a4)) {aVar.h("Device-ID", a4);
}
直接看com.bilibili.api.e.a吧:
public class e {private static String a;public static String a() {return a;}public static void b(String str) {a = str;}
}
一樣的也是b函數調用,可以用hook也可以直接查找用例,這里就不帶大家找了:
public void run() {if (!j.a) {com.bilibili.api.d.b(j.c(this.a));}// 這是加密值com.bilibili.api.e.b(com.bilibili.lib.biliid.utils.f.a.c(this.a));
}
先找this.a:
a(Context context) {this.a = context;
}
context 上下文環境 —— 管理資源
Context
就是應用在 Android 系統中的運行環境,提供了操作資源、啟動組件、獲取系統服務的能力。
那么這里就不用管,直接看com.bilibili.lib.biliid.utils.f.a.c
public static String c(@Nullable Context context) {// 如果之前已經有緩存的 f13193c,就直接返回,避免重復計算if (!TextUtils.isEmpty(f13193c)) {return f13193c;}// 如果 context 是 null,啥也沒法干,直接返回空字符串if (context == null) {return "";}// 從 e.k().f(context) 獲取一個值(可能是存儲的 ID)String f = c2.f.b0.c.a.e.k().f(context);f13193c = f;// 如果獲取到的值非空,就直接返回if (!TextUtils.isEmpty(f)) {return f13193c;}// 如果還是沒拿到,就調用 g(context) 生成一個新的 IDf13193c = g(context);// 把新生成的 ID 存起來(持久化到本地,比如 SharedPreferences)c2.f.b0.c.a.e.k().x(f13193c, context);// 返回最終的 IDreturn f13193c;
}// 前面很多都是從之前所說的存儲中拿,重點看g函數
static String g(Context context) {// 1. 先調用 f(context) 獲取一個基礎值(可能是之前存儲的 ID 或默認值)String f = f(context);// 2. 如果 f 太短(小于 4 個字符),說明無效,需要生成一個新的if (f.length() < 4) {// a. 獲取 Android 系統 ID(每臺設備唯一)String androidId = Settings.Secure.getString(context.getContentResolver(), "android_id");// b. 獲取設備型號并進行處理(g.g(Build.MODEL))String modelHash = g.g(Build.MODEL);// c. 拼接 Android ID 和設備型號,形成一個新的字符串f = androidId + "@" + modelHash;}// 3. 對最終的字符串做進一步處理(b(f)return b(f);
}// 主要
String androidId = Settings.Secure.getString(context.getContentResolver(), "android_id"); // 之前獲取過
// b. 獲取設備型號并進行處理(g.g(Build.MODEL) 清除空格)
String modelHash = g.g(Build.MODEL); // Build.MODEL:設備型號// c. 拼接 Android ID 和設備型號,形成一個新的字符串
f = androidId + "@" + modelHash;// b(f) : 自定義算法
public static String b(String str) {// 1. 將字符串轉換成字節數組byte[] bytes = str.getBytes();// 2. 對第一個字節做異或運算,增加混淆性// bytes[0] = bytes[0] ^ (bytes.length & 255)bytes[0] = (byte) (bytes[0] ^ ((byte) (bytes.length & 255)));// 3. 從第二個字節開始,每個字節都和前一個字節異或for (int i = 1; i < bytes.length; i++) {bytes[i] = (byte) ((bytes[i - 1] ^ bytes[i]) & 255);}try {// 4. 將處理后的字節數組進行 Base64 編碼(flags = 11)return new String(Base64.encode(bytes, 11));} catch (Exception unused) {// 5. 編碼失敗時,返回原始字符串return str;}
}
梳理好之后,就可以直接模擬出python代碼(經過校驗,是一致的):
def obfuscate_string(s: str) -> str:"""型號加密,不補 Base64 '='"""# 1. 將字符串轉換成字節數組bytes_arr = bytearray(s.encode('utf-8'))if not bytes_arr:return s# 2. 對第一個字節做異或運算bytes_arr[0] = bytes_arr[0] ^ (len(bytes_arr) & 0xFF)# 3. 從第二個字節開始,每個字節都和前一個字節異或for i in range(1, len(bytes_arr)):bytes_arr[i] = (bytes_arr[i - 1] ^ bytes_arr[i]) & 0xFFtry:# 4. Base64 編碼,URL-safe,并去掉結尾 '='encoded = base64.urlsafe_b64encode(bytes_arr)return encoded.decode('utf-8').rstrip('=')except Exception:# 5. 編碼失敗時返回原始字符串return sdef get_device_id():"""獲取device_id"""androidId = '5d01aa95f9aca38c'models = ["Pixel 7", "Redmi K50", "ONEPLUS 9 Pro", "Vivo V23", "MI 12X","SM-G998B", "Xiaomi 11T", "OPPO Reno8", "Pixel 7 Pro", "Lenovo Legion Y90","Realme GT Neo3", "MI 11 Ultra", "Redmi Note 11", "ONEPLUS A6013", "VOG-L29","P50 Pro", "OPPO Find X5", "Vivo X80", "Pixel 6a", "Redmi K40","SM-G991U", "Xiaomi 12S", "Realme GT", "ONEPLUS 8T", "Vivo Y76","OPPO Reno7", "MI 10 Pro", "Pixel 5", "Lenovo K12 Pro", "Redmi Note 10","SM-G990F"]model = random.choice(models)modelHash = model.replace(' ', '').strip()return obfuscate_string(androidId + "@" + modelHash)