当前位置: 首页 > news >正文

Yaklang websocket劫持教程

背景

随着Web应用的发展与动态网页的普及,越来越多的场景需要数据动态刷新功能。在早期时,我们通常使用轮询的方式(即客户端每隔一段时间询问一次服务器)来实现,但是这种实现方式缺点很明显:
大量请求实际上是无效的,这导致了大量带宽的浪费。

这时候我们急需一个新的技术来解决这一痛点,Websocket应运而生: WebSocket是一种网络传输协议,可在单个TCP连接上进行 全双工通信
,位于OSI模型的应用层。

Websocket的诞生也给我们带来了新的挑战,我们能否对websocket的请求与响应进行劫持与修改呢?要想做到这一点,我们首先得了解websocket协议。

websocket协议细节

等等,看到这个标题的时候先别急着划走,实际上websocket协议比我们想象中的要简单,他实际上 几乎 等同于原始的TCP
socket,只不过多出了额外的协议头以及一个升级的过程。

我们先来看websocket的升级过程,先是客户端发起协议升级请求,其采用标准的HTTP报文格式,且必须使用 GET 请求方法:

GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==

这里我们需要关注的最后四行的特殊请求头:

  • Connection: Upgrade:表示要升级协议

  • Upgrade: websocket:表示要升级到websocket协议

  • Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号

  • Sec-WebSocket-Key:与后面服务端响应头Sec-WebSocket-Accept配套,提供基本的校验。其本身是一个 bas64编码过的随机16字节

服务器返回101状态码的响应,至此完成协议升级:

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

这里我们需要的关注的是最后的Sec-WebSocket-Accept请求头,其与前文的Sec-WebSocket-Key对应,主要有以下两个目的:

  • 确保服务器理解 WebSocket 协议

  • 防止客户端意外请求 WebSocket 升级

Sec-WebSocket-Accept请求头是由Sec-WebSocket-Key计算而成的,其伪代码如下:

toBase64(sha1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))

协议升级后,双方开始使用websocket协议进行通讯。我们来看看websocket的协议细节,一个经典的概览图如下:

0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len |    Extended payload length    ||I|S|S|S|  (4)  |A|     (7)     |             (16/64)           ||N|V|V|V|       |S|             |   (if payload len==126/127)   || |1|2|3|       |K|             |                               |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +|     Extended payload length continued, if payload len == 127  |+ - - - - - - - - - - - - - - - +-------------------------------+|                               |Masking-key, if MASK set to 1  |+-------------------------------+-------------------------------+| Masking-key (continued)       |          Payload Data         |+-------------------------------- - - - - - - - - - - - - - - - +:                     Payload Data continued ...                :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +|                     Payload Data continued ...                |+---------------------------------------------------------------+

如果看不懂无所谓,我们逐个字段进行讲解:

FIN :1 bit

如果是1,表示这是消息的最后一个分片,如果是0,表示不是是消息的最后一个分片。 通常为1

RSV1, RSV2, RSV3 :各占1 bit

一般情况下全为0
。当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用WebSocket扩展,连接出错。

Opcode : 4 bit

操作代码,Opcode的值决定了应该如何解析后续的数据, 可以简单地理解为消息类型,一般通讯时为%x1或%x2 。可选值如下:

  • %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片
  • %x1:表示这是一个文本帧(frame)
  • %x2:表示这是一个二进制帧(frame)
  • %x3-7:保留的操作代码,用于后续定义的非控制帧
  • %x8:表示连接断开
  • %x9:表示这是一个ping操作
  • %xA:表示这是一个pong操作
  • %xB-F:保留的操作代码,用于后续定义的控制帧

Mask : 1 bit

表示是否要对数据进行掩码操作。 客户端向服务端发送数据时该bit为1,否则为0 。掩码算法在后续Masking key提到。

Payload length : 数据的长度,单位是字节。其可能为7/7+16/1+64 bit。

假设数据长度 = x,如果

  • 0<=x<=125:用这7个bit来代表数据长度。

