APP逆向——某站device-id参数(2)
免责声明
- 本博客所涉及的 爬虫技术、逆向分析方法 仅用于 学习、研究和技术交流。
- 文中所有示例代码、工具和方法,均不得用于以下行为:
- 未经授权的数据采集
- 侵犯他人知识产权
- 干扰或破坏正常业务系统
- 任何违反国家法律法规的行为
- 因读者将本教程内容用于 非法用途 而产生的一切后果,由读者本人承担,本博客作者不承担任何法律及连带责任。
- 请在学习和实践过程中 遵守相关法律法规、网站/平台的服务协议及 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)