吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2818|回复: 7
收起左侧

[CTF] 【BUUCTF】Reverse篇部分总结

[复制链接]
BurYiA 发表于 2022-8-4 13:41

孩子可太想升个级了,放一些之前写的一些buuctf逆向题目的总结

easyre

首先查一下壳以及其他相关信息
BurYiA1589757804.png
可以看到是 64 位程序,没壳,那就可以直接用IDA打开了

IDA一键F5
BurYiA1589757813.png
真·easyre

reverse1

拿到题目,继续查壳,看信息
BurYiA1589759515.png
64位程序,没壳
IDA打开后找主函数逻辑,然后.....点了半天没找到
直接shift+F12查看字符串
BurYiA1589759557.png

可以发现这里有一个关键字符串

this is the right flag!

点进去,x查看交叉引用便可以找到主函数
BurYiA1589759589.png

可以看到的是在这个字符串上面有个 strcpy 对比,其中一个是我们输入的字符串,而另一个不出意外就是flag了,点进去看看
BurYiA1589759608.png

加上 flag 前缀后我们直接提交,但很难受的就是可以发现flag是错误的。
我们继续去分析他的主函数
在上面我们可以看到他对 flag 做了与一个操作

for ( j = 0; ; ++j )
{
    v8 = j;
    v2 = j_strlen(Str2);
    if ( v8 > v2 )
        break;
    if ( Str2[j] == 111 )
        Str2[j] = 48;
}

注意他的第二个 if 判断,按 r 我们把 ascll 码变成字符,可以发现他是把字符串中的所有的 “o” 变成了 “0”
然后就不用我多说了吧,提交flag就行

reverse2

查壳

Lily_1611716862.png
64位程序,无壳
用IDA打开程序,分析主函数

Lily_1611676056.png

可以看到程序是将我们输入的数据与flag做对比,并且在对比之前将flag中的r和i字符换成了1
剩下的就不用多说了吧,记得flag的格式“flag{}”

当然,比对类的题目可以直接用动态调试来查看存储在程序中的字符串是什么
ELF文件,用gdb打开,在第一个输入点处ctrl+c断下来,单步随便输入一串字符串后就可以在strcmp处下断了(在输入后最好单步再走几步,将输入的函数先执行完成)
再用continue运行,就能直接看strcmp函数中的参数了,如下图

Lily_1611716671.png

内涵的软件

查看程序基本信息

Lily_1612490409.png

32位程序,没有加壳
选择IDA32打开查看主要函数

Lily_1612490642.png

很明显答案已经摆出来了,结合题目要求“注意:得到的 flag 请包上 flag{} 提交”换一下flag格式就可以了

新年快乐

查看程序基本信息

Lily_1612491085.png

32位程序,可以很惊奇的发现终于带壳了
掏出x32dbg开始手脱

UPX的壳一般会有pushad和popad
打开后一路F9找pushad,找到后F8运行一步,再在栈顶下硬件访问断点(也可以单步,找大跳转就行,单步的过程中遇到向上的跳转记得在下面F4,向上的跳转是循环,没必要再去单步跟循环了)

Lily_1612493785.png

直接F9,就可以发现已经过了popad,在下面不远处我们可以发现一个很大的跳转,一般就是程序入口了

Lily_1612493935.png

dump后用IDA打开进行分析

Lily_1612494663.png

可以看到程序还是很简单的,重点就是在考察脱壳能力

guessgame

查看程序相关信息

Lily_1612495883.png

64位程序,没有加壳
IDA打开分析

Lily_1612496053.png

可以看到没有什么有用的东西

Shift+F12看一下字符串窗口

Lily_1612496072.png

...行吧

helloword

一道安卓题目
先查壳

Lily_1612499427.png

无壳,直接拖进jadx中进行分析

Lily_1612498926.png

xor

Linux下的程序,直接拖入IDA分析

Lily_1612503006.png

可以看到程序逻辑是将我们输入的字符串从第二位开始异或,再与存储在程序内部的字符串做比较(Shift+E可以将数据提取出来,记得先全选,在提取)

Lily_1612503179.png

因为xor再异或一次便可以得到原先的内容,写脚本跑一下便可

#include<bits/stdc++.h>
using namespace std;