126<=x<=65535:7个bit设置为126(1111110)。后续2个字节代表一个16位的无符号整数,该无符号整数的值为数据的长度(大端序)。

  • 65535<x:7个bit设置为127(1111111)。后续8个字节代表一个64位的无符号整数,该无符号整数的值为数据的长度(大端序)。

Masking-key :0/32 bit

假如前文所述Mask为1,则此Masking-key占32 bit(即四个字节),否则为0 bit。Masking-
key用于将客户端传输给服务器的数据进行掩码操作。前文的Payload length,不包括Masking-key的长度。

具体的掩码算法伪代码如下:

设原数据为bytes,Masking-key为key,则:

for i in range(len(bytes)):

bytes[i] ^= key[i&3]

Payload data :(x+y) byte

载荷数据包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。

在前文的升级阶段没有协商使用扩展的话,扩展数据数据为0字节。 剩下的应用数据就是传输的原始socket内容
,因此也一般会结合其他压缩算法/协议使用,如protobuf。

websocket劫持实现

在了解了websocket协议之后,我们实现websocket劫持就变得很简单了,用一张流程图来展示:

其中重点主要是原始数据与websocket帧之间的转换。

解析原始数据

前面说过,websocket协议实际上几乎只是比原始socket多了一个头,那么我们解析原始数据可以分为以下几步:

  1. 设初始n=2,即抛弃前两个websocket头字节

  2. 判断第2个byte的后7个bit(payload length),如果为126,则n+2,如果其127,则n+8

  3. 判断第2个byte的第1个bit(mask位)是否为1,如果为1,则从n~n+4位取出masking-key,并将n+4,将n位后的数据进行掩码处理

  4. 返回n位后的数据,即为原始数据

重新封装成websocket帧

可以分为以下几步:

  1. 第1个byte照抄(也可以根据需要修改后4位bit及opcode,修改消息类型)

  2. 第2个byte第1个bit(mask位)照抄,后7位bit根据修改后的数据长度进行处理

  3. 如果数据长度大于125,则要写入uint16或uint64的数据长度字节(大端序)

  4. 如果mask位为1,则生成并写入32位的随机masking-key,再将数据进行掩码处理与写入,此时即封装好了的websocket帧

websocket劫持实现时遇到的坑点

这里讲下在websocket劫持实现时遇到的坑点,仅供参考

保持协议的完整性

实际上前文提到的劫持所使用的技术都是中间人技术,这里我遇到的坑点就是没保持协议的完整性,我在处理时从服务器端接收到了101状态码的响应,但却没有将其写入回客户端,导致客户端断开,整个websocket的升级也就失败了,所以需要提醒的就是在劫持时要保持协议的完整性,该发送或接收到的内容都要到位。

实现FrameReader而非简单的Read

我之前的一个错误实例如下:

这里实际上犯了几个错误:

  1. reader.Read()是非阻塞的,也就是说如果缓冲中没有数据的话,它会不断地返回0和EOF,但是我这里判断如果n<=0则会不断continue,这会导致不断创建新的4096字节的bytes,无法释放

  2. 后续我将b作为websocket帧来处理,但是b的大小只有4096,假如数据量超大,这样写毫无疑问是错误的

后来其他师傅发现了这个bug并指出这几点错误,我才意识到我应该抽象出一个FrameReader来去读取websocket帧,根据读取到的前几个字节来判断最终要读取的长度。

新版yak的websocket尝鲜

websocket劫持尝鲜

经过一番努力之后,终于实现了websocket劫持功能,在yak的mitm标准库中新增了wscallback与wsforcetext两个函数,我们来看一个简单的用例:

go fn{mitm.Start(8084, mitm.wsforcetext(true),mitm.wscallback(fn(data, isRequest){if isRequest {data = "Hijack request"} else {data = "Hijack Response"}return data}))
}for {time.sleep(1)
}

wscallback参数接受一个函数作为参数,该函数拥有2个参数: data([]byte类型)和isRequest(bool类型)并
接收一个返回值(必须存在返回值) ,作为修改后的数据。

