密码学1024大作业

1024作业


1.padding oracle attack

题目

In thisassignment, you must decrypt a challenge ciphertext generated using AES inCBC-mode with PKCS #5 padding.
(Note: technically this is PKCS #7 padding,since the block size of AES is 16 bytes. But the padding is done in exactly
thesame way as PKCS #5 padding.) To do so, you will be given access to a server thatwill decrypt any ciphertexts you
send it (using the same key that was used togenerate the challenge ciphertext)…but that will only tell you whether or
notdecryption results in an error!

问题描述

给一串CBC模式下使用AES算法加密的密文(填充标准为PKCS #5)
以及一个可以解密收到的任何密文的服务器(使用的密钥与生成密文的密钥相同)并且这个服务器会告诉我们密文解密是否错误,尝试解密得到明文
(明文编码对应的ascii字符可以组成有意义的信息)
提示:使用padding oracle attack来进行解密工作

思路

首先我们将密文分组,前面8个字节为初始化向量,后面字节为加密后的数据,通过构造前面初始向量即可破解出第一组密文的明文。
1.将初始化向量全部设置为0,提交IV+第一组密文的组合,返回校验失败
2.依次将初始化向量最后一个字节从0x01~0xFF递增,直到解密的明文最后一个字节为0x01,成为一个正确的padding,
此时可异或计算出第一组密文解密后最后一个字节。
3.下面我们将构造填充值为0x02 0x02的场景,即存在2个填充字节,填充值为0x02,此时我们已经知道了中间值得最后一位为0x3D,
计算出初始向量的最后一位为 0x3D xor 0x02 = 0x3F, 即初始向量为0000000000000003F,遍历倒数第二个字节从0x00~0xFF,直到响应成功。
按此步骤则可将之后的所有明文解密出来

关键代码

oracle.py

s = None

def Oracle_Connect():
    import socket
    global s
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('128.8.130.16', 49101))
    except socket.error as e:
        print e
        return -1

    print("Connected to server successfully." )

    return 0

def Oracle_Disconnect():
    if not s:
        print "[WARNING]: You haven't connected to the server yet."
        return -1

    s.close()
    print "Connection closed successfully."

    return 0

# Packet Structure: < num_blocks(1) || ciphertext(16*num_blocks) || null-terminator(1) >
def Oracle_Send(ctext, num_blocks):
    if not s:
        print "[WARNING]: You haven't connected to the server yet."
        return -1

    msg = ctext[:]
    msg.insert(0, num_blocks)
    msg.append(0)

    s.send(bytearray(msg))
    recvbit = s.recv(2)

    try:
        return int(recvbit)
    except ValueError as e:
        return int(recvbit[0])
from oracle import *
import sys

if len(sys.argv) < 2:
    print "Usage: python sample.py <filename>"
    sys.exit(-1)

f = open(sys.argv[1])
data = f.read()
f.close()

ctext = [(int(data[i:i+2],16)) for i in range(0, len(data), 2)]

Oracle_Connect()

rc = Oracle_Send(ctext, 3)
print "Oracle returned: %d" % rc

Oracle_Disconnect()

参考链接

https://blog.csdn.net/flurry_rain/article/details/78216841
https://github.com/HPShark/PA2-AES
https://www.jianshu.com/p/833582b2f560


问题

所提供的服务器可能出现了问题,一直无法返回正常响应

2.Crypto Challenge Set 2 #6 #7 #8

题目&问题描述

####6.Byte-at-a-time ECB decryption (Harder)
在Challenge 12的基础上,还需要在明文前添加一段随机长度的随机字符串,则加密函数变为:
AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)
已知加密函数接口encrypt(string),解密target-bytes。


Challenge 12要求为:
在加密前,在明文后面添加一段文本(添加之前需要对该文本进行Base64解码)。文本如下:
Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK
使用一个恒定未知的密钥,通过ECB模式加密。加密函数格式为:
AES-128-ECB(your-string || unknown-string, random-key)
已知加密函数的接口encrypt(string),需要解密”unknown-string”。