unsigned char ida_chars[] =
{
  102,  10, 107,  12, 119,  38,  79,  46,  64,  17, 
  120,  13,  90,  59,  85,  17, 112,  25,  70,  31, 
  118,  34,  77,  35,  68,  14, 103,   6, 104,  15, 
   71,  50,  79,   0
};
unsigned char flag[44]={102,0};

int main(int argc, char const *argv[])
{
    for (int i = 1; i < 33; i++)
        flag[i] = ida_chars[i] ^ ida_chars[i-1];

    cout << flag;

    return 0;
}

// 10110
// 11011
// 10110

reverse3

查看程序相关信息

Lily_1612513351.png

32位程序,没有加壳

用IDA打开分析主要函数

Lily_1612515691.png

可以看到首先是将输入的字符串进行一个函数变换
变换后的字符串中每个元素再加上自己的下标
完成后再与程序内部所存储的字符串进行对比

跟进对输入的字符串进行操作的函数后可以发现是一个Base64编码的操作
则我们只要将程序内存储的字符串先进行每个元素减去下标的操作,在进行Base64解码即可反推出flag

#include<bits/stdc++.h>
using namespace std;

unsigned char ida_chars[] =
{
  101, 51, 110, 105, 102, 73, 72, 57, 98, 95, 
  67, 64, 110, 64, 100, 72, 0
};
unsigned char flag[44]={0};

int main(int argc, char const *argv[])
{
    for (int i = 0; i < 16; i++)
        flag[i] = ida_chars[i] - i ;

    cout << flag;
    return 0;
}

不一样的flag

查壳,看信息

BurYiA1589758255.png

32位,没壳

IDA打开我们来分析逻辑

BurYiA1589758359.png

首先我们可以看到在下面他有提示让输入上下左右,其次在上面我们注意到有这么一行

mov     ebx, offset __data_start__ ; "*11110100001010000101111#"

注意最后的那个字符串基本是由 0 和 1 组成,并且首位是 * ,末尾是 #,那这个是绝对是个迷宫题没跑了
既然是迷宫题,我们就需要知道他是几行几列的迷宫题,这样我们才能去解
F5 分析逻辑,我们注意主函数最后的这一块

BurYiA1589758404.png

可以看到迷宫的一层有5个字母,并且flag就是我们输入的内容
那显然迷宫就是这样的了

*1111
01000
01010
00010
1111#

flag 就不写了,扫一眼就出来了

SimpleRev

一道Linux下的题目,但我死活跑不起来,就很气...
程序基本信息

Lily_1612579762.png

64位程序
IDA打开进行分析,主要的flag验证部分在Decry函数,因太长不好截图,直接粘贴在下面

unsigned __int64 Decry()
{
    char v1; // [rsp+Fh] [rbp-51h]
    int v2; // [rsp+10h] [rbp-50h]
    int v3; // [rsp+14h] [rbp-4Ch]
    int i; // [rsp+18h] [rbp-48h]
    int v5; // [rsp+1Ch] [rbp-44h]
    char src[8]; // [rsp+20h] [rbp-40h] BYREF
    __int64 v7; // [rsp+28h] [rbp-38h]
    int v8; // [rsp+30h] [rbp-30h]
    __int64 v9[2]; // [rsp+40h] [rbp-20h] BYREF
    int v10; // [rsp+50h] [rbp-10h]
    unsigned __int64 v11; // [rsp+58h] [rbp-8h]

    v11 = __readfsqword(0x28u);
    *(_QWORD *)src = 'SLCDN';                     // 原先的数据在内存中为小端存储
                                                  // 故应为:NDCLS
    v7 = 0LL;
    v8 = 0;
    v9[0] = 'wodah';                              // 同理应为:shadow
    v9[1] = 0LL;
    v10 = 0;
    text = join(key3, (const char *)v9);          // text = killshadow
    strcpy(key, key1);
    strcat(key, src);                             // key = ADSFKNDCLS
    v2 = 0;
    v3 = 0;
    getchar();
    v5 = strlen(key);
    for ( i = 0; i < v5; ++i )                    // 将key中大写全部换成小写
    {
        if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
            key[i] = key[v3 % v5] + 32;
        ++v3;                                       // v3 = v5
    }                                             // key = adsfkndcls
    printf("Please input your flag:");
    while ( 1 )
    {
        v1 = getchar();
        if ( v1 == '\n' )
            break;
        if ( v1 == ' ' )
        {
            ++v2;
        }
        else                                        // 输入字符不为空格或者回车时
        {
            if ( v1 <= '`' || v1 > 'z' )
            {
                if ( v1 > '@' && v1 <= 'Z' )            // 大写字符
                {
                    str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
                    ++v3;
                }
            }
            else                                      // 小写字符
            {
                str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
                ++v3;
            }
            if ( !(v3 % v5) )
            putchar(' ');
            ++v2;
        }
    }
    if ( !strcmp(text, str2) )
        puts("Congratulation!\n");
    else
        puts("Try again!\n");
    return __readfsqword(0x28u) ^ v11;
}