isRequest参数用于判断劫持到的是否为websocket请求(true即websocket请求,false为websocket响应),data参数则为劫持到的原始数据。

接下来我们使用go来启动一个websocket的测试服务器,这里需要安装依赖:“github.com/gorilla/websocket”:

package mainimport ("fmt""net/http""os""time""github.com/gorilla/websocket"
)func main() {var upgrader = websocket.Upgrader{}f, err := os.CreateTemp("", "test-*.html")if err != nil {panic(err)}f.Write([]byte(`<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"/><title>Sample of websocket with golang</title><script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script><script>$(function() {var ws = new WebSocket('ws://' + window.location.host + '/ws');ws.onmessage = function(e) {$('<li>').text(event.data).appendTo($ul);ws.send('{"message":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}');};var $ul = $('#msg-list');});</script>
</head>
<body>
<ul id="msg-list"></ul>
</body>
</html>`))index := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {http.ServeFile(w, r, f.Name())})http.Handle("/", index)http.Handle("/index.html", index)http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {// msg := &RecvMessage{}ws, err := upgrader.Upgrade(w, r, nil)if err != nil {panic(err)return}defer ws.Close()go func() {for {_, msg, err := ws.ReadMessage()if err != nil {panic(err)return}fmt.Printf("server recv from client: %s\n", msg)}}()for {time.Sleep(time.Second)ws.WriteJSON(map[string]interface{}{"message": fmt.Sprintf("Golang Websocket Message: %v", time.Now()),})}})err = http.ListenAndServe(":8884", nil)if err != nil {panic(err)}
}

现在,我们访问http://127.0.0.1:8884,会发现屏幕会每秒输出一条json内容,例如:

{"message":"Golang Websocket Message: 2022-09-05 15:17:22.497926 +0800 CST m=+7.689153001"}

同时,在终端中会每秒输出一条以下内容:

server recv from client: {"message":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}

这时候我们挂上代理http://127.0.0.1:8084/,重启websocket服务器进行访问,然后会发现上述的内容都会发生改变,屏幕输出的内容变为:

Hijack Response

同时,终端输出的内容变为:

server recv from client: Hijack request

直接发起websocket请求

还是使用上述的websocket的测试服务器作为服务端,启动。

yak中编写如下代码,运行:

rsp, req, err = poc.Websocket(`GET /ws HTTP/1.1
Host: 127.0.0.1:8884
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: Upgrade
Sec-WebSocket-Key: LIb4U+i+y+phoP4B2y6uoA==
Sec-WebSocket-Version: 13
Upgrade: websocket`, poc.websocketFromServer(func(data, cancel){dump(data)
}), poc.websocketOnClient(func(wsClient) {go fn {for {wsClient.WriteText(`{"message": "hello"}`)time.Sleep(1)}}
}))
die(err)

解释一下上述代码,poc.Websocket指定了这个请求需要去对websocket请求进行收发处理,其实际上是poc.Http(,poc.websocket(true))的简写。第一个参数是我们熟悉的websocket升级请求,后面跟着的是可选参数函数:

  1. poc.websocketFromServer,这个函数接受一个函数作为参数,其中data为从服务端接收到的数据,cancel是一个无参数函数,用于直接中断websocket连接。

  2. poc.websocketOnClient,这个函数接受一个函数作为参数,其中wsClient是一个结构体,可以直接使用其的一些方法,如:

1. `c.Stop()`,结束websocket连接2. `c.Write([]byte)`,往websocket写入内容3. `c.WriteText([]byte)`,同`c.Write([]byte)`4. ...

通过程序输出可以看到我们正常建立了websocket连接并完成了收发。

新版yakit的websocket劫持尝鲜

Yak版本 1.1.2

Yakit版本 1.1.2

websocket劫持

正常启动yakit的MITM,然后也启动上文提到的websocket服务器:

挂载代理访问http://127.0.0.1:8884/,出现websocket升级的请求,手动放行:

等待websocket协议升级完成后,我们成功劫持到了websocket的请求,按下劫持响应并修改请求内容,最后按下提交数据:

可以看到服务器已经接收到修改过后的请求:

