H&NCTF2024 Re Misc 题解(相同二进制样本变异逆向分析)

Re

maybe_xor

这题考察的应该是同类型样本的通用数据提取脚本!

  1. 考察base64解密,异或解密
  2. 通过解析汇编字节码定位数据偏移位置
  3. 根据汇编字节码特征提取xor汇编指令的操作数
  4. 跟远程服务器交互脚本的编写,我这里用的pwntools

接下来就剩爆破了:

请添加图片描述

解题思路如下:

0.单个文件分析

根据提示知道发来的base64数据是一个ELF文件,写个脚本dump出文件来分析:

import base64

def decode_base64_to_file(encoded_string, output_file):
    # 解码 base64 字符串
    decoded_data = base64.b64decode(encoded_string)
    
    # 将解码后的数据写入文件
    with open(output_file, 'wb') as file:
        file.write(decoded_data)
    
    print("解密完成并已写入文件。")

# 要解密的 base64 加密字符串
encoded_string = "f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAi4EECAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAgAQIAAAAAACABAgAAAAAkAQAAAAAAACQBAAAAAAAAAAQAAAAAAAArrlN8YKXNzPs2ZPJdlSHtIHATpGVaYXjt/bYswBasGk2W8kAuUzG5HPyOFN2nAhCSiLuV5eK1agtakZGTk31sO9xB3jj/veFaxP44L9HCv8stf+dYW5dY0rPUmOdbDSRJ3MWY8LpnDsd7/+wCPa0ZeUd84dzzJgKypt6gMR3nUoe22sGmPhl70F9uCmANVWbA9Fda326m8+Uy7spyiRwNEnJswj6eCnwBeJczfKnziKl5xsIMNPCjkgsp4c0fc4qjhJ1joRMxkiuorlRoQzRQ5tlCu2f8bhheisCMrUB7UxEH+wtUjjqIQkrP5g04SIGdQm3dLZ2WJH058bdUk7gEnA9HNwot0xIdFTTc8CLiMERJvZIg+wYSI01+f7//0iJ57kYAAAA86RIMcmKBAw070iDwQGKBAw0NEiDwQGKBAw0rkiDwQGKBAw0pUiDwQGKBAw05UiDwQGKBAw0DkiDwQGKBAw0BUiDwQGKBAw0J0iDwQGKBAw0PEiDwQGKBAw04EiDwQGKBAw06UiDwQGKBAw0MEiDwQGKBAw0iEiDwQGKBAw0J0iDwQGKBAw0iEiDwQGKBAw0YUiDwQGKBAw0WUiDwQGKBAw0H0iDwQGKBAw0SEiDwQGKBAw0WEiDwQGKBAw0vkiDwQGKBAw0REiDwQGKBAw0wkiDwQGKBAw0ZUiDwQG4PAAAAA8F/vwnMFAFq/9bov2eNJ2k/wseRqP82hUpnSg4ZiKRcuGwii6wNI8yDNBRjXuHHPkDDbVvwQ0rTCifJ6RzPFBBKiPvO8NU/gQtElhBEg+ZUOal+BQk5A+YFkJcmgG9zX7mzKU3urAXlVqzELQGmo/Qi4EGv77k4nBIMNGdeLfhKTvFxZa3v7R19pg1UuOvuob+YG+n2dFf6N03sIN0gQmAkL6X8aw1WFukpYxeXAJ8Nyi44bL+04D10ZpvtblNVb9T6iVIekZJk7hKJJ2+nAdG3Fi0DK6iJHqs5vtKOo2/D78z+NmtQ54ME3xbR6jSapk4N7fpY8fkL7OsEevXtsDJQYONI6J/NBO72pXSxtRK3w2BKlfP38GIyx96KFVHFrQcj50PVgmN4Qg1lfEMx+QQLyc04NbTHXd39wvvD1/jeKyQP2oAM4JSpikgRxDD/XSZBPZlEnU/MZYbSp/RAITpclspQNh/A/dH5BDW9GNtYNX79rYTkiT9weTQhAUQ3Z7sJlzL+3maCxMDEU/9OlRsG+iY6lEyJVBC7XVtEKJ9Uswt2xzMxt0I+0trGCPdMhI/f2/5K0m1VJ4Lp0ep7OGobAtsBxnjW9CncgPMfhur/Ybi2R/hHbrYPOC8HiCh43YAeejDzeK2aMFi+0/AFt4nsAVXEQWzH9kdH4TilJ62dnkt5xeUM/p8zCI/oQmGOg=="
output_file = "output"  # 输出文件的名称

