workminer之dht通信部分

workminer是通过SSH爆破传播的挖矿木马,感染后会释放xmrig挖矿程序利用主机的CPU挖取北方门罗币。该样本能够执行特定的指令,指令保存在一个配置文件config中,config文件类似于xml文件,里面有要执行的指令和参数,样本中内嵌了一个config,无攻击指令。该样本通过使用自己扩展的DHT协议来更新config,有两种方式,一是访问8个引导节点(样本内嵌,公开的)加入p2p网络,通过来寻找具有特定前缀id的同伙,来更新config,二是会随机扫描其它IP的UDP端口(IP是随机生成的,端口是一个范围内的随机端口),若扫中其它受害主机,受害主机会返回config,用这种方式来更新config。
workminer属于Mozi僵尸网络组织,主要攻击Iot设备,作者已于2021年被中国警方逮捕,该僵尸网络目前在互联网上还持续活跃。本文主要介绍workminer的dht通信部分,workminer使用go和C语言混合编译,C语言主要用于与同伙节点通信,拉取和更新config。

基础知识一:Linux系统调用

为了识别样本中的socket相关函数,我们需要了解Linux系统调用,尤其是socket相关函数调用的基础知识。
linux系统中使用int 80来调用linux系统api,linux中调用socket相关函数,是通用系统调用sys_socketcall的实现的,该接口的系统调用号为102,函数定义为

#include <sys/socket.h>
int socketcall(int subcall, unsigned long *args);
调用的时候,寄存器eax保存调用号,ebx为第1个参数,ecx为第2个参数,通过eax和ebx就能知道是哪个api
ebx int  subcall
ecx unsigned long *
eax 102
int 0x80

第一个参数subcall要调用的api号,在 /usr/include/linux/net.h中定义,使用find命令找到这个文件

# cat /usr/include/linux/net.h | grep SYS                     
#define SYS_SOCKET      1               /* sys_socket(2)                */
#define SYS_BIND        2               /* sys_bind(2)                  */
#define SYS_CONNECT     3               /* sys_connect(2)               */
#define SYS_LISTEN      4               /* sys_listen(2)                */
#define SYS_ACCEPT      5               /* sys_accept(2)                */
#define SYS_GETSOCKNAME 6               /* sys_getsockname(2)           */
#define SYS_GETPEERNAME 7               /* sys_getpeername(2)           */
#define SYS_SOCKETPAIR  8               /* sys_socketpair(2)            */
#define SYS_SEND        9               /* sys_send(2)                  */
#define SYS_RECV        10              /* sys_recv(2)                  */
#define SYS_SENDTO      11              /* sys_sendto(2)                */
#define SYS_RECVFROM    12              /* sys_recvfrom(2)              */
#define SYS_SHUTDOWN    13              /* sys_shutdown(2)              */
#define SYS_SETSOCKOPT  14              /* sys_setsockopt(2)            */
#define SYS_GETSOCKOPT  15              /* sys_getsockopt(2)            */
#define SYS_SENDMSG     16              /* sys_sendmsg(2)               */
#define SYS_RECVMSG     17              /* sys_recvmsg(2)               */
#define SYS_ACCEPT4     18              /* sys_accept4(2)               */
#define SYS_RECVMMSG    19              /* sys_recvmmsg(2)              */
#define SYS_SENDMMSG    20              /* sys_sendmmsg(2)              */

DHT协议基础

DHT协议是一种BT协议,使用udp来通信,通信内容使用bencode编码。可参考bt协议详解 DHT篇(下) - 蓝猫163 - 博客园 (cnblogs.com)。

DHT协议有以下几种报文

ping

最基础的请求就是ping。这时KPRC协议中的“q”=“ping”。Ping请求包含一个参数id,它是一个20字节的字符串包含了发送者网络字节序的nodeID。对应的ping回复也包含一个参数id,包含了回复者的nodeID。
示例

ping请求={"t":"aa", "y":"q","q":"ping", "a":{"id":"abcdefghij0123456789"}}  B编码=d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe 
回复={"t":"aa", "y":"r", "r":{"id":"mnopqrstuvwxyz123456"}} 
B编码=d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re

find_node

Findnode被用来查找给定ID的node的联系信息。这时KPRC协议中的q=“find_node”。find_node请求包含2个参数,第一个参数是id,包含了请求node的nodeID。第二个参数是target,包含了请求者正在查找的node的nodeID。当一个node接收到了find_node的请求,他应该给出对应的回复,回复中包含2个关键字id和nodes,nodes是一个字符串类型,包含了被请求节点的路由表中最接近目标node的K(8)个最接近的nodes的联系信息
示例

