15.Chromium指纹浏览器开发教程之WebAudio指纹定制

WebAudio指纹概述

浏览器中的 WebAudio API 提供了丰富的功能,其中包括了大量生成和处理音频数据的API。WebAudio API 的音频指纹技术是一种利用音频信号的特征来唯一标识音频的技术。因为WebAudio API 提供了丰富的音频处理功能,包括合成、过滤、分析等,通过一系列功能组合,可以使生成的音频指纹具有一定的独特性。生成音频指纹的过程通常涉及到对音频数据进行数学运算或特征提取,这使得生成的指纹在一定程度上是稳定的,即使音频本身有一定的变化也不会影响其唯一性。而且Web Audio API 提供了实时处理音频数据的能力,因此可以用于实时生成和识别音频指纹。

WebAudio API主要用在AudioContext上下文中,在进行任何其他操作之前,始终需要创建一个AudioContext的实例,如图4-7所示。

在有了音频来源之后,通过节点压缩,就可以得到Buffer输出了。在实际的WebAudio API 操作过程中,通用的做法是创建单个AudioContext实例,并在所有后续处理中重复使用。每个AudioContext实例都具有一个目标属性,该属性用于表示该上下文中所有音频的目标。此外,还存在一种特殊类型的AudioContext,即OfflineAudioContext。在获取音频指纹的时候,通常是会创建OfflineAudioContext,主要原因在于它不会将音频呈现给设备硬件,而是快速生成音频并将其保存到AudioBuffer中。因此,OfflineAudioContext的目标是内存中的数据结构,而常规的AudioContext的目标是音频设备。

创建OfflineAudioContext实例时,需要传递三个参数:

  1. 通道数:通道数表示音频数据中的声道数量。在数字音频中,通常有单声道和立体声两种通道配置。单声道通常用于单一音频源,而立体声则包含左右两个独立的声道,用于模拟左右声源的位置和方向。通道数还可以扩展到更多声道,如环绕声,以提供更加沉浸式的音频体验。
  2. 样本总数:样本总数指的是音频数据中包含的采样数量。音频数据是通过对连续时间信号进行采样来获取的,每个采样点对应着一个特定时间点上的音频振幅值。样本总数决定了音频的持续时间,即音频的长度。采样率和样本总数共同决定了音频的时长,样本总数越多,音频的时长就越长。
  3. 采样率:采样率表示每秒钟对声音信号进行采样的次数,单位为赫兹Hz,采样率决定了数字音频的精度和质量,以及其能够表示的频率范围。常见的标准采样率包括44.1 kHz和48 kHz,它们通常用于CD音质和音频制作。更高的采样率,如96 kHz或192kHz可以提供更高的音频质量和更广的频率响应范围,但也会增加文件大小。

Oscillator振荡器是一种产生周期性波形的电子设备或软件组件。在音频领域中,振荡器通常用于生成声音信号的基础波形,例如正弦波、方波、锯齿波等。这些基础波形可以用于合成各种声音,是合成器和音频处理中的重要组件之一。振荡器通过产生连续的电压或数字信号来生成波形。在软件中,振荡器通常是由算法来模拟的,这些算法根据所需的波形形状和参数生成连续的样本值。在处理音频时需要一个来源,振荡器是一个很好的选择,因为它是通过数学方法生成样本的,可以生成具有指定频率的周期波形。

压缩节点是一种用于动态范围压缩的节点类型。它可以降低音频信号的动态范围,即减小最响亮部分与最安静部分之间的差异,从而提高音频的平均音量并减少峰值。压缩节点通常用于音频信号处理的动态范围控制,以确保音频在播放过程中的一致性和平衡。压缩节点通常作为音频处理图中的一个节点,与其他节点,如声音源、效果器等连接在一起,以对输入信号进行压缩处理。通过调整压缩节点的参数,可以控制压缩的程度和效果,从而实现对音频信号动态范围的调节和控制。