# 调用函数解码并写入文件
decode_base64_to_file(encoded_string, output_file)

将文件放入IDA进行分析:
根据提示知道是异或运算所以提取出所有的Xor的操作数
发现byte_804808F是另一组异或数据

LOAD:000000000804818B                 sub     rsp, 18h
LOAD:000000000804818F                 lea     rsi, byte_804808F
LOAD:0000000008048196                 mov     rdi, rsp
LOAD:0000000008048199                 mov     ecx, 18h
LOAD:000000000804819E                 rep movsb
LOAD:00000000080481A0                 xor     rcx, rcx
LOAD:00000000080481A3                 mov     al, [rsp+rcx]
LOAD:00000000080481A6                 xor     al, 0EFh
LOAD:00000000080481A8                 add     rcx, 1
LOAD:00000000080481AC                 mov     al, [rsp+rcx]
LOAD:00000000080481AF                 xor     al, 52
LOAD:00000000080481B1                 add     rcx, 1
LOAD:00000000080481B5                 mov     al, [rsp+rcx]
...
LOAD:000000000804826C                 xor     al, 0C2h
LOAD:000000000804826E                 add     rcx, 1
LOAD:0000000008048272                 mov     al, [rsp+rcx]
LOAD:0000000008048275                 xor     al, 65h
LOAD:0000000008048277                 add     rcx, 1
LOAD:000000000804827B                 mov     eax, 3Ch ; '<'
LOAD:0000000008048280                 syscall                 ; LINUX - sys_exit

用idapython提取出Xor的数据:

import idc
import idautils

def parse_hex_string(value):
    if value.endswith("h"):
        value = value[:-1]  # 去除"h"后缀
    return int(value, 16)
    
# 设置开始和结束地址
start_addr = 0x8048398
end_addr = 0x804846D
hex_value = []
# 从开始地址逐个遍历到结束地址
current_addr = start_addr
while current_addr < end_addr:
    # 获取当前地址的汇编指令
    disasm = idc.GetDisasm(current_addr)
    # 如果指令是 XOR 指令
    if "xor" in disasm:
        # 获取操作数
        operands = disasm.split(",")
        print(operands)
        op1 = operands[0].strip()
        op2 = operands[1].strip()
        
        print("操作数2: {}".format(op2))
        hex_value.append(parse_hex_string(op2) )
    # 移动到下一个指令
    current_addr = idc.next_head(current_addr)
print(hex_value)

提取出的Xor操作数:

[239, 82, 174, 165, 229, 14, 5, 39, 60, 224, 233, 48, 136, 39, 136, 97, 89, 31, 72, 88, 190, 68, 194, 101]

ida手动提取出byte_804808F:

[0xE3, 0xB7, 0xF6, 0xD8, 0xB3, 0x00, 0x5A, 0xB0, 0x69, 0x36, 0x5B, 0xC9, 0x00, 0xB9, 0x4C, 0xC6, 0xE4, 0x73, 0xF2, 0x38, 0x53, 0x76, 0x9C, 0x08]

上脚本:

next_byte = [239, 82, 174, 165, 229, 14, 5, 39, 60, 224, 233, 48, 136, 39, 136, 97, 89, 31, 72, 88, 190, 68, 194, 101]
target = [0xE3, 0xB7, 0xF6, 0xD8, 0xB3, 0x00, 0x5A, 0xB0, 0x69, 0x36, 0x5B, 0xC9, 0x00, 0xB9, 0x4C, 0xC6, 0xE4, 0x73, 0xF2, 0x38, 0x53, 0x76, 0x9C, 0x08]
flag = ""
for i in range(len(next_byte)):
    tmp = (next_byte[i]^target[i])&0xff
    if(tmp<=0xf):
        flag +="0"+hex(tmp)[2:]
    else:
        flag +=hex(tmp)[2:]