find_node请求={"t":"aa", "y":"q","q":"find_node", "a":{"id":"abcdefghij0123456789","target":"mnopqrstuvwxyz123456"}}
B编码=d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe
回复={"t":"aa", "y":"r", "r":{"id":"0123456789abcdefghij", "nodes":"def456..."}}
B编码=d1:rd2:id20:0123456789abcdefghij5:nodes9:def456...e1:t2:aa1:y1:re

get_peers

这个请求用来表明发出announce_peer请求的node,正在某个端口下载torrent文件。announce_peer包含4个参数。第一个参数是id,包含了请求node的nodeID;第二个参数是info_hash,包含了torrent文件的infohash;第三个参数是port包含了整型的端口号,表明peer在哪个端口下载;第四个参数数是token,这是在之前的get_peers请求中收到的回复中包含的。收到announce_peer请求的node必须检查这个token与之前我们回复给这个节点get_peers的token是否相同。如果相同,那么被请求的节点将记录发送announce_peer节点的IP和请求中包含的port端口号在peer联系信息中对应的infohash下。
这个请求报文本样本没有
其响应报文的处理同find_node报文

announce_peer

这个请求用来表明发出announce_peer请求的node,正在某个端口下载torrent文件。announce_peer包含4个参数。第一个参数是id,包含了请求node的nodeID;第二个参数是info_hash,包含了torrent文件的infohash;第三个参数是port包含了整型的端口号,表明peer在哪个端口下载;第四个参数数是token,这是在之前的get_peers请求中收到的回复中包含的。收到announce_peer请求的node必须检查这个token与之前我们回复给这个节点get_peers的token是否相同。如果相同,那么被请求的节点将记录发送announce_peer节点的IP和请求中包含的port端口号在peer联系信息中对应的infohash下。
这个请求报文本样本没有
不支持这个报文

workminer的DHT通信部分

workminer通过自己改良过的DHT协议来请求和config,下面来介绍workminer的dht通信过程。
首先会生成一个特殊的version,则workminer节点发出的dht报文中都会有这个version字段。version字段共9个字节,前5个字节为1:v43:(31 3A 76 34 3A),后面4个字节中根据下面的规则生成的。
第1个字节是随机产生的,第2个字节是硬编码的0x42或由config文件中[ver]字段指定,第3个字节和第4个字节是通过自定义的校验算法生成的。

下面是抓取的ping包,通过这个特殊的标记,wokerminer在处理接收到的包时就能够识别出哪些包是正常的dht包,哪些包中由同伙发出来的,对于正常的dht协议的请求和响应包,走正常的处理流程,对于同伙的包(ping和find_node),就会交换config。

上面这个计算校验和的算法是识别同伙的关键,如下代码,若结果为0表示是同伙的包,若不是0则是正常的dht数据包。

// 根据version字段来判断是同伙发来的包
int  GenCheckSum_82AAB68(unsigned __int16 a1, char *buf_a2, unsigned int buflen_a3)
{char j; // [esp+5h] [ebp-Bh]unsigned int i; // [esp+Ch] [ebp-4h]for ( i = 0; i < buflen_a3; ++i ){a1 ^= (unsigned __int8)buf_a2[i] << 8;for ( j = 8; j; --j ){if ( (a1 & 0x8000) != 0 )a1 = (2 * a1) ^ 0x8005;elsea1 *= 2;}}return a1;}
int main(){char version[] = { 0x31,0x3a ,0x76 ,0x34 ,0x3a ,0x26 ,0x42 ,0x53 ,0x77};int checksum = GenCheckSum_82AAB68(0,version,9);if(checksum == 0){puts("Mozi pkt");}else{puts("Normal dht pkt");}return 0;
}

从下列中随机选择一个端口,若端口为0,则取random()%64510+1024,监听这个udp端口。

port_list[28] = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,8080,8000,1900,1434,1027,6881,4000
30301,5353,11211,8082,8081,8083,5060]

为这UDP端口添加iptables规则。

