b站so层sign算法

news2024/12/23 18:23:15

前言 

大家好呀,欢迎来到我的博客!!! 本期我将带来b站so层sign算法实现

设备: pixel4 android10

下载地址: aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzI4MTI5MS9oaXN0b3J5X3Y2MTgwNTAw

版本: 6.18.0

工具: charles(抓包) socksdroid(流量转发) jadx(反编译dex) ida(反编译so)

我看了下b站这算法并没有更新,但是so层做了混淆,新版的不太好分析,所以干脆分析老版本的,最新版的so层混淆如下图

目录

前言 

声明 

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!

抓包

定位sign位置

用32位的ida把它转为汇编

算法

总结


声明 


本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!

抓包

1 抓包没什么说的,往上滑或往下滑都能刷新抓到包

2 翻页后文本对比下发现只有两个时间戳是不同的然后加密得到不同的sign,其他参数可以暂时固定 

定位sign位置

3 首先把apk拖到jadx反编译一下,这个时候大部分都会去搜字符串,但是由于很多地方都使用了sign关键字,直接搜的话太麻烦了,所以也可以搜查询参数中比较特殊的字符串,比如ad_extra,banner_hash,statistics,他们最终肯定会参与params的组装,然后可以顺着找到sign  我这里采用的是hook hashmap的put方法,这样比搜索快一些,代码如下

 Java.perform(function (){
        function showStacks() {
        console.log(
            Java.use("android.util.Log")
                .getStackTraceString(
                    Java.use("java.lang.Throwable").$new()
                )
        );
    }

var hashMap = Java.use("java.util.HashMap");
    hashMap.put.implementation = function (a, b) {
        if(a.equals("ad_extra")){
            showStacks();
            console.log("hashMap.put: ", a, b);
        }
        return this.put(a, b);
    }
    }
    )

4 只有一处,顺着堆栈找可以找到com.bilibili.okretro.f.a.d 

5 如下图

6 点击h这个方法里

7 再点进g这个方法 ,g加载s这个native方法

 

8 s来源于libbili.so这个文件

9 找到对应的so文件,只有32位的arm架构,下面那个是模拟器的,所以选择第一个里面的libbili.so

用32位的ida把它转为汇编

10 在导出表里面没有发现java的字眼,所以是动态注册,同时可以看到标志JNI_OnLoad,代表动态注册

11 点进去JNI_OnLoad 发现里面嵌套了很多函数,找到RegisterNatives动态注册很麻烦,这里可以用hook脚本,输出当前类下的所有native方法对应c中方法的偏移量,然后再找到c中的函数代码

// 获取 RegisterNatives 函数的内存地址,并赋值给addrRegisterNatives。
var addrRegisterNatives = null;

// 列举 libart.so 中的所有导出函数(成员列表)
var symbols = Module.enumerateSymbolsSync("libart.so");


for (var i = 0; i < symbols.length; i++) {
    var symbol = symbols[i];
    console.log(symbol.name)
    //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
    if (symbol.name.indexOf("art") >= 0 &&
        symbol.name.indexOf("JNI") >= 0 &&
        symbol.name.indexOf("RegisterNatives") >= 0 &&
        symbol.name.indexOf("CheckJNI") < 0) {

        addrRegisterNatives = symbol.address;
        console.log("RegisterNatives is at ", symbol.address, symbol.name);
        //break
    }
}


if (addrRegisterNatives) {
    // RegisterNatives(env, 类型, Java和C的对应关系,个数)
    Interceptor.attach(addrRegisterNatives, {
        onEnter: function (args) {
            var env = args[0];        // jni对象
            var java_class = args[1]; // 类
            var class_name = Java.vm.tryGetEnv().getClassName(java_class);
            var taget_class = "com.bilibili.nativelibrary.LibBili";
            if (class_name === taget_class) {
                //只找我们自己想要类中的动态注册关系
                console.log("\n[RegisterNatives] method_count:", args[3]);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    // Java中函数名字的
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    // 参数和返回值类型
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    // C中的函数内存地址
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    // 地址、偏移量、基地址
                    var offset = ptr(fnPtr_ptr).sub(find_module.base);
                    //console.log("name:", name, "name:", sig, "module_name:", find_module.name, "offset:", offset);
                    console.log("name:", name, "name:", sig, "offset:", offset);

                }

            }
        }
    });
}

 12 这里因为app一开始加载的时候已经加载了所有的so文件,所以需要以重新启动app的方式来hook,而不能通过附加形式 hook,如下图

