CTF实战入门:Python脚本破解图片隐写与RSA加密

1. 项目概述:从一场比赛看CTF的实战魅力

最近在带一些刚接触网络安全的新人,聊起CTF(Capture The Flag,夺旗赛),很多人第一反应是“门槛高”、“看不懂”。正好,前段时间蓝桥杯的CTF选拔赛刚结束,我带着几个新人复盘了一下,发现其中几道题特别有代表性,简直就是为新手量身定做的“入门说明书”。尤其是那些涉及图片隐写和RSA加密的题目,看似神秘,其实只要掌握一点Python脚本的编写能力,就能轻松破解。这让我觉得,与其让大家对着抽象的术语发怵,不如直接上手,用一场真实的比赛作为引子,手把手地带大家走一遍从“看到题目一脸懵”到“写出脚本拿到flag”的完整过程。

CTF比赛里的Misc(杂项)和Crypto(密码学)类别,是很多新手最先接触的领域。Misc中的图片隐写,考验的是你的信息搜集和工具使用能力;而Crypto中的RSA,则是理解现代密码学基础的绝佳窗口。这两者都有一个共同点:非常适合用Python来自动化处理。Python丰富的库和简洁的语法,能让你把精力集中在解题逻辑上,而不是复杂的语法细节。通过这次对蓝桥杯CTF赛题的拆解,我希望不仅能教会你解这几道题,更能让你建立起“遇到问题 -> 分析特征 -> 寻找工具/编写脚本 -> 解决问题”的CTF基础思维模式。无论你是零基础的在校学生,还是想转行安全领域的开发者,这篇内容都将为你打开一扇实操的大门。

2. 核心思路解析:隐写与RSA的解题通法

在深入代码之前,我们必须先理清面对这类题目的通用思考路径。很多新手卡壳,不是因为不会写代码,而是不知道从何入手。CTF解题,尤其是Misc和Crypto,往往遵循一个相对固定的流程:识别题目类型、搜集或提取隐藏信息、应用对应算法或工具进行解码/解密。

对于图片隐写题,核心思路是“怀疑一切”。一张普通的图片,可能通过修改文件结构、在像素数据中嵌入信息、利用颜色通道的最低有效位(LSB)隐藏数据、甚至将压缩包、文本文件直接附加在图片文件末尾等方式来隐藏flag。我们的任务就是像侦探一样,用各种工具进行“体检”。通常的检查清单包括:用 file 命令查看真实文件类型;用 binwalk foremost 工具分离可能内嵌的文件;用 strings 命令搜索可读字符串;用 exiftool 查看图片元数据;用 Stegsolve zsteg 等专用工具分析LSB隐写。而Python脚本在这里的角色,往往是自动化执行这些检查中的某一项,或者处理工具输出后的进一步解码。

对于RSA密码题,核心思路是“理解参数与攻击场景”。RSA的安全性建立在“大数分解难题”上,但CTF中的RSA题目,为了考察知识点,通常会故意设置一些“不安全”的参数。解题的关键在于拿到题目给出的公钥(通常包含n和e)或加密后的密文c,然后寻找n的分解方式(例如n较小、n由两个很接近的素数生成、p或q不当重用等),从而计算出私钥d,最终解密得到明文m(也就是flag)。Python的 gmpy2 Crypto 库提供了大数运算和RSA计算的核心函数,我们的脚本就是根据不同的攻击场景(如模数分解、共模攻击、低加密指数攻击等),组织这些计算逻辑。

无论是哪种题型,编写Python脚本都不是第一步。第一步永远是 手动分析 ,用眼睛和基础工具去观察、去尝试。当发现某个步骤重复、繁琐,或者需要特定的数学计算时,才是Python脚本登场的时候。脚本是思维的延伸和效率的工具,而不是替代品。

3. 实战准备:构建你的Python解题环境

工欲善其事,必先利其器。在开始破解之前,我们需要一个顺手的Python环境。对于CTF解题而言,环境配置追求的是“全”和“快”,不需要像大型项目那样严谨。这里我推荐两种方案,你可以任选其一。

方案一:本地Python环境 + 虚拟环境 这是最灵活的方式。首先确保你安装了Python 3.6以上版本。我强烈建议使用 conda venv 创建一个独立的虚拟环境,避免包冲突。