####7.PKCS#7 padding validation
检查一段文本,是否为有效的PKCS#7填充,如果是,则去掉填充。

####8.CBC bitflipping attacks
首先生成一个随机AES密钥,然后实现两个功能。
功能1:对于userdata,在前面添加 “comment1=cooking%20MCs;userdata=”,在字符串后面添加
”;comment2=%20like%20a%20pound%20of%20bacon”,然后对该文本进行填充和加密,返回加密后的结果。
对于userdata的内容,不允许存在”;“和”=“。
功能2:解密字符串,语法分析查找是否存在”admin=true;“,返回True或False。
如果函数1实现正确,那么不会出现函数2中查找的字符串。
现在,需要做的是修改密文,基于CBC模式的比特翻转攻击,来使其可以找到。

思路

####6.Byte-at-a-time ECB decryption (Harder)
要考虑random-prefix。

求target-bytes的长度和加密块的大小。
(1)通过不断对”A”、”AA”、”AAA”……加密,通过密文块的长度以及填充规则,可以得到random-prefix+target-bytes的总长度以及加密块的大小。

(2)加密两个块大小的’A’列表,然后不断的添加’A’,当出现两个相同的块的时候,可以得到单独random-prefix的填充长度。

(3)加密三个块大小的’A’,找到其中两个相同块起始位置的offset,则第一个相同块前面部分为random-prefix及其填充,从而可以得到random-prefix的长度;从总长度中减掉random-prefix长度,即为target-bytes的长度。

猜测得到random-prefix
(3)将your-string设置为”A” * random-prefix填充长度 + “AAAAAAA”,进行加密,提取第一个密文块,为”AAAAAAAB1”的加密结果,B1为unknown-string的第一个字符;然后尝试对”AAAAAAAX”加密,X为任意字符,同样提取第一个密文块,与刚才密文块对比,找到与其相同的密文块,即可以找到target-bytes的第一个字符。

(4)以此类推,即可求得target-bytes。

####7.PKCS#7 padding validation
提取字符串最后一位c,转化为10进制数,即为填充长度paddingCount。
比较字符串的倒数paddingCount的位置上的字符是否等于c,如果相等,则为有效填充。

####8.CBC bitflipping attacks
profile_for(string):输入用户数据字符串string,如果其中包含”;“或”=“,则替换为”=“。将字符串前面和后面添加指定的字符串。

parsestring(string):首先将使用split(‘;’)对字符串进行分割,对分割后的每一块进行分析。以’=‘为基准,前面部分为关键字,后面部分为值,
添加到account字典中。

attack()攻击方法:

在userdata中如果输入”;admin=true”,经过配置函数之后,会更改为”admin_true”。
我们可以计算出两个更改后的’
‘位置为32和38;由于CBC模式是将前一个密文块异或到后一个明文块上进行加密的,因此我们可以通过更改相应密文的
前一个密文块,来实现将其解密为’;admin=true’。

代码

####6.Byte-at-a-time ECB decryption (Harder)

import base64
import Crypto.Random
import Crypto.Random.random
from Crypto.Cipher import AES

def xor_byte_strings(input_bytes_1,input_bytes_2):
    return bytes([x^y for x,y in zip(input_bytes_1,input_bytes_2)])

def pkcs7_pad(data,block_size):
    # get the size of padding
    padding_size = block_size - len(data)%block_size
    if padding_size == 0:
        padding_size  = block_size 
    # add it
    padding = (chr(padding_size)*padding_size).encode()
    return data+padding

def AES_ECB_encode(plaintext_b, key_b):
    cipher = AES.new(key_b, AES.MODE_ECB)
    ciphertext = cipher.encrypt(plaintext_b)
    return ciphertext

# returns a sepecified lenth of random bytes
def generate_random_key(key_length):
    return Crypto.Random.get_random_bytes(key_length)

