HTTPS流量抓包分析解密(TLS1.2)

本文的https流量分析基于之前自己生成的密钥、证书和搭建的支持https访问的apache服务器点此查看

证书密钥分析

参数解析

查看证书信息:

openssl x509 -in servernew.crt -text -noout

证书信息

回顾前文对RSA算法的简介,公钥是(e,n),分别是ExponentModulus

查看私钥信息:

openssl rsa -in pri_key.pem -text -noout
私钥信息

由前文知私钥是(d,n)。私钥中的信息参考这篇博客:

https://blog.csdn.net/KAlbertLee/article/details/71106528

私钥信息解析
privateExponent私钥的d
prime1n的素数因子p
prime2n的素数因子q
exponent1d mod(p-1)
exponent2d mod(q-1)
coefficientCRT系数q-1 mod p

exponent1exponent2coefficient 是与 CRT(中国剩余定理)相关的参数,用于提高解密操作的效率。具体可以看这篇博客,讲的比较清楚。

https://blog.csdn.net/qq_43589852/article/details/127691919

https://www.di-mgt.com.au/crt_rsa.html

查看公钥信息:

openssl rsa -pubin -in pub_key.pem -text -noout

已经包含在证书信息里了,不再赘述。

脚本验证

可以用python脚本验证信息是否正确。

验证prime1prime2:

modulus="00:c7:a7:0d:02:7a:d0:67:28:90:71:ef:4a:a3:b3:\
    4d:f4:62:36:d5:ee:54:81:83:04:95:3b:fd:72:7d:\
    38:30:ad:a9:d4:43:9b:02:55:52:71:e1:22:fc:8c:\
    22:11:92:b2:d6:31:28:0e:3a:01:34:02:30:e0:76:\
    c9:e5:58:ea:47:97:0a:3e:09:03:c9:65:83:e6:35:\
    50:5b:99:94:a5:98:63:06:77:28:7d:79:a1:77:e5:\
    a7:42:86:fa:18:a9:e8:05:00:bf:33:e5:cd:6f:d1:\
    16:d4:31:29:3e:6d:b9:fc:74:f0:ca:fd:fb:c7:7e:\
    1d:89:13:d1:b9:8a:f3:7d:1f"
prime1="00:f3:e8:76:3f:99:a6:3a:62:c3:4a:b1:17:ec:97:\
    53:6e:a4:fc:70:4e:fb:29:6b:57:99:c3:31:15:bf:\
    55:3e:8f:40:92:96:60:99:d7:50:5d:16:c4:ea:36:\
    60:e9:3b:16:55:de:2f:11:52:4f:fc:e8:94:2b:71:\
    08:86:fd:bc:c9"
prime2="00:d1:8c:ec:83:d0:79:62:13:80:c5:86:10:e9:ca:\
    85:2d:e7:cf:71:ca:8b:a2:49:74:3f:aa:4b:55:0a:\
    50:90:4b:82:fc:21:ad:2a:63:e5:59:1d:ef:f7:2e:\
    64:38:b8:58:44:75:ce:4a:2b:2c:fe:fe:3b:d3:63:\
    bf:33:6b:a6:a7"
modulus=modulus.replace(":","").replace(" ","")
prime1=prime1.replace(":","").replace(" ","")
prime2=prime2.replace(":","").replace(" ","")

modulus_int=int(modulus,16)
prime1_int=int(prime1,16)
prime2_int=int(prime2,16)
print( (prime1_int*prime2_int) == modulus_int)

结果为True。

验证 (e * d) mod φ(n) = 1是否成立。