# 使用venv创建虚拟环境
python -m venv ctf-env
# 激活环境 (Linux/macOS)
source ctf-env/bin/activate
# 激活环境 (Windows)
ctf-env\Scripts\activate

激活后,安装核心依赖库。这些库覆盖了从二进制处理、密码学计算到图像处理的大部分需求:

pip install pycryptodome gmpy2 pillow opencv-python-headless numpy pandas
  • pycryptodome : 强大的密码学工具库,提供了标准的RSA、AES等算法的实现。
  • gmpy2 : 高性能的多精度算术库,处理CTF中动辄几百位的大整数分解和模幂运算必不可少,速度比Python原生整数运算快很多。
  • pillow (PIL): Python图像处理的事实标准库,用于读取、操作图片像素数据。
  • opencv-python-headless : 另一个图像处理库,在某些特定场景(如频域分析)下可能用到,安装headless版本无需GUI界面,更轻量。
  • numpy : 科学计算基础,配合图像处理进行数组操作非常方便。
  • pandas : 不是必须,但在处理一些结构化的数据或日志时能节省大量时间。

方案二:使用预配置的CTF Docker镜像或在线环境 如果你不想折腾本地环境,或者需要在多台机器上快速开始,使用Docker是极佳选择。社区有很多维护良好的CTF专用镜像,如 pwn.red/jupyter ,里面预装了包括Python、 pwntools binwalk zsteg 等上百种工具。只需一条命令:

docker run -p 8888:8888 -it pwn.red/jupyter

然后在浏览器中打开对应的Jupyter Notebook链接,就能获得一个开箱即用的完整Web IDE环境。这对于新手来说,能极大降低环境配置带来的挫败感,让你直接聚焦于解题本身。

注意: 无论选择哪种方式,请务必在开始前测试核心库的导入。在Python交互环境中尝试 import Crypto, gmpy2, PIL ,确保没有报错。环境问题往往是阻碍第一步的最大“隐形成本”。

4. 图片隐写实战:从“体检”到“手术”

我们假设拿到一张名为 suspect.jpg 的图片,题目提示flag就藏在其中。下面,我们按照从常规到深入的顺序,用Python脚本配合其他工具,进行一步步排查。

4.1 第一步:基础信息收集与文件分析

在动任何脚本之前,先用系统命令进行快速筛查,这能给你后续的脚本编写提供方向。