使用异或的方式解密了两个公钥,异或使用密钥为
4E 66 5A 8F 80 C8 AC 23 8D AC 47 06 D5 4F 6F 7E
将内嵌在样本的config提取出来,共624字节,前524字节为加密的config内容,后96字节为文件的签名。使用第1个公钥来对加密的config验证签名。若验证签名通过,则使用go语言来解析解密后的config,执行config中的指令。若config中含有[ver]标签,使用ver标签的值来重新计算数据包version字段。
公钥为

第一个公钥 用于验证config前528字节的完整性
02 d5 d5 e7 41 ee dc c8 10 6d 2f 48 0d 04 12 21 
27 39 c7 45 0d 2a d1 40 72 01 d1 8b cd c4 16 65 
76 57 c1 9d e9 bb 05 0d 3b cf 6e 70 79 60 f1 ea 
ef第二个公钥 用于验证config前428字节的完整性
02 c0 a1 43 78 53 be 3c c4 c8 0a 29 e9 58 bf c6
a7 1b 7e ab 72 15 1d 64 64 98 95 c4 6a 48 c3 2d
6c 39 82 1d 7e 25 f3 80 44 f7 2d 10 6b cb 2f 09 
c6解密后的config
[ss]ssh[/ss][cpu].x[/cpu][hp]88888888[/hp]

构造自身nodeid,nodeid有20个字节,有以下几种可能
一 1/1000的可能 使用字符串888888d1:ad2:id20:作为前缀,即38 38 38 38 38 38 64 31 3A 61 64 32 3A 69 64 32 30 3A xx xx,后两个字节为随机值。
二 49/1000的可能,使用888888作为前缀,即38 38 38 38 38 38 xx xx xx xx xx xx xx xx xx xx xx xx xx xx,后面12个字节为随机值
三 950/1000的可能,使用随机生成的nodeid.

接着使用上面的构造的selfNodeId,向内置的8个引导节点发送ping请求。TransactionId使用6E 70 00 00

router.bittorrent.com:6881 
bttracker.debian.org:6881
router.utorrent.com:6881
dht.transmissionbt.com:6881
212.129.33.59:6881
82.221.103.244:6881
130.239.18.159:6881
87.98.162.88:6881


使用wireshark抓包可以看到包的内容

这个包的含义如下,

请求参数(request arguments)
a:{id:38383838383864313a6164323a696432303a9550}
请求类型(request type)
q:ping
会话Id(Transaction Id)
t:706e0000
版本(Version)
v:1942d177
消息类型(Message Type)q表示请求
y:q

设置一个全局的队列,用来存储的节点信息,这个队列结构如下所示。

struct NODE
{char node_id[20];//节点的node_idchar addr[128];//地址信息 可存储Ipv4和ipv6的sockaddr结构int addrlen;//sockaddr结构的长度
};
struct NODE_LIST
{int head;//队列头int end;//队列尾NODE nodelist[64];//队列,队列大小为64
};

下面进入一个死循环,不断的向外发包和处理响应包。
每隔899秒,向内嵌的8个公共节点发送ping请求。
从config中取出[nd]标签的值,这是一个以,分割的列表,如url1,ip,url2,...,向其中的每个地址发送ping请求,使用端口6881,会话id使用6E 70 00 00。(内嵌的config中没有nd标签,不会执行)

每隔299秒,若node_list不为空的话,从其中随机取一个node,发送find_node请求。
find_node消息中的target_node_id使用以下的规则构造,有4种可能。
一是 targetid完全使用随机值,即20个随机的字节
二是 将 123888d1:ad2:id20: 作为target_nodeid的前18个字节,后2个字节随机
三是 以888888作为target_nodeid的前6个字节,后14个字节随机
四是 以888888d1:ad2:id20:作为target_nodeid的前18个字节,后2个字节随机
会话id为6E 66 00 00

构造一段随机长度的buf,长度为从[3,97]中取一个随机数,将第二个字节为0x2e,使用前面的算法计算校验值,将2个字节的校验值添加到buf末尾。

['8080','8000', '1900', '1434', '1027', '6881', '4000', '30301', '5353', '11211', '8082', '8081', '8083', '5060']中随机取一个端口,构造一个随机的IP地址,将上面构造好的数据发出去。执行100次。
当同伙收到这个报文后,会将加密的config发回来,这里将这个报文定义为GET_CONF报文。

每隔599秒,向node_list中所有节点发送一次find_node请求,target_nodeid有两种情况,会话id为6E660000.
一是 以888888d1:ad2:id20:为前缀,后面为随机值
二是 以888888前缀后面为随机值