def determine_block_size():
    """Determines the block size of encryption by continuously
    appending and encrypting data and measuring the size of the 
    output.  
    """
    data = b''
    initial_length = len(encryption_oracle(data))
    while True:
        # 明文长度每次增加1
        data += b'A'
        result_length = len(encryption_oracle(data))
        # 当密文长度发生变化时,中断
        if result_length != initial_length:
            break
    # 两次密文长度差,就是block_size
    block_size = result_length - initial_length
    need_prefix_padding_size = len(data)
    return block_size

# 加密函数:每次都会在data前面添上一个随机数,后面附上一段话,再用AES的ECB模式进行加密
# 目标:获取附加内容
def encryption_oracle(data):
    unknown_string = '''Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK'''
    suffix = base64.b64decode(unknown_string.encode())
    data_to_encrypt = pkcs7_pad(random_prefix + data + suffix, block_size)
    ciphertext = AES_ECB_encode(data_to_encrypt, key)
    return ciphertext

def count_the_same_length(bytes1,bytes2):
    count=0
    xor = xor_byte_strings(bytes1,bytes2)
    for i in xor:
        if i==0:
            count+=1
        else:
            break
    return count

def determine_prefix_padding_size(block_size):
    data = b''
    cipher_of_pre = encryption_oracle(data)

    data += b'A'
    init_block_num = count_the_same_length(cipher_of_pre,encryption_oracle(data))//block_size
    while(True):
        data+=b'A'
        cipher_of_A = encryption_oracle(data)
        same_block_num = count_the_same_length(cipher_of_pre, cipher_of_A)//block_size
        if same_block_num != init_block_num:
            break
        else:
            cipher_of_pre = cipher_of_A
    return same_block_num*block_size-len(data)+1
    

def decrypt_byte(block_size, prefix_padding_size, decrypted_message):
    """Decrypts a ciphertext one byte at a time by continuosly encrypting a message and comparing the ciphertext.
    """
    # 我们需要把探针+1的长度
    # s.t. 探针+1+前缀长度 向assume_block_size补齐
    # 这里的1即:byte combinations
    probe_length = block_size - ((1 + len(decrypted_message)+prefix_padding_size) % block_size)

    # s.t. 总体长度为assume_block_size的整数倍
    testing_length = prefix_padding_size + probe_length + (len(decrypted_message) + 1)
    
    # A dictionary of every possible last byte from encrypting 
    # different probe + decrypted_message + byte combinations
    # probes for instance, "AAAAAAAA", "AAAAAAAB", "AAAAAAAC"
    byte_dict = {}
    for byte in range(256):
        test_data = b"A"*(probe_length) + decrypted_message + bytes([byte])
        test_ciphertext = encryption_oracle(test_data)
        byte_dict[test_ciphertext[:testing_length]] = byte
    comparison_ciphertext = encryption_oracle(b"A"*(probe_length))[:testing_length]
    plaintext = bytes([byte_dict.get(comparison_ciphertext, 0)])
    return plaintext


def main():
    # Determine the block size. 
    # is assumed to be equal to block_size.
    assume_block_size = determine_block_size()
    prefix_padding_size = determine_prefix_padding_size(assume_block_size)
    
    #print("assume_block_size: {}".format(assume_block_size))
    #print("prefix_padding_size: {}".format(prefix_padding_size))
    
    length_of_encrypted_unknown_string = len(encryption_oracle(b''))
    discovered_string = b''
    for i in range(length_of_encrypted_unknown_string):
        discovered_string += decrypt_byte(assume_block_size, prefix_padding_size, discovered_string)
    print(discovered_string.decode())


if __name__ == '__main__':
    # random_prefex, block_size and key are transparent to attackers
    block_size = AES.block_size
    key = generate_random_key(16)
    random_prefix = generate_random_key(Crypto.Random.random.randint(1,32))
    # test 
    print("random_prefix: {}".format(len(random_prefix)))
    # conduct of attack
    main()