privateExponent="\
    00:ad:ec:2d:5e:22:a4:d7:a8:b3:a4:3d:23:95:55:\
    86:ac:44:be:a6:40:77:27:57:7e:2f:8e:d1:eb:e1:\
    7f:88:90:50:68:93:f8:3d:e1:1b:f0:0e:83:0e:e3:\
    f8:6d:bc:90:c4:1c:90:5b:4c:56:6d:fb:16:9f:03:\
    7c:3f:a9:e4:73:ab:ee:73:d1:be:d9:f4:46:47:20:\
    f0:41:93:5f:f6:c7:cf:c8:ba:bf:0d:4f:57:e4:4c:\
    c6:2b:79:b5:0e:e9:1a:34:0f:e4:80:68:30:5d:23:\
    bc:26:30:b3:e4:42:89:96:5b:81:cb:75:29:75:38:\
    d7:f9:05:7a:90:20:ed:9d:11"
privateExponent=privateExponent.replace(":","").replace(" ","")
d=int(privateExponent,16)
e=65537
euler_var=(prime1_int-1)*(prime2_int-1)
print( ( ( d*e )%euler_var ) == 1)

结果为True。

wireshark抓包

注意要选取Adapter for loopback traffic capture才能抓取本地地址127.0.0.1的数据包。

wires hark

流量分析

client hello

tcp.port==443来进行过滤,发现一个信息为Client Hello的数据包,它里面含Server Name: localhost等信息,是客户端发起连接的数据包。

server name

该数据包还包含以下信息:

支持的TLS版本:

Version

随机数。

随机数

支持的加密算法。

加密套件列表

server hello

Server Hello 数据包是在 HTTPS 握手过程中服务器发送给客户端的响应数据包。

TLS版本和随机数

TLS 1.2

选择的加密算法

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 是一个加密套件的名称,它由多个部分组成,每个部分代表不同的加密算法和密钥交换算法。ECDHE(Elliptic Curve Diffie-Hellman Ephemeral),椭圆曲线临时Diffie-Hellman密钥交换。它是一种密钥交换协议,用于安全地协商会话密钥。

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 表示使用 ECDHE 密钥交换和 RSA 签名算法进行密钥协商,使用 AES-128-GCM 进行对称加密,以及使用 SHA-256 进行消息摘要和认证。这个加密套件提供了安全的密钥交换和加密算法,以保护通信的机密性和完整性。

椭圆曲线格式

ec_point_formats

ec_point_formats 字段包含了一个或多个椭圆曲线点压缩格式的标识符,每个标识符表示一种点压缩格式。uncompressed表示不使用压缩,即完整表示椭圆曲线上的点坐标。ansiX962_compressed_prime表示使用 ANSI X9.62 标准定义的有限域椭圆曲线上的点压缩格式。ansiX962_compressed_char2表示使用 ANSI X9.62 标准定义的二进制域椭圆曲线上的点压缩格式。

Certificate

Certificate 数据包包含了用于验证证书合法性和建立信任的关键信息。在 HTTPS 连接中,客户端会验证服务器发送的证书,并根据证书的有效性来决定是否信任服务器。证书的签名保证了证书的真实性,而证书中的公钥用于加密和验证数字签名,确保通信的机密性和完整性。

证书有关信息

证书

证书

公钥信息

(e,n))

认证信息

认证

证书用sha256WithRSAEncryption进行数字签名,encrypted就是证书的数字签名。证书可以分为tbsCertificatesignatureAlgorithmsignatureValue三部分,数字签名就是对第一部分进行哈希计算,使用私钥对计算得到的摘要值进行加密操作,生成数字签名。客户端可以使用服务器的公钥对证书的签名进行验证,确保证书的完整性和真实性。

下面用python来演示这一过程:

from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
with open('pri_key.pem', 'r') as f:
    private_key = RSA.import_key(f.read())