frida -U -f tv.danmaku.bili -l hook_code.js

13 这里在ida里按G就能弹出运行框,输入偏移量就可以调到指定函数位置

14 转化JNIEnv对象后如下图

15 往下滑看返回值 这里其实是进行了一堆md5加密,参数1是url路径后面的参数(除了sign),参数2就是加密的sign

16 通过java层hook s方法即可看出 

 Java.perform(function (){
     let LibBili = Java.use("com.bilibili.nativelibrary.LibBili");
LibBili["s"].implementation = function (sortedMap) {
    console.log(`LibBili.s is called: sortedMap=${sortedMap}`);
    let result = this["s"](sortedMap);
    console.log(`LibBili.s result=${result}`);
    return result;
};
    }
    )

算法

17 最终sign python算法,z最终生成的与params中的一样,验证算法是正确的

import hashlib
from urllib.parse import quote, unquote
params = {
    "ad_extra": "E86F4CFF1F8FA890A75155EEAA51E6AE4FA9DBE62FCE708186D0CE5EF37B86948620D8BA1D991685B1288E2EDE09C6D52F8C2D33D59872EAE1EB776D11F71523CE1AF2112D8A950B98F6A1A48F848BC6871A849C3ED14308F46431A85625726A929A8906FA0C16FEE2CEB33209AE6F1E0C6856961045F53A0FE3470E4E223F48DAE7923040EAC4541BE6F728DEA350329AC40887CB773083BB4D6D91DCCCDF8D16C5672A5E344293F5EFD2F3654B88602781A8869076E96FF8359FC76D3CD5851A733D0CF38E11DC869D660D1624928815C2A13497B215CCEA52053B302039B9B93DFABDD6A71A16AC8898285A37C7DEB5AB5ADD788C2456B5D9B2F8FDB1ACD334E8127D56B144B523155DE8AB49A1D1173CB590E379CCF33EFAE8C388100D5CEA7AD220E2AAA2256FF16D4BE28C8AA3D7BAE19B1FE6AA860276BB86B27ACCA34B8E081D67E8C699CF4ED4D7A45E8556B05584B35B1E11E80B9B41DC51C47B260C602E07B1936C73DDB8D7FFBBD148894822C5F7C9A688C5A25DB2CA92D77CA7C35E3AFD807D0AE95967943A42B30D0F0EF8EAD9D2E74A20BB4EA72014B5A3BFD53B2ECFB15B47455D97A4FBFDDFB4A3E30853E0B9CBF16AC70F25CB6B939540328256BF42AB6DF9D3DD4649E3F0B340376B162F859D5EE92D99A778FB313E28BCAD195FB59A30EC374436735A9732BF013A78FE3F606425B48A74137C267DAB91A00962C5FFECB7E798AC130FCF7F9428A05082C6D717D13F129F809818E05DB11EA3DE2EA80728D30DB7EECAE1231085C4E3B47B98506F261D89D15997AC09FB46DBA3444A438F43A59D232385F3C5548DFF3F51733A80A80880E7945035A18DCDDCDEB85A2DCCF755CD1AEBCA759CB2BE4AF6D5AB3A9FA8F7429DD37B740E33D80E1F11B8BD4DD312DEAECEEBAB7DC6FF57EFC5A81D3D7D02E798AA5CDCD387EAD885EE8D89368FA301463658FA52",
    "appkey": "1d8b6e7d45233436",
    "autoplay_card": "11",
    "autoplay_timestamp": "0",
    "build": "7500300",
    "c_locale": "zh-Hans_CN",
    "channel": "alifenfa",
    "column": "2",
    "column_timestamp": "0",
    "device_name": "Pixel 4",
    "device_type": "0",
    "disable_rcmd": "0",
    "flush": "8",
    "fnval": "464",
    "fnver": "0",
    "force_host": "0",
    "fourk": "1",
    "guidance": "0",
    "https_url_req": "0",
    "idx": "1698066410",
    "inline_danmu": "2",
    "inline_sound": "1",
    "interest_id": "0",
    "login_event": "0",
    "mobi_app": "android",
    "network": "wifi",
    "open_event": "",
    "platform": "android",
    "player_net": "1",
    "pull": "false",
    "qn": "32",
    "recsys_mode": "0",
    "s_locale": "zh-Hans_CN",
    "splash_id": "",
    "statistics": "{\"appId\":1,\"platform\":3,\"version\":\"7.50.0\",\"abtest\":\"\"}",
    "ts": "1698066416",
    "video_mode": "1",
    "voice_balance": "0",
    # "sign": "002c2395f37e8800095c41e08b652517"
}
sorted_params = sorted(params.items(), key=lambda x: x[0])
sorted_str = '&'.join([f'{k}={v}' for k, v in sorted_params])