####7.PKCS#7 padding validation

def valid_padding(paddedMsg, block_size):
    if len(paddedMsg) % block_size != 0:
        return False
    last_byte = paddedMsg[-1] 
    if last_byte >= block_size:
        return False
    padValue = bytes([last_byte]) * last_byte
    if paddedMsg[-last_byte:] != padValue:
        return False
    if not paddedMsg[:-last_byte].decode('ascii').isprintable():
        return False
    return True

def remove_padding(paddedMsg, block_size):
    try:
        if not valid_padding(paddedMsg, block_size):
            raise ValueError
    except ValueError:
        print(f"{ paddedMsg } has invalid PKCS#7 padding.")
        return
    
    last_byte = paddedMsg[-1]
    unpadded = paddedMsg[:-last_byte]
    print(f"Padding removed successfully...")
    print(f"Before padding removal: { paddedMsg }")
    print(f"After padding removal: { unpadded }")

def main():
    block_size = 16

    # Test case 1: incorrect value < required:
    paddedMsg = b'ICE ICE BABY\x03\x03\x03\x03'
    remove_padding(paddedMsg, block_size)

    # Test caes 2: incorrect value > required:
    paddedMsg = b"ICE ICE BABY\x05\x05\x05\x05" 
    remove_padding(paddedMsg, block_size)

    # Test case 3: incorrect length:
    paddedMsg = b"ICE ICE BABY\x04\x04\x04"
    remove_padding(paddedMsg, block_size)

    # Test case 4: variable numbers:
    paddedMsg = b"ICE ICE BABY\x01\x02\x03\x04"
    remove_padding(paddedMsg, block_size)

    # Test case 5: correct padding 
    paddedMsg = b"ICE ICE BABY\x04\x04\x04\x04"
    remove_padding(paddedMsg, block_size)


if __name__ == "__main__":
    main()

####8.CBC bitflipping attacks

from Crypto import Random
from Crypto.Cipher import AES

def AES_ECB_decode(ciphertext_b, key_b):
    cipher = AES.new(key_b, AES.MODE_ECB)
    plaintext = cipher.decrypt(ciphertext_b)
    return plaintext

def AES_ECB_encode(plaintext_b, key_b):
    cipher = AES.new(key_b, AES.MODE_ECB)
    ciphertext = cipher.encrypt(plaintext_b)
    return ciphertext

def pkcs7_pad(data,block_size):
    # get the size of padding
    padding_size = block_size - len(data)%block_size
    if padding_size == 0:
        padding_size  = block_size 
    # add it
    padding = (chr(padding_size)*padding_size).encode()
    return data+padding
    
def pkcs7_uppad(data):
    # 判断是否存在padding
    padding = data[-data[-1]:]
    # 不存在就直接输出
    if not all(padding[i] == len(padding) for i in range(0,len(padding))):
        return data
    # 存在就去掉后输出
    return data[:-data[-1]]

def xor_byte_strings(input_bytes_1,input_bytes_2):
    return bytes([x^y for x,y in zip(input_bytes_1,input_bytes_2)])

def AES_CBC_encode(data, key, iv):
    data = pkcs7_pad(data,AES.block_size)
    ciphertext = b''
    previous_block_input = iv

    # Cycles through the data, one block at a time
    for i in range(0, len(data), AES.block_size):
        # Pads the current block
        plaintext_block = data[i:i + AES.block_size]
        
        # XORs the current block with the previous block. If it is
        # the first block it XORs with the iv value.
        xor_input = xor_byte_strings(plaintext_block, previous_block_input)
        
        # Encrypts the block using AES ECB and builds the ciphertext
        ecb_encrypted_block = AES_ECB_encode(xor_input, key)
        ciphertext += ecb_encrypted_block

        # Sets the block to be XOR'd with for the next block
        previous_block_input = ecb_encrypted_block
    return ciphertext