#signedCertificate部分
cer="308201c502142f5eccde876513440a6032e091ca302202c79db0300d06092a864886f70d01010b0500306d310b300906035504061302434e3110300e06035504080c074265694a696e673110300e06035504070c076265696a696e6731123010060355040a0c096c6f63616c686f737431123010060355040b0c096c6f63616c686f73743112301006035504030c096c6f63616c686f7374301e170d3233303532383133333235385a170d3234303532373133333235385a306d310b300906035504061302434e3110300e06035504080c074265694a696e673110300e06035504070c076265696a696e6731123010060355040a0c096c6f63616c686f737431123010060355040b0c096c6f63616c686f73743112301006035504030c096c6f63616c686f737430819f300d06092a864886f70d010101050003818d0030818902818100c7a70d027ad067289071ef4aa3b34df46236d5ee54818304953bfd727d3830ada9d4439b02555271e122fc8c221192b2d631280e3a01340230e076c9e558ea47970a3e0903c96583e635505b9994a598630677287d79a177e5a74286fa18a9e80500bf33e5cd6fd116d431293e6db9fc74f0cafdfbc77e1d8913d1b98af37d1f0203010001"
#encrypted部分
s="78475c466dde9daa15388bcb87eb43ec0d220abf2e4fcad9e5041c3889711e177bbf352ee8cd18ffd3ab3704b9994a7483a4fe23ab4bbcb746ff9e718e6daac0bd5a698559bf12daec8b36f63bdb605d7467184ee27801241a6dacfa9e24667844d0e9c4b52926dc37ec8fc1ce569bf472b2c197f8582aca21d16426ee10d505"
s=bytes.fromhex(s)
cer=SHA256.new(bytes.fromhex(cer))
pkcs1_15.new(private_key.publickey()).verify(cer,s)

verify无返回值,如果验证失败会抛出异常。此处为了方便读取私钥文件再生成公钥(复制粘贴原来代码是这样的),不要在意这些细节。

Server Key Exchange

Server Key Exchange 数据包是在 SSL/TLS 握手过程中,由服务器发送给客户端的一部分数据。它包含了服务器使用的密钥协商算法所需的信息,以便客户端能够生成共享密钥。

ECDH算法的椭圆曲线参数和公钥

ECDH

服务端和客户端根据自己的私钥和对方的公钥就可以计算相同的椭圆曲线点。

Server Hello Done

Server Hello Done数据包是TLS握手过程中服务器发送给客户端的消息,表示服务器已完成Hello阶段的握手过程。它本身不包含其他的具体内容,它的主要目的是通知客户端服务器已经发送了所有必要的信息,并准备好等待客户端的下一步操作。

Client Key Exchange

主要还是密钥协商中的信息,ECDH算法的公钥。

publickey

Change Cipher Spec

Change Cipher Spec(更改密码规范)是一种特殊的协议消息,用于在握手过程中通知对方切换到新的加密规范。

Change Cipher Spec 消息非常简单,只包含一个字节的值 0x01,表示切换到新的密码规范。

Change Cipher Spec 消息的作用是:

  1. 提示对方从握手过程中使用的临时加密规范切换到最终的加密规范。
  2. 表示握手过程的某个阶段已经完成,可以进入下一个阶段。
  3. 通知对方从现在开始使用新的密钥和加密算法来加密和解密数据。

在 TLS 1.2 握手过程中,Change Cipher Spec 消息是在 Finished 消息之前发送的。接收到 Change Cipher Spec 消息后,对方会确认切换到新的加密规范,并在之后的通信中使用新的密钥和加密算法。需要注意的是,Change Cipher Spec 消息本身不提供任何加密或认证,它只是一个切换信号。加密和认证是由后续的加密协议和加密算法来实现的。

Encrypted Handshake Message

Encrypted Handshake Message用于将部分或全部的握手消息进行加密,以确保握手过程的机密性和完整性,已经是加密以后的密文。

之后的TLS数据包基本都是加密以后的了。

wires hark截图

HTTPS流量解密

TLS协议数据包中的Encrypted Application Data一般是由会话密钥加密的,而会话密钥并没有直接给出。密钥如何生成的呢?需要Pre-Master SecretClient RandomServer Random和密钥派生函数Key Derivation Function。其中,客户端随机数和服务端随机数都是可以从抓到的数据包中获取的,TLS 1.2 中的 PRF 算法使用 HMAC(Hash-based Message Authentication Code)和分割函数来生成密钥材料。这个算法的python实现可以参考这个链接:

https://github.com/python-tls/tls/blob/master/tls/_common/prf.py