可以看到是一个加密变换,key和最终的text都能直接得到(注意小端的问题),那么写个脚本跑一下即可

#include<bits/stdc++.h>
using namespace std;

char key[] = "adsfkndcls";
char text[] = "killshadow";
char flag[44] = {0};

int main(int argc, char const *argv[])
{
    for (int i = 0; i < 10; i++)
    {
        for (char j = 65; j < 126; j++)
        { 
            if(j>=91 && j<=96)
                continue;
            if((j - 39 - key[i] + 97) % 26 + 97 == text[i])
            {
                flag[i] = j;
                break;
            }
        }
    }
    cout << flag;

    return 0;
}

Java逆向解密

.class 文件,用jadx直接打开看java代码

import java.util.ArrayList;
import java.util.Scanner;

/* renamed from: reverse  reason: default package */
public class reverse {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("Please input the flag :");
        String str = s.next();
        System.out.println("Your input is :");
        System.out.println(str);
        Encrypt(str.toCharArray());       // toCharArray() 方法将字符串转换为字符数组。
    }

    public static void Encrypt(char[] arr) {
        int[] KEY;
        ArrayList<Integer> Resultlist = new ArrayList<>();
        for (char c : arr) {
            Resultlist.add(Integer.valueOf((c + '@') ^ 32));    // valueOf() 方法用于返回给定参数的原生 Number 对象值,参数可以是原生数据类型, String等。
                                                                //此处返回一个int值
        }
        System.out.println(Resultlist);
        ArrayList<Integer> KEYList = new ArrayList<>();
        for (int i : new int[]{180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65}) {
            KEYList.add(Integer.valueOf(i));
        }
        System.out.println("Result:");
        if (Resultlist.equals(KEYList)) {
            System.out.println("Congratulations!");
        } else {
            System.err.println("Error!");
        }
    }
}

计算什么的好烦的,直接爆破

import java.util.ArrayList;

/* renamed from: reverse  reason: default package */
public class reverse {
    public static void main(String[] args) {
        int text[] = {180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65};
        ArrayList<Integer> flag = new ArrayList<>();

        for(int i=0; i<18; i++)
            for(char c=33; c<128; c++)
            {
                if(Integer.valueOf((c + '@') ^ 32) == text[i])
                    flag.add(Integer.valueOf(c));
            }
            // flag.add(33);

        System.out.println(flag);
    }
}

爆破得到的数组通过ascll转一下即可
需要注意点的是需要将全部的可见字符全部用于跑字典,不然就会出现问题

[GXYCTF2019]luck_guy

DIE 分析后可以看到为 elf 64 位程序,先用ida64打开看是否可以分析出什么
发现有一个 get_flag 函数,那不出意外获取 flag 的逻辑就在这里了

unsigned __int64 get_flag()
{
  unsigned int v0; // eax
  int i; // [rsp+4h] [rbp-3Ch]
  int j; // [rsp+8h] [rbp-38h]
  __int64 s; // [rsp+10h] [rbp-30h] BYREF
  char v5; // [rsp+18h] [rbp-28h]
  unsigned __int64 v6; // [rsp+38h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v0 = time(0LL);
  srand(v0);
  for ( i = 0; i <= 4; ++i )
  {
    switch ( rand() % 200 )
    {
      case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);
        strcat((char *)&s, f1);  // GXY{do_not_
        strcat((char *)&s, &f2);
        printf("%s", (const char *)&s);
        break;
      case 2:
        printf("Solar not like you");
        break;
      case 3:
        printf("Solar want a girlfriend");
        break;
      case 4:
        s = 0x7F666F6067756369LL;
        v5 = 0;
        strcat(&f2, (const char *)&s);
        break;
      case 5:
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            *(&f2 + j) -= 2;
          else
            --*(&f2 + j);
        }
        break;
      default:
        puts("emmm,you can't find flag 23333");
        break;
    }
  }
  return __readfsqword(0x28u) ^ v6;
}

