软件安全 | 磁盘分区与内存分析

Overview

这个系列是我在学习软件安全这门课程时的学习笔记,仅用作记录和参考

读取自己电脑或手机里的磁盘分区信息

这个任务主要是了解自己电脑的磁盘格式

环境准备

市面上查看磁盘的工具有很多,当然也可以直接使用命令行查看,但是要注意,数据无价,在实验时没有100%的把握不要轻易在实体机上操作,以免造成无法挽回的损失,当然推荐使用虚拟机环境进行实验

我在这里就不使用命令行工具和一些老工具了,都ai时代了,还是用点高级的gui吧

DiskGenius下载网址

下载好后启动即可

查看磁盘的基本布局

打开之后显示如上述,发现在左侧总共发现了两个磁盘,且都为硬盘,其中HD0是笔记本原厂自带的,HD1是自行扩展的金士顿,同时我的电脑上安装了Arch Linux双系统,因此实验案例几乎覆盖了大部分的场景

HD 0

在最上面一行显示了该磁盘的磁盘格式为GPT,同时有系统盘OS(C:)新加卷(D:)RESTORE(5),除此之外还能看到许多小的分区

在这里可以找到更详细的信息,可以发现我们的新加卷都是NTFS格式的,其余的分区有自己的独特文件格式

现在逐一介绍一下这部分的内容

序号卷标文件系统容量用途说明
0SYSTEM (0)FAT32260 MBEFI 系统分区 (ESP)。用于存放启动文件(如 EFI 引导加载程序、Boot Manager、BIOS 配置数据等)。是 GPT 磁盘启动时必须存在的分区。
1MSR (1)MSR16 MBMicrosoft Reserved 分区。仅存在于 GPT 磁盘上,被 Windows 保留用于系统管理(如磁盘管理动态扩展分区时使用),用户看不到。
2OS (C:)NTFS212.8 GBWindows 系统主分区,即操作系统和应用程序所在的分区(C 盘)。
3分区 (3)NTFS1.1 GB小分区,属性“H”表示 隐藏分区
4新加卷 (D:)NTFS235.6 GB用户数据分区(D 盘),用于存放个人文件。
5RESTORE (5)NTFS26 GB恢复分区,通常用于恢复出厂系统或一键恢复。
6MYASUSFAT32260 MB厂商维护分区,常见于华硕笔记本,用于 BIOS/工具更新或 ASUS 自带恢复环境。

在引导的时候,UEFI会找到SYSTEM(0)分区,从里面进行启动引导

HD 1

