多数字拼接的字节码尝试暴力解析回各数字值

随机生成n个整数数值(取值区间-10000到10000),把整数随机用1/2/4/8字节存储为二进制

随机生成m个浮点数值(取值区间 0.001到1000.0),把浮点数值随机用2/4/8字节存储为二进制

n+m个数值随机顺序组成一个二进制字节码,如:b'40631fd13ff110b3ffffffffffffd955e44144014878'

根据已知的取值范围,还原成原始数值

当字节码很长时可先猜一些:大端小端、字节存储范围、数值组成数量、数值类型来加快拆解速度。

测试代码:

# -*- coding: utf8 -*-
import time
import random
import struct
from binascii import b2a_hex, a2b_hex
import os
## 终端显示颜色
if os.name == 'nt':       # Windows
    import ctypes,sys
    STD_OUTPUT_HANDLE = -11
    
    # Windows CMD命令行 字体颜色定义 text colors
    黑字 = 0x00 # black.
    暗蓝字 = 0x01 # dark blue.
    暗绿字 = 0x02 # dark green.
    暗青字 = 0x03 # dark skyblue.
    暗红字 = 0x04 # dark red.
    暗紫字 = 0x05 # dark pink.
    暗黄字 = 0x06 # dark yellow.
    暗白字 = 0x07 # dark white.
    灰字 = 0x08 # dark gray.
    蓝字 = 0x09 # blue.
    绿字 = 0x0a # green.
    青字 = 0x0b # skyblue.
    红字 = 0x0c # red.
    紫字 = 0x0d # pink.
    黄字 = 0x0e # yellow.
    白字 = 0x0f # white.
    
    # Windows CMD命令行 背景颜色定义 background colors
    暗蓝底 = 0x10 # dark blue.
    暗绿底 = 0x20 # dark green.
    暗青底 = 0x30 # dark skyblue.
    暗红底 = 0x40 # dark red.
    暗紫底 = 0x50 # dark pink.
    暗黄底 = 0x60 # dark yellow.
    暗白底 = 0x70 # dark white.
    灰底 = 0x80 # dark gray.
    蓝底 = 0x90 # blue.
    绿底 = 0xa0 # green.
    青底 = 0xb0 # skyblue.
    红底 = 0xc0 # red.
    紫底 = 0xd0 # pink.
    黄底 = 0xe0 # yellow.
    白底 = 0xf0 # white.
    
    std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
    
    def set_cmd_text_color(color, handle=std_out_handle):
        Bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color)
        return Bool
    
    def resetColor():
        set_cmd_text_color(红字 | 绿字 | 蓝字)
    
    def 打印_黑(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黑字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_红(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(红字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_绿(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(绿字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_黄(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黄字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_蓝(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(蓝字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_紫(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(紫字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_青(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(青字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(白字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    
    def 打印_灰(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(灰字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底黑字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黑字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_蓝底黄字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黄字 | 蓝底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_蓝底白字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(白字 | 蓝底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
elif os.name == 'posix':  # Linux
    '''
    格式: print('\033[显示方式;前景颜色;背景颜色m ..........\033[0m')
    print('\033[31;42m 123\033[0m')
    
    显示方式
        0 默认
        1 高亮显示
        4 下划线
        5 闪烁
        7 反白显示
        8 不可见
    
    颜色 前景色 背景色
    黑色     30     40
    红色     31     41
    绿色     32     42
    黄色     33     43
    蓝色     34     44
    紫色     35     45
    青色     36     46
    白色     37     47
    '''
    
    def 打印_灰(TEXT):
        print(f"\033[0;30;1m{TEXT}\033[0m")
    def 打印_红(TEXT):
        print(f"\033[0;31;1m{TEXT}\033[0m")
    def 打印_绿(TEXT):
        print(f"\033[0;32;1m{TEXT}\033[0m")
    def 打印_黄(TEXT):
        print(f"\033[0;33;1m{TEXT}\033[0m")
    def 打印_蓝(TEXT):
        print(f"\033[0;34;1m{TEXT}\033[0m")
    def 打印_紫(TEXT):
        print(f"\033[0;35;1m{TEXT}\033[0m")
    def 打印_青(TEXT):
        print(f"\033[0;36;1m{TEXT}\033[0m")
    def 打印_白(TEXT):
        print(f"\033[0;37;1m{TEXT}\033[0m")
    
    def 打印_白底黑字(TEXT):
        print(f"\033[0;30;47m{TEXT}\033[0m")
    def 打印_白底红字(TEXT):
        print(f"\033[0;31;47m{TEXT}\033[0m")
    def 打印_白底绿字(TEXT):
        print(f"\033[0;32;47m{TEXT}\033[0m")
    def 打印_白底黄字(TEXT):
        print(f"\033[0;33;47m{TEXT}\033[0m")
    def 打印_白底蓝字(TEXT):
        print(f"\033[0;34;47m{TEXT}\033[0m")
    def 打印_白底紫字(TEXT):
        print(f"\033[0;35;47m{TEXT}\033[0m")
    def 打印_白底青字(TEXT):
        print(f"\033[0;36;47m{TEXT}\033[0m")


def SHOW_L(L):
    if len(L) < 30:
        for i in L:
            print(i)
    else:
        打印_黄(f"数量={len(L)} 大于 30 太多了,不显示")


## 生成测试用的随机数字组合的字节码 (大端小端, 数字类型, 存储空间, 符号情况, FMT, 字节码, 数值)
def 随机生成(数量, 大端小端, 整数区间, 浮点区间, 指定可选存储空间):
    字节码_合并 = b''
    FMT_合并 = f'{D_大端小端[大端小端]}'
    L_组成信息 = []
    for i in range(数量):
        #可选存储空间 = []                 # [1,2,4,8]
        可选数字类型 = ('整数', '浮点')
        可选符号情况 = ['无','有']
        
        数字类型 = random.choice(可选数字类型)
        #print(f"数字类型={数字类型}")
        
        if 数字类型 == '整数':
            数值 = random.randint(整数区间[0], 整数区间[1])           # 产生 1   到 10  的一个整数型随机数(包含1和10)
        else:
            数值 = random.uniform(浮点区间[0], 浮点区间[1])
        #打印_紫(f"数值={数值}")
        
        
        if 数值 < 0:
            符号情况 = '有'
        else:
            符号情况 = random.choice(可选符号情况)
        #print(f"符号情况={符号情况}")
        
        可选存储空间 = []
        if 数字类型 == '整数':
            if 符号情况 == '有':
                ## 8字节(64bit) +/-  -9223372036854775807 <= number <= 9223372036854775807
                if 数值 > 9223372036854775807 or 数值 < -9223372036854775807:
                    print(f"8字节有符号(q)存不下 {数值}")
                else:
                    可选存储空间.append(8)
                
                ## 4字节(32bit) +/-  -2147483648 <= number <= 2147483647
                if 数值 > 2147483647 or 数值 < -2147483648:
                    print(f"4字节有符号(i)存不下 {数值}")
                else:
                    可选存储空间.append(4)
                
                ## 2字节(16bit) +/-  -32768 <= number <= 32767
                if 数值 > 32767 or 数值 < -32768:
                    print(f"2字节有符号(h)存不下 {数值}")
                else:
                    可选存储空间.append(2)
                
                ## 1字节(8bit)  +/-   -128 <= number <= 127
                if 数值 > 127 or 数值 < -128:
                    print(f"1字节有符号(b)存不下 {数值}")
                else:
                    可选存储空间.append(1)
            else:
                ## 8字节(64bit) +/-  0 <= number <= 18446744073709551615
                if 数值 > 18446744073709551615:
                    print(f"8字节无符号(Q)存不下 {数值}")
                else:
                    可选存储空间.append(8)
                
                ## 4字节(32bit) +/-   0 <= number <= 4294967295
                if 数值 > 4294967295:
                    print(f"4字节无符号(I)存不下 {数值}")
                else:
                    可选存储空间.append(4)
                
                ## 2字节(16bit) +     0 <= number <= 65535
                if 数值 > 65535:
                    print(f"2字节无符号(H)存不下 {数值}")
                else:
                    可选存储空间.append(2)
                
                ## # 1字节(8bit)  +   0 <= number <= 255
                if 数值 > 255:
                    print(f"1字节无符号(B)存不下 {数值}")
                else:
                    可选存储空间.append(1)
        else:
            ## 8字节(64bit) +/-  -1.7e+308 <= number <= -1.7e+308
            if 数值 > 1.7e+308 or 数值 < -1.7e+308:
                print(f"8字节double(d)存不下 {数值}")
            else:
                可选存储空间.append(8)
            
            ## 4字节(32bit) +/-   -3.4e+38 <= number <= 3.4e+38
            if 数值 > 3.4e+38 or 数值 < -3.4e+38:
                print(f"4字节float(f)存不下 {数值}")
            else:
                可选存储空间.append(4)
            
            ## 2字节(16bit) +     -65504.0 <= number <= 65504.0
            if 数值 > 65504.0 or 数值 < -65504.0:
                print(f"2字节float(e)存不下 {数值}")
            else:
                可选存储空间.append(2)

        if 指定可选存储空间 != []:
            交集 = set(指定可选存储空间) & set(可选存储空间)
            if len(交集) != 0:
                可选存储空间 = list(交集)
            else:
                打印_红(f"随机数无法按指定存储空间内存放,采用可用存储空间存放")
        
        存储空间 = random.choice(可选存储空间)
        #print(f"可选存储空间={可选存储空间} 存储空间={存储空间}")
        
        FMT = f"{D_大端小端[大端小端]}{D_字母[数字类型][存储空间][符号情况]}"
        #print(f"FMT={FMT}")
        
        字节码 = struct.pack(FMT, 数值)
        字节码_合并 += 字节码
        FMT_合并 += FMT[1]
        #打印_绿(f"字节码={b2a_hex(字节码)}")
        #打印_黄((大端小端, 数字类型, 存储空间, 符号情况, FMT, b2a_hex(字节码), 数值))
        L_组成信息.append((大端小端, 数字类型, 存储空间, 符号情况, FMT, b2a_hex(字节码), 数值))
    #打印_青(f"字节码_合并={b2a_hex(字节码_合并)}  FMT_合并={FMT_合并}  {len(字节码_合并)}(Bytes)")
    return(字节码_合并, FMT_合并, L_组成信息)


# 有序 构成元素(限定) 拆分数量(限定)
def 整数拆分_有序_限定构成元素_限定拆分数量(整数值, k):
    for num in range(1, 整数值+1):
        D['运行次数'] += 1
        if num in D['构成元素']:    # 可用于构成元素的数值
            D['缓存数组'][k] = num
            rest = 整数值 - num
            if rest == 0:
                if len(D['缓存数组'][1:k+1]) == D['拆分数量']:
                    D['有效次数'] += 1
                    D['有效结果'].append(D['缓存数组'][1:k+1])
                else:
                    D['无效次数'] += 1
            else:
                D['递归次数'] += 1
                整数拆分_有序_限定构成元素_限定拆分数量(rest, k+1)
        else:
            D['无效次数'] += 1

# 有序 构成元素(限定) 拆分数量(不限)
def 整数拆分_有序_限定构成元素_不限拆分数量(整数值, k):
    for num in range(1, 整数值+1):
        D['运行次数'] += 1
        if num in D['构成元素']:    # 可用于构成元素的数值
            D['缓存数组'][k] = num
            rest = 整数值 - num
            if rest == 0:
                D['有效次数'] += 1
                D['有效结果'].append(D['缓存数组'][1:k+1])
            else:
                D['递归次数'] += 1
                整数拆分_有序_限定构成元素_不限拆分数量(rest, k+1)
        else:
            D['无效次数'] += 1

# 有序 构成元素(不限) 拆分数量(限制)
def 整数拆分_有序_不限构成元素_限定拆分数量(整数值, k):
    for num in range(1, 整数值+1):
        D['运行次数'] += 1
        D['缓存数组'][k] = num
        rest = 整数值 - num
        if rest == 0:
            if len(D['缓存数组'][1:k+1]) == D['拆分数量']:
                D['有效次数'] += 1
                D['有效结果'].append(D['缓存数组'][1:k+1])
            else:
                D['无效次数'] += 1
        else:
            D['递归次数'] += 1
            整数拆分_有序_不限构成元素_限定拆分数量(rest, k+1)

# 有序 构成元素(不限) 拆分数量(不限)
def 整数拆分_有序_不限构成元素_不限拆分数量(整数值, k):
    for num in range(1, 整数值+1):
        D['运行次数'] += 1
        #打印_灰(num)
        D['缓存数组'][k] = num
        rest = 整数值 - num
        if rest == 0:
            D['有效次数'] += 1
            D['有效结果'].append(D['缓存数组'][1:k+1])
        else:
            D['递归次数'] += 1
            整数拆分_有序_不限构成元素_不限拆分数量(rest, k+1)


## 单数字暴力猜解
def 字节转数字(BYTES, 可选存储空间, 可选大端小端, 可选数字类型, 可选符号情况, 已知或假定_数值区间_范围):
    L_符合数值 = []
    L_符合格式 = []
    尝试次数 = 0
    #可选存储空间 = [len(BYTES)]
    for 存储空间 in 可选存储空间:
        if 存储空间 > 1:
            for 大端小端 in 可选大端小端:
                for 数字类型 in 可选数字类型:
                    for 符号情况 in 可选符号情况:
                        #打印_灰(f"存储空间={存储空间} 大端小端={大端小端} 数字类型={数字类型} 符号情况={符号情况}")
                        FMT = f"{D_大端小端[大端小端]}{D_字母[数字类型][存储空间][符号情况]}"
                        解析数字 = struct.unpack(FMT, BYTES)
                        #print(f"FMT={FMT} 解析数字={解析数字[0]}")
                        #解析数字
                        if type(解析数字[0]) == int:
                            if 已知或假定_数值区间_范围['整数区间'][0] <= 解析数字[0] <= 已知或假定_数值区间_范围['整数区间'][1]:
                                #打印_绿(f"找到整数解:{解析数字[0]}")
                                L_符合数值.append(解析数字[0])
                                L_符合格式.append(FMT[1])
                        else:
                            if 已知或假定_数值区间_范围['浮点区间'][0] <= 解析数字[0] <= 已知或假定_数值区间_范围['浮点区间'][1]:
                                #打印_绿(f"找到浮点解:{解析数字[0]}")
                                L_符合数值.append(解析数字[0])
                                L_符合格式.append(FMT[1])
                        尝试次数 += 1
        else:
            for 符号情况 in 可选符号情况:
                #打印_灰(f"存储空间={存储空间} 大端小端=无(定值) 数字类型=整数(定值) 符号情况={符号情况}")
                FMT = f"{D_字母['整数'][存储空间][符号情况]}"
                解析数字 = struct.unpack(FMT, BYTES)
                #print(f"FMT={FMT} 解析数字={解析数字[0]}")
                #解析数字
                if 已知或假定_数值区间_范围['整数区间'][0] <= 解析数字[0] <= 已知或假定_数值区间_范围['整数区间'][1]:
                    #打印_绿(f"找到整数解:{解析数字[0]}")
                    L_符合数值.append(解析数字[0])
                    L_符合格式.append(FMT)
                尝试次数 += 1
    #打印_黄(f"尝试次数={尝试次数}")
    return(L_符合数值, L_符合格式)

## 多数字暴力猜解
def 尝试找出合理解析(字节码, L_可用组合, 已知或假定_数值区间_范围, 已知或假定_大端小端_范围, 已知或假定_数值类型_范围):
    ## 字节转数字,判断是否在合理范围内
    from io import BytesIO  ## 内存 字节码 文件操作
    MBF = BytesIO(字节码)    ## 初始化时可以直接赋值
    TIME_S = time.time()
    for L in L_可用组合:
        #print(L)

        if '大端' in 已知或假定_大端小端_范围:
            大端测试 = 'V'
            L_大端测试 = []
            L_大端测试格式 = []
            MBF.seek(0)
            for i in L:
                #print(i)
                DATA = MBF.read(i)
                L_符合数值, L_符合格式 = 字节转数字(DATA, (i,), ('大端',), 已知或假定_数值类型_范围, ('无','有'), 已知或假定_数值区间_范围)
                if L_符合数值 == []:
                    大端测试 = 'X'
                    break
                else:
                    L_大端测试.append(set(L_符合数值))
                    L_大端测试格式.append(set(L_符合格式))
            if 大端测试 == 'V':
                打印_绿(f"大端测试成功 {L} {['/'.join(i) for i in L_大端测试格式]} {L_大端测试}")
        
        if '小端' in 已知或假定_大端小端_范围:
            小端测试 = 'V'
            L_小端测试 = []
            L_小端测试格式 = []
            MBF.seek(0)
            for i in L:
                #print(i)
                DATA = MBF.read(i)
                L_符合数值, L_符合格式 = 字节转数字(DATA, (i,), ('小端',), 已知或假定_数值类型_范围, ('无','有'), 已知或假定_数值区间_范围)
                if L_符合数值 == []:
                    小端测试 = 'X'
                    break
                else:
                    L_小端测试.append(set(L_符合数值))
                    L_小端测试格式.append(set(L_符合格式))
            if 小端测试 == 'V':
                打印_绿(f"小端测试成功 {L} {['/'.join(i) for i in L_小端测试格式]} {L_小端测试}")
    打印_蓝(f"字节转数字尝试 用时{time.time()-TIME_S:.2f}秒")
    print()

## 全局变量
D_大端小端 = {'大端':'>', '小端':'<'}
D_字母 = {'整数':{8:{'有':'q', '无':'Q'}, 4:{'有':'i', '无':'I'}, 2:{'有':'h', '无':'H'}, 1:{'有':'b', '无':'B'}}, '浮点':{8:{'有':'d', '无':'d'}, 4:{'有':'f', '无':'f'}, 2:{'有':'e', '无':'e'}}}


if __name__ == '__main__':
    组成数量 = 4
    指定可选存储空间 = [1,2,4,8]    # [] 表示根据数值区间自动选择
    整数区间 = (-10000, 10000)
    浮点区间 = (0.001, 1000.0)
    可选大端小端 = ('大端', '小端')
    设定大端小端 = random.choice(可选大端小端)
    打印_白底黑字(f"设定大端小端={设定大端小端} 指定可选存储空间={指定可选存储空间} 组成数量={组成数量}")
    ## L_组成信息 [(大端小端, 数字类型, 存储空间, 符号情况, FMT, b2a_hex(字节码), 数值), ()]
    字节码, FMT, L_组成信息 = 随机生成(组成数量, 设定大端小端, 整数区间, 浮点区间, 指定可选存储空间)
    for i in L_组成信息:
        打印_灰(i)
    打印_青(f"字节码={b2a_hex(字节码)}  FMT={FMT}  {len(字节码)} (Bytes) {[i[2] for i in L_组成信息]}")
    print()

    ## 整数拆分部分
    # 已知 组成单元范围 [1,2,4,8]
    # 已知 组成拆分数量 拆分成n个数

    构成元素 = set([i[2] for i in L_组成信息])     ## 已知组成元素的存储大小范围
    #构成元素 = set([4,8])                         ## 猜测组成元素的存储大小范围
    #构成元素 = set()                              ## 所有大小都试一遍

    拆分数量 = 组成数量     # 已知 组成拆分数量 拆分成n个数
    #拆分数量 = 0           # 所有拆分数量都试一遍

    TIME_S = time.time()
    整数值 = len(字节码)
    D = {'有效结果':[], '运行次数':0, '有效次数':0, '递归次数':0, '无效次数':0, '构成元素':构成元素, '拆分数量':拆分数量, '运行时间':-1, '缓存数组':[i for i in range(整数值+1)]}
    if 构成元素 == set():
        D['构成元素'] = set((1,2,4,8))  # 单个数字(整数或浮点)可以存为 1/2/4/8 个字节
        打印_红(f"构成元素=空集合 不限制组成元素的值,但是单个数字(整数或浮点)只可以存为 1/2/4/8 个字节 所以重置元素限制范围为 {D['构成元素']}")

    if 拆分数量 > 1:    # 拆分数量(限制)
        if type(D['构成元素'])==set and len(D['构成元素']) != 0:
            打印_紫("# 有序 构成元素(限定) 拆分数量(限定)")
            整数拆分_有序_限定构成元素_限定拆分数量(整数值, 1)      # 有序 构成元素(限定) 拆分数量(限定)
        else:
            打印_紫("# 有序 构成元素(不限) 拆分数量(限制)")
            整数拆分_有序_不限构成元素_限定拆分数量(整数值, 1)      # 有序 构成元素(不限) 拆分数量(限制)
    elif 拆分数量 == 0: # 拆分数量(不限)
        if type(D['构成元素'])==set and len(D['构成元素']) != 0:
            打印_紫("# 有序 构成元素(限定) 拆分数量(不限)")
            整数拆分_有序_限定构成元素_不限拆分数量(整数值, 1)      # 有序 构成元素(限定) 拆分数量(不限)
        else:
            打印_紫("# 有序 构成元素(不限) 拆分数量(不限)")
            整数拆分_有序_不限构成元素_不限拆分数量(整数值, 1)      # 有序 构成元素(不限) 拆分数量(不限)
    elif 拆分数量 == 1: # 不用拆分
        D['有效结果'].append([len(字节码)])
    else:   # 数量不对
        打印_红(f"错误 拆分 拆分数量={拆分数量} < 1")

    D['运行时间'] = time.time()-TIME_S
    打印_黄(f"运行时间={D['运行时间']:.2f} 运行次数={D['运行次数']} 有效次数={D['有效次数']} 递归次数={D['递归次数']} 无效次数={D['无效次数']} 构成元素={D['构成元素']} 拆分数量={D['拆分数量']}")
    SHOW_L(D['有效结果'])


    ## 猜解字节部分
    # 已知 大端小端 范围
    # 已知 数值区间 范围
    # 已知 数值类型 范围 【整数,浮点】

    已知或假定_数值区间_范围 = {'整数区间':整数区间, '浮点区间':浮点区间}
    #已知或假定_数值区间_范围 = {'整数区间':(-10, 100), '浮点区间':(-10.0, 10.0)}

    #已知或假定_大端小端_范围 = 可选大端小端
    已知或假定_大端小端_范围 = (设定大端小端,)

    #已知或假定_数值类型_范围 = ('整数', '浮点')
    已知或假定_数值类型_范围 = ('整数', '浮点')

    L_可用组合 = D['有效结果']
    尝试找出合理解析(字节码, L_可用组合, 已知或假定_数值区间_范围, 已知或假定_大端小端_范围, 已知或假定_数值类型_范围)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值