那么这个预主密钥Pre-Master Secret到底是何方神圣,在使用Elliptic Curve Diffie-Hellman密钥交换算法时,客户端和服务器都会生成并交换一部分公开的信息,然后各自独立计算出预主密钥。但是,

我们要怎么知道它们生成的私钥?

emmm这个嘛,还真不知道,不过用别的方法也不是不可以啦。

sslkey.log

添加环境变量SSLKEYLOGFILE=your_path/sslkey.log,该文件用于记录 SSL/TLS 密钥日志。当设置了 SSLKEYLOGFILE 环境变量并指定了一个文件路径时,SSL/TLS 库会将每个会话的密钥写入该文件中。密钥日志文件通常使用 Wireshark 等网络分析工具进行读取和解析。添加完成后,重新抓包拿数据。

打开sslkey.log,东西也太多了吧。

sslkey.log

这时候就需要查询一下NSS Key Log Format了,访问:

http://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format

搜一下,发现TLS1.2版本只有Client_Random这个标签。

label

又从这个网站找到了Client Random的含义:

CLIENT_RANDOM <64 bytes of hex encoded client_random> <96 bytes of hex encoded master secret>

可以根据第一个client_random定位相关的数据包。

11行:

11

分组174:

174

这样我们就知道了本次传输的Client RandomServer RandomMaster Secret。48字节的 Master Secret是使用ClientRandom ServerRandomPre-Master Secret 进行密钥派生函数计算得到的。Master Secret 被用作生成会话密钥、计算加密和解密所需的密钥材料。

会话密钥生成

Matser Secret如何生成会话密钥呢,从这篇文章可知:

https://www.cryptologie.net/article/340/tls-pre-master-secrets-and-master-secrets/

其实还是由密钥派生函数计算得到的,密钥派生函数根据主密钥、服务端随机数和客户端随机数计算任意长度的key_block,会话密钥由key_block分解得到。

TLS1.2的文档里找到了AES-128-GCM模式下key_block的结构:

key_block

但是这里好像是因为我理解能力太差,其实这里的mac_key_length是0(后来写代码发现的),其实前文也暗示了:

前文

因为GCM模式本来就有消息认证的功能,所以No MAC key is used

python生成key_block

之前提到过,prf函数生成指定长度的key_block,那么本次测试需要多长的key_block,首先是密钥,AES-128-GCM说明密钥是16字节,两个密钥就是32字节,那么向量多长呢?在这里可知向量长度12字节(文档里说的nonce和IV差不多是一个意思,IV 是一个在加密过程开始前确定的固定随机数,用于初始化加密算法的状态;而 nonce 是每次加密过程中产生的动态随机数,用于确保每次加密的唯一性)。

但是向量的具体构造读了官方文档我也没明白,后来从这篇文章知道向量的前四个字节是prf生成的,后八个字节是序列号。

那就可以确定prf函数的输出长度了,32+4+4=40。

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.primitives import hashes

if __name__=="__main__":
    masterkey="0bff8d120e26976f43f680c1858d299792218d1800ee8134748978a55fe0e1a817dbbbe21c8b5a7c1d7274738c056c1d"
    masterkey=bytes.fromhex(masterkey)
    label="key expansion"
    cli_rand=bytes.fromhex("cbc4865be717e19b80fc4183856136c1f0d7eef6372a32468e7a088952a12dc1")
    ser_rand=bytes.fromhex("d8a9d933fc12aeb18bde818fc3365ad67d643026e781bda0208482cfbaa958f1")
    sd=ser_rand+cli_rand
    numHMAC=0
    b=prf(masterkey,label.encode(),sd,hashes.SHA256(),numHMAC+40)

https://github.com/python-tls/tls/blob/master/tls/_common/prf.py

aes gcm

密钥已经生成了,但在解密前,我们需要了解一下aes gcm模式,毕竟它和我们熟悉的ECB、CBC不同。GCM模式的输入多了一个Additional Authentication Data,它用于认证,不会被加密。同时,输出多了一个Authentication Tag,用于检查数据的完整性,长度为16字节。文档中给出了Additional Authentication Data的组成结构:

additional_data = seq_num + TLSCompressed.type +
                        TLSCompressed.version + TLSCompressed.length;

seq_num长度为4字节,TLSCompressed.typeTLSCompressed.version都在数据包里可以找得到。长度其实并不是数据包里的703,因为后面的密文包含了8字节的nonce部分和16字节的tag,故长度为hex(703-16-8)=0x02a7

data

根据文档,Authentication Tag长度为16字节,附加在密文中。

python代码解密

from __future__ import absolute_import, division, print_function

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms,modes
from cryptography.hazmat.primitives.ciphers.modes import GCM

if __name__=="__main__":
    masterkey="0bff8d120e26976f43f680c1858d299792218d1800ee8134748978a55fe0e1a817dbbbe21c8b5a7c1d7274738c056c1d"
    masterkey=bytes.fromhex(masterkey)
    label="key expansion"
    cli_rand=bytes.fromhex("cbc4865be717e19b80fc4183856136c1f0d7eef6372a32468e7a088952a12dc1")
    ser_rand=bytes.fromhex("d8a9d933fc12aeb18bde818fc3365ad67d643026e781bda0208482cfbaa958f1")
    sd=ser_rand+cli_rand
    numHMAC=0
    b=prf(masterkey,label.encode(),sd,hashes.SHA256(),numHMAC+40)
    enc_data=bytes.fromhex("0000000000000001c7bd05b6c40016d227f8010ec6457525eb9167d604937ec7aea94968e171ebaf08bdb1e9984fb2329adbfa36a50ee4e6a21d1e88cd4939b7e0ae18e203474bfe3adf4b55f18741bf114b6defa6d5238963722259dc11495739f0012990c97394f863304a85f667d3c8289152c0b3222344e15e6010ba023725be87260f6e3b6a28dd9796fb5d96d9bdade8969bde46772077b09ba5e24665d026a3865bf2e20d9082d114810626e162cd1cc7449bb607fea4f2b56cc78e51d9943df6b2782cae3fe42f4cd4505fa23dd6b3e73e4837645d8ef6583522fc98d6a1658d52272ef53a713c6e6bd995841a1cf3619bdb9673872938b93564538820971afeb1ae74a6a75132e06d1f8cef1f7ebe710db7c7a8306b9b9cedc0b085c2af1ceac365025cd8b7bd5e0607365537f0ac33b7a8662bb38b8e909c049a41035d3902821d97a47f3baf9ada3ec4c0283e79d13289c00cd9e55c25714a9ccb1bf74245e033532b29d0cda938bc3c3f69c820284ae08d3aec86d6534b791427a81e4749fef3d92a7ee33202bda2dc50306a74370d8acc775c23e61b9d5564698ad2b13a7475c7c87646a3596b94ef3e48006a94e1388e4da6358d96cfc0cbc5a0d35a19177d04e2cd6f51a0de689e6931032b30ec58791eeaf448f82cd2f724a253a873d48c9ac98f05572a5950207026800d764372f66e77c2abdb70c27da0c08c8c7f4ee4ffa72ea78e660b8bc9f63f40c393e86c1f6856ab9adc1d8f057126a0112f6ec481a3c52a586890897ea3e082da6c676dfaf78a8993e2319bc375eb69d01347691a3985df8c550f414a28c172a2a548ab57064b2233d58cf1b3fe746c15005b3fe6750c038e472a3c1b8bf6f0418d5c1e38fa196f3ad97a3574987ca9f25e9361d00be1c763453d868ead3b865810c838a591d458c1957183371fd48567d132f96c41847f4a6ecc223bbfdf57f9f973bf6498dafbe36065bacd")
    addition=bytes.fromhex("00"*7+"01"+"17030302a7")
    key=b[numHMAC:numHMAC+16]
    iv=b[numHMAC+32:numHMAC+32+4]
    iv+=enc_data[:8]
    enc_data=enc_data[8:]
    tag=enc_data[-16:]
    enc_data=enc_data[:-16]
    dec=Cipher(algorithms.AES(key),modes.GCM(iv,tag)).decryptor()
    dec.authenticate_additional_data(addition)
    s=dec.update(enc_data)+dec.finalize()
    print(s)