AudioBuffer是WebAudio API中表示音频数据的数据结构。它用于存储音频样本的实际数据,并提供了一组函数来访问和操作这些数据。在WebAudio API中,AudioBuffer通常作为音频源节点的输入,用于播放音频或将其传递给其他音频处理节点进行进一步处理。AudioBuffer中包含音频数据的实际样本,这些样本表示音频波形在离散时间点上的振幅值。音频数据可以来自于多种来源,例如从服务器加载、用户录制或通过WebAudio API生成。每个AudioBuffer实例都具有固定的采样率和通道数。这些属性在创建AudioBuffer时被指定,并决定了AudioBuffer中存储的音频数据的格式和结构。此外,AudioBuffer提供了一组函数来访问和操作存储的音频数据。例如,可以使用getChannelData获取特定通道的音频数据,并使用set修改音频数据的值。这些函数使得可以直接对音频数据进行编辑和处理,例如音频混合、剪辑、变速、变调等操作。AudioBuffer通常作为音频源节点的输入,用于播放音频或将其传递给其他音频处理节点进行进一步处理。通过创建AudioBufferSourceNode并将AudioBuffer作为其缓冲区传递给它,可以将AudioBuffer中存储的音频数据进行播放或传递给音频图中的其他节点。由于音频数据以二进制格式存储在AudioBuffer中,因此可以在Web Audio API中高效地进行音频处理操作,而无需频繁地将数据从JavaScript代码中复制到Web Audio API中。

在WebAudio指纹的计算过程中,一般使用OfflineAudioContext上下文,接着设置特殊的振荡器来作为音频源,在经过压缩节点操作之后,就可以使用getChannelData来获取生成的AudioBuffer了,指纹即是通过对该Buffer的计算完成的。

let audioContext = new (window.OfflineAudioContext ||window.webkitOfflineAudioContext)(1, 44100, 44100);let outputValue;if (!audioContext) {outputValue = 0;} else {let oscillator = audioContext.createOscillator();oscillator.type = "triangle";oscillator.frequency.value = 10000;let compressor = audioContext.createDynamicsCompressor();if (compressor.threshold) compressor.threshold.value = -50;if (compressor.knee) compressor.knee.value = 40;if (compressor.ratio) compressor.ratio.value = 12;if (compressor.reduction) compressor.reduction.value = -20;if (compressor.attack) compressor.attack.value = 0;if (compressor.release) compressor.release.value = 0.25;oscillator.connect(compressor);compressor.connect(audioContext.destination);oscillator.start(0);audioContext.startRendering();audioContext.oncomplete = function(event) {outputValue = 0;let renderedBuffer = event.renderedBuffer.getChannelData(0);let bufferAsString = '';for (let i = 0; i < renderedBuffer.length; i++) {bufferAsString += renderedBuffer[i].toString();}let fullBufferHash = hash(bufferAsString);console.log('Full buffer hash: ' + fullBufferHash);for (let i = 4500; i < 5000; i++) {outputValue += Math.abs(renderedBuffer[i]);}console.log('Output value: ' + outputValue);compressor.disconnect();};}

前两行代码创建了一个OfflineAudioContext实例。使用离线音频上下文允许程序处理和渲染音频数据而不实时播放。使用window.OfflineAudioContext或window.webkitOfflineAudioContext确保代码兼容不同的浏览器。使用createOscillator 创建一个振荡器,用于生成音频信号。使用createDynamicsCompressor 创建一个动态压缩器节点,该节点用于减少音频信号的动态范围。然后将振荡器连接到压缩器,压缩器再连接到音频上下文的输出

当渲染完成后,oncomplete 定义的函数被调用,getChannelData用于获取渲染后的音频数据,接着将音频数据转换为字符串,并计算其哈希值。代码最后计算了特定样本范围内的数据的绝对值之和,这个数是音频指纹常用的指纹数字。

WebAudio指纹获取