print(flag)
#0ce5587d560e5f9755d6b2f9889ec4a7bd6cba60ed325e6d

进行异或运算后发现:数据正确

1.多个文件分析

对比多个数据样本发现区别!

  1. 每个样本的xor操作数都不一样
  2. 目标数据的地址偏移也是不断变化的
    所以目标:找到方法能够提取不同样本的目标数据!

样本1,发现代码段位于文件最后,数据段在文件上部分

LOAD:0000000008048457                 add     rcx, 1
LOAD:000000000804845B                 mov     al, [rsp+rcx+18h+var_18]
LOAD:000000000804845E                 xor     al, 0F3h
LOAD:0000000008048460                 add     rcx, 1
LOAD:0000000008048464                 mov     al, [rsp+rcx+18h+var_18]
LOAD:0000000008048467                 xor     al, 6Fh
LOAD:0000000008048469                 add     rcx, 1
LOAD:000000000804846D                 mov     eax, 3Ch ; '<'
LOAD:0000000008048472                 syscall                 ; LINUX - sys_exit
LOAD:0000000008048472 start           endp
LOAD:0000000008048472
LOAD:0000000008048472 LOAD            ends
LOAD:0000000008048472
LOAD:0000000008048472
LOAD:0000000008048472                 end start

样本2,发现代码段位于文件中间,数据段在文件最后

LOAD:0000000008048265                 add     rcx, 1
LOAD:0000000008048269                 mov     al, [rsp+rcx]
LOAD:000000000804826C                 xor     al, 0C2h
LOAD:000000000804826E                 add     rcx, 1
LOAD:0000000008048272                 mov     al, [rsp+rcx]
LOAD:0000000008048275                 xor     al, 65h
LOAD:0000000008048277                 add     rcx, 1
LOAD:000000000804827B                 mov     eax, 3Ch ; '<'
LOAD:0000000008048280                 syscall                 ; LINUX - sys_exit
LOAD:0000000008048280 ; ---------------------------------------------------------------------------
LOAD:0000000008048282                 dw 0FCFEh, 3027h, 550h
LOAD:0000000008048288                 dq 9D349EFDA25BFFABh, 0DAFCA3461E0BFFA4h, 91226638289D2915h
LOAD:00000000080482A0                 dq 8F34B02E8AB0E172h, 1C877B8D51D0

2.通过解析汇编字节码定位数据偏移位置

先看ida里的伪代码:

LOAD:000000000804818B                 sub     rsp, 18h
LOAD:000000000804818F                 lea     rsi, byte_804808F
LOAD:0000000008048196                 mov     rdi, rsp

我要提取的是byte_804808F的地址,但是他是动态变化的!!
提取出汇编码:48 8D 35 F9 FE FF FF
48 8D 35是lea rsi的硬编码,F9 FE FF FF便是要跳转的偏移了!
可以通过Capstone来解析x86汇编代码来定位到byte_804808F的数据偏移位置!
找到偏移位置后提取0x18个数据就可以了!

def decrypt_base64_and_extract1(encoded_string):
    # 解密 base64 字符串
    decoded_data = base64.b64decode(encoded_string)

    # 寻找匹配的字节序列
    match_index = decoded_data.find(b'\x48\x89\xe7')

    # 如果找到匹配的字节序列
    if match_index != -1:
        # 寻找等于0x34的数据的下一个数据
        extracted_data = []
        index = match_index  # 匹配字节序列之后的索引
        # 提取匹配到的字节序列前面的指定字节数
        extracted_data = decoded_data[index - 7: index]
        # 初始化Capstone
        md = Cs(CS_ARCH_X86, CS_MODE_64)
        md.detail = True
        offest1 = 0
        # 反汇编二进制数据
        for i in md.disasm(extracted_data, 0x1000):
            print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
            # 汇编指令字符串
            assembly_instruction = i.op_str

            # 提取方括号中的算式
            start_index = assembly_instruction.find("[")
            end_index = assembly_instruction.find("]")
            expression = assembly_instruction[start_index + 1:end_index]

            # 检查字符串中是否包含加号或减号
            if "+" in expression:
                operator = "+"
            elif "-" in expression:
                operator = "-"
            else:
                print("无法识别的操作符。")
                exit()

            # 提取操作数和偏移量
            tokens = expression.split(operator)
            rip = match_index
            offset = int(tokens[1].strip(), 16)

            # 根据操作符执行加法或减法
            if operator == "+":
                result = rip + offset
            else:
                result = rip - offset
            offest1 = result
            print("计算结果:", hex(result))

        extracted_data = []
        while len(extracted_data) < 0x18:  # 提取0x18个元素
            extracted_data.append(decoded_data[offest1])  # 提取下一个数据
            offest1 += 1
        return extracted_data
    else:
        return None
