安全矩阵

 找回密码
 立即注册
搜索
查看: 5319|回复: 0

0基础手把手入坑CTF逆向(2)——静态分析学习

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2021-2-24 21:56:52 | 显示全部楼层 |阅读模式
原文链接:0基础手把手入坑CTF逆向(2)——静态分析学习

前言
已发表文章列表:
《0基础手把手入坑CTF逆向(1)——汇编语言学习》(https://mp.weixin.qq.com/s/GumrtTSrqvVc7TstXfuD0g

通过前面一篇文章我们学习了汇编语言,接下来我们就要讲一讲逆向中的两大必备技能,静态分析和动态调试了。俗话说:“工欲善其事,必先利其器”,我们挑选一个合适的静态分析工具是非常有必要的,我推荐初学者使用IDA来进行静态分析,毕竟在CTF逆向静态分析中IDA可谓是最NB的工具,没有之一。下面就介绍一下IDA的基本用法,万一有遗漏的地方还请大家多多包涵,提出宝贵意见。

IDA讲解
0x00 IDA简介
静态分析,也就是在不运行二进制程序的情况下直接分析程序中的机器指令等各种信息。通过静态的分析反汇编代码或者伪代码来解出Flag。IDA就是目前最常用到的用来静态分析的反汇编工具,它使用的是递归下降反汇编算法,支持的文件类型也非常丰富,除了常见的PE(Windows下的可执行文件)、ELF(Linux下的可执行文件)格式之外还支持DOS、Java、.NET等文件格式。下面我们就一起来学习一下在CTF逆向中IDA的常见用法。
本文所用练习程序附件:链接:https://pan.baidu.com/s/1W-nSQH6P29to0Bn9zn1zXQ   提取码:yuye

0x01 IDA用法讲解
IDA安装目录
IDA安装目录中有三个是我们需要知道的,第一个dbgsrv文件夹是关于远程调试的,例如远程调试安卓或者IOS、linux、windows等机器,需要被调试方开启相应的服务和端口让IDA去连接;第二个是idc文件夹,主要存放一些脚本文件,我们可以利用脚本来进行一些运算,例如字符串提取转换,批量修改和提取内容等操作,不过现在在CTF逆向中一般使用python写脚本解决一些运算;第三个文件夹是插件文件夹,我们可以给IDA安装各种插件,比如识别加密算法、美化IDA显示等等,大家可以去下面吾爱破解去寻找自己喜欢的插件,并学习如何安装和使用。
https://www.52pojie.cn/thread-1016307-1-1.html
剩下的几个文件夹:
cfg主要包含各种配置文件,比如基本IDA配置文件ida.cfg
ids文件夹主要包含符号文件,这些文件用于描述可被加载到IDA的二进制文件引用的共享库内容
loaders文件夹包含在文件加载过程中用于识别可执行文件的ida扩展
procs文件夹包含IDA所支持的处理器模块
sig文件夹包含IDA在各种模式匹配操作中利用的现有代码的签名
til文件夹包含一些类型库信息,IDA通过这些信息记录特定于各种编译器库的数据结构的布局

IDA分为32位和64位两个版本,我们在选择反汇编程序的时候要选择对应的IDA版本。

如果不确定程序是32位还是64位大家可以把程序拖入010Editor查看其PE文件结构
一般32位文件在文件开头的不远处有如下图所示PE...L





64位程序的话就是就是如下图所示


加载文件
我们在打开IDA时会看到下面一个画面,点击New按钮会让你选择你想要打开的程序,不过我一般都是点击Go,然后在打开的界面中将文件拖拽进去,下面的Previous按钮或者双击下面列表中的项目可以快速打开之前打开过的文件。
因为打英文太麻烦,而且大家的英文水平肯定也不差,所以下面我就用IDA7.0永乐汉化版的IDA来进行讲解,功能和英文版都是一样的,更方便大家的理解和学习。最后我会使用一个真实的CTF题目去做演示讲解。

我们打开文件的时候会弹出下面窗口且正确识别出了我们程序的版本(80386(PE)),我们初学者使用里面的默认设置就可以了,点击确定就可以加载文件进入IDA了(初次使用IDA的时候IDA可能会弹出是否使用“Proximity Browser”的对话框,我们选择NO就可以)

下图中几个文件为IDA加载程序时产生的数据库文件,关闭项目时这几个文件将被存档为一个IDB文件,里面保存着被分析程序的分析结果(例如分析时添加的注释、分析时的界面等),当被分析文件目录下有对应的数据库文件时,会弹出确认窗口,点击OverWrite就是覆盖,点击Load existing就是加载已存在,我们如果点击了加载已存在IDA就会打开现有数据库文件,将界面恢复为上次关闭时的状态,上次分析时添加修改的内容也不会变。

IDA退出时会提示如下图,默认时保存分析时的数据库文件,我们也可以点击不要保存数据库。

因为IDA功能特别多而且也并不一定都适用于CTF逆向,所以为了初学者入门,我只挑选CTF逆向中常用功能进行讲解。

主界面
刚加载完程序后会显示为如下图所示界面,左侧为被分析的程序所用到的函数,点击可查看其汇编代码,右侧为图形视图(CTRL+鼠标滚轮可以调整窗口大小),里面将条件判断和交叉引用都标注了出来,绿色的线代表条件成立之后跳转的代码,红色的就是没有成功跳转的代码。然后上面的箭头指向的一栏是各种数据的展示窗口。

在图形视图上使用空格键可以切换为代码视图,代码视图标注了当前代码的地址,分析出的函数、参数、局部变量、常量、结构体、交叉引用信息等。

大家如果想让IDA像OD一样显示操作码,就可在选项-》常规-》按照下图把操作码字节数改为8就可以了。

上面那条彩带叫做导航带,导航带时加载文件的地址空间的线性视图,默认情况下其会呈现二进制文件的整个地址范围。不同的颜色表示不同类型的文件,如代码或者数据。同时,还会有一个细小的当前未知指示符(默认为黄色)指向与当前反汇编窗口中显示的地址对应的导航带地址。我们通常分析程序时略过其库函数而分析程序的常规函数,我们可以根据导航带确定大致的范围。

IDA还有很多展示数据的视图,我们可以在视图中打开子视图,例如shift+F12打开程序的字符串窗口,系统会从二进制文件中自动分析出程序使用了哪些字符串,还可以在窗口上点击右键—》建立,来设置扫描类型,我们双击窗口中的字符串反汇编窗口将跳到该字符串所在的地址处,然后我们结合交叉引用可以快速定位程序中任何引用该字符串的位置。有时候通过字符串窗口查找关键字符串来定位程序关键代码位置是效率非常高的一种方法(后面我演示的题目也是使用的字符串定位的关键函数)。


双击字符串到引用位置,然后按CTRL+X显示出该字符串的交叉引用,如下图所示该字符串在函数sub_401090中被引用了。

我们双击即可跳到字符串引用处。

视图里面的输入(imports)窗口,可以列出当前可执行文件调用的所有函数(也就是程序的导入表),包括函数名称、包含该函数的库的名称,还有其虚拟地址,双击即可跳转到反汇编窗口的函数地址处。

输出窗口显示程序的导出信息,如果没有导出信息,默认有一个就是程序的OEP,名称为start

名称窗口:将IDA能识别的函数、数据、名称都列了出来,分为以下几类。
F:常规函数
L:库函数
I:导入的名称
C:命名代码
D:数据
A:字符串数据

十六进制窗口:我们可以按F2键对其中的数据进行修改,修改后再次按F2键即可应用。
结构体窗口:IDA可以根据类型库识别常用的系统结构体还有库中的结构体,我们也可以在分析的时候添加自己识别出的结构体。
枚举窗口:枚举窗口和结构体窗口类似,自己也可以定义枚举类型,以帮助自己更好的分析反汇编代码。
区段窗口:显示程序各个段的起始地址、结束地址、权限等。
签名窗口:签名也就是sig文件,我们可以使用IDA的fair工具制作常见库的sig文件,当加载库的sig文件时,库中的函数名称就能按照我们制作的识别出来。

跳转
我们在反汇编窗口中按G可以打开跳转界面,我们在框中输入一个地址(十六进制)即可跳转到指定地址处。执行完跳转后我们按ESC键即可返回刚才跳转前的位置。若要再想看跳转后的位置按CTRL+ENTER键即可,这两个操作可在多个跳转之间反复横跳。

交叉引用

上面说到的交叉引用(XREF)可以知道指定代码相互调用的关系。例如下图中的 CODE XREF:sub_401090+1FF↑o,"o"表示偏移量,表示该调用地址是401090h函数的+1FF的位置,也就是40128F处,我们双击或者在此处按enter键即可跳到40128F处。
我们在分析时可能会搞不清一些名称的前缀含义,前缀有以下几种:
sub_XXXXXX,表示地址XXXXXX处的函数XXXXXX为函数的起始地址
loc_XXXXXX,表示地址XXXXXX处的一个指令
byte_XXXXXX ,表示位置XXXXXX处的8位数据
word_XXXXXX ,表示位置XXXXXX处的16位数据
dword_XXXXXX, 表示位置XXXXXX处的32位数据
unk_XXXXXX ,表示位置XXXXXX处大小未知的数据
j_XXXXXX, 表示跳转到XXXXXX处的跳转指令

F5生成伪代码
下面就要讲一讲IDA最NB的功能,没有之一,IDA插件HexRays,也就是传说中的F5大法好,我们可能在分析一个程序的时候不喜欢看汇编代码,我们想要更直观一些的分析程序逻辑,那么我们就可以使用IDA的F5功能,让整个函数以类C的伪代码形式展现,使得我们分析起来更加的容易。
用法:我们只需要在函数处,按F5即可。例如下图我使用交叉引用跳转到sub_401090函数中,然后按F5即打开了伪代码窗口,第一行为函数的原型,然后下面为局部变量的声明,而且每个局部变量后面使用注释表明这个变量所在的位置,然后是函数的逻辑,生成的伪代码中的变量名可能会在不同机器不同版本IDA上面有所不同。

但是F5并不是万能的,也会出现失败的时候,据大多数原因是与这个函数相关的某些参数设置错误,例如这个函数中调用其他函数的调用约定出现错误,导致参数解析失败或者调用前后栈不平衡。有时候我们可能会遇到call analysis failed,这时候我们就要在错误提示界面上找到其错误的位置,修改函数原型声明中的调用约定即可。例如把thiscall改为cdecl。还有一种是sp-analusis failed ,这种错误可能是因为编译器的优化问题使得某个函数调用的调用约定出错,或者该函数的参数个数出错,导致IDA算错了栈指针的变化量,也有可能是程序代码有一些干扰代码,让IDA的反汇编分析出现错误。比如用push + n条指令 + retn来实际跳转,而IDA会以为retn是函数要结束,结果它分析后发现调用栈不平衡,因此就提示sp analysis failed。出现这种错误就只能在选项(options)->常规-》勾选堆栈指针,然后反汇编窗口地址处右边会多一列栈的偏移量,在没有使用动态长度数组的正常程序中,在初始化完毕,调用前后的栈的偏移量不变。然后我们一点点的看完栈指针,将其与正常栈指针的变化规律相比较就可以找出其问题所在并修改即可。

重命名
可以看到系统帮我们识别出了许多的函数并添加上了名称,例如GetDlgItemTextA、strlen、ExitProcess等。我们如果遇到一个函数或者数据不知道干什么的,IDA也没有识别出来,我们可以双击进去查看其逻辑,分析出其是做什么的,然后自己给它命名,例如:我点击了40BB50函数进去之后分析出了他是异或加密函数,我们就可以在函数名称上面点击右键选择重命名全局项目,然后输入自己给它起的名称例如:sub_EncodeXor,这样这个程序中所有引用这个此函数的地方就都变成了sub_EncodeXor,也就不用自己再挨个去识别了,不止是函数,数据同样使用此方法,例如下图中的dword_429464你也可以给它命名为dword_UserName等(低版本的IDA没有撤销功能,所以操作前要小心)。

注释
我们分析程序的时候如果需要对某句逻辑添加自己的注释,那么我们就可以在此句后面按”/“,即可添加注释。

给汇编代码添加注释是按“ ;”

添加标记
如果我们分析过程中想在某处做个标记,然后方便自己查找,那么我们把光标点到我们想要标记的位置,然后按下ALT+M,然后给这个位置取个名字点击保存即可,然后如果我们标记了很多位置,按CTRL+M即可查看自己标记过的位置列表,双击即可跳转过去。

格式化指令操作数
在IDA中可以格式化指令使用的常量,我们分析的时候应将常量尽可能的使用符号名称代替,从而分析起来更加的清晰明了,一般IDA会自动根据反汇编指令的上下文进行格式化,但是有时候IDA就将常量显示为一个十六进制数。我们可以将光标移动到想要格式化的常量上面,单击右键即可转换其进制,或者让其当成字符串展示,例如我们知道了函数所使用的参数属于什么类型,我们可以右键-》使用标准符号常量选择其对应的标准显示符号

函数操作
IDA允许手动干预创建、编辑、删除函数,新函数由不属于某个函数的现有指令创建,或者由未被IDA以任何方式定义的原始数据创建。

如下图所示,由这么一段数据,我们一看就知道这肯定是一个函数,但是我们点击F5将其转变为伪代码时IDA提示“请将光标在一个函数上”,可见IDA并没有将其正确的识别为函数,我们就可以先将其创建成函数,再按F5生成伪代码来分析,我们将光标移到要创建函数的第一个字节上,也就是loc_80000290处右键-》创建函数,或者直接快捷键P,即可以函数的形式重组代码,在重组代码时IDA会将数据转换成代码以便分析函数的结构,若能找到函数的结束部分IDA将生成一个新的函数名,以函数的形式重组代码。如果无法确定函数的结束部分或者发现非法指令,这个操作将会终止。


重组函数后名称变为了sub_8000290。

按F5即可正确识别出伪代码。

代码和数据转换
其实我们仔细想一想为什么会产生安全问题?本质原因还是计算机区别不出来什么是数据,什么是代码。IDA也一样,反汇编的时候可能无法正确的区分数据和代码,数据字节可能被错误的识别为代码字节,而代码字节被错误的识别为数据字节。有些程序就是用这种IDA的缺陷来对抗静态反汇编的,不过我们可以利用我们的人脑来帮助IDA度过难关。IDA可以让我们将某段十六进制数据(机器码)指定为代码或数据。
假设我们确信某段十六进制数据是一段指令,我们只需要将光标移到其起始位置按C键即可,按P键可以将其某段代码定义为子程序,并列出参数调用。若要取消定义则按U键,数据将重新以十六进制形式显示。
举个例子:
下图为IDA错误的反汇编代码,因为地址0x4010D9和0x4040DB分别为JZ和JNZ也就是说无论如何都会跳转到loc_4010DD+2的位置,也就是4010DF处,但是程序相应位置并没有识别出来4040DF处的代码,所以明显出现了识别错误,我们在4010DD处使用快捷键D将其转化为数据。

然后,在地址4040DF处使用快捷键C将其转化为代码,这样我们就修正了反汇编代码显示出了正常的内容。其中的dw 0E89h就是添加的对抗静态反汇编的垃圾数据,目的就是干扰IDA的反汇编,使其误将数据当成代码来解析,从而产生了错误的逻辑。

我们上面说到4010DD处的两个字节数据(dw 0E89Ah)为多余字节,虽然经过代码的修正后能够看到正常的反汇编代码,但是不能直接进行反编译,此时我们将多余的两个字节转化为空指令(nop指令,对应字节码为0x90),这样函数就能直接的正常反编译了。
修改方法为鼠标光标移到需要修改的数据地址处,依次选择菜单栏中的编辑(Edit)-》修补程序(Patch program)-》单字节更改方式(Change byte)  来进行修改。

字符串
我们在确定某段数据是一个字符串的时候,把光标移动到其开头处按A即可将其转换为字符串,IDA默认是C语言形式的,如果想要其他形式的可以在Options-》字符串文本,设置其他字符串格式的默认值。

IDA有时无法确定ASCII字符串,发生这种错误的原因是这个字符串在程序中没有被直接调用,例如下图中,我们只需要将光标移动到40478EH处,按A键,该处就会被定义并生成一个变量名,如果要将其恢复则按U键,可以在名称窗口(Names)看到这些字符串变量。

数组
例如我们C语言写一个数组如下
static int a[3]={0x11,0x22,0x33};
IDA生成的汇编代码如下
mov     edi, dword_407030[eax]
其中dword_407030位置指向的就是一个数组,

将光标放到此处按“ * ” 键即可打开数组排列调整窗口,数组大小我们填写3,每行项数填0让其根据页面自动调整即可,其余默认。

调整完成后

结构体
结构体(struct)是一种数据结构,可以将不同类型的数据结构组合到一个复合的数据类型中,结构体可以被声明为变量、指针、或数组,从而实现比较复杂的数据结构。
我们先来看下面一个例子:
正在上传…[url=]重新上传[/url][url=]取消[/url]
上面的MyStruct就是我们定义的一个结构体。然后在main函数中对结构体中的各个变量赋值,最后打印出来。
IDA中显示如下,在CALL上面分析传参方式,三个变量以缓冲区方式传参,推测是结构体传参
正在上传…[url=]重新上传[/url][url=]取消[/url]
我们在结构体窗口按Ins键插入一个结构体

然后我们点击结构体名称。然后按D,添加结构体中的变量
选中变量,按N重命名变量

我们可以在局部堆栈中将变量转换成结构体类型
选中变量,按ALT+Q转换成指定结构体

使用结构体解析变量后,可以看到反汇编窗口中变量显示以结构体形式显示。

枚举类型源码如下,枚举类型weekday里面包含了周一到周日


IDA中反汇编如下

我们可以在子视图枚举窗口中按Ins键插入一个新的枚举类型weekday,然后在新建的枚举类型中按N添加枚举成员,0对应MONDAY,1对应TUESDAY以此类推

定义完之后我们返回反汇编代码,在想要转换的数据上面按M即可选择将其转换为我们设置的枚举成员

介绍完了基本的数据类型,如果大家想再深层次的去理解反汇编代码推荐大家看一本《C++反汇编与逆向分析技术解密》在里面你可以学到例如:怎么识别基本数据类型、查找程序入口、各种表达式的运算、流程控制语句(if、else等)、还有函数的工作原理,变量的储存访问方式、数组和指针、结构体和类、构造函数析构函数虚函数、继承、异常处理等几乎全面的开发软件所需要的语法的汇编形式。

IDA 脚本功能(IDC和ythonp)
如下图所示打开脚本文件,支持.idc和.py文件类型。然后我们选择我们的.idc脚本文件,执行脚本,脚本的输出结果将在IDA提示窗口中展示。

IDA脚本还支持命令功能,说这个之前先说一下SMC,也就是代码自修改技术,就是在可执行文件中保存着加密数据,只有在程序运行时才会由程序在某处通过一段还原代码来解密这段加密代码,然后执行这段解密后的代码。
例如我们找到了其解密函数,分析出了其解密过程,就可以编写一个解密脚本来代替SMC,例如下面脚本

我们选择脚本文件加载脚本以后按shift+F12即可打开IDC命令执行窗口,我们就可以在右侧执行函数并传入参数
解密前401060处汇编代码

执行脚本解密后401060处汇编代码

在实际环境中可能解密算法很复杂,但是思路是一样的,在IDA中对这种SMC或者其他技术加密的代码也可以使用其他方法,例如用OD动态调试,然后用IDA的“Additional binary file”功能附加二进制文件将解密文件重新加载,这样做是很有效的,具体参考《加密与解密(第四版)》16.10节。

签名
有时候我们在反编译程序的时候,如果没有正确的选择其库,这时候IDA就会识别不出来这个函数的真实名称,可能只会给到你相应的函数地址,例如 识别到了strlen()函数,但是IDA不知道他是strlen,本来是call strlen可能给你显示的是call sub_xxxxxx,这样就不利于我们进行分析。
我们可以按shift+F5打开应用库模块列表,然后按Ins键来应用正确的库,应用成功后IDA会重新自动分析全部代码。如果IDA没有重新进行自动分析,我们就可以在选项-》常规-》分析  点击重新分析程序即可。


如果没有我们想要的库文件,IDA也提供自己创建签名文件,具体的制作方法大家可自行百度。

插件
IDC脚本适用于小型任务和快速开发工作,但它不支持复杂的数据类型和一些复杂的任务,IDA提供了插件功能,并且支持win32API和很多标准库,这给插件的开发提供了很好的条件。
我们可以自己开发IDA插件或者直接使用其他人开发的插件。
hex-rays下载页面:https://www.hex-rays.com/
OpenRce下载页面:http://www.openrce.org/downloads/
看雪论坛工具面板:https://bbs.pediy.com/forum-53.htm
IDA安装插件很简单,将已经编译好的插件模块复制到IDA安装目录的plugins目录中即可,注意有些插件可能会有兼容性问题,安装前先阅读其说明文档,查看其支持的IDA版本和使用说明。

IDA调试器
IDA支持本地调试和远程调试(包括windows32/64、windowsCE/ARM、Mac OS 32/64、Linux32/64/ARM 、Android等);
具体的等下篇文章介绍动态调试的时候再说吧。

使用IDA的一些小技巧
如果想了解某个函数的参数传递情况,在其函数上按CTRL+K即可打开其栈,查看其中所使用到的变量,也可以自己重命名。
查找main函数
在windows和Linux下,很多可执行文件都不是直接从main()函数开始执行的,而是先进行一些初始化操作,然后再转到main()函数。
我们查找main()函数的技巧如下:
1.main()函数经常在可执行文件的靠前位置,因为很多链接器是先处理对象文件后处理静态库。
2.VC的入口点(IDA中的start()函数)会直接调用main()函数,在start()函数中被调用的函数有三个参数,并且返回值被传入exit()函数的,可以重点查看。
例如下面的sub_271000()函数

3.GCC将main()函数的地址传入__libc_start_main来调用main函数,查看调用的参数即可找到main()函数的地址

其他静态分析操作
其实我们拿到一个程序的时候不止是使用IDA来静态分析,我们也可以使用其他工具来获取一些信息。
例如:如果是可执行文件,我们就会选择一些PE工具来进行分析
1.查看链接器版本(4.20-VB5,2.25-Delphi7)
2.查看OEP代码,看是否加壳、未加壳的编译器环境(代码是否正常,如果有pushad这类的就代表可能是不正常,加壳了)
3.查看区段信息(.text微软  CODE宝蓝)
4.查看导入表导出表信息(看使用的函数名)
5.查看资源段信息(看有无自定义的资源段,里面可能藏着好东西)
6.查看TLS信息(有无设置TLS回调函数)
7.文件大小和图标情况有时候也会有线索

实战CTF题目演示
题目1
我们打开后是这个样子的,让我们输入密码,我们小于6位程序就没反应,输入大于6位程序就自动退出了,说明密码可能是6位。

下面我们拖入IDA进行分析shift+F12打开字符串窗口,发现两个可疑字符串,可能是关键逻辑

我们双击过去,然后ctrl+x查找交叉引用,然后点击进入引用字符串的sub_401090+1FF函数。


然后我们按F5进行转为伪代码,并分析如下

我们点击string发现只能存3位,输入6位的话后面的3位会溢出到后面的var_101,var_100,var_FF处。

这三个位置刚好是v12,v13,v14 这三个参数

在v12,v14,v13的数值上点击右键选择字符形式即可看到'x' 'Y' 'z'

因为122+1=123,所以我们得输入122xyz才可以显示出来flag

附件里面我还放了一个题目二,WP写的不太详细,给大家点提示,需要将数据自己定义为函数,再F5进行生成伪代码
加载的时候别忘记选择ARM小端

从这个字符串入手,查找交叉引用,然后将引用他的地方转换为函数再F5,加密函数在sub_8000274,自己去解密。


小结因为第一篇文章写完之后随后的公司的HW和出差还有开发任务接踵而至,安排的比较满(要恰饭的嘛^_^),所以这篇文章鸽了好久,以后尽量把这一系列持续写下去把,算是巩固自己查缺补漏,也希望能给一些喜欢逆向而又没有入门的小伙伴们一些帮助吧。本文章的目的也是为了引领小白选手入门,目的是学完以后可以自己实现做一些简单的CTF题目还有可以照着别人的WP来自己操作一遍。若要更好的使用IDA,扎实的汇编功底,熟练的掌握程序开发运行流程,都是不可或缺的,所以说任重而道远,切不可浮沙筑高台。

参考资料《加密与解密(第4版)》作者:看雪、《从0到1:CTFer成长之路》作者:NU1L、《CTF特训营》作者:FlappyPig




















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-20 19:18 , Processed in 0.017538 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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