def AES_CBC_decode(data, key, iv):
    plaintext = b''
    previous_block_input = iv

    # Cycles through the data, one block at a time
    for i in range(0, len(data), AES.block_size):

        # The current encrypted block
        encrypted_block = data[i:i + AES.block_size]

        # Decrypts the block using AES ECB, and builds the plaintext
        decrypted_block = pkcs7_uppad(AES_ECB_decode(encrypted_block, key))
        plaintext += xor_byte_strings(previous_block_input, decrypted_block)
        
        # Sets the block to be XOR'd with for the next block
        previous_block_input = encrypted_block
    return pkcs7_uppad(plaintext)

class Oracle:
    def __init__(self):
        self._key = Random.new().read(AES.key_size[0])
        self._iv = Random.new().read(AES.block_size)
        self._prefix = "comment1=cooking%20MCs;userdata="
        self._suffix = ";comment2=%20like%20a%20pound%20of%20bacon"

    def encrypt(self, data):
        """Adds the prefix and the suffix specified in the challenge and encrypts the data with AES-128-CBC"""
        data = data.replace(';', '').replace('=', '')  # Remove special characters to avoid injection
        plaintext = (self._prefix + data + self._suffix).encode()
        return AES_CBC_encode(plaintext, self._key, self._iv)

    def decrypt_and_check_admin(self, ciphertext):
        """Decrypts the string and returns whether the characters ";admin=true;" are in the string"""
        data = AES_CBC_decode(ciphertext, self._key, self._iv)
        return b';admin=true;' in data

def find_block_length(encryption_oracle):
    """Returns the length of a block for the block cipher used by the encryption_oracle.
    To find the length of a block, we encrypt increasingly longer plaintexts until the size of the
    output ciphertext increases too. When this happens, we can then easily compute the length of a block
    as the difference between this new length of the ciphertext and its initial one.
    """
    my_text = ''
    ciphertext = encryption_oracle(my_text)
    initial_len = len(ciphertext)
    new_len = initial_len

    while new_len == initial_len:
        my_text += 'A'
        ciphertext = encryption_oracle(my_text)
        new_len = len(ciphertext)

    return new_len - initial_len


def find_prefix_length(encryption_oracle, block_length):
    """Returns the length of the prefix that the encryption oracle prepends to every plaintext."""

    # Encrypt two different ciphertexts
    ciphertext_a = encryption_oracle('A')
    ciphertext_b = encryption_oracle('B')

    # Find their common length
    common_len = 0
    while ciphertext_a[common_len] == ciphertext_b[common_len]:
        common_len += 1

    # Make sure that the common length is multiple of the block length
    common_len = int(common_len / block_length) * block_length

    # Try to add an increasing number of common bytes to the plaintext till they until
    # the two ciphertexts will have one extra identical block
    for i in range(1, block_length + 1):
        ciphertext_a = encryption_oracle('A' * i + 'X')
        ciphertext_b = encryption_oracle('A' * i + 'Y')

        # If there is one more identical block, it will mean that by adding i bytes
        # we made the common input (including prefix) to the same length multiple of
        # a block size. Then we can easily get the length of the prefix.
        if ciphertext_a[common_len:common_len + block_length] == ciphertext_b[common_len:common_len + block_length]:
            return common_len + (block_length - i)