original_string = sorted_str
# 分割字符串,然后仅对值进行编码
parts = original_string.split("&")
encoded_parts = []
for part in parts:
    key, value = part.split("=")
    encoded_value = quote(value)
    encoded_parts.append(f"{key}={encoded_value}")
# 重新组合编码后的部分
encoded_string = "&".join(encoded_parts)

# 盐值 560c52ccd288fed045859ed18bffd973
str = encoded_string+'560c52ccd288fed045859ed18bffd973'
sign = hashlib.md5(str.encode('utf-8')).hexdigest()
print(sign)

总结

1出于安全考虑,本章未提供完整流程,调试环节省略较多,只提供大致思路,具体细节要你自己还原,相信你也能调试出来.

2本人写作水平有限,如有讲解不到位或者讲解错误的地方,还请各位大佬在评论区多多指教,共同进步,也可加本人微信lyaoyao__i(两个_)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1150962.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中微CMS8S3680/69xx系列单片机

最近在使用中微CMS8S3680/69xx系列8位单片机来进行电源控制软件开发。 总体觉得这款单片机简单易用&#xff0c;特别是它的数字功能可以映射到任意脚&#xff0c;甚至包括程序的烧录脚&#xff0c;对于PCB布局特别灵活。另外它的存储器资源也是很丰富的&#xff0c;16K字节ROM…

实战 | SQL注入

一、资产搜集 我们都知道sql注入的传参有些是明文的&#xff0c;有些是经过编码或者加密的&#xff0c;所以我们搜索的时候不要仅限于inurl:.php?id1&#xff0c;可以额外的尝试搜搜1的base64编码值MQ&#xff0c;即可以搜索inurl:.php?idMQ&#xff0c;或者搜索1的md5加密值…

C代码的单元测试

C代码中集成gtest单元测试_gtest测试c语言_山河故人~的博客-CSDN博客 Linux安装gtest_gtest安装_山河故人~的博客-CSDN博客 一&#xff1a;安装gtest: 1. 安装gtest 采用源码安装的方式&#xff0c;需确保cmake已经安装。 git clone https://github.com/google/googletest …

网络安全(黑客)—小白自学路线

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

商业模式画布的9大模块全解读,产品经理不可不知!

“商场如战场”&#xff0c;在当今瞬息万变的商业环境中&#xff0c;创造出独特且创新的商业模式是每个企业家、策略家和决策者的首要任务。为了在激烈的市场竞争中取得优势&#xff0c;我们需要一个强大且直观的工具来帮助我们规划和塑造公司的商业模式&#xff0c;这个经常被…

从零开始实现神经网络(一)_NN神经网络

参考文章&#xff1a;神经网络介绍 一、神经元 这一神经网络的基本单元&#xff0c;神经元接受输入&#xff0c;对它们进行一些数学运算&#xff0c;并产生一个输出。 这里有三步。 首先&#xff0c;将每个输入&#xff08;X1&#xff09;乘以一个权重&#xff1a; 接下来&…

如何备份和恢复微信聊天记录?微信聊天记录1分钟轻松备份和恢复。

微信是一个非常流行的应用程序&#xff0c;不仅在中国&#xff0c;而且在全世界。这个应用程序允许来自其他国家的用户与他们的中国朋友进行群聊、语音消息、视频通话、发送贴纸或 GIF 以及照片。它可以为学生和商人/女性发送重要文件&#xff0c;以及位置共享以防游客在访问中…

内网穿透实现在外远程访问NAS威联通(QNAP)