同时我们拦截到了服务器的响应,修改响应内容然后按下提交数据:‘

发现浏览器中显示我们修改过后的响应:

websocket fuzzer

在MITM中的HTTP History找到websocket的升级响应,按下FUZZ按钮:

跳转到websocket fuzzer页面,我们尝试建立连接:

建立websocket连接完成后可以在右侧看到实时的服务器请求与响应:

我们尝试在下方发送数据框发送websocket请求:

可以看到成功发送websocket请求:

websocket fuzzer

在MITM中的HTTP History找到websocket的升级响应,按下FUZZ按钮:

[外链图片转存中…(img-WKp0XoFM-1676620638602)]

跳转到websocket fuzzer页面,我们尝试建立连接:

[外链图片转存中…(img-AfHcTU1R-1676620638602)]

建立websocket连接完成后可以在右侧看到实时的服务器请求与响应:

[外链图片转存中…(img-hTxQJ96B-1676620638603)]

我们尝试在下方发送数据框发送websocket请求:

[外链图片转存中…(img-xogbIIIJ-1676620638603)]

可以看到成功发送websocket请求:

[外链图片转存中…(img-Ky0DLWRZ-1676620638603)]

[外链图片转存中…(img-glbr0Rp5-1676620638603)]

##最后
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

相关文章:

Yaklang websocket劫持教程

背景 随着Web应用的发展与动态网页的普及&#xff0c;越来越多的场景需要数据动态刷新功能。在早期时&#xff0c;我们通常使用轮询的方式(即客户端每隔一段时间询问一次服务器)来实现&#xff0c;但是这种实现方式缺点很明显: 大量请求实际上是无效的&#xff0c;这导致了大量…...

基于AIOT技术的智慧校园空调集中管控系统设计与实现

毕业论文&#xff08;设计&#xff09;题 目 基于AIOT技术的智慧校园空调集中管控系统设计与实现指导老师 XXXX 专业班级 电子商务2XXXX 姓 名 XXXX 学 号 20XXXXXXXXX 20XX年XX月XX日摘要近年来&#xff0c;随着物联网技术和人工智能技术的快速发展&#xff0c;智慧校园逐渐…...

【每日一题】 将一句话单词倒置,标点不倒置

用C语言将一句话的单词倒置&#xff0c;标点不倒置。 比如输入&#xff1a; i like shanghai. 输出得到&#xff1a; shanghai. like i 这道题目有很多种做法&#xff0c;既可以用递归&#xff0c;也可以分成两部分函数来写&#xff0c;本文就详细来讲解分装为两个函数的做法。…...

宽刈幅干涉雷达高度计SWOT(Surface Water and Ocean Topography)卫星进展(待完善)

> 以下信息搬运自SWOT官方网站等部分文献资料&#xff0c;如有侵权请联系&#xff1a;sunmingzhismz163.com > 排版、参考文献、部分章节待完善 > 2023.02.17.22:00 初稿概况 2022年12月16日地表水与海洋地形卫星SWOT (Surface Water and Ocean Topography)在加利福尼…...

openjdk源码==类加载过程

jdk\src\share\bin\main.c main JLI_Launch jdk\src\share\bin\java.c JLI_Launch jdk\src\solaris\bin\java_md_solinux.c JVMInit ContinueInNewThread JavaMain InitializeJVM jdk\src\share\bin\java.h CreateJavaVM 调用JNI hotspot\src\share\vm\prims\j…...

vue2的后台管理系统 迁移到 vue3后台管理系统

重构的流程1.新建项目,确定脚手架版本2.项目整体迁移3.重构路由,axios,element-plus等项目所需要的依赖4.迁移组件内容(需要的配置项移步到5目录and6目录)4-1.Login页面4-2. Home页4-3.Students管理内部的页面4-3-1.studentList(学生列表)4-3-2.InfoList(信息列表)4-3-3.InfoLi…...

2023年美赛F题