下面是处理接收到的数据包的逻辑
首先使用select和recvfrom接收udp的包,将dht相关的包中关键字段解析出来,如下图所示。对dht数据包进行解析,对其中的关键的字段提取,并返回数据包的类型,根据这个类型值进入不同的处理逻辑,提取出来的字段dht字段有

version: 可判断是否为同伙的包
transctionid :可判断ping和find_node的响应
nodeid :对方的nodeid
targetid: find_node请求的目标id
nodes :find_node响应的nodes列表


数据的类型有以下几种

enum DHTPktType : int
{DHTPktType_OTHER = -1,//其它DHTPktType_ERROR = 0,//报错信息DHTPktType_RESPONSE,//响应包 包括ping、find_node的响应DHTPktType_PING,// ping请求DHTPktType_FIND_NODE,// find请求DHTPktType_GET_PEERS,//get_peers请求DHTPktType_ANNOUNCE_PEER,//annouce_peer 请求DHTPktType_MOZI_CNF_REQ,//get_conf请求 即上面的GET_CONF报文DHTPktType_MOZI_ENCODING_CNF //接收到对方发来加密的config
};

下面介绍这几种报文的处理逻辑

ping、find_node的响应

若会话id为6E 70 00 00,说明是本机发出去的ping请求的响应包,将对方的nodeid、ip、port添加进入全局队列。
若会话id为6E 66 00 00,说明是本机发出来的find_node请求的响应,将对方的信息加入队列,检查version字段。若不是Mozi节点发来的包,将其中的nodes字段中8个节点加入队列。若是Mozi节点的包,且nodes大小为624,这其实是加密的config,处理这个config。

ping请求

若收到别的节点发来的ping请求,将对方信息加入队列,进行正常的回复。

find_node、get_peers的请求

这两类请求的处理逻辑相同。若是非同伙的包,按照正常的dht协议来处理,从队列中取8个节点信息发给对方。
若是同伙发来的请求,有4/5的概率,会将自身加密的config填充进nodes字段发送给对方。

announce_peer请求

这类请求不支撑,发送一个error信息。

GET_CONF请求

这不是DHT请求,就是前面随机构造的数据包,数据包长度<=99字节,最后两个字段是校验值,当节点收到此请求,会将自身的config发送给对方,大小为624字节。

收到对方的config

若不是DHT报文,且大于99字节,这是别的节点发来的加密的config,处理这个config.

config的处理

对同伙获取的config大小为624字段,其中前528字段是config1,后面96字段是签名信息,使用第1个公钥验证其完整性,
对于config1,取其前428为config2,428到524的96字段为签名信息,使用第2个公钥签证其完整性。
然后对config2进行解密,向对方节点发送一个ping请求,将config文件中的[cpu]、[ss]、[sv]三个标签的值,提取hp字段,这是的nodeid的前缀,提取ver字段,重新计算verison。

cpu 不知道含义
ss 不知道含义
sv 不知道含义
hp nodeid的前缀
ver 这是一个byte值,作为version字段的第二个字节

最后调用go函数main_deal_conf来处理config,提取出下列标签,进而执行不同的操作。

slan 这是个开关变量,作用未知
swan 这是个开关变量,未知
spl 这是一个url,用于下载更新的bash脚本
sdf 格式为"url|filename",会从url中下载文件保存为/tmp/+filename
sud 其中含有一个url,用于更新本地的病毒母体
ssh 其中含有一个url,用于下载bash脚本来执行
sdr 用于下载文件执行
srn 用于执行命令
scount 指向用于回连的url,默认为`http://ia.51.la/go1`,受害者会周期性的访问这个地址,用于便于攻击者知道受害者的信息

IOC

如何快速识别workminer的流量,可以根据下面的特征

正常的dht报文的version字段符合文中的校验算法
nodeid以88888、88888888、888888d1:ad2:id20:开头
会话id中为6E 70 00 00或6E 66 00 00
udp的载荷小于99,符合文中的校验算法
upd的载荷大小为624
有对外随机的udp扫描行为,目标端口为['8080','8000', '1900', '1434', '1027', '6881', '4000', '30301', '5353', '11211', '8082', '8081', '8083', '5060']访问url
http://ia.51.la/go1连接下列地址udp
router.bittorrent.com:6881 
bttracker.debian.org:6881
router.utorrent.com:6881
dht.transmissionbt.com:6881
212.129.33.59:6881
82.221.103.244:6881
130.239.18.159:6881
87.98.162.88:6881访问门罗币矿池
xmr.crypto-pool.fr:6666扫描TCP以下端口
2222
3389
22222
443
55554
9000
2223
9090
8888
8022
6000
9999
2323
2002
7777
2022
666
444
5555
222
26
2382
830
4118
23
50000流量中含有
SSH-2.0-Go

