当前位置: 首页 > news >正文

frida_hook_libart(简单解释)

一:直接取代码


//frida -U -f com.xingin.xhs -l hook_art.js -o xhsart.log
//frida -U -f com.tencent.mobileqq -l hook_art.js -o qqart.logconst STD_STRING_SIZE = 3 * Process.pointerSize;
class StdString {constructor() {this.handle = Memory.alloc(STD_STRING_SIZE);}dispose() {const [data, isTiny] = this._getData();if (!isTiny) {Java.api.$delete(data);}}disposeToString() {const result = this.toString();this.dispose();return result;}toString() {const [data] = this._getData();return data.readUtf8String();}_getData() {const str = this.handle;const isTiny = (str.readU8() & 1) === 0;const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();return [data, isTiny];}
}function prettyMethod(method_id, withSignature) {const result = new StdString();Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);return result.disposeToString();
}/*
GetFieldID is at  0xe39b87c5 _ZN3art3JNI10GetFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetMethodID is at  0xe39a1a19 _ZN3art3JNI11GetMethodIDEP7_JNIEnvP7_jclassPKcS6_
NewStringUTF is at  0xe39cff25 _ZN3art3JNI12NewStringUTFEP7_JNIEnvPKc
RegisterNatives is at  0xe39e08fd _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
GetStaticFieldID is at  0xe39c9635 _ZN3art3JNI16GetStaticFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetStaticMethodID is at  0xe39be0ed _ZN3art3JNI17GetStaticMethodIDEP7_JNIEnvP7_jclassPKcS6_
GetStringUTFChars is at  0xe39d06e5 _ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh
FindClass is at  0xe399ae5d _ZN3art3JNI9FindClassEP7_JNIEnvPKc
*/function hook_libart() {var symbols = Module.enumerateSymbolsSync("libart.so");var addrGetStringUTFChars = null;var addrNewStringUTF = null;var addrFindClass = null;var addrGetMethodID = null;var addrGetStaticMethodID = null;var addrGetFieldID = null;var addrGetStaticFieldID = null;var addrRegisterNatives = null;var so_name = "lib";      //TODO 这里写需要过滤的sofor (var i = 0; i < symbols.length; i++) {var symbol = symbols[i];if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 &&symbol.name.indexOf("CheckJNI") < 0 &&symbol.name.indexOf("_ZN3art3JNIILb0") >= 0) {if (symbol.name.indexOf("GetStringUTFChars") >= 0) {addrGetStringUTFChars = symbol.address;console.log("GetStringUTFChars is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("NewStringUTF") >= 0) {addrNewStringUTF = symbol.address;console.log("NewStringUTF is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("FindClass") >= 0) {addrFindClass = symbol.address;console.log("FindClass is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("GetMethodID") >= 0) {addrGetMethodID = symbol.address;console.log("GetMethodID is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("GetStaticMethodID") >= 0) {addrGetStaticMethodID = symbol.address;console.log("GetStaticMethodID is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("GetFieldID") >= 0) {addrGetFieldID = symbol.address;console.log("GetFieldID is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("GetStaticFieldID") >= 0) {addrGetStaticFieldID = symbol.address;console.log("GetStaticFieldID is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("RegisterNatives") >= 0) {addrRegisterNatives = symbol.address;console.log("RegisterNatives is at ", symbol.address, symbol.name);} else if (symbol.name.indexOf("CallStatic") >= 0) {console.log("CallStatic is at ", symbol.address, symbol.name);Interceptor.attach(symbol.address, {onEnter: function (args) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var java_class = args[1];var mid = args[2];var class_name = Java.vm.tryGetEnv().getClassName(java_class);if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {var method_name = prettyMethod(mid, 1);console.log("<>CallStatic:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name);}}},onLeave: function (retval) { }});} else if (symbol.name.indexOf("CallNonvirtual") >= 0) {console.log("CallNonvirtual is at ", symbol.address, symbol.name);Interceptor.attach(symbol.address, {onEnter: function (args) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var jobject = args[1];var jclass = args[2];var jmethodID = args[3];var obj_class_name = Java.vm.tryGetEnv().getObjectClassName(jobject);var class_name = Java.vm.tryGetEnv().getClassName(jclass);if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {var method_name = prettyMethod(jmethodID, 1);console.log("<>CallNonvirtual:", DebugSymbol.fromAddress(this.returnAddress), class_name, obj_class_name, method_name);}}},onLeave: function (retval) { }});} else if (symbol.name.indexOf("Call") >= 0 && symbol.name.indexOf("Method") >= 0) {console.log("Call<>Method is at ", symbol.address, symbol.name);Interceptor.attach(symbol.address, {onEnter: function (args) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var java_class = args[1];var mid = args[2];var class_name = Java.vm.tryGetEnv().getObjectClassName(java_class);if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {var method_name = prettyMethod(mid, 1);console.log("<>Call<>Method:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name);}}},onLeave: function (retval) { }});}}}if (addrGetStringUTFChars != null) {Interceptor.attach(addrGetStringUTFChars, {onEnter: function (args) {},onLeave: function (retval) {if (retval != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var bytes = Memory.readCString(retval);console.log("[GetStringUTFChars] result:" + bytes, DebugSymbol.fromAddress(this.returnAddress));}}}});}if (addrNewStringUTF != null) {Interceptor.attach(addrNewStringUTF, {onEnter: function (args) {if (args[1] != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var string = Memory.readCString(args[1]);console.log("[NewStringUTF] bytes:" + string, DebugSymbol.fromAddress(this.returnAddress));}}},onLeave: function (retval) { }});}if (addrFindClass != null) {Interceptor.attach(addrFindClass, {onEnter: function (args) {if (args[1] != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var name = Memory.readCString(args[1]);console.log("[FindClass] name:" + name, DebugSymbol.fromAddress(this.returnAddress));}}},onLeave: function (retval) { }});}if (addrGetMethodID != null) {Interceptor.attach(addrGetMethodID, {onEnter: function (args) {if (args[2] != null) {var clazz = args[1];var class_name = Java.vm.tryGetEnv().getClassName(clazz);var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var name = Memory.readCString(args[2]);if (args[3] != null) {var sig = Memory.readCString(args[3]);console.log("[GetMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));} else {console.log("[GetMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress));}}}},onLeave: function (retval) { }});}if (addrGetStaticMethodID != null) {Interceptor.attach(addrGetStaticMethodID, {onEnter: function (args) {if (args[2] != null) {var clazz = args[1];var class_name = Java.vm.tryGetEnv().getClassName(clazz);var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var name = Memory.readCString(args[2]);if (args[3] != null) {var sig = Memory.readCString(args[3]);console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));} else {console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress));}}}},onLeave: function (retval) { }});}if (addrGetFieldID != null) {Interceptor.attach(addrGetFieldID, {onEnter: function (args) {if (args[2] != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var name = Memory.readCString(args[2]);if (args[3] != null) {var sig = Memory.readCString(args[3]);console.log("[GetFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));} else {console.log("[GetFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress));}}}},onLeave: function (retval) { }});}if (addrGetStaticFieldID != null) {Interceptor.attach(addrGetStaticFieldID, {onEnter: function (args) {if (args[2] != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var name = Memory.readCString(args[2]);if (args[3] != null) {var sig = Memory.readCString(args[3]);console.log("[GetStaticFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));} else {console.log("[GetStaticFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress));}}}},onLeave: function (retval) { }});}if (addrRegisterNatives != null) {Interceptor.attach(addrRegisterNatives, {onEnter: function (args) {console.log("[RegisterNatives] method_count:", args[3], DebugSymbol.fromAddress(this.returnAddress));var env = args[0];var java_class = args[1];var class_name = Java.vm.tryGetEnv().getClassName(java_class);var methods_ptr = ptr(args[2]);var method_count = parseInt(args[3]);for (var i = 0; i < method_count; i++) {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));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);console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));}},onLeave: function (retval) { }});}
}setImmediate(hook_libart);