大致观察后可以发现是在 case 4 中给 f2 赋值,之后再 case 5 中对 f2 进行变换,最后在 case 1 中进行合并输出
写个脚本跑一些就可以得到结果
需要注意到是,f2 是存储在栈中的,所以写 f2 数组时应该倒着写,

char arr[] = {0x69, 0x63, 0x75, 0x67, 0x60, 0x6f, 0x66, 0x7f, 0};

得到的 flag 改成 flag{} 格式再提交...

findit

又一道安卓 apk 题目
Android-packinfo 查壳后没有发现什么信息,直接用 jadx 打开
找到主函数

package com.example.findit;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
    /* access modifiers changed from: protected */
    @Override // android.support.v7.app.ActionBarActivity, android.support.v4.app.FragmentActivity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText edit = (EditText) findViewById(R.id.widget2);
        final TextView text = (TextView) findViewById(R.id.widget1);
        final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
        final char[] b = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
        ((Button) findViewById(R.id.widget3)).setOnClickListener(new View.OnClickListener() {
            /* class com.example.findit.MainActivity.AnonymousClass1 */

            public void onClick(View v) {
                char[] x = new char[17];
                char[] y = new char[38];
                for (int i = 0; i < 17; i++) {
                    if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
                        x[i] = (char) (a[i] + 18);
                    } else if ((a[i] < 'A' || a[i] > 'Z') && (a[i] < 'a' || a[i] > 'z')) {
                        x[i] = a[i];
                    } else {
                        x[i] = (char) (a[i] - '\b');
                    }
                }
                if (String.valueOf(x).equals(edit.getText().toString())) {
                    for (int i2 = 0; i2 < 38; i2++) {
                        if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {
                            y[i2] = b[i2];
                        } else {
                            y[i2] = (char) (b[i2] + 16);
                            if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
                                y[i2] = (char) (y[i2] - 26);
                            }
                        }
                    }
                    text.setText(String.valueOf(y));
                    return;
                }
                text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
            }
        });
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

很明显的可以看到有个地方会输出答案错误,那他上面的函数不出意外就是 flag 的输出函数了
继续写个脚本跑就行,要注意的是如果使用c/c++写,数组需要定义为 unsigned char,不然会溢出造成显示出问题

#include<iostream>
using namespace std;

char b[] = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
unsigned char y[40]={0};