本节会使用JavaScript脚本编写一个获取WebAudio音频信息的脚本。这段代码利用JavaScript的WebAudio API来生成和处理音频数据,最终目的是生成一个音频哈希值和一个从特定样本范围内计算得到的输出值。以下是具体代码:

从4.3.2的代码中可以看出,音频指纹的修改点很多,但是最终是使用getChannelData来获取渲染后的音频数据的,因此可以选择该函数作为音频指纹修改点。对其中的音频数组进行遍历噪声,从而影响整个音频。

WebAudio相关的Chromium源码位于“src\third_party\blink\renderer\modules\webaudio”目录之中,重点要修改的是AudioBuffer相关的,因此选择其中的audio_buffer.cc文件作为指纹定制的文件。

getChannelData有两个重载,具体代码如下:

NotShared<DOMFloat32Array> AudioBuffer::getChannelData(unsigned channel_index,ExceptionState& exception_state) {if (channel_index >= channels_.size()) {exception_state.ThrowDOMException(DOMExceptionCode::kIndexSizeError,"channel index (" + String::Number(channel_index) +") exceeds number of channels (" +String::Number(channels_.size()) + ")");return NotShared<DOMFloat32Array>(nullptr);}return getChannelData(channel_index);}NotShared<DOMFloat32Array> AudioBuffer::getChannelData(unsigned channel_index) {if (channel_index >= channels_.size()) {return NotShared<DOMFloat32Array>(nullptr);}return NotShared<DOMFloat32Array>(channels_[channel_index].Get());}

从JavaScript代码获取音频指纹的时候,只传递了一个参数,因此选择第二个函数作为切入函数。该函数用于获取音频缓冲区中特定通道的数据。其中的函数签名如下:

  1. NotShared<DOMFloat32Array>:返回类型是 NotShared 包装的 DOMFloat32Array 对象。NotShared 是一种智能指针,表示该对象不应与其他对象共享。
  2. AudioBuffer:::表明该函数是 AudioBuffer 类的成员。
  3. getChannelData:接收一个无符号整数参数 channel_index,表示要获取的数据通道索引。

然后检查传入的通道索引 channel_index 是否超出了音频通道的数量。如果超出了有效范围,表示请求的通道不存在。否则将获取到的通道数据包装成 DOMFloat32Array对象并返回。

由此可见,可以在最后的通道数据正式返回之前,将里边的数据进行遍历,挨个进行微调之后,从而完成音频指纹定制:

  //ruyiconst base::CommandLine* ruyi_command_line =base::CommandLine::ForCurrentProcess();if (ruyi_command_line->HasSwitch(blink::switches::kRuyi)) {const std::string ruyi_fp =ruyi_command_line->GetSwitchValueASCII(blink::switches::kRuyi);absl::optional<base::Value> json_reader =base::JSONReader::Read(ruyi_fp);double webaudio_data =*(json_reader->GetDict().FindDouble("webaudio"));DOMFloat32Array* channels__ = channels_[channel_index].Get();size_t channel_size = channels__->length();for (size_t i = 0; i < channel_size; i++) {channels__->Data()[i] += (0.00001 * webaudio_data);}return NotShared<DOMFloat32Array>(channels__);}//ruyi end

修改的代码从 JSON 中读取一个名为 webaudio 的双精度浮点数值,为了防止音频数据修改过大,这里对音频数据进行遍历的时候,将该值按一定比例加到指定通道的音频数据样本上。

在修改完毕之后,可以到音频检测网站audiofingerprint.openwpm.com进行测试,传递不同参数,可以得到不同的指纹信息,如图4-8所示:

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

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

相关文章

2025年赣教云智慧作业微课PPT模板

江西的老师们注意&#xff0c;2025年赣教云智慧作业微课PPT模版和往年不一样&#xff0c;千万不要搞错了&#xff0c;图上的才是正确的2025年的赣教云智慧作业微课PPT模版&#xff0c;赣教云智慧作业官网有问题&#xff0c;无法正确下载该模板&#xff0c;需要该模板的&#xf…