二:代码解释

Frida 脚本旨在监控 Android 应用程序中 libart.so 库的 Java Native Interface (JNI) 调用。这个脚本通过拦截特定的 JNI 方法,捕获和记录它们的调用信息,帮助我们分析应用程序的行为。

1. 脚本结构

该脚本主要分为几个部分:

  • 常量和类定义: 定义了一些常量和 StdString 类,用于处理 C++ 字符串。
  • 辅助函数: prettyMethod 函数用于获取方法的可读名称。
  • hook_libart 函数: 该函数负责查找和拦截 libart.so 中的 JNI 方法。
  • 设置和执行: 最后调用 setImmediate 执行 hook_libart 函数。

2. 关键功能

2.1 StdString
class StdString {constructor() {this.handle = Memory.alloc(STD_STRING_SIZE);}dispose() {const [data, isTiny] = this._getData();if (!isTiny) {Java.api.$delete(data);}}disposeToString() {const result = this.toString();this.dispose();return result;}toString() {const [data] = this._getData();return data.readUtf8String();}_getData() {const str = this.handle;const isTiny = (str.readU8() & 1) === 0;const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();return [data, isTiny];}
}
  • 功能: 这个类用于处理 C++ 中的 std::string 对象,提供了字符串的读取和释放方法。
  • 内存管理: dispose 方法负责释放非小字符串的内存。