int main(int argc, char const *argv[])
{

    for (int i2 = 0; i2 < 38; i2++) 
    {
        if ( (b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z') ) 
            y[i2] = b[i2];
        else
        {
            y[i2] = (char) (b[i2] + 16);
            if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') 
                y[i2] = (char) (y[i2] - 26);
        }
    }
    cout << y << endl;
    for (int i = 0; y[i]!=0; i++)
    {
        cout << (int)y[i] << " ";
    }

    return 0;
}

简单注册器

还是安卓
主要逻辑在这里

public void onClick(View v) {
    int flag = 1;
    String xx = editview.getText().toString();
    if (!(xx.length() == 32 && xx.charAt(31) == 'a' && xx.charAt(1) == 'b' && (xx.charAt(0) + xx.charAt(2)) - 48 == 56)) {
        flag = 0;
    }
    if (flag == 1) {
        char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
        x[2] = (char) ((x[2] + x[3]) - 50);
        x[4] = (char) ((x[2] + x[5]) - 48);
        x[30] = (char) ((x[31] + x[9]) - 48);
        x[14] = (char) ((x[27] + x[28]) - 97);
        for (int i = 0; i < 16; i++) {
            char a = x[31 - i];
            x[31 - i] = x[i];
            x[i] = a;
        }
        textview.setText("flag{" + String.valueOf(x) + "}");
        return;
    }
    textview.setText("输入注册码错误");
}

直接跑就行,够清晰了,跑出来记得加一个 flag 框

[GWCTF 2019]pyre

pyc 文件,用在线工具逆向源码

#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num

for i in range(l - 1):
    code[i] = code[i] ^ code[i + 1]

print code
code = [
    '\x1f',
    '\x12',
    '\x1d',
    '(',
    '0',
    '4',
    '\x01',
    '\x06',
    '\x14',
    '4',
    ',',
    '\x1b',
    'U',
    '?',
    'o',
    '6',
    '*',
    ':',
    '\x01',
    'D',
    ';',
    '%',
    '\x13']

根据题目意思写了一个 c 语言版本的,用来测试

#include<bits/stdc++.h>
using namespace std;

string arr = "flag{abcabc}";
int brr[32];

int main(int argc, char const *argv[])
{
    int length = arr.length();

    for (int i = 0; i < length; i++)
    {
        int num = ((arr[i] + i) % 128 + 128) % 128;
        brr[i] = num;
    }
    for (int i = 0; i<length; i++)
        cout << (int)brr[i] << ", ";
    cout << endl;

    for(int i=0; i<length-1; i++)
        brr[i] = brr[i] ^ brr[i + 1];
    for (int i = 0; i<length; i++)
        cout << (int)brr[i] << ", ";
    cout << endl;

    for(int i=length-2; i>=0; i--)
        brr[i] = brr[i] ^ brr[i + 1];
    for (int i = 0; i<length; i++)
        cout << (int)brr[i] << ", ";
    cout << endl;

//     l = len(input1)
// for i in range(l):
//     num = ((input1[i] + i) % 128 + 128) % 128
//     code += num
    return 0;
}

异或部分倒着写就行,前面的求余那一块直接爆破即可

#include<iostream>
#include<cstring>
using namespace std;

char code[] = { 0x1f, 0x12, 0x1d, '(', '0', '4', 0x01, 0x06, 0x14, '4', ',', 0x1b, 'U', '?', 'o', '6', '*', ':', 0x01, 'D', ';', '%', 0x13, 0};
// char code[] = {102, 109, 99, 106, 127, 102, 104, 106, 105, 107, 109, 8, 0};
// char code[] = {11, 14, 9, 21, 25, 14, 2, 3, 2, 6, 101, 8, 0};
char arr[100] = {0};

int main(int argc, char const *argv[])
{
    int l = strlen(code);

    for (int i = 0; i<l; i++)
        cout << (int)code[i] << ", ";
    cout << endl;

    for(int i=l-1; i>=0; i--)
        code[i] = code[i] ^ code[i + 1];
    for (int i = 0; i<l; i++)
        cout << (int)code[i] << ", ";
    cout << endl;

    for (int i = 0; i < l; i++)
        for (int j = 32; j < 128; j++)
            if( ((j + i) % 128 + 128) % 128 == code[i])// && j>=32 && j<=126)
            {
                arr[i] = j;
                // break;
            }

    cout << arr << endl;
    for (int i = 0; i<l; i++)
        cout << (int)arr[i] << " ";
    cout << endl;

    return 0;
}

得到的 flag 记得换格式提交

[BJDCTF2020]JustRE

没啥说的,函数不多,翻一下就出来了

INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
  CHAR String[100]; // [esp+0h] [ebp-64h] BYREF

  if ( a2 != 272 )
  {
    if ( a2 != 273 )
      return 0;
    if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    {
      sprintf(String, Format, ++dword_4099F0);
      if ( dword_4099F0 == 19999 )
      {
        sprintf(String, " BJD{%d%d2069a45792d233ac}", 19999, 0);
        SetWindowTextA(hWnd, String);
        return 0;
      }
      SetWindowTextA(hWnd, String);
      return 0;
    }
    EndDialog(hWnd, (unsigned __int16)a3);
  }
  return 1;
}

免费评分

参与人数 3威望 +1 吾爱币 +21 热心值 +3 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
m52pj + 1 热心回复!
HongHu106 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

akarus 发表于 2022-8-4 22:00
不错,LZ能打包下原文件吗
iloveasdl 发表于 2022-8-5 08:47
 楼主| BurYiA 发表于 2022-8-5 19:07
akarus 发表于 2022-8-4 22:00
不错,LZ能打包下原文件吗

啊这个,基本都是buu上的原题,当时也没有刻意保存文件,这个……不好意思
zclandy520 发表于 2022-8-6 13:55
谢谢分享
ludonghengsb 发表于 2022-8-6 21:23
感谢大佬分享!
hyxwh123 发表于 2022-8-10 10:45
写的真好!请问可以发一下资料嘛
13613105079 发表于 2024-1-26 21:32
感谢分享  有crypto的吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 14:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表