# 1. 查看文件真实类型,有时.jpg可能是.png伪装的
file suspect.jpg
# 2. 搜索文件中所有可打印字符串,flag可能以明文形式存在
strings suspect.jpg | grep -i “flag{”
# 3. 查看图片的Exif信息,摄影师注释、GPS坐标都可能藏信息
exiftool suspect.jpg

如果 file 命令显示除了JPEG image data外还有“Zip archive data”之类的信息,那很可能图片里捆绑了一个压缩包。如果 strings 找到了可疑的Base64字符串或类似 flag{ 的片段,那方向就明确了。

4.2 第二步:使用Python进行自动化初步筛查

当手动检查发现线索后,我们可以用Python脚本将这个过程自动化,特别是当需要处理大量文件时。编写一个脚本 basic_check.py

import os
import subprocess
from pathlib import Path

def check_image(file_path):
    print(f”[*] 正在检查文件: {file_path}”)
    # 检查文件类型
    result = subprocess.run([‘file’, file_path], capture_output=True, text=True)
    print(f”文件类型: {result.stdout.strip()}”)
    
    # 使用strings搜索flag常见格式
    result = subprocess.run([‘strings’, file_path], capture_output=True, text=True)
    lines = result.stdout.split(‘\n’)
    for line in lines:
        if ‘flag{‘ in line.lower() or ‘ctf{‘ in line.lower() or ‘key’ in line:
            print(f”发现可疑字符串: {line}”)
    
    # 这里可以添加调用exiftool的代码,如果已安装
    # try:
    #     result = subprocess.run([‘exiftool’, file_path], capture_output=True, text=True)
    #     if ‘Comment’ in result.stdout:
    #         print(“发现注释信息:”, result.stdout)
    # except FileNotFoundError:
    #     print(“未找到exiftool,跳过元数据检查。”)

if __name__ == ‘__main__’:
    img_path = ‘suspect.jpg’
    if Path(img_path).exists():
        check_image(img_path)
    else:
        print(f”文件 {img_path} 不存在!”)

这个脚本封装了基础检查,你可以根据需要扩展,比如自动解码发现的Base64字符串。

4.3 第三步:深入像素层——LSB隐写分析与提取

如果前述方法都无效,那么flag很可能通过LSB(最低有效位)隐写术藏在图片的像素数据中。原理很简单:一个像素的RGB值每个通道是0-255,修改其最低的1个比特位(0或1),对人眼来说几乎无法察觉,但却可以用于编码信息。

我们可以用Python的PIL库来提取LSB信息。假设信息是按顺序存储在R通道的LSB中,下面是一个通用提取脚本 lsb_extract.py

from PIL import Image
import numpy as np

def lsb_extract(image_path, channel=0, bit_plane=0):
    “””
    提取指定颜色通道和位平面的LSB信息。
    channel: 0=R, 1=G, 2=B
    bit_plane: 0=最低位 (LSB), 1=次低位, 以此类推
    “””
    img = Image.open(image_path)
    # 将图像转换为RGB数组
    pixels = np.array(img)
    # 提取指定通道的数据
    channel_data = pixels[:, :, channel]
    # 获取指定位平面的值 (0或1)
    bit_values = (channel_data >> bit_plane) & 1
    # 将二维数组展平为一维
    bits = bit_values.flatten()
    # 将比特位分组为字节(8位一组)
    bytes_list = []
    byte = 0
    for i, bit in enumerate(bits):
        byte = (byte << 1) | bit
        if (i + 1) % 8 == 0:
            bytes_list.append(byte)
            byte = 0
    # 将字节转换为字符,尝试解码为ASCII
    extracted = bytes(bytes_list).decode(‘ascii’, errors=‘ignore’)
    # 通常隐写的信息会有可读头或尾,我们打印前200个字符看看
    print(f”提取结果 (通道{channel}, 位平面{bit_plane}) 预览:”)
    print(extracted[:200])
    # 可以尝试搜索常见标志
    if ‘flag{‘ in extracted:
        print(“\n[+] 发现flag!”)
        start = extracted.find(‘flag{‘)
        # 简单假设flag以}结束
        end = extracted.find(‘}’, start) + 1
        if end > start:
            print(extracted[start:end])
    return extracted

if __name__ == ‘__main__’:
    # 尝试不同的通道和位平面组合
    for ch in range(3): # R, G, B
        for bp in range(2): # LSB和次低位
            print(f”\n=== 尝试通道 {ch}, 位平面 {bp} ===”)
            lsb_extract(‘suspect.jpg’, channel=ch, bit_plane=bp)

这个脚本会系统地尝试从RGB三个通道的最低两位提取信息。运行后,仔细观察输出中是否有可读的英文单词或 flag{ 格式的字符串。

实操心得: LSB提取出的数据常常是杂乱无章的,因为信息可能不是从第一个像素开始存储的,或者采用了不同的编码(如每个字节的LSB)。这时,可以尝试修改脚本,比如从第N个像素开始读取,或者尝试不同的比特平面组合。另一个技巧是,将提取出的比特流直接保存为二进制文件,然后用 file 命令检查其类型,可能会发现它其实是一个ZIP或PNG文件的开头。

4.4 第四步:文件分离与进阶隐写

如果 binwalk 分析显示图片内嵌了其他文件,我们可以用Python调用 binwalk 进行自动分离,或者手动编写代码解析文件结构。例如,有一种常见的隐写是将一个ZIP文件附加在JPG文件末尾。JPG文件以 FF D9 结束,之后的数据就是附加物。我们可以用Python脚本将其切割出来:

def extract_appended_data(jpg_path, output_path):
    with open(jpg_path, ‘rb’) as f:
        data = f.read()
    # 查找JPG结束标记
    jpg_end = data.find(b’\xff\xd9’)
    if jpg_end == -1:
        print(“未找到标准的JPG结束标记。”)
        return False
    # JPG结束标记占2字节,所以附加数据从其后开始
    appended_data = data[jpg_end + 2:]
    if not appended_data:
        print(“未发现附加数据。”)
        return False
    with open(output_path, ‘wb’) as f:
        f.write(appended_data)
    print(f”附加数据已提取到 {output_path},文件大小:{len(appended_data)} 字节”)
    # 尝试用file命令识别提取出的文件
    import subprocess
    subprocess.run([‘file’, output_path])
    return True

extract_appended_data(‘suspect.jpg’, ‘extracted.zip’)

提取出的 extracted.zip 可能需要密码,密码有时又藏在图片的元数据或LSB中,这就形成了环环相扣的谜题。

5. RSA密码破解实战:理解数学与编写攻击脚本

RSA题目通常会给你一个 pub.key (公钥文件)和一个 flag.enc (加密后的文件)。你需要从公钥中提取参数,找到漏洞,计算私钥,最后解密。

5.1 第一步:提取RSA公钥参数

首先,我们需要从公钥文件中获取模数 n 和公钥指数 e 。使用Python的 Crypto 库可以很方便地做到这一点。

from Crypto.PublicKey import RSA

# 方法1:从PEM格式的公钥文件读取
with open(‘pub.key’, ‘r’) as f:
    key_data = f.read()
pub_key = RSA.import_key(key_data)
n = pub_key.n
e = pub_key.e
print(f”模数 n = {n}”)
print(f”公钥指数 e = {e}”)
print(f”n的十进制位数: {len(str(n))}”) # 判断n的大小

如果公钥文件是OpenSSL生成的,可能会是 -----BEGIN PUBLIC KEY----- 格式的PEM文件。有时题目会直接给你 n e 的值,那就更简单了。

5.2 第二步:判断攻击场景并分解n

这是RSA题目的核心。我们需要根据 n 的特点选择攻击方法。

场景1:n较小,可直接分解 如果 n 的十进制位数在256位以下(大约80个十进制数字以内),可以尝试用本地工具或在线网站(如factordb.com)直接分解。在Python中,我们可以用 sympy 库或 gmpy2 is_prime next_prime 函数辅助进行简单爆破(仅适用于极小的n)。

import gmpy2
from sympy import factorint

n = 1234567890123456789012345678901234567890 # 示例,实际替换为题目n
if len(str(n)) < 80:
    # 使用sympy分解(适用于中等大小的整数)
    factors = factorint(n)
    print(f”分解结果: {factors}”)
    if len(factors) == 2 and all(p > 1 for p in factors):
        p, q = list(factors.keys())
        print(f”p = {p}, q = {q}”)

注意: 对于超过100位的 n ,本地分解需要很长时间甚至不可行,必须寻找其他漏洞。

场景2:n由两个非常接近的素数生成——费马分解法 如果 p q 很接近,那么 n 可以近似看作一个平方数。费马分解法对此非常有效。

import gmpy2

def fermat_factorization(n):
    a = gmpy2.isqrt(n) + 1
    b2 = a*a - n
    while not gmpy2.is_square(b2):
        a += 1
        b2 = a*a - n
    b = gmpy2.isqrt(b2)
    p = a + b
    q = a - b
    return int(p), int(q)

n = 非常大的整数
p, q = fermat_factorization(n)
print(f”费马分解成功: p={p}, q={q}”)
print(f”验证 n == p*q: {n == p * q}”)

场景3:公钥指数e非常小(如e=3)——低加密指数攻击 如果 e 很小,并且明文 m 也很小(满足 m^e < n ),那么加密过程 c = m^e mod n 实际上就等于 m^e (因为没有模运算溢出)。此时直接对密文 c e 次方即可得到明文 m

import gmpy2
from Crypto.Util.number import long_to_bytes

c = 密文整数
e = 3
# 尝试对c开e次方根
m_int, is_exact = gmpy2.iroot(c, e)
if is_exact:
    m = long_to_bytes(int(m_int))
    print(f”低加密指数攻击成功,明文: {m}”)

场景4:相同的n,不同的e加密了同一消息——共模攻击 如果两次加密使用了相同的模数 n 但不同的公钥指数 e1 e2 ,并且 e1 e2 互素,那么可以通过扩展欧几里得算法找到 x y 使得 e1*x + e2*y = 1 ,然后计算 m = (c1^x * c2^y) mod n 来恢复明文。

import gmpy2
from Crypto.Util.number import long_to_bytes

n = 模数
e1, c1 = 公钥指数1和密文1
e2, c2 = 公钥指数2和密文2
# 使用扩展欧几里得算法求系数
gcd, x, y = gmpy2.gcdext(e1, e2)
# 确保gcd(e1, e2) == 1
if gcd != 1:
    print(“e1和e2不互素,共模攻击不适用”)
else:
    # 计算明文
    if x < 0:
        c1 = gmpy2.invert(c1, n)
        x = -x
    if y < 0:
        c2 = gmpy2.invert(c2, n)
        y = -y
    m = (pow(c1, x, n) * pow(c2, y, n)) % n
    print(f”共模攻击成功,明文: {long_to_bytes(m)}”)

5.3 第三步:计算私钥并解密

一旦成功分解 n 得到 p q ,计算私钥 d 就是标准流程:

  1. 计算 phi(n) = (p-1)*(q-1)
  2. 计算私钥指数 d = e^(-1) mod phi(n) ,即 e 关于 phi(n) 的模逆元。
from Crypto.Util.number import inverse, long_to_bytes

# 假设我们已经有了 p, q, e, c (密文整数)
phi = (p - 1) * (q - 1)
# 计算私钥指数 d
d = inverse(e, phi) # 或者用 gmpy2.invert(e, phi)
print(f”私钥指数 d = {d}”)
# 解密: m = c^d mod n
m_int = pow(c, d, n)
# 将整数明文转换为字节
flag = long_to_bytes(m_int)
print(f”解密得到的flag: {flag}”)

最后得到的 flag 很可能就是最终的答案。

6. 蓝桥杯CTF赛题实例串联解析

让我们将上面的知识串联起来,模拟一个蓝桥杯CTF中可能出现的复合题型。题目描述:“一张看似普通的风景图,背后却隐藏着秘密。flag被加密了,钥匙就在图片中。”

第一步:分析图片 我们拿到 challenge.jpg 。用 file binwalk 检查,发现图片末尾附加了一个ZIP文件。用前面提到的Python脚本 extract_appended_data 将其分离,得到 hidden.zip 。解压 hidden.zip 需要密码。

第二步:寻找密码 尝试用 strings exiftool 查看图片,在Exif的Comment字段发现一串Base64编码的字符串: U2FyYWggMTIz 。解码后得到 Sarah 123 。尝试用这个作为ZIP密码,成功解压。里面有两个文件: pub_key.pem encrypted.bin

第三步:破解RSA 用Python脚本读取 pub_key.pem ,得到 n e (假设 e=65537 )。发现 n 不大,只有120位十进制数字。我们将其提交到 factordb.com 或使用 sympy.factorint 进行分解,成功得到 p q

from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse, long_to_bytes
import gmpy2

with open(‘pub_key.pem’, ‘r’) as f:
    pub_key = RSA.import_key(f.read())
n, e = pub_key.n, pub_key.e

# 假设从factordb得到分解结果
p = 1234567890123456789012345678901234567890123456789012345678901234567
q = 9876543210987654321098765432109876543210987654321098765432109876543
assert p * q == n

phi = (p-1)*(q-1)
d = inverse(e, phi)

with open(‘encrypted.bin’, ‘rb’) as f:
    c = int.from_bytes(f.read(), ‘big’) # 密文是二进制大整数

m_int = pow(c, d, n)
flag = long_to_bytes(m_int)
print(flag) # 输出:flag{This_Is_The_Final_Flag}

至此,我们通过“图片隐写提取密码 -> 解压得到RSA材料 -> 分解n计算私钥 -> 解密”这一完整链条拿到了flag。

7. 常见问题与排查技巧实录

在实际操作中,你肯定会遇到各种报错和意外情况。这里记录了几个最典型的问题和我的解决思路。

问题1: ModuleNotFoundError: No module named ‘Crypto’ 这是最常见的问题。因为Python有一个历史遗留的、同名的 crypto 包(全小写)。正确的包名是 pycryptodome

  • 解决方案 :确保使用 pip install pycryptodome 安装。如果已经安装但仍有问题,尝试导入时使用 from Cryptodome.PublicKey import RSA (注意 Cryptodome 的‘D’大写)。在某些系统上,可能需要卸载假冒的 crypto 包: pip uninstall crypto

问题2:分解大整数n时,脚本卡死或无结果 本地分解大整数(如2048位)在普通计算机上是不现实的。

  • 排查思路
    1. 检查n的长度 print(len(str(n))) 。如果超过250位十进制数,基本放弃本地分解。
    2. 寻找特殊关系 :检查 n 是否是光滑数(能被小素数整除),可以用 gmpy2.gcd(n, small_prime) 快速测试一批小素数。或者检查 n 是否是完全平方数(即 p=q ),但这在RSA中几乎不可能。
    3. 利用已知漏洞 :回忆常见的攻击场景。 n 是否可能由两个非常接近的素数生成(用费马分解法尝试)?题目是否给出了 n 以外的其他提示,如 p+q p-q 的值?有时题目会故意给出 n e ,但 n 本身是容易分解的,考察的就是你对 n 大小的敏感度。
    4. 求助于资源 :将 n 的十进制字符串复制到 factordb.com 查询。如果该网站也没有记录,那这道题很可能不是考分解,而是考其他攻击方式(如共模攻击、低加密指数广播攻击等),需要重新审题。

问题3:LSB隐写提取出的数据全是乱码,找不到flag 这说明提取方式可能不对。信息可能不是从第一个像素开始存储的,或者存储顺序(RGB通道顺序、位平面顺序)有变化,甚至可能先经过了加密或编码。

  • 排查技巧
    1. 偏移尝试 :修改提取脚本,不从像素数组的索引0开始,而是从索引100、1000等位置开始读取比特流。
    2. 全通道全位平面扫描 :编写一个循环脚本,自动尝试所有RGB通道组合(如只取R,只取G,只取B,R+G, R+G+B等)以及不同的位平面(0到7),并将提取出的比特流保存为文件,再用 file 命令检查文件类型。有时隐藏的是一个完整的PNG或ZIP文件头。
    3. 检查文件头 :将提取出的原始字节( bytes_list )的前几个字节打印出来( print(bytes_list[:10]) ),对照常见的文件魔数(Magic Number),例如 PK (ZIP)、 PNG JFIF (JPEG)等。
    4. 考虑编码 :尝试将提取出的字节流用不同的编码解码(如 utf-8 latin-1 ),或者先进行Base64解码、ROT13解密等简单变换再看。

问题4:解密RSA后得到的明文是一串毫无意义的字节 恭喜你,私钥计算和解密过程很可能已经成功了!但明文可能不是直接的ASCII文本。

  • 后续操作
    1. 转换为十六进制 print(flag.hex()) ,看是否是一个可读的十六进制字符串,或者对应着某个文件的头。
    2. 尝试常见编码 :除了 decode(‘utf-8’) ,还可以尝试 decode(‘latin-1’) ,或者直接 print(repr(flag)) 查看原始字节表示。
    3. 保存为文件 :将 flag 字节内容直接写入文件,例如 with open(‘output’, ‘wb’) as f: f.write(flag) ,然后用 file output 命令判断其类型。很可能解密出来的是一个图片或另一个压缩包,需要你进行下一步分析。

问题5:脚本运行一切正常,但得到的flag提交不正确 这是最令人头疼的情况。首先,请百分之百确认你提交的flag格式与题目要求一致(是 flag{...} 还是 FLAG{...} 或者别的格式?是否包含空格?)。

  • 深度检查
    1. 核对每一个中间步骤 :重新计算 p q ,验证 p*q == n 是否严格成立。检查 phi 的计算是否正确。检查 d 的计算( d*e % phi == 1 )。
    2. 密文是否正确 :确保你读取的密文 c 是完整的、没有经过错误编码的。有时密文是十六进制字符串,需要先 int(c, 16) 转换;有时是Base64编码,需要先解码。
    3. 题目是否有陷阱 :有些题目会在最后一步进行额外的编码或哈希。例如,解密出的明文可能是 md5(real_flag) ,你需要对这个明文再进行一次MD5解密(查彩虹表)。仔细阅读题目描述,每一个单词都可能有提示。

编写CTF解题脚本,三分靠写,七分靠调试。耐心、细致的观察和对每一个中间结果的验证,是通往正确答案的唯一路径。养成将关键中间变量打印出来检查的习惯,这能帮你节省大量时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值