总结

workminer的作者使用了一种变形的dht协议来更新config,确认非常聪明,具有很好的隐蔽性。config 文件中有些标签的含义还是没有分析清楚。

参考资料

  • Linux系统调用 int 80h int 0x80_怎么寻找int80指令-CSDN博客
  • Linux system call table 정리(32bit, 64bit) (tistory.com)
  • 深度追踪Mozi僵尸网络:360安全大脑精准溯源,揪出幕后黑手_360社区
  • BitTorrent 分布式散列表(DHT)协议详解 | 寂静花园 (addesp.com)
  • bt协议详解 DHT篇(下) - 蓝猫163 - 博客园 (cnblogs.com)
  • P2P Botnet: Mozi分析报告 (360.com)
  • P2P僵尸网络深度追踪——Mozi(二)二叉树吃瓜记-安全客 - 安全资讯平台 (anquanke.com)
  • 『P2P僵尸网络漏洞研究——mozi』 netgear路由器漏洞复现-安全客 - 安全资讯平台 (anquanke.com)
  • 【转】ECDSA 签名验证原理及C语言实现_ecdsa 嵌入式c语言-CSDN博客

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

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

相关文章

spring cloud eureka 初始化报错(A bean with that name has already been defined)

报错内容 The bean ‘eurekaRegistration’, defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration E u r e k a C l i e n t C o n f i g u r a t i o n . c l a s s ] , c o u l d n o t b e r e g i s t e r e d . A …

回归预测 | Matlab实现NGO-ESN北方苍鹰算法优化回声状态网络多输入单输出回归预测

回归预测 | Matlab实现NGO-ESN北方苍鹰算法优化回声状态网络多输入单输出回归预测 目录 回归预测 | Matlab实现NGO-ESN北方苍鹰算法优化回声状态网络多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现NGO-ESN北方苍鹰算法优化回声状态网络…

《QT实用小工具·四十五》可以在界面上游泳的小鱼

1、概述 源码放在文章末尾 该项目实现了灵动的小鱼&#xff0c;可以在界面上跟随鼠标点击自由的游泳&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include "magicfish.h" #include <QtMath> #include <QPainter>…

C++:map和set的封装

关于红黑树的模拟实现&#xff0c;大家不清楚的先去看看博主的博客再来看这篇文章&#xff0c;因为set和map的封装底层都是利用用的红黑树。所以这里不会过多介绍红黑树的相关内容&#xff0c;而更多的是去为了契合STL中的红黑树去进行改造&#xff0c;让封装的set和map能够去复…

CMake使用

一、CMake 是什么 CMake 是一个跨平台的自动化构建系统&#xff0c;它使用配置文件 CMakeLists.txt 来管理软件构建过程。CMake 基于 Makefile 做了二次开发。 二、单个文件目录 # CMake 最低版本号要求 cmake_minimum_required(VERSION 3.16.3)# 工程名 project(CMakeSingle)…

uniapp自定义返回事件(封装)

uniapp自定义返回事件 在我们使用uniapp时&#xff0c;我们导航栏一般都是自定义的&#xff0c;比如用uview框架的导航栏&#xff0c;那么返回事件通常会遇到以下几个问题 返回事件前需要做一些额外的处理 h5项目刷新页面后返回失效 返回按钮点击后到指定页面 如果只是监听返…

PhotosCollage for Mac:优雅且实用的照片拼贴软件

PhotosCollage for Mac是一款优雅且实用的照片拼贴软件&#xff0c;为Mac用户提供了一个便捷、高效的平台&#xff0c;以创建精美、个性化的照片拼贴作品。 PhotosCollage for Mac v1.4.1激活版下载 该软件界面简洁直观&#xff0c;操作便捷。用户只需将想要拼贴的照片拖入“照…

CSS基础:position定位的5个类型详解!

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

Andorid复习

组件 TextView 阴影 android:shadowColor"color/red" 阴影颜色android:shadowRadius"3.0" 阴影模糊度&#xff08;大小&#xff09;android:shadowDx"10.0" 横向偏移android:shadowDy"10.0" 跑马灯 这里用自定义控件 public cla…