关键点1.绿色GDP(GGDP)是否比传统GDP更好好的衡量标准?2.如果GGDP成为经济健康的主要量标准&#xff0c;可能会对环境产生什么影响?3建立一个简单的模型&#xff0c;估计GGDP取代GDP作为经济健康的主要衡量标准&#xff0c;对减缓气候变化产生的影响。4.GGDP取代GDP可能会遇到…...

【数据结构与算法分析】介绍蛮力法以及相关程序案例

文章目录蛮力法之排序选择排序冒泡排序实际应用蛮力法之最近对和凸包问题最近对问题凸包问题蛮力法(brute force)&#xff0c;其本质跟咱常说的暴力法是一样的&#xff0c;都是一种简单直接地解决问题的方法&#xff0c;通常直接基于问题的描述和所涉及的概念定义进行求解。 蛮…...

用股票交易量查询接口是怎么查询a股全天总成交量的?

用股票交易量查询接口是怎么查询a股全天总成交量的&#xff1f;今天下班就以通达信给大家讲解一下&#xff0c;通常是在K线图的底部状态栏&#xff0c;可以在日线进行查看a股成交量。在市场栏底部的子图中。 有当天成交的数量。成交量是表示一定的时间内已经成交的中的成交数量…...

求职季哪种 Python 程序员能拿高薪?

本文以Python爬虫、数据分析、后端、数据挖掘、全栈开发、运维开发、高级开发工程师、大数据、机器学习、架构师这10个岗位&#xff0c;从拉勾网上爬取了相应的职位信息和任职要求&#xff0c;并通过数据分析可视化&#xff0c;直观地展示了这10个职位的平均薪资和学历、工作经…...

如何选择好的IB课程学校?

在上海除了拼中考&#xff0c;你还可以走一条更有“选择权”的路——国际化学校&#xff01; 然而选择学校时&#xff0c;让家长最头痛的事情&#xff0c;莫过于为孩子选择什么样的国际化课程。 今天我们来聊聊IB课程&#xff01; 三大主流国际课程中&#xff0c;被公认含金量最…...

2023美赛ABCDEF题思路+参考文献+代码

选题建议、ABCDEF题参考文献、ABCDEF题思路&#xff08;后续更新视频和代码&#xff09;、D题数据、数据集及处理方式已更新&#xff0c;其他日内更新。下文包含&#xff1a;2023年美国大学生数学建模竞赛&#xff08;以下简称美赛&#xff09;A - F题思路解析、选题建议、代码…...

DataEase 制作数据可视化大屏经验分享

前言 DataEase 简介 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务的改进与优化。DataEase 支持丰富的数据源连接&#xff0c;能够通过拖拉拽方式快速制作图表&#xff0c;并可以方便地与他人分享。 更多详细介…...

前端基础-2day

前端基础 这里写目录标题前端基础div和span标签div 标签span标签列表有序列表无序列表自定义列表图片超链接标签表格 table表格合并表单标签表单控键属性div和span标签 div 标签 没有具体的含义&#xff0c;用于划分页面区域&#xff0c;独占一行 快捷键&#xff1a;div{}*3 …...

在线一键JS混淆还原

当今&#xff0c;随着互联网的发展&#xff0c;越来越多的网站开始使用JavaScript来实现动态交互和用户体验。但是&#xff0c;由于JavaScript代码的开放性和易于复制&#xff0c;网站管理员需要采取一些措施来保护他们的代码。这就是JavaScript混淆工具产生的原因。 jsjiami.…...

Java基本语法

目录 一、注释方式 1、单行注释 // 2、多行注释 /*...*/ 3、文档注释 /**....*/ 二、标识符和关键字 三、数据类型 拓展及面试题讲解 1、整数拓展 进制 二进制0b 八进制0 十六进制0x 2、字符拓展 编码Unicode表 2字节 0~65536 3、字符串拓展 4、布尔值拓展 一、注释方式…...

什么表单设计工具能快速提升办公效率?

在信息化快速发展的年代&#xff0c;谁能掌握更先进的技术&#xff0c;谁就能拥有更广阔的发展前景。在以前的办公环境中&#xff0c;传统的表单制作工具占据了主流地位&#xff0c;随着办公自动化的快速发展&#xff0c;传统表单工具的弊端也暴露出来了&#xff0c;采用更先进…...