3.根据汇编字节码特征提取xor汇编指令的操作数

先定位到:mov al, [rsp+rcx]的偏移位置在开始找xor的操作数

mov     al, [rsp+rcx]的硬编码:b'\x8A\x04\x0C'

由于xor的编码是0x34,而且每个xor的距离都是9个字节,所以提取起来比较简单!

def extract_data_from_base64(encoded_string):
    # 解密 base64 字符串
    decoded_data = base64.b64decode(encoded_string)

    # 寻找匹配的字节序列
    match_index = decoded_data.find(b'\x8A\x04\x0C')

    # 如果找到匹配的字节序列
    if match_index != -1:
        # 寻找等于0x34的数据的下一个数据
        extracted_data = []
        index = match_index + 3  # 匹配字节序列之后的索引
        while len(extracted_data) < 0x18:  # 提取0x18个元素
            if decoded_data[index] == 0x34:  # 如果当前数据等于0x34
                extracted_data.append(decoded_data[index + 1])  # 提取下一个数据
            index += 9
        return extracted_data
    else:
        return None
4.最后的解题脚本

一共要交互128次的样子,flag就被输出了!

import base64
from pwn import *
from capstone import *
sh=remote('hnctf.imxbt.cn',53833)
context(log_level='debug',arch='amd64',os='linux')

def extract_data_from_base64(encoded_string):
    # 解密 base64 字符串
    decoded_data = base64.b64decode(encoded_string)

    # 寻找匹配的字节序列
    match_index = decoded_data.find(b'\x8A\x04\x0C')

    # 如果找到匹配的字节序列
    if match_index != -1:
        # 寻找等于0x34的数据的下一个数据
        extracted_data = []
        index = match_index + 3  # 匹配字节序列之后的索引
        while len(extracted_data) < 0x18:  # 提取0x18个元素
            if decoded_data[index] == 0x34:  # 如果当前数据等于0x34
                extracted_data.append(decoded_data[index + 1])  # 提取下一个数据
            index += 9
        return extracted_data
    else:
        return None

def decrypt_base64_and_extract1(encoded_string):
    # 解密 base64 字符串
    decoded_data = base64.b64decode(encoded_string)

    # 寻找匹配的字节序列
    match_index = decoded_data.find(b'\x48\x89\xe7')

    # 如果找到匹配的字节序列
    if match_index != -1:
        # 寻找等于0x34的数据的下一个数据
        extracted_data = []
        index = match_index  # 匹配字节序列之后的索引
        # 提取匹配到的字节序列前面的指定字节数
        extracted_data = decoded_data[index - 7: index]
        # 初始化Capstone
        md = Cs(CS_ARCH_X86, CS_MODE_64)
        md.detail = True
        offest1 = 0
        # 反汇编二进制数据
        for i in md.disasm(extracted_data, 0x1000):
            print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
            # 汇编指令字符串
            assembly_instruction = i.op_str

            # 提取方括号中的算式
            start_index = assembly_instruction.find("[")
            end_index = assembly_instruction.find("]")
            expression = assembly_instruction[start_index + 1:end_index]

            # 检查字符串中是否包含加号或减号
            if "+" in expression:
                operator = "+"
            elif "-" in expression:
                operator = "-"
            else:
                print("无法识别的操作符。")
                exit()

            # 提取操作数和偏移量
            tokens = expression.split(operator)
            rip = match_index
            offset = int(tokens[1].strip(), 16)

            # 根据操作符执行加法或减法
            if operator == "+":
                result = rip + offset
            else:
                result = rip - offset
            offest1 = result
            print("计算结果:", hex(result))

        extracted_data = []
        while len(extracted_data) < 0x18:  # 提取0x18个元素
            extracted_data.append(decoded_data[offest1])  # 提取下一个数据
            offest1 += 1
        return extracted_data
    else:
        return None