def cbc_bit_flip(encryption_oracle):
    """Performs a CBC bit flipping attack to accomplish admin privileges in the decrypted data."""

    # Get the length of a block and the length of the prefix
    block_length = find_block_length(encryption_oracle.encrypt)
    prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length)

    # Compute the number of bytes to add to the prefix to make its length a multiple of block_length
    additional_prefix_bytes = (block_length - (prefix_length % block_length)) % block_length
    total_prefix_length = prefix_length + additional_prefix_bytes

    # Compute the number of bytes to add to the plaintext to make its length a multiple of block length
    plaintext = "?admin?true"
    additional_plaintext_bytes = (block_length - (len(plaintext) % block_length)) % block_length

    # Make the plaintext long one block_length and encrypt it
    final_plaintext = additional_plaintext_bytes * '?' + plaintext
    ciphertext = encryption_oracle.encrypt(additional_prefix_bytes * '?' + final_plaintext)

    # Because XORing a byte with itself produces zero, we can produce the byte that we want
    # by changing the bytes of the block before the plaintext
    semicolon = ciphertext[total_prefix_length - 11] ^ ord('?') ^ ord(';')
    equals = ciphertext[total_prefix_length - 5] ^ ord('?') ^ ord('=')

    # Put the pieces of our forged ciphertext together to generate the full ciphertext
    forced_ciphertext = ciphertext[:total_prefix_length - 11] + bytes([semicolon]) + ciphertext[total_prefix_length - 10: total_prefix_length - 5] + bytes([equals]) + ciphertext[total_prefix_length - 4:]

    return forced_ciphertext


def main():
    encryption_oracle = Oracle()
    forced_ciphertext = cbc_bit_flip(encryption_oracle)

    # Check if the ciphertext was forced properly
    assert encryption_oracle.decrypt_and_check_admin(forced_ciphertext)

if __name__ == '__main__':
    main()

参考链接

https://github.com/JesseEmond/matasano-cryptopals
http://www.hackdig.com/12/hack-53949.htm

3.AES key-encoded in the machine readable zone of a European ePassport

题目

题目链接:
https://mysterytwister.org/challenges/level-2/aes-key–encoded-in-the-machine-readable-zone-of-a-european-epassport

问题描述

1.AES加密模式为CBC,初始化矢量即IV为零,填充为01-00。此外,相应的密钥在身份证件上的机器可读区域(MRZ)等表格中,
它与欧洲的电子护照一起使用时并不十分完整。
2.目标是找到以下base64编码消息的明文:
9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI
3.加密过程已经生成了密钥KENC。解密过程可通过图中给出的字符来推导出KENC
4.传输过程存在丢失,文档给出了恢复方法与获得KENC的协议与例子
5.解密前解码base64代码

总体就是一个不断恢复的过程

思路

求丢失数字 -> 求key -> 用key求base64编码加密前的密文 -> 解密

代码

from hashlib import *
from base64 import *
from Crypto.Cipher import AES
from binascii import *
def jiaoyan(x):
    k = []
    a = bin(int(x,16))[2:]
    for i in range(0,len(a),8):
        if (a[i:i+7].count("1"))%2 == 0:
            k.append(a[i:i+7])
            k.append('1')
        else :
            k.append(a[i:i+7])
            k.append('0')
    a1 = hex(int(''.join(k),2))
    #print("this is " + x + "---" +a1)
    return a1[2:]
passport = '12345678<8<<<1110182<111116?<<<<<<<<<<<<<<<4'
c = b'''9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMa
        zJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI'''
c = b64decode(c)
a = passport[21:27]
b = [7,3,1]
res = 0
for i in range(len(a)):
    res += int(a[i]) * b[i%3]
    res %= 10
passport = passport[:27] + str(res) + passport[28:]
mrz = passport[:10] + passport[13:20] + passport[21:28]
print(mrz)

keyseed = sha1(mrz.encode()).hexdigest()[:32]
print(keyseed)
keyseed += '00000001'
key = sha1(unhexlify(keyseed)).hexdigest()
print(key)
keya = jiaoyan(key[:16])
keyb = jiaoyan(key[16:32])
key = unhexlify(keya+keyb)
print(key)
aes = AES.new(key, mode = AES.MODE_CBC , iv = b'\x00'*16)
print(aes.decrypt(c))

参考链接

https://blog.csdn.net/weixin_48392428/article/details/117227205
https://blog.csdn.net/Koz_0/article/details/109540921

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值