文章目录 前言1. 威联通安装cpolar内网穿透2. 内网穿透2.1 创建隧道2.2 测试公网远程访问 3. 配置固定二级子域名3.1 保留二级子域名3.2 配置二级子域名 4. 使用固定二级子域名远程访问 前言 购入威联通NAS后&#xff0c;很多用户对于如何在外在公网环境下的远程访问威联通NAS…

系列四十、请谈一下Spring中事务的传播行为

一、概述 事务的传播行为指的是当一个事务方法被另一个事务方法调用时&#xff0c;这个事务方法应该如何进行。事务的传播行为至少发生在两个事务方法的嵌套调用中才会出现。 二、传播行为分类

专门解决数学问题的大模型

01 项目介绍 LLEMMA&#xff1a;一个专门解决数学问题的开源大语言模型&#xff0c;能力超过所有已知的开源模型 LLEMMA由多个大学和Eleuther AI公司共同研发&#xff0c;模型能够理解和生成数学表达式、解决数学问题&#xff0c;并与其他计算工具&#xff08;如Python解释器…

Jenkins Gerrit Trigger插件配置

安装Jenkins 以Jenkins 2.361.1版本为例 docker pull jenkins/jenkins:2.361.1运行容器&#xff0c;将主机的8080端口映射到容器的8080端口&#xff0c;同时将主机的50000端口映射到容器的50000端口&#xff08;用于构建代理&#xff09; docker run -d -p 8080:8080 -p 500…

操作系统(Linux)外壳程序shell 、用户、权限

文章目录 操作系统和shell外壳Linux用户普通用户的创建和删除用户的切换 Linux 权限Linux 权限分类文件访问权限修改文件的权限权限掩码粘滞位 大家好&#xff0c;我是纪宁。 这篇文章将介绍 Linux的shell外壳程序&#xff0c;Linux用户切换机Linux权限的内容。 操作系统和shel…

基于SpringBoot的养老院信息管理系统

基于SpringBoot的养老院信息管理系统&#xff0c;java项目&#xff0c;springboot项目&#xff0c;idea都能打开运行。 推荐环境配置&#xff1a;idea jdk1.8 maven mysql5.5/mysql5.7 主要技术: SpringBoot&#xff0c;MySql&#xff0c;ajax&#xff0c;MyBatis 本系统的主要…

Vue 路由指南:畅游单页应用的地图(Vue Router 和 <router-view>)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

STM32F407的系统定时器

文章目录 系统定时器SysTick滴答定时器寄存器STK_CTRL 控制寄存器STK_LOAD 重载寄存器STK_VAL 当前值寄存器STK_CALRB 校准值寄存器 初始化 Systick 定时器SysTick_InitSysTick_CLKSourceConfig delay_us寄存器delay_us库函数delay_xms短时delay_ms长时SysTick_Config 系统定时…

电阻距离------Resistance distance

原来的解释来自维基百科&#xff1a;https://en.wikipedia.org/wiki/Resistance_distance 在图论中&#xff0c;简单连通图G的两个顶点之间的电阻距离等于电网上两个等效点之间的电阻&#xff0c;电网被构造为与G相对应&#xff0c;每条边被一欧姆的电阻代替。它是图上的度量。…

Jenkins安装(Jenkins 2.429)及安装失败解决(Jenkins 2.222.4)

敏捷开发与持续集成 敏捷开发 敏捷开发以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发。在敏捷开发中&#xff0c;软件项目在构建初期被切分成多个子项目&#xff0c;各个子项目的成果都经过测试&#xff0c;具备可视、可集成和可运行使用的特征。…

geatpy-遗传算法

参考: geatpy 官网 关注的点 在实操过程中,主要遇到以下问题: 不等式约束代码里怎么写?几种书写方式之间有何细节差别要注意入门案例一 包含不等式约束 import geatpy as ea import numpy as np# 构建问题 r = 1 # 目标函数需要用到的额外数据 @ea.Problem.single def …

黑豹程序员-架构师学习路线图-百科:PowerDesigner数据库建模的行业标准

PowerDesigner最初由Xiao-Yun Wang&#xff08;王晓昀&#xff09;在SDP Technologies公司开发完成。 目前PowerDesigner是Sybase的企业建模和设计解决方案&#xff0c;采用模型驱动方法&#xff0c;将业务与IT结合起来&#xff0c;可帮助部署有效的企业体系架构&#xff0c;并…