2.2 prettyMethod 函数
function prettyMethod(method_id, withSignature) {const result = new StdString();Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);return result.disposeToString();
}
  • 功能: 该函数调用 art::ArtMethod::PrettyMethod 获取方法的可读名称,便于后续的日志记录。
2.3 hook_libart 函数

这是脚本的核心部分,负责查找和拦截多个 JNI 方法:

  • 符号查找: 使用 Module.enumerateSymbolsSync 获取 libart.so 中的符号,并根据名称过滤出相关的 JNI 方法。

  • 拦截 JNI 方法: 对于每个找到的 JNI 方法,通过 Interceptor.attach 进行拦截,记录方法调用的详细信息。

例如,拦截 GetStringUTFChars 方法:

if (addrGetStringUTFChars != null) {Interceptor.attach(addrGetStringUTFChars, {onEnter: function (args) {},onLeave: function (retval) {if (retval != null) {var module = Process.findModuleByAddress(this.returnAddress);if (module != null && module.name.indexOf(so_name) == 0) {var bytes = Memory.readCString(retval);console.log("[GetStringUTFChars] result:" + bytes, DebugSymbol.fromAddress(this.returnAddress));}}}});
}
  • 参数和返回值处理: 在 onEnteronLeave 中处理方法的参数和返回值,记录相关信息。

3. 监控的 JNI 方法

该脚本监控了多个 JNI 方法,包括:

  • GetFieldID
  • GetMethodID
  • GetStaticFieldID
  • GetStaticMethodID
  • RegisterNatives
  • NewStringUTF
  • GetStringUTFChars
  • FindClass
  • CallStatic
  • CallNonvirtual

这些方法的调用通常涉及 Java 和 C/C++ 之间的交互,监控这些方法可以帮助分析应用程序的行为,尤其是在涉及 JNI 的部分。

4. 使用方法

要使用这个脚本,需要:

  1. 安装 Frida: 确保您已经在设备上安装了 Frida Server,并且在我们的计算机上安装了 Frida 工具。

  2. 运行命令: 使用如下命令运行脚本:

    frida -U -f com.xingin.xhs -l hook_art.js -o xhsart.log
    frida -U -f com.tencent.mobileqq -l hook_art.js -o qqart.log
    

    这里 -U 表示连接到 USB 设备,-f 指定要启动的应用程序,-l 指定要加载的脚本,-o 指定输出日志文件。