SystemVerilog——Axi4Lite_To_Localbus

摘要&#xff1a;用SystemVerilog对Axi4转localbus进行编写与仿真 如果需要从PS端对PL进行寄存器的读写操作&#xff0c;从znyq M_AXI_HPM_FPD出来&#xff0c;经过axi_interconnect 模块分出多个通道&#xff08;不同的地址&#xff09;&#xff0c;经过一个axi_slave模块&am…...

硬件_IMX6ULL的LCD控制器

硬件_IMX6ULL的LCD控制器 文章目录硬件_IMX6ULL的LCD控制器一、 LCD控制器模块介绍1.1 硬件框图1.2 数据传输与处理1.3 时序控制二、 LCD控制器寄存器简介2.1 LCDIF_CTRL寄存器2.2 LCDIF_CTRL1寄存器2.3 LCDIF_TRANSFER_COUNT寄存器2.4 LCDIF_VDCTRL0寄存器2.5 LCDIF_VDCTRL1寄…...

ICLR 2022—你不应该错过的 10 篇论文(下)

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 ICLR 2023已经放榜&#xff0c;但是今天我们先来回顾一下去年的ICLR 2022&#xff01; ICLR 2022将于2022年 4 月 25 日星期一至 4 月 29 日星期五在线举行&#xff08;连续第三年&#xff01;&#x…...

国内外优秀程序员的私域博客大全

文章目录 国内外优秀程序员的私域博客大全**国内的优秀程序员****国外的优秀程序员**结语国内外优秀程序员的私域博客大全 国内的优秀程序员 1、风雪之隅-惠新宸 擅长领域:PHP、PECL等 Laruance惠新宸——国内最有影响力的PHP技术专家,PHP开发组核心成员, Zend顾问, PHP7及…...

【C++ Primer Plus】第六章:分支语句和逻辑运算符

文章目录第六章 分支语句和逻辑运算符6.1 字符函数库cctype6.2 ?:运算符6.3 读取数字的输入6.4 cin的处理过程char类型intdoublechar数组使用char数组来存储输入6.5 写入到文本文件中6.6 读取文本文件6.7 总结第六章 分支语句和逻辑运算符 6.1 字符函数库cctype C从C语言继承…...

堡垒机的主要功能是什么?为什么需要堡垒机?

堡垒机是一种用于管理和控制服务器的工具&#xff0c;其主要功能是为管理人员提供安全、便捷的远程管理和操作方式。为什么需要堡垒机呢&#xff1f;下面我们将详细阐述堡垒机的主要功能和必要性。 一、堡垒机的主要功能&#xff1a; ①、用户认证和授权管理&#xff1a;堡垒机…...

记录spring中Transactional事务注解失效的六个场景

记录spring中Transactional事务注解失效的六个场景 方法内的自调用 原因&#xff1a;通过this内部调用其他带有Transactional注解的方法&#xff0c;是通过this进行调用&#xff0c;并没有通过cglib代理对象进行调用&#xff0c;导致方法未被增强导致无法检测内部事务 解决方…...

【23种设计模式】行为型模式详细介绍(下)

前言 本文为 【23种设计模式】行为型模式 相关内容介绍&#xff0c;下边将对访问者模式&#xff0c;模板模式&#xff0c;策略模式&#xff0c;状态模式&#xff0c;观察者模式&#xff0c;备忘录模式&#xff0c;中介者模式&#xff0c;迭代器模式&#xff0c;解释器模式&…...

dbeaver工具连接达梦数据库

、一 概述 DBeaver 是一个基于 Java 开发&#xff0c;免费开源的通用数据库管理和开发&#xff0c;DBeaver 采用 Eclipse 框架开发&#xff0c;支持插件扩展&#xff0c;并且提供了许多数据库管理工具&#xff1a;ER 图、数据导入/导出、数据库比较、模拟数据生成等&#xff0…...