2.5.1DOS下常用工具 curl,netstat,telnet命令使用

curl命令 Win10及以上系统默认已安装Curl&#xff0c;打开命令提示符输入 curl --help&#xff0c;若显示帮助信息则无需安装 ​​手动安装方法​​ 官网下载&#xff1a;访问 curl官网 选择Windows版本curl for Windows若需在 Windows XP 等旧系统使用&#xff0c;需选择更…

使用Redis实现实时排行榜

为了实现一个实时排行榜系统&#xff0c;我们可以使用Redis的有序集合&#xff08;ZSet&#xff09;&#xff0c;其底层通常是使用跳跃表实现的。有序集合允许我们按照分数&#xff08;score&#xff09;对成员&#xff08;member&#xff09;进行排序&#xff0c;因此非常适合…

Linux——firewalld防火墙(笔记)

目录 一&#xff1a;Firewalld防火墙的概述 &#xff08;1&#xff09;firewalld简介 &#xff08;2&#xff09;firewalld&iptables的关系 &#xff08;3&#xff09;firewalld与iptables service的区别 1. ‌规则管理方式‌ 2. ‌默认策略与设计逻辑‌ 3. ‌配置文…

JS中实现类似sleep、wait、delay的延时功能

前言 编写代码时很多时候需要进行流程化的操作&#xff0c;各个流程间通常需要等待一定时间&#xff0c;这在很多语言中通常可以使用 sleep 、 wait 、 delay 等函数来实现。JavaScript原生并没有类似的功能&#xff0c;想要延时通常就是使用 setTimeout(functionRef, delay) …

Elasticsearch:使用 ES|QL 进行搜索和过滤

本教程展示了 ES|QL 语法的示例。请参考 Query DSL 版本&#xff0c;以获得等效的 Query DSL 语法示例。 这是一个使用 ES|QL 进行全文搜索和语义搜索基础知识的实践介绍。 有关 ES|QL 中所有搜索功能的概述&#xff0c;请参考《使用 ES|QL 进行搜索》。 在这个场景中&#x…

Java 动态代理实现

Java 动态代理实现 一、JDK动态代理二、CGLIB动态代理三、动态代理的应用场景四、JDK代理与CGLIB代理比较 动态代理是Java中一种强大的技术&#xff0c;它允许在运行时创建代理对象&#xff0c;用于拦截对目标对象的方法调用。 一、JDK动态代理 JDK动态代理是Java标准库提供的代…

Apache IoTDB V2.0.2/V1.3.4 发布|新增表模型权限管理、UDF、嵌套查询功能

Release Announcement Version 2.0.2/1.3.4 Apache IoTDB V2.0.2、V1.3.4 已经发布&#xff01; V2.0.2 作为树表双模型正式版本&#xff0c;主要新增表模型权限管理、用户管理以及相关操作鉴权&#xff0c;并新增了表模型 UDF、系统表和嵌套查询等功能。 V1.3.4 主要新增模式…

鸿蒙开发11-ARKUI框架

ARKUI&#xff08;方舟 UI 框架&#xff09;是 HarmonyOS Next&#xff08;原 OpenHarmony&#xff09;的核心 UI 开发框架&#xff0c;基于声明式编程范式&#xff0c;支持 ArkTS 语言&#xff0c;能够高效构建跨设备的响应式应用。以下是对 ARKUI 框架及开发的详细介绍&#…

Linux 进程间通信详解

一.进程间通信介绍 1. 进程间通信概念 进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;是指在不同进程之间传递或交换信息的一种机制。在操作系统中&#xff0c;进程是资源分配和独立运行的基本单位&#xff0c;它们拥有各自独立的内存空间和系统资源。…

从0开始掌握动态规划

动态规划的核心思想 -- 以空间换时间 复杂点说通过分解问题为子问题并存储子问题解来优化复杂计算的算法策略。 简单看个问题。 一&#xff0c;初始&#xff1a;求最长连续递增子序列 nums [10,9,2,5,3,7,101,18] 求上面数组中的最长连续递增子序列&#xff0c;输出其长度 …