5. 总结

这个 Frida 脚本是一个强大的工具,用于监控 Android 应用程序中的 JNI 调用。通过拦截 libart.so 中的关键方法,可以深入了解 Java 和本地代码之间的交互,帮助进行调试、分析和安全研究。脚本的灵活性和可扩展性使其能够适应多种分析需求。

相关文章:

frida_hook_libart(简单解释)

一&#xff1a;直接取代码 //frida -U -f com.xingin.xhs -l hook_art.js -o xhsart.log //frida -U -f com.tencent.mobileqq -l hook_art.js -o qqart.logconst STD_STRING_SIZE 3 * Process.pointerSize; class StdString {constructor() {this.handle Memory.alloc(STD_S…...

计算机网络八股整理(二)

计算机网络八股整理&#xff08;二&#xff09; 应用层 1&#xff1a;dns的全称了解过吗&#xff1f; dns全称domain-name-system&#xff0c;翻译过来就是域名系统&#xff0c;是在计算机网络中将域名转换成ip地址的分布式数据库系统&#xff1b; 域名服务器的层级类似一个树…...

强化学习off-policy进化之路(PPO->DPO->KTO->ODPO->ORPO->simPO)

需要LLM在训练过程中做生成的方法是 On Policy&#xff0c;其余的为Off Policy。 On Policy是包含了反馈机制&#xff0c;Off Policy不包含反馈机制。 若进行环境交互的模型与被更新的模型是相同的模型&#xff0c;通常这种更新策略被称为on-policy的策略。on-policy的方法会有…...

Linux 如何创建逻辑卷并使用

一、逻辑卷的介绍 生成环境中逻辑卷使用率很高 逻辑卷的诞生&#xff1a;如果对磁盘直接使用fdisk分区&#xff0c;那么这中分区&#xff0c;我们叫做Linux的标准分区&#xff0c;Linux的标准分区格式化成文件系统之后&#xff0c;挂载使用&#xff0c;那么一旦文件系统的空间…...

java实现将图片插入word文档