比Teambition、Worktile 更适合研发团队的几大工具盘点

Worktile 和 Teambitiom 哪个更好&#xff1f;两个产品各有特点。1.Teambition 优势&#xff1a;操作简单、个人版永不收费、更适合小型团队&#xff1b;2.Teambition 劣势&#xff1a;无法满足中大型团队复杂的项目管理、自定义能力弱、无法与钉钉以外的工具打通等&#xff1b…...

matlab图像处理常用功能以及函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、matlab灰度处理相关二、形态学的一些函数1.腐蚀2.膨胀3.开运算4.闭运算三、其他一些可能会用到的方法1.使用hough进行直线检测2.圆检测3.闭合形状检测4.寻找…...

eBPF 之 ProgramType、AttachType和InputContext

1. ProgramType 定义定义在 include/uapi/linux/bpf.h 文件中&#xff0c;不同 Linux 版本会有变化&#xff0c;以下是 Linux 5.19 版本定义&#xff1a;enum bpf_prog_type {BPF_PROG_TYPE_UNSPEC,BPF_PROG_TYPE_SOCKET_FILTER,BPF_PROG_TYPE_KPROBE,BPF_PROG_TYPE_SCHED_CLS,…...

C++运行时类型识别RTTI

C技能 runtime type identification(RTTI) 运行时类型识别在使用多态的时候经常用到。本文将会介绍RTTI的几个特征。1. 运行时类型转换下面的程序模仿了dynamic_cast<type_id>()类型转化符号&#xff0c;根据每个类的id来判断当前的类型&#xff0c;如果id不匹配&#xf…...

ubuntu系统做网站/seo网站关键词

题目描述 一些数字可能拥有以下的性质&#xff1a; 性质 1&#xff1a;是偶数&#xff1b; 性质 2&#xff1a;大于 4 且不大于 12。 小A 喜欢这两个性质同时成立的数字&#xff1b;Uim 喜欢这至少符合其中一种性质的数字&#xff1b;八尾勇喜欢刚好有符合其中一个性质的数字…...

网站建设企业推荐/免费新闻源发布平台

python可视化#导入两个库import numpy as npimport matplotlib.pyplot as plt#第一个参数就是x轴的初始值#第二个参数是x轴的终止值#第三个返回num均匀分布的样本&#xff0c;也就是0-12的区间取多少个点&#xff0c;如果为曲线的最好数值大一点x np.linspace(0, 12, 50)y np…...

青岛 生物类网站建设/如何成为百度广告代理商

什么是千人千面呢&#xff0c;对于千人千面&#xff0c;官方给的解释为&#xff1a;“定向推广依靠淘宝网庞大的数据库&#xff0c;构建出买家的兴趣模型。它能从细分类目中抓取那些特征与买家兴趣点匹配的推广宝贝&#xff0c;为展现在目标客户浏览的网页上&#xff0c;帮助您…...

关于配色的网站/广州推广工具

会发现这里这两个方法无法重载 因为在 public Integer method(List stringList)&#xff1b;和 public String method(List integers)&#xff1b;中 参数最终都是在编译时候擦除类型 变成List 这样就无法重载了&#xff0c;重载必须方法签名不同...

wordpress 文章编辑 插件/互联网推广广告

Akka 和 Storm 的设计差异 Akka 和 Storm 都是实现低延时, 高吞吐量计算的重要工具. 不过它们并非完全的竞品,如果说 Akka 是 linux 内核的话, storm 更像是类似 Ubuntu 的发行版.然而 Storm并非 Akka 的发行版, 或许说 Akka 比作 BSD, Storm 比作 Ubuntu 更合适. 实现的功能差…...

正规做兼职的网站/免费的推文制作网站

数组的实例上都有一个叫做 forEach 的方法&#xff0c;这个方法定义在 Array.prototype 上&#xff0c;所以数组的所有实例都可以使用 forEach 这个方法。 forEach 方法的语法结构如下&#xff1a;   1 var ary [1, 2, 3, 4, 5, 5, 6, 8, 9]; 2 ary.forEach(function(index,…...