Python Requests 库:从安装到精通

摘要 本文详细介绍 Python Requests 库的安装与使用&#xff0c;通过常见示例让你轻松掌握。 一、引言 在当今的互联网时代&#xff0c;与各种 Web 服务进行交互是非常常见的需求。Python 作为一门功能强大且易于学习的编程语言&#xff0c;提供了许多用于网络请求的库&…

Manus技术架构、实现内幕及分布式智能体项目实战

Manus技术架构、实现内幕及分布式智能体项目实战 模块一&#xff1a; 剖析Manus分布式多智能体全生命周期、九大核心模块及MCP协议&#xff0c;构建低幻觉、高效且具备动态失败处理能力的Manus系统。 模块二&#xff1a; 解析Manus大模型Agent操作电脑的原理与关键API&#xf…

C算术运算符 printf输出格式 字符指针打印输出 使用scanf函数进行输入

一 算术运算符 加, 一元取正 - 减, 一元取负 * 乘 / 除 % 求余 -- 自减1 自加1 逻辑运算符 && 逻辑与 || 逻辑或 ! 逻辑非 关系运算符 > 大于 > 大于等于 < 小于 < 小于等于 等于 ! 不等于 位运算符号 & 按位与 | 按位或 ^ 按位异或…

STM32中Hz和时间的转换

目录 一、常见的频率单位及其转换 二、计算公式 三、STM32中定时器的应用 四、例子 一、常见的频率单位及其转换 赫兹&#xff08;Hz&#xff09;是频率的国际单位&#xff0c;表示每秒钟周期性事件发生的次数。 1 kHz&#xff08;千赫兹&#xff09; 1,000 Hz1 MHz&#…

《分布式软总线:不同频段Wi-Fi环境下设备发现兼容性难题》

分布式软总线技术作为实现设备互联互通的关键&#xff0c;正逐渐成为构建万物互联世界的基石。然而&#xff0c;当分布式软总线面临不同频段Wi-Fi环境时&#xff0c;设备发现的兼容性问题成为了阻碍其广泛应用的一大挑战。这一问题不仅影响着用户体验&#xff0c;也制约着分布式…

MCP(Model Context Protocol 模型上下文协议)科普

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;是由人工智能公司 Anthropic 于 2024年11月 推出的开放标准协议&#xff0c;旨在为大型语言模型&#xff08;LLM&#xff09;与外部数据源、工具及服务提供标准化连接&#xff0c;从而提升AI在实际…

【mongodb】数据库操作

目录 1. 查看所有数据库2. 切换到指定数据库&#xff08;若数据库不存在&#xff0c;则创建&#xff09;3. 查看当前使用的数据库4. 删除当前数据库5.默认数据库 1. 查看所有数据库 1.show dbs2.show databases 2. 切换到指定数据库&#xff08;若数据库不存在&#xff0c;则…

ICPR-2025 | 让机器人在未知环境中 “听懂” 指令精准导航!VLTNet:基于视觉语言推理的零样本目标导航

作者&#xff1a;Congcong Wen, Yisiyuan Huang, Hao Huang ,Yanjia Huang, Shuaihang Yuan, YuHao, HuiLin and Yi Fang 单位&#xff1a;纽约大学阿布扎比分校具身人工智能与机器人实验室&#xff0c;纽约大学阿布扎比分校人工智能与机器人中心&#xff0c;纽约大学坦登工程…

基于DeepSeek的考研暑假日志分析

注&#xff1a;我去年考研时写了日志&#xff0c;大致记录了我每天的主要活动。由于过于琐碎&#xff0c;一直没有翻看。突发奇想&#xff0c;现在利用deepseek总结其中规律。 从你的日志中可以总结出以下规律和活动兴衰起落&#xff1a; ​​一、学习活动规律与演变​​ ​​…