插入图片所用依赖 private static void insertImage(XWPFDocument document, String path) {List<XWPFParagraph> paragraphs document.getParagraphs();for (XWPFParagraph paragraph : paragraphs) {CTP ctp paragraph.getCTP();for (int dwI 0; dwI < ctp.sizeO…...

初识java(3)

大家好&#xff0c;今天我们来讲讲我们的老伙计-变量&#xff0c;在哪一门编程语言中&#xff0c;变量的作用都是不可或缺的&#xff0c;那么下面我们就来详细了解一下java中的变量。 一.变量概念 在程序中&#xff0c;除了有始终不变的常量外&#xff0c;有些内容可能会经常…...

coqui-ai TTS 初步使用

项目地址&#xff1a;https://github.com/coqui-ai/TTS 1. 创建一个新的conda环境&#xff0c;如果自己会管理python环境也可以用其他方法 克隆项目下来 pip install -r requirements.txt # 安装依赖 pip install coqui-tts # 只要命令行工具的话 下载自己想要的模型 …...

matlab代码--卷积神经网络的手写数字识别

1.cnn介绍 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习的算法&#xff0c;在图像和视频识别、图像分类、自然语言处理等领域有着广泛的应用。CNN的基本结构包括输入层、卷积层、池化层&#xff08;Pooling Layer&#xff09;、全连…...

Scala—Map用法详解

Scala—Map用法详解 在 Scala 中&#xff0c;Map 是一种键值对的集合&#xff0c;其中每个键都是唯一的。Scala 提供了两种类型的 Map&#xff1a;不可变 Map 和可变 Map。 1. 不可变集合&#xff08;Map&#xff09; 不可变 Map 是默认的 Map 实现&#xff0c;位于 scala.co…...

极狐GitLab 17.6 正式发布几十项与 DevSecOps 相关的功能【六】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…...

ES6 、ESNext 规范、编译工具babel

ES6 、ESNext 规范、编译工具简介 ES6ES&#xff08;ECMAScript&#xff09; vs JS常量进一步探讨 obj对象的扩展面试&#xff1a;使对象属性也不能更改——Object.freeze(obj) 解构deconstruction变量的解构赋值&#xff1a;数组解构赋值&#xff1a;对象解构赋值&#xff1a;…...

DeepSpeed 配置文件(DeepSpeed Configuration Files)详解:中英文解释

中文版 本文详细介绍 DeepSpeed 配置文件&#xff0c;结合 4 卡 3090 的实际使用场景&#xff0c;重点解释各个参数的含义&#xff0c;并提供应对爆显存的方案。 DeepSpeed 配置文件详解&#xff1a;从基础到实战 DeepSpeed 是用于加速大规模分布式训练的重要工具&#xff0c…...

前端JavaScript(一)---基本介绍

Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言&#xff0c;主要目的是为了解决服务器端语言&#xff0c;比如Perl&#xff0c;遗留的速度问题&#xff0c;为客户提供更流畅的浏览效果。当时服务端需要对…...

文本处理之sed

1、概述 sed是文本编辑器&#xff0c;作用是对文本的内容进行增删改查。 和vim不一样&#xff0c;sed是按行进行处理。 sed一次处理一行内容&#xff0c;处理完一行之后紧接着处理下一行&#xff0c;一直到文件的末尾 模式空间&#xff1a;临时储存&#xff0c;修改的结果临…...

uniapp在App端定义全局弹窗,当打开关闭弹窗会触发onShow、onHide生命周期怎么解决?

在uniapp(App端)中实现自定义弹框&#xff0c;可以通过创建一个透明页面来实现。点击进入当前页面时&#xff0c;页面背景会变透明&#xff0c;用户可以根据自己的需求进行自定义&#xff0c;最终效果类似于弹框。 遇到问题&#xff1a;当打开弹窗(进入弹窗页面)就会触发当前页…...

计算机网络 实验七 NAT配置实验

一、实验目的 通过本实验理解网络地址转换的原理和技术&#xff0c;掌握扩展NAT/NAPT设计、配置和测试。 二、实验原理 NAT配置实验的原理主要基于网络地址转换&#xff08;NAT&#xff09;技术&#xff0c;该技术用于将内部私有网络地址转换为外部公有网络地址&#xff0c;从…...

数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!

文章目录 前言一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本 快排1.2.2 挖坑法 快排1.2.3 lomuto前后指针 快排 二、归并排序总结 前言 继上篇学习了排序的前面两个部分:直接插入排序和选择排序 今天我们来学习排序中常用的交换排序以及非常稳定的归并排序 快排可是有多…...

【kafka04】消息队列与微服务之Kafka 图形工具

Kafka 在 ZooKeeper 里面的存储结构 topic 结构 /brokers/topics/[topic] partition结构 /brokers/topics/[topic]/partitions/[partitionId]/state broker信息 /brokers/ids/[o...N] 控制器 /controller 存储center controller中央控制器所在kafka broker的信息 消费者 /c…...

剖析前后端 API 接口参数设计:JSON 数据结构化全攻略

在当今软件开发领域&#xff0c;前后端分离架构已成为主流趋势。而 API 接口作为前后端之间数据交互的桥梁&#xff0c;其设计的合理性对系统的可维护性和扩展性起着至关重要的作用。JSON&#xff08;JavaScript Object Notation&#xff09;作为一种轻量级的数据交换格式&…...

vue3 多种方式接受props,定义ref,reactive

定义props 1 第一种 interface AddType { dialogStudyVisible: boolean; } const props defineProps<AddType>(); 第二种 // const props defineProps({ // dialogStudyVisible:{ // type:Boolean, // default:false // } // }) 第三种 // const …...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...