sleep(1)

sh.recvuntil(b'Expected bytes: ')
tar = sh.recv(48)
sh.recvuntil(b'Bytes')
sh.sendline(tar)
idx = 128
while idx > 0:
    # 要解密的 base64 加密字符串
    encoded_string = ""  # 在这里替换为您的 base64 加密字符串
    sh.recvuntil(b'ELF:  ')
    line = sh.recvline()
    encoded_string = line.rstrip(b"\n")

    # 调用函数解密并提取数据
    next_byte = extract_data_from_base64(encoded_string)
    target = decrypt_base64_and_extract1(encoded_string)
    print(next_byte)
    print(target)
    flag =""
    for i in range(len(next_byte)):
        tmp = (next_byte[i]^target[i])&0xff
        if(tmp<=0xf):
            flag +="0"+hex(tmp)[2:]
        else:
            flag +=hex(tmp)[2:]
    sleep(0.5)
    sh.recvuntil(b'Bytes?')
    sh.sendline(flag)
    idx -= 1
sh.interactive()
最喜欢的逆向题

flag是明文写的主要是要找:
看伪代码:

    if ( Buffer[5] == 105 && Buffer[7] == Buffer[10] && Buffer[15] == Buffer[22] )

所以符合条件的flag是:H&NCTF{Do_Y0u_like_F5_1n_Rev}

    case 105:
      printf("H&NCTF{Do_Y0u_like_F5_1n_Rev}");
childmaze

表面是迷宫逆向,其实只要找到关键字符串发现就是个异或加密!
考察的主要是眼力
flag:H&NCTF{Ch411enG3_0f_M4z3}
发现是简单的异或加密:

target = "H'L@PC}Ci625`hG2]3bZK4{1~"
for i in range(len(target)):
    print(chr( ord(target[i]) ^ (i%7)),end="")
#输出:H&NCTF{Ch411enG3_0f_M4z3}

代码审计的时候发现了关键代码:

    qmemcpy(KeyStr, "H'L@PC}Ci625`hG2]3bZK4{1~", 25);
    copyKeystr = KeyStr;
    ...
    
    for ( i = 0i64; i < 0x19; ++i )             // 关键位置
    {
      LODWORD(v306) = *(copyKeystr + i) ^ (i % 7u);
      *&v308 = &v306;
      *(&v308 + 1) = _$LT$char$u20$as$u20$core..fmt..Display$GT$::fmt::h4b602de30937fa88;
      *&Tmp = &off_7FF738153868;
      *(&Tmp + 1) = 1i64;
      v312 = &v308;
      v313 = 1ui64;
      std::io::stdio::_print::h6e1d82d125fe1319(&Tmp);
    }
DO YOU KNOW SWDD?

发现是smc,动调调试找到主要加密逻辑!发现scanf只能输入一个字符
找到逻辑就可以直接搓脚本了!
flag:H&NCTF{I_LOVE_SWDD_WHAT_ABOUT_YOU}

target = "S_VYFO_CGNN_GRKD_KLYED_IYE"
tag = 0
print("H&NCTF{",end="")
for cha in target:
    tag = 0
    for i in range(65,91):
        if((i + 10 - 65) % 26 + 65 == ord(cha)):
            print(chr(i),end="")
            tag = 1
            break
    if(tag==0):
        print(cha,end="")
print("}",end="")
# H&NCTF{I_LOVE_SWDD_WHAT_ABOUT_YOU}

解题路径:动态调试找到加密逻辑:
找到加密逻辑:

// positive sp value has been detected, the output may be wrong!
void __cdecl __noreturn sub_417000(char *Str1)
{
  char v1; // [esp-FCh] [ebp-208h]
  char Str2[36]; // [esp+D0h] [ebp-3Ch] BYREF
  int i; // [esp+F4h] [ebp-18h]
  int v4; // [esp+100h] [ebp-Ch]

  __CheckForDebuggerJustMyCode(&unk_41E015);
  v4 = 10;
  for ( i = 0; Str1[i]; ++i )
  {
    if ( Str1[i] >= 65 && Str1[i] <= 90 )
      Str1[i] = (Str1[i] + v4 - 65) % 26 + 65;
  }
  strcpy(Str2, "S_VYFO_CGNN_GRKD_KLYED_IYE");
  if ( !j_strcmp(Str1, Str2) )
  {
    printf("yes, you are right\n", v1);
    exit(0);
  }
  printf("Try Again!", v1);
  exit(0);
}
hnwanna