解密成功:

运行结果

wireshark解密https流量

wires hark解密https流量有两种方法,用RSA私钥解密和使用密钥日志文件。

RSA私钥解密限制很大,根据wireshark官方文档

https://wiki.wireshark.org/TLS

RSA私钥文件只能在以下情况下使用:

  • 服务器选择的密码套件未使用 (EC)DHE。
  • 协议版本为 SSLv3,(D)TLS 1.0-1.2。它不适用于TLS 1.3。
  • 私钥与服务器证书匹配。它不适用于客户端证书,也不适用于证书颁发机构 (CA) 证书。
  • 会议尚未恢复。握手必须包含ClientKeyExchange握手消息。

通常建议使用密钥日志文件,因为它适用于所有情况,但需要持续从客户端或服务器应用程序导出机密信息的能力。RSA 私钥的唯一优点是它只需要在 Wireshark 中配置一次即可启用解密,但受到上述限制。

至少,它无法解密本次的数据。

所以选择密钥日志文件(就是前面用的sslkey.log)方法:

编辑->首选项->协议->TLS,添加log filename。

TLS

不可思议的事情发生了:

TLS数据包变成了明文

与我们的解密结果一致
它不适用于客户端证书,也不适用于证书颁发机构 (CA) 证书。

  • 会议尚未恢复。握手必须包含ClientKeyExchange握手消息。

通常建议使用密钥日志文件,因为它适用于所有情况,但需要持续从客户端或服务器应用程序导出机密信息的能力。RSA 私钥的唯一优点是它只需要在 Wireshark 中配置一次即可启用解密,但受到上述限制。

至少,它无法解密本次的数据。

所以选择密钥日志文件(就是前面用的sslkey.log)方法:

编辑->首选项->协议->TLS,添加log filename。

在这里插入图片描述

不可思议的事情发生了:

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

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要启用TLS 1.2协议,您可以按照以下步骤进行操作: 1. 打开注册表编辑器,可以通过运行命令regedit来打开。 2. 导航到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols路径。 3. 在Protocols下右键,选择新建,然后选择项,创建一个名为TLS 1.2的新项。 4. 在TLS 1.2下右键,选择新建,然后选择项,创建一个名为Server的新项。 5. 在Server下右键,选择新建,然后选择DWORD值,创建一个名为Enabled的新DWORD值。 6. 双击Enabled,将数值数据设置为1,表示启用TLS 1.2协议。 7. 同样,在TLS 1.2下右键,选择新建,然后选择项,创建一个名为Client的新项。 8. 在Client下右键,选择新建,然后选择DWORD值,创建一个名为Enabled的新DWORD值。 9. 双击Enabled,将数值数据设置为1,表示启用TLS 1.2协议。 10. 完成后,关闭注册表编辑器。 通过以上步骤,您已经成功启用了TLS 1.2协议。请注意,这些步骤是为了在Windows操作系统中启用TLS 1.2协议。\[1\]同时,启用TLS 1.2协议可以提高安全性,因为它使用了ECDHE来增强随机性,并且具有前向安全性。与RSA相比,ECDHE在服务器密钥泄露的情况下更加安全,因为之前的连接对应密钥无法被解密,从而保护了数据的安全性。此外,进行Finished校验也是非常必要的,以防止传输过程中的篡改。\[2\]\[3\] #### 引用[.reference_title] - *1* [windows server 2008 r2 中IIS启用TLS 1.2(安装SSL后用TLS 1.2)](https://blog.csdn.net/weixin_32210881/article/details/119262828)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [HTTPSTLS1.2连接详解](https://blog.csdn.net/qq_40276626/article/details/120396330)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值