日本极致产品力 | 源自内蒙古,日本99.7%的人都喝过都百年畅销饮料

​《极致产品力》日本深度研学是一个顾问式课程,可以帮助企业找产品、找方向、找方法,在日本终端市场考察中洞悉热销产品背后的成功逻辑,了解最新最前沿的产品趋势和机会。结合日本消费趋势中国转化的众多经验,从品牌、包装、卖点、技术和生产工艺等多方面寻找中国市场的解决方…

WIFI/BT中蓝牙的硬件资源是如何调度的 UART和PCM接口传输的是什么信号

安卓或IOS手机中&#xff0c;wifi/bt中的蓝牙是如何调度硬件资源的&#xff0c;尤其是UART和PCM是如何分配的。M.2 wifi/bt模块或其他形式的模块中&#xff0c;蓝牙是如何调度硬件资源的&#xff0c;尤其是UART和PCM是如何分配的。今天我们就图文并茂的解决这个问题。 蓝牙文件…

新买的设备自带的仪器校准证书,是否可以作为校准报告使用?

设备在刚刚购买时&#xff0c;有些商家会承诺&#xff0c;在交付设备的同时&#xff0c;还会交付设备的校准证书&#xff0c;这证书是附赠的&#xff0c;属于商家给客户的一种福利&#xff0c;面对附赠的仪器校准证书&#xff0c;很多客户也会有疑惑&#xff0c;这附赠的证书有…

UTONMOS:用区块链技术拓展商业边界在哪里?

引言 大约从 2021 年Web 3 这个新概念开始受到风险基金和科技圈的普遍关注。但如果你对过去几年区块链的发展历史足够了解&#xff0c;就应该已经意识到现在的 Web 3 并不是什么新技术&#xff0c;甚至不是旧技术的进步&#xff0c;它只是一个基于区块链技术的宏大构想。 我是…

Unity 如何制作和发布你的 Package

一、制作你的第一个 Package Unity Package 不做过多赘述&#xff0c;像 URP 本质上也是一个 Package&#xff0c;在 Unity 中可以通过菜单栏 → Window → Package manager 来管理你当前的所有 Package 本篇文章主要介绍&#xff1a;如何制作并发布属于你的 Package 1.1 Pac…

自制贪吃蛇小游戏

此片文章涉及到到控制台设置的相关操作&#xff0c;虚拟键码&#xff0c;宽字符输出等&#xff0c;有些地方大家可能会看不懂&#xff0c;可以阅读以下文章来进一步了解&#xff1a; 控制台程序设置-CSDN博客 效果展示&#xff1a; QQ2024428-181932 源码已放在文章结尾 目录 …

Graph Neural Networks(GNN)学习笔记

本学习笔记的组织结构是&#xff0c;先跟李沐老师学一下&#xff0c;再去kaggle上寻摸一下有没有类似的练习&#xff0c;浅做一下&#xff0c;作为一个了解。 ———————————0428更新—————————————— 课程和博客看到后面准备主要看两个&#xff1a;GCN和…

ubuntu安装Anaconda安装及conda使用

一. 安装anaconda3详细教程 1、下载镜像 清华大学开源软件镜像站下载地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 下拉到最低端选择Linux&#xff0c;选择最新版&#xff08;32/64位&#xff09;下载。这里我下载的是版本Anaconda3-4.3.30-Linux…

Java中的File类

File类概述和构造方法 File&#xff1a;它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言&#xff0c;其封装的并不是一个真正存在的文件&#xff0c;仅仅是一个路径名而已&#xff0c;它可以存在&#xff0c;也可以不存在 我们对Fie的操…

大厂常见算法50题-替换空格

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法一 String类replace方法解法二 遍历替换总结 题目 解法一 String类replace方法 String类自带的replace&#xff0c;方法传入两个char类型的参数&#xff0c;分…

【MySQL 数据宝典】【索引原理】- 004 优化示例-join in exist

一、join 优化原理 1.1 基本连接方式介绍 JOIN 是 MySQL 用来进行联表操作的&#xff0c;用来匹配两个表的数据&#xff0c;筛选并合并出符合我们要求的结果集。 1.2 驱动表的定义 1.2.1 什么是驱动表 多表关联查询时,第一个被处理的表就是驱动表,使用驱动表去关联其他表.驱…