flag:H&NCTF{ozxyfjfxdzsnydlfrj}
思路:找到Assembly-CSarp.dll里的LoadLevel()和a(),加密逻辑就出来了
逻辑出来了脚本也就出来了!

def a(input_str, shift):
    text = ""
    for c in input_str:
        if c.isalpha():
            text += chr((ord(c.lower()) - ord('a') + shift) % 26 + ord('a'))
        else:
            text += c
    return text

print("H&NCTF{"+a("justaeasyunitygame",5)+"}")
#H&NCTF{ozxyfjfxdzsnydlfrj}

解题路程:
找到核心逻辑dll文件,Assembly-CSarp.dll
Dnspy进行C#反编译
找到LoadLevel()和a()就可以获得flag了!

// GameWindow
// Token: 0x0600001B RID: 27 RVA: 0x0000265C File Offset: 0x0000085C
public void LoadLevel()
{
	GameObject gameObject = Object.Instantiate<GameObject>(this.levelArr[this.levelCount]);
	gameObject.name = "Level" + this.levelCount.ToString();
	gameObject.transform.SetParent(base.transform, false);
	this.player.transform.localPosition = gameObject.transform.Find("StartPoint").localPosition;
	if (this.levelCount == 5)
	{
		this.FlagText = GameObject.Find("Tip").GetComponent<Text>();
		string input = "justaeasyunitygame";
		string str = this.a(input, this.levelCount);
		this.FlagText.text = "H&NCTF{" + str + "}";
	}
}
	// Token: 0x0600001A RID: 26 RVA: 0x000025F8 File Offset: 0x000007F8
	public string a(string input, int shift)
	{
		string text = "";
		foreach (char c in input)
		{
			if (char.IsLetter(c))
			{
				text += ((char)(((int)(c - 'a') + shift) % 26 + 97)).ToString();
			}
			else
			{
				text += c.ToString();
			}
		}
		return text;
	}

Misc

ManCraft - 娱乐题

击杀牢大就可获得key,获得flag
H&CTF{d4ea9253161048e7a129d337bd4e383f}

请添加图片描述

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
buuctfmisc是一个CTF比赛中的题目,涉及到网络分析工具Wireshark的使用。Wireshark是一款开源的网络协议分析工具,可以用于捕获和分析网络数据包。在buuctfmisc题目中,可能需要使用Wireshark来解析和分析给定的网络数据包,以获取关键信息或者解决问题。 具体的题解步骤可能因题目而异,但通常的解题思路如下: 1. 下载并安装Wireshark:首先需要从Wireshark官网下载并安装适合你操作系统的版本。 2. 打开Wireshark并开始捕获数据包:打开Wireshark后,选择合适的网络接口开始捕获数据包。可以通过点击"Capture"按钮或者使用快捷键Ctrl + E来开始捕获。 3. 分析捕获的数据包:Wireshark会将捕获到的数据包以列表形式展示出来。你可以通过点击每个数据包来查看其详细信息,包括源IP地址、目标IP地址、协议类型等。 4. 过滤数据包:如果题目要求只关注特定的数据包,你可以使用Wireshark提供的过滤功能来筛选出符合条件的数据包。过滤条件可以根据协议、源IP地址、目标IP地址等进行设置。 5. 提取关键信息:根据题目要求,你可能需要从数据包中提取关键信息。Wireshark提供了多种功能来帮助你提取数据,比如导出数据包、导出特定协议的数据等。 6. 分析数据包内容:根据题目要求,你可能需要进一步分析数据包的内容。Wireshark可以解析多种协议,你可以查看每个数据包的协议栈、协议字段等信息。 7. 解决问题或回答题目:根据你对数据包的分析和提取的关键信息,你可以解决问题或者回答题目。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值