第二块磁盘是我自己添加的磁盘,逻辑上跟在上一块磁盘的后面,可以发现这里也有很多分区,这是我在刚学计算机时分的区,后来使用的时候发现根本不需要分这么多,现在我在使用的时候,发现C盘经常红,:(

详细信息在这里,同样也是有一个MSR分区,但是找不到SYSTEM分区,这是因为额外加的盘不需要引导系统启动,也就没有必要保留SYSTEM分区,同时注意到这里少了很多分隔分区,因为是自己安装的磁盘,自然也就没有出厂时就有的分区

Linux

新加卷(G:)之后就是我安装的Arch Linux了,对于Linux来说磁盘分区就简单很多了,没有那么多要求,一个基础的分区是这样的

这里直接以Arch wiki上的截图为例子,对于GPT格式,需要三个分区/boot[SWAP]/(root)

对于我自己的图来说,/boot识别成了esp分区,用于存放引导Arch LinuxEFI系统分区,格式要求为FAT32[SWAP]的格式要求为Linux Swap partition/(root)的格式要求为EXT4

如果是BIOS+MBR的电脑,则只需要两个分区,但是现在买的电脑几乎见不到BIOS+MBR的配置,这也是Linux为老电脑留下的可行路径

至此的电脑的磁盘分区就结束了,这次实验只是简单了解一下电脑的磁盘分区信息,后续如果有机会再继续深入了解

编写程序查看内存布局和页表数据

编译程序

先编写一个适合的c语言程序

#include <stdio.h>

int global_var = 0x41424344;

int main() {
    char stack_buf[64];
    snprintf(stack_buf, sizeof(stack_buf), "Hello from stack\n");
    printf("Hello, World!\n");
    fflush(stdout);

#ifdef _WIN32
    system("pause");
#else
    getchar();
#endif
    return 0;
}
C

然后编译它,我询问了GPT,它告诉我推荐使用vs developer command prompt来编译,因为可以生成PDB

cl /Zi /Od hello.c /link /debug
Bash

/Zi 生成 PDB,/Od 禁用优化便于调试。

windbg调试

首先在windbg中载入该程序File -> Launch excutable,在完成载入后需要加载符号表

.symfix
.sympath+ /path/to/your/pdb_folder     
.reload -f
Bash

等待进度条读完后即可

列出模块

lm             # 列出加载的模块
lmv m demo*    # 查看demo模块的详细信息
Bash

我们先不管kernel的模块,因为需要双机调试,先来分析一下各个字段的含义

字段含义
start模块在内存中的起始地址
end模块在内存中的结束地址
module name模块名称(通常为文件名的前缀)
(deferred)表示符号文件尚未加载(延迟加载符号)
(pdb symbols)表示已成功加载符号文件(PDB)
(private pdb symbols)表示这是使用你自己编译生成的私有符号文件(非微软公共符号)
C:\ProgramData\Dbg\sym\demo.pdb\...符号文件(PDB)的路径

在这里说明:

  • demo是我自己编写的程序模块,起始地址是 00007ff7'6d9d0000,结束地址是 00007ff7'6da79000
  • WinDbg成功加载了它的符号表,并能从C:\ProgramData\Dbg\sym\ 中找到
  • 系统模块如 KERNELBASE, KERNEL32, ntdll 也被加载,但有的符号是“延迟加载(deferred)”,还没展开

再看lmv m demo的输出

字段含义
Loaded symbol image file:WinDbg 实际加载的可执行文件路径
Image path:程序运行时的镜像路径(可执行文件路径)
Image name:模块名
Timestamp:编译时间(即 PE 文件头中的时间戳)👉 Thu Oct 9 21:32:32 2025 表示我的 demo.exe 是在该时间编译的
CheckSum:PE 文件的校验值(一般调试时为 0)
ImageSize:模块在内存中占用的大小(十六进制)👉 0xA9000 = 688,128 字节 (~672KB)
Translations:程序资源(例如字符串表、版本信息等)的语言代码页组合。这里 0409 是英语(美国),04b004e4 是代码页。
Information from resource tables:若程序包含版本号、公司名、文件描述等信息,会显示在这里(我的程序没有定义,所以为空)。

查看进程虚拟内存分布概况

!address -summary
Bash

这条命令主要是用来查看进程的虚拟内存布局,它统计了内存的使用类别、状态、保护属性等信息

0:000> !address -summary

                                     
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                     22     7ffe`fd079000 ( 127.996 TB)          100.00%
<unknown>                                17        1`02329000 (   4.034 GB)  99.70%    0.00%
Image                                    22        0`007c7000 (   7.777 MB)   0.19%    0.00%
MappedFile                               14        0`00259000 (   2.348 MB)   0.06%    0.00%
Stack                                     3        0`00100000 (   1.000 MB)   0.02%    0.00%
Heap                                      2        0`000ff000 (1020.000 kB)   0.02%    0.00%
Other                                     4        0`0002a000 ( 168.000 kB)   0.00%    0.00%
TEB                                       1        0`00004000 (  16.000 kB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                              19        1`02525000 (   4.036 GB)  99.75%    0.00%
MEM_IMAGE                                28        0`007d0000 (   7.812 MB)   0.19%    0.00%
MEM_MAPPED                               17        0`00282000 (   2.508 MB)   0.06%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                 22     7ffe`fd079000 ( 127.996 TB)          100.00%
MEM_RESERVE                              13        1`02604000 (   4.037 GB)  99.77%    0.00%
MEM_COMMIT                               51        0`00973000 (   9.449 MB)   0.23%    0.00%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READONLY                            27        0`004fc000 (   4.984 MB)   0.12%    0.00%
PAGE_EXECUTE_READ                         7        0`0042b000 (   4.168 MB)   0.10%    0.00%
PAGE_READWRITE                           13        0`00041000 ( 260.000 kB)   0.01%    0.00%
PAGE_READWRITE | PAGE_GUARD               2        0`00006000 (  24.000 kB)   0.00%    0.00%
PAGE_WRITECOPY                            2        0`00005000 (  20.000 kB)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                    24f`0be91000     7da5`c78bf000 ( 125.648 TB)
<unknown>                              7ff4`d3850000        1`00020000 (   4.000 GB)
Image                                  7ff8`926ee000        0`001e8000 (   1.906 MB)
MappedFile                             7ff4`d3755000        0`000fb000 (1004.000 kB)
Stack                                    73`7ce00000        0`000f8000 ( 992.000 kB)
Heap                                    24f`0bc8d000        0`000f2000 ( 968.000 kB)
Other                                   24f`0bb80000        0`00020000 ( 128.000 kB)
TEB                                      73`7cc37000        0`00004000 (  16.000 kB)
PEB                                      73`7cc36000        0`00001000 (   4.000 kB)
SCSS

Usage Summary

字段解释:

字段含义
Usage内存用途分类(例如程序映像、堆、栈、映射文件等)
RgnCount该类型的区域(Region)数量
Total Size该类型内存区域的总大小
%ofBusy占用的已分配内存(非空闲)的比例
%ofTotal占整个虚拟地址空间的比例

常见的类型含义:

类型含义
Free空闲区域(未被使用)
<unknown>WinDbg 暂时无法识别的分配(可能是大块保留内存或匿名映射)
Image可执行文件或 DLL 的映射区域(即模块代码段)
MappedFile文件映射区域(例如内存映射文件)
Stack线程栈
Heap进程堆(如 mallocnew 分配的区域)
Other未分类的区域(例如 page heap metadata、activation context 等)
PEB / TEB进程环境块(PEB)与线程环境块(TEB)

重点解释:

  • 在我的进程的虚拟地址空间中,绝大多数是空闲的( 127.9 TB),其实这个数字不是随便算出来的,目前我们的计算机大部分都是64位的,那么对于一个进程的虚拟地址来说,最大就可以使用。但是出于硬件实现和系统设计考虑,windows只启用了其中的一部分
架构用户态虚拟地址空间内核态虚拟地址空间
x64 Windows0x0000000000000000 – 0x00007FFFFFFFFFFF (128 TB)0xFFFF080000000000 – FFFF… (128 TB)

因此在WinDbg中会显示127.9TB

  • 在这一块的下面还有一块预留的虚拟地址空间,大小为 4GB ,这一块地址空间是windows预留的大块虚拟内存,用于防止地址空间碎片化等等,因此这 4GB 也不代表我们的程序真的占用了 4GB 的内存,同时它的状态是MEM_RESERVE,也就是预留,没有提交占用物理内存
0:000> !address 7ff4`d3850000

Usage:                  <unknown>
Base Address:           00007ff4`d3850000
End Address:            00007ff5`d3870000
Region Size:            00000001`00020000 (   4.000 GB)
State:                  00002000          MEM_RESERVE
Protect:                <info not present at the target>
Type:                   00020000          MEM_PRIVATE
Allocation Base:        00007ff4`d3850000
Allocation Protect:     00000004          PAGE_READWRITE


Content source: 0 (invalid), length: 100020000
SCSS
  • 后续的image是程序代码,占用7.7MBHeap是堆,占用1MBStack是堆栈,占用1MB,这些是MEM_COMMIT的,也就是可以在物理内存中找到的

Type Summary

字段解释:

类型含义
MEM_PRIVATE进程私有内存(例如堆、栈、全局变量、匿名分配)
MEM_IMAGE映像文件(EXE/DLL)
MEM_MAPPED映射文件或共享内存(Memory-mapped files)
  • 大部分分配的 4G 内存是进程分配了大量的私有虚拟地址空间
  • MEM_IMAGE是程序和库的代码段
  • MEM_MAPPED是少量的映射文件

State Summary

字段含义

状态含义
MEM_FREE空闲(未使用)
MEM_RESERVE已保留地址空间(但尚未实际分配物理内存页)
MEM_COMMIT已提交(实际有物理内存或分页文件支持)

Protect Summary

字段含义

属性含义
PAGE_READONLY只读页(通常为常量数据)
PAGE_EXECUTE_READ可执行且可读(程序代码段)
PAGE_READWRITE可读可写(堆、栈、全局变量)
PAGE_GUARD保护页(如栈溢出检测)
PAGE_WRITECOPY写时复制页(DLL 共享数据)
  • 可执行的页占约 4MB → 程序和系统模块的代码段;
  • 可读写页约 260KB → 你的堆、全局变量、局部数据;
  • 有少量 PAGE_GUARD → 栈的保护机制。

Largest Region by Usage

这部分列出了每种类型中最大的一块连续区域

查看内存中的数据

全局变量

因为我们加载了符号表,那么我们可以直接查看我们程序中global_var的地址

? &global_var
Bash

然后我们就能查看到我们定义在全局的变量(int)0x41424344 -> (char)DCBA,小端存储

同时注意到在后面还有Hello from stackHello, world等字符串,这其实和程序的编译有关

在 PE 中:

  • .data段存放可写的全局变量
  • .rdata段存放只读的字符串常量

现代编译器会优化布局,把:

  • 全局变量
  • 字符串常量
  • 函数内的格式化字符串(如"Hello from stack\n"
  • 以及 "Hello, World!\n", "pause"

放在同一页或连续区域中,以减少页数量

所以在内存上,他们是按上图排布的

通过ida我们也能看出来

security cookie

同时注意到,ida中后面还跟着两个数据unsigned __int64 _security_cookie_complementuintptr_t _security_cookie

这两个数据其实就和该程序的保护措施有关,我们知道在Linux ELF中有canary这样的防护,目标就是为了防止栈溢出

那么在Windows中也有这样的防护cookie,名字叫__security_cookie,这个变量是 Visual Studio 编译器在启用了 /GS 安全检查时自动生成的一个 64 位随机值。

而上面的__security_cookie_complement变量是__security_cookie取反的结果

    /* 
     * Do nothing if the global cookie has already been initialized.  On x86, 
     * reinitialize the cookie if it has been previously initialized to a 
     * value with the high word 0x0000.  Some versions of Windows will init 
     * the cookie in the loader, but using an older mechanism which forced the 
     * high word to zero. 
     */  
  
    if (__security_cookie != DEFAULT_SECURITY_COOKIE  
#if defined (_M_IX86)  
        && (__security_cookie & 0xFFFF0000) != 0  
#endif  /* defined (_M_IX86) */  
       )  
    {  
        __security_cookie_complement = ~__security_cookie;  
        return;  
    } 
C

但是在ida中是静态的,WinDbg是动态运行的,因此两者的值可能不一样

同时我们注意到一个关键的问题,总所周知Linux ELF文件的canary保存在fs:[0x28],在TLS结构体中,要溢出覆盖其实还是很麻烦的,但是在PE中,该值保存在.data段中,还是可读可写,如果.data段中出现溢出,是不是可以修改cookie为我们想要的值呢,这只是一种想法,我还没有去验证它,可能也不行

栈上的数据

想要看到栈上的内存,首先要做的是先运行到main函数中初始化这个栈,才能看到

bp demo!main
g
Bash

运行到断点之后再输入p单步执行,就到了一个相对能看的界面了 🙂

能看到我们的断点和代码了也可能是我操作还不熟悉,有方法一开始就能看到

这个时候查看栈中还没有数据,只有几个返回地址

单步执行之后栈上的数据就出现了

还有一些数据应该是调用函数的时候产生的,在此不做过多深入

页表数据

要查看页表数据需要进行内核调试,但是我的电脑不支持这种调试模式,因此尝试调试虚拟机

环境配置

准备win10虚拟机vmware,提前为虚拟机拍摄快照以免操作失误无法开机

下载VirtualKD

在根据虚拟机的位数选择安装target32还是target64,安装完成之后在本机运行vmmon64.exe(我是windbg preview

然后能发现我们先前安装的目标虚拟机,选择WinDbg Preview的路径,就可以Run debugger

有这样的显示之后,重启虚拟机,并且按F8选择禁用驱动程序强制签名

然后就能连接上windbg了。要注意这个有概率蓝屏失败,我也不知道为什么,但是重启就行了,进入之后输入g命令运行,然后运行我们写好的程序,此时windbg可能显示busy无法输入命令,我们只要输入ctrl+pause就可以断住输入命令

基础分析

!sysinfo machineid
Bash

查看目标机器的信息

!process 0 0
Bash

显示所有进程的基本信息,这个结果很长,就不在此展示了,在其中可以找到我们运行的程序

这其中能找到关键信息Dirbase,用于存储进程虚拟地址空间根目录的数据结构

!process ffffc00f464a42c0 1
Bash

能查看demo.exe进程的详细信息

.process /r /p ffffc00f464a42c0
Bash

切换到demo.exe进程

找到目标进程的虚拟地址

!address
Bash

查看地址,这个的结果也很长,我们目前只关心peb的地址,peb是进程环境块,根据偏移就可以找到我们程序的虚拟地址

!peb 83cb3e4000
Bash

在这个的显示中,我们可以找到ImageBaseAddress的地址,我们查看这个地址的内存发现结果如下

这就是一个很明显的DOS文件头,那说明我们已经里目标虚拟地址不远了,注意其中PE头的偏移量是00 01 00 00也就是1000h

ida中的起始地址为140001000最后的偏移量正好就是上面的PE偏移量,因此在后续找虚拟地址的时候可以忽略这个

目标字符串的偏移量经过计算为99020h,那么就找到我们的虚拟地址了

计算物理地址

windbg中有自动计算物理地址的命令!pte!vtop但是要注意几个点:

  • !pte默认使用$cr3的值,但是对于这个程序来说$cr3不一定等于DirBase我之前猜测是$cr3保存的是当前cpu的值,也就是在程序暂停之后,cpu还在跑,就切换到别的进程的$cr3上了,但是后来一想又有点问题,好像是kva shadow,后面有时间再研究吧
  • !vtop也要使用DirBase中的值来做第一个参数,且虚拟地址中不能有占位符,也就是必须要!vtop 0 00007ff61cdc9020

很有意思啊 🙁

这样我们就能自动找到对应的物理地址,但是我们要深入理解还是要手算一遍的

这张图我感觉是很好的展示了怎么计算,但是要注意,理论和实际还是有差别的,图上用的$cr3,但是在这里用$cr3就算不出来了,必须要用DirBase

接下来是手算的过程:

我们要计算的目标地址是0x00007ff61cdc9020

那么对应的数据分别如下:

  • offset=0000 00100000
  • pte=1 1100 1001=0x1C9
  • pde=0 1110 0110=0xE6
  • ppe=1 1101 1000=0x1D8
  • pxe=0 1111 1111=0xFF
  • DirBase=0x33d87000

那么计算公式就出来了

下面是计算结果

1: kd> !dq 33d87000+0xff*8
#33d877f8 8a000000`97c93867 00000000`00000000
#33d87808 00000000`00000000 00000000`00000000
#33d87818 00000000`00000000 00000000`00000000
#33d87828 00000000`00000000 00000000`00000000
#33d87838 00000000`00000000 00000000`00000000
#33d87848 00000000`00000000 00000000`00000000
#33d87858 00000000`00000000 00000000`00000000
#33d87868 00000000`00000000 0a000001`3cd33863
1: kd> !dq 97c93000+0x1d8*8
#97c93ec0 0a000000`54b94867 00000000`00000000
#97c93ed0 00000000`00000000 00000000`00000000
#97c93ee0 00000000`00000000 00000000`00000000
#97c93ef0 00000000`00000000 00000000`00000000
#97c93f00 00000000`00000000 00000000`00000000
#97c93f10 00000000`00000000 00000000`00000000
#97c93f20 00000000`00000000 00000000`00000000
#97c93f30 00000000`00000000 00000000`00000000
1: kd> !dq 54b94000+0xe6*8
#54b94730 0a000000`a7895867 00000000`00000000
#54b94740 00000000`00000000 00000000`00000000
#54b94750 00000000`00000000 00000000`00000000
#54b94760 00000000`00000000 00000000`00000000
#54b94770 00000000`00000000 00000000`00000000
#54b94780 00000000`00000000 00000000`00000000
#54b94790 00000000`00000000 00000000`00000000
#54b947a0 00000000`00000000 00000000`00000000
1: kd> !dq a7895000+0x1c9*8
#a7895e48 82000000`b7bc7847 82000000`b17f6847
#a7895e58 82000000`32305847 82000000`2dc03847
#a7895e68 82000001`20959005 82000000`5135b005
#a7895e78 00000000`00000000 00000000`00000000
#a7895e88 00000000`00000000 00000000`00000000
#a7895e98 82000000`96bef005 00000000`00000000
#a7895ea8 82000000`2f2fd005 82000000`4bb14005
#a7895eb8 82000001`2095a005 82000000`50e57005
SCSS

我们抽出一个来观察

1: kd> !dq 54b94000+0xe6*8
#54b94730 0a000000`a7895867 00000000`00000000
#54b94740 00000000`00000000 00000000`00000000
#54b94750 00000000`00000000 00000000`00000000
#54b94760 00000000`00000000 00000000`00000000
#54b94770 00000000`00000000 00000000`00000000
#54b94780 00000000`00000000 00000000`00000000
#54b94790 00000000`00000000 00000000`00000000
#54b947a0 00000000`00000000 00000000`00000000
SCSS

54b94000h是我们先前算出来的三级页表基地址,对应的pde0xe6,因此是54b94000+0xe6*8,计算出来的结果是54b94730h,这个页表处的8位结果是0a000000 a7895867

63                                                   0
+-----------------------------------------------------+
|  PFN (物理页号)        |        flags (控制位)      |
|  [51................12] | [11................0]     |
+-----------------------------------------------------+
Diff

对于页表项来说位52-63intel规范中的保留位,因此真正的物理页号是000000097C93h,然后舍弃低 12 位,计算出下一处的页表基地址

最后我们计算出来的值是b7bc7847h,和windbg自动计算出来的值是一样的

在物理内存中查看也是正确的,终于是结束了 🙂

总结

这次实验主要是分析了磁盘分区信息,然后分析一个程序的内存布局和页表数据,在实验中也是有很多未知的错误出现,自己解决这些错误也是一种乐趣。但是也注意到,这次试验其实还有很多内容了解的不是很透彻,例如$cr3DirBase的不一致问题,后续有时间我再继续研究了绝对不是ddl要到了

评论

  1. Garyの主人
    Windows Edge
    2 月前
    2025-10-11 13:53:52

    努力更新的Gary٩(ˊᗜˋ*)و

  2. Daik321
    Windows Edge
    2 月前
    2025-10-18 21:26:20

    质量很高

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