PE文件格式族

1. PE文件格式简介

PE文件全称为Portable Executable,表示为“可移植的可执行文件”,专为Windows操作系统使用的可执行文件格式,我们平时常见的exe、dll、sys等都是PE格式。

具体可查看详细资料:PE文件格式

2. PE文件格式布局

PE文件格式在X86平台下称为PE32,PE文件结构=DOS头+PE头+节表+.text/.data/.rdata,布局图如下:

MS-DOS Header
MS-DOS Stub(DOS残留)
PE Signature(PE签名或标识)
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER
section header(节表1,节表数组的元素1)
section header(节表2,节表数组的元素2)
section header(节表3,节表数组的元素3)
…………
.text(代码段,节表数据区1)
.data(数据段,节表数据区2)
.bss(未初始化数据段,节表数据区3)

学习PE文件格式之前,先要了解几个重要的概念:

  • 映像文件:PE文件被加载到内存中执行时的状态,它是一个程序编译和链接后的最终二进制文件,注意:这是在运行之前的状态;
  • VA:虚拟地址,表示程序加载到内存后的地址,即进程的虚拟地址空间使用的内存地址,用于在运行时访问内存中的数据和代码,VA是相对于进程基址的偏移量;
  • RVA:相对虚拟地址,相对于模块基址的偏移量,用于定位模块内部的代码和数据;
  • FOA:文件偏移地址,映像文件在磁盘中相对于文件起始处的偏移,用于定位可执行文件中的代码和数据在文件中的位置;

总结:通常在没有使用ASLR机制的环境下,一个模块的实际加载虚拟地址就是它的映像基址。但是实际环境下由于ASLR的机制VA和映像基址往往不同。FOA对应文件,RVA对应内存中的模块基址,VA则对应进程在内存中的最终地址,这三个地址可以进行互相转换:

  • RVA互相转换VA:VA=ImageBase+RVA,RVA=VA-ImageBase;
  • RVA转换为FOA:(1)确定目标RVA所在的节:VA<=目标RVA<VA+VirtualSize,(2)计算FOA:FOA=(RVA-VA)+PointerToRawData;
  • FOA转换为RVA:(1)确定目标FOA所在的节:PointerToRawData<=目标FOA<PointerToRawData+SizeOfRawData(2)计算RVA:RVA=(FOA-PointerToRawData+VA;

3. MS-DOS头

MS-DOS头通常被称为DOS MZ Header,是PE文件格式的起始部分,之所以保留了传统的DOS头,是为了确保文件的向后兼容性,即兼容DOS系统,PE文件都可以在DOS环境下运行。

MS-DOS头由IMAGE_DOS_HEADER和DOS Stub两部分组成。IMAGE_DOS_HEADER的结构体如下:

typedef struct _IMAGE_DOS_HEADER
{
     WORD e_magic;              // "MZ",用于标识是否是可执行文件
     WORD e_cblp;
     WORD e_cp;
     WORD e_crlc;
     WORD e_cparhdr;
     WORD e_minalloc;
     WORD e_maxalloc;
     WORD e_ss;
     WORD e_sp;
     WORD e_csum;
     WORD e_ip;
     WORD e_cs;
     WORD e_lfarlc;
     WORD e_ovno;
     WORD e_res[4];
     WORD e_oemid;
     WORD e_oeminfo;
     WORD e_res2[10];
     LONG e_lfanew;             //PE头相对于文件起始处的偏移量
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

通过结构体成员发现,DOS头中声明的寄存器都是16位的,32位和64位模式下就只有e_magic和e_lfanew两个结构体成员:

  • e_magic:DOS签名,值必须为4D5A,转换为ASCII码就是MZ,确定其是不是一个PE文件;
  • e_lfanew:IMAGE_NT_HEADER相对于文件起始处的偏移,用于找到PE头;

IMAGE_DOS_HEADER后面紧跟的就是DOS Stub,它是DOS残留的一段程序,主要作用是:当系统环境为MS-DOS时,输出“This program cannotbe run in DOS mode”,并退出程序,说明该程序不能在DOS环境下运行,相当于两个环境:16位DOS代码在DOS系统运行,32位/64位程序在WIndows环境下运行。

打开010 Editor程序随机打开一个exe文件就可以看到PE文件的结构信息:

4. PE头

PE文件头(PE File Header)是PE文件的核心标识部分,也被称为NT头,由PE签名标准PE头可选PE头三部分组成,NT头跟在DOS Stub的后面,在_IMAGE_DOS_HEADER结构的e_lfanew元素(0x3C位置处),存了PE头的偏移量。

PE头的结构体是IMAGE_NT_HEADERS,定义如下:

typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature;         //0x00:PE标识
  IMAGE_FILE_HEADER       FileHeader;        //0x04:PE标准头
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;    //0x18:PE可选头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

4.1 PE Signature

首先第一个成员是Signature,它是一个4字节的值为“PE\0\0”的签名,用于标识PE文件,位置通常是0x3C。

4.2 IMAGE_FILE_HEADER

Signature后面是FileHeader,也就是标准头,它是一个IMAGE_FILE_HEADER结构体,也被称为“COFF文件标头”:

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;                    //0x04:目标机器类型
  WORD  NumberOfSections;           //0x06:PE中节的数量
  DWORD TimeDateStamp;              //0x08:时间戳,连接器产生此文件的时间差
  DWORD PointerToSymbolTable;       //0x0C:指向符号表的指针,COFF 符号表的文件偏移量
  DWORD NumberOfSymbols;            //0x10:符号表中符号数量
  WORD  SizeOfOptionalHeader;       //0x12:可选头的大小,也就是IMAGE_OPTIONAL_HEADER的大小
  WORD  Characteristics;            //0x14:文件属性标志
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

这里为什么使用0x前缀表示十六进制,而不是使用h后缀呢?个人习惯吧,一般情况下地址使用0x前缀表示十六进制,数据(值)则使用h后缀表示十六进制;具体的成员的功能在上面的注释中已经说明了。

SizeOfOptionalHeader这个成员,定义了IMAGE_OPTIONAL_HEADER的大小,32位系统下的值默认是E0h,而64位系统下默认则是F0h,是可以进行修改的。

Characteristics成员是按位来看的,成员值是多个属性的组合,每一位的文件属性标志定义:

// 文件属性标志
#define IMAGE_FILE_RELOCS_STRIPPED          0x0001    //文件不包含重定位信息,只能在原定的基址加载。如果原定基址不可用,加载器会报出错误
#define IMAGE_FILE_EXECUTABLE_IMAGE         0x0002    //文件可执行,如果该位未设置,意味着存在链接器错误
#define IMAGE_FILE_LINE_NUMS_STRIPPED       0x0004    //不存在行信息
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED      0x0008    //不存在符号信息
#define IMAGE_FILE_AGGRESSIVE_WS_TRIM       0x0010    //已废弃,主动调整工作区
#define IMAGE_FILE_LARGE_ADDRESS_AWARE      0x0020    //高地址警告,应用可处理大于 2GB 的地址
#define IMAGE_FILE_BYTES_REVERSED_LO        0x0080    //小尾存储(已废弃)
#define IMAGE_FILE_32BIT_MACHINE            0x0100    //基于32位体系结构
#define IMAGE_FILE_DEBUG_STRIPPED           0x0200    //不存在调试信息
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP  0x0400    //如果映像文件在可移动介质上,完全加载并复制到内存交换文件中
#define IMAGE_FILE_NET_RUN_FROM_SWAP        0x0800    //如果映像文件在网络介质上,完全加载并复制到内存交换文件中
#define IMAGE_FILE_SYSTEM                   0x1000    //映像文件是系统文件
#define IMAGE_FILE_DLL                      0x2000    //映像文件是DLL文件
#define IMAGE_FILE_UP_SYSTEM_ONLY           0x4000    //文件只能在单处理器机器上运行
#define IMAGE_FILE_BYTES_REVERSED_HI        0x8000    //大尾存储(已废弃)

4.3 IMAGE_OPTIONAL_HEADER

PE标准头后面就是可选头,它提供了必要的程序运行时的加载信息,IMAGE_OPTIONAL_HEADER结构体定义如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;                            //0x18:魔数
  BYTE                 MajorLinkerVersion;               //0x1A:链接器主版本号
  BYTE                 MinorLinkerVersion;               //0x1B:链接器次版本号
  DWORD                SizeOfCode;                       //0x1C:所有代码节的总大小
  DWORD                SizeOfInitializedData;            //0x20:所有已初始化数据节的总大小
  DWORD                SizeOfUninitializedData;          //0x24:所有未初始化数据节的总大小
  DWORD                AddressOfEntryPoint;              //0x28:程序入口地址OEP,就是一个RVA
  DWORD                BaseOfCode;                       //0x2C:代码节起始地址RVA
  DWORD                BaseOfData;                       //0x30:数据节起始地址RVA
  DWORD                ImageBase;                        //0x34:映像文件默认装入的起始地址(4000H)
  DWORD                SectionAlignment;                 //0x38:内存中节对齐粒度
  DWORD                FileAlignment;                    //0x3C:文件中节对齐粒度(1000H)
  WORD                 MajorOperatingSystemVersion;      //0x40:操作系统主版本号
  WORD                 MinorOperatingSystemVersion;      //0x42:操作系统次版本号
  WORD                 MajorImageVersion;                //0x44:映像文件主版本号
  WORD                 MinorImageVersion;                //0x46:映像文件次版本号
  WORD                 MajorSubsystemVersion;            //0x48:子系统主版本号
  WORD                 MinorSubsystemVersion;            //0x4A:子系统次版本号
  DWORD                Win32VersionValue;                //0x4C:保留。总是为0
  DWORD                SizeOfImage;                      //0x50:PE文件在内存中文件的总大小
  DWORD                SizeOfHeaders;                    //0x54:所有头+节表的总大小,按照文件对齐
  DWORD                CheckSum;                         //0x58:映像文件CRC校验和,判断文件是否被修改
  WORD                 Subsystem;                        //0x5c:用户运行映像的子系统类型
  WORD                 DllCharacteristics;               //0x5e:映像文件的DLL属性,总是0
  DWORD                SizeOfStackReserve;               //0x60:初始化时保留的栈大小
  DWORD                SizeOfStackCommit;                //0x64:初始化时实际提交的线程栈大小
  DWORD                SizeOfHeapReserve;                //0x68:初始化时保留的堆大小
  DWORD                SizeOfHeapCommit;                 //0x6c:初始化时实际提交的堆大小
  DWORD                LoaderFlags;                      //0x70:已废弃
  DWORD                NumberOfRvaAndSizes;              //0x74:数据目录项的总数
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];     //0x78:指向数据目录中第一个IMAGE_DATA_DIRECTORY结构体指针
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

具体看一下每个成员的作用,注释中说明的这里不再提及:

  • 第1项:Magic:表明映像文件的类型,107H表示ROM映像,10BH表示PE32,20BH表示PE32+,即64位的PE文件;
  • 第4项:SizeOfCode:表示代码节的总大小,指的是文件对齐后的总大小;
  • 第7项:AddressOfEntryPoint:表示程序的入口地址,相对于映像文件基址的偏移量,对于exe文件这就是程序的开始地址,对于DLL文件是可选的,对于驱动文件是初始化函数的地址;
  • 第8项:BaseOfCode:表示代码节的RVA,也就是代码段的基址,相对于映像文件加载基址的偏移量,代码节通常跟在PE头后面,以“.text”表示代码节;
  • 第9项:BaseOfData:表示数据节的RVA,也就是数据段的基址,相对于映像文件加载基址的偏移量,数据节通常在文件末尾,以“.data”表示数据节;
  • 第10项:ImageBase: 映像文件默认装入的起始地址,必须是64KB的整数倍,exe文件默认是400000H,DLL文件默认是10000000H;
  • 第11项:SectionAlignment:内存中的节对齐粒度,默认与页大小相等,即4096字节;
  • 第12项:FileAlignment:映像文件中数据对齐粒度,默认为200H,即512个字节,如果要设置值,范围必须为512-64K范围内的2的幂,如果SectionAlignment成员的值小于系统页大小,则FileAlignment与SectionAlignment两者成员的值必须相同;
  • 第20项:SizeOfImage:PE(映像)文件在内存中的总大小,必须是SectionAlignment的整数倍;
  • 第21项:SizeOfHeaders:DOS头+PE签名+PE标准头+PE可选头+节表按照FileAlignment对齐后的大小;
  • 第22项:CheckSum:映像文件的CRC检验值,校验装载时的所有驱动、DLL等;
  • 第25项:SizeOfHeapReserve:初始化时保留的栈虚拟内存大小,默认为100000H,即1MB;
  • 第26项:SizeOfStackCommit:初始化时实际提交的栈虚拟内存大小;
  • 第27项:SizeOfHeapReserve:初始化时保留的堆虚拟内存大小,默认为100000H,即1MB;
  • 第28项:SizeOfHeapCommit:初始化时实际提交的栈虚拟内存大小,默认为页大小,即4KB;
  • 第30项:NumberOfRvaAndSizes:数据目录项总数量,默认为10H,即16个;
  • 第31项:DataDirectory:IMAGE_DATA_DIRECTORY结构体数组;

IMAGE_DATA_DIRECTORY结构体定义如下:

typedef struct _IMAGE_DATA_DIRECTORY {
  DWORD VirtualAddress;      //数据目录的 RVA
  DWORD Size;                //数据目录的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

数组各项的定义如下:

DataDirectory[0] =   EXPORT Directory           //导入表RVA和大小
DataDirectory[1] =   IMPORT Directory           //导入表RVA和大小
DataDirectory[2] =   RESOURCE Directory         //资源表RVA和大小
DataDirectory[3] =   EXCEPTION Directory        //异常表RVA和大小
DataDirectory[4] =   CERTIFICATE Directory      //证书表FOA和大小
DataDirectory[5] =   BASE RELOCATION Directory  //基址重定位表RVA和大小
DataDirectory[6] =   DEBUG Directory            //调试信息RVA和大小
DataDirectory[7] =   ARCH DATA Directory        //指定架构信息RVA和大小
DataDirectory[8] =   GLOBALPTR Directory        //全局指针寄存器RVA
DataDirectory[9] =   TLS Directory              //线程私有存储表RVA和大小
DataDirectory[10] =  LOAD CONFIG Directory      //加载配置表RVA和大小
DataDirectory[11] =  BOUND IMPORT Directory     //绑定导入表RVA和大小
DataDirectory[12] =  `IAT` Directory              //导入地址表RVA和大小
DataDirectory[13] =  DELAY IMPORT Directory     //延迟导入描述符RVA和大小
DataDirectory[14] =  CLR Directory              //CLR数据RVA和大小
DataDirectory[15] =  Reserverd                  //保留

5. 节表(Section Header)

PE可选头后面就是节头信息,也称为“节表”。节表中的每个成员都是IMAGE_SECTION_HEADER结构体,也就是说节表是一个结构体数组类型,记录了各个节的属性信息,大小均为40个字节。

IMAGE_SECTION_HEADER结构体定义如下:

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];    //0x00:节区名,比如.text、.data、.rdata等,长度最多8个字节
  union {
    DWORD PhysicalAddress;                //0x08:物理地址
    DWORD VirtualSize;                    //虚拟内存中节区大小
  } Misc;//存储的是当前节在没有对齐前的真实大小
  DWORD VirtualAddress;                   //0x0c:虚拟内存中节区RVA(相对虚拟地址)
  DWORD SizeOfRawData;                    //0x10:当前节在文件中对齐后的大小,值必须为 FileAlignment的整数倍
  DWORD PointerToRawData;                 //0x14:当前节在文件中的偏移量FOA
  DWORD PointerToRelocations;             //0x18:指向重定位表的指针,重定位偏移(用于obj文件)
  DWORD PointerToLinenumbers;             //0x1c:指向行号表的指针,行号表偏移(调试用)
  WORD  NumberOfRelocations;              //0x20:重定位项目数量(用于obj文件)
  WORD  NumberOfLinenumbers;              //0x22:行号数量
  DWORD Characteristics;                  //0x24:节区属性(按位设置)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Characteristics节区属性按位设置,有以下标志位:

// 节区属性
#define IMAGE_SCN_CNT_CODE                0x00000020  //节区包含代码
#define IMAGE_SCN_CNT_INITIALIZED_DATA    0x00000040  //节区包含已初始化数据
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA  0x00000080  //节区包含未初始化数据
#define IMAGE_SCN_ALIGN_1BYTES            0x00100000  //1字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_2BYTES            0x00200000  //2字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_4BYTES            0x00300000  //4字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_8BYTES            0x00400000  //8字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_16BYTES           0x00500000  //16字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_32BYTES           0x00600000  //32字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_64BYTES           0x00700000  //64字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_128BYTES          0x00800000  //128字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_256BYTES          0x00900000  //256字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_512BYTES          0x00A00000  //512字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_1024BYTES         0x00B00000  //1024字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_2048BYTES         0x00C00000  //2048字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_4096BYTES         0x00D00000  //4096字节对齐,仅用于目标文件
#define IMAGE_SCN_ALIGN_8192BYTES         0x00E00000  //8192字节对齐,仅用于目标文件
#define IMAGE_SCN_LNK_NRELOC_OVFL         0x01000000  //节区包含扩展的重定位项
#define IMAGE_SCN_MEM_DISCARDABLE         0x02000000  //节区可根据需要丢弃,比如.reloc在进程开始后被丢弃
#define IMAGE_SCN_MEM_NOT_CACHED          0x04000000  //节区不会被缓存
#define IMAGE_SCN_MEM_NOT_PAGED           0x08000000  //节区不可分页
#define IMAGE_SCN_MEM_SHARED              0x10000000  //节区可共享给不同进程
#define IMAGE_SCN_MEM_EXECUTE             0x20000000  //节区可作为代码可执行
#define IMAGE_SCN_MEM_READ                0x40000000  //节区可读
#define IMAGE_SCN_MEM_WRITE               0x80000000  //节区可写

6. 节区Sections

节表后面就是节了,就是节表中存储的每个节,PE文件格式要求至少有两个节区:.text节和.data节,当然实际环境下肯定会更多,以下是常见的节区:

  • .text节:默认的代码节区,用于保存可执行代码;
  • .data节:默认的读/写数据节区,用于保存已初始化的变量等信息;
  • .rdata节:默认的只读数据节区;
  • .bss节:用于保存未初始化数据节区;
  • .idata节:用于保存导入表信息;
  • .edata节:用于保存导出表信息;
  • .reloc节:重定位表节,用于保存重定位表信息;

7. 导出表结构

导出表是Windows系统可执行文件的一个重要数据结构,记录了当前模块自身提供的函数和数据(名称和地址),以供其他程序调用。区别于导入表,导入表是记录了当前模块所依赖的外部函数和数据。

导出表的结构是一个IMAGE_EXPORT_DIRECTORY,定义如下:

typedef struct _IMAGE_EXPORT_DIRECTORY
{
    DWORD   Characteristics;       //0x00,保留,恒为0x00000000
    DWORD   TimeDateStamp;         //0x04,文件的产生时间戳
    WORD    MajorVersion;          //0x08,主版本号
    WORD    MinorVersion;          //0xA,次版本号
    DWORD   Name;                  //0xC:指向文件名的RVA
    DWORD   Base;                  //0x10:导出函数的起始序号
    DWORD   NumberOfFunctions;     //0x14:导出函数总数
    DWORD   NumberOfNames;         //0x18:以名称导出函数的总数
    DWORD   AddressOfFunctions;    //0x1C:导出函数地址表的RVA
    DWORD   AddressOfNames;        //0x20:函数名称地址表的RVA
    DWORD   AddressOfNameOrdinals; //0x24:函数名序号表的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

关键成员解析:

  • AddressOfFunctions:导出函数地址表的RVA,这是一个RVA数组,每个元素指向一个导出函数的实际代码位置;
  • AddressOfNames:函数名称地址表的RVA,这也是一个RVA数组,每个元素指向一个导出函数名称的字符串;
  • AddressOfNameOrdinals:函数名序号表的RVA,这是一个WORD数组,用于将名称索引映射到地址索引。

8. PE32+结构

64位PE文件格式称为PE+或PE32+,它是PE32的扩展,并没有本质上的区别,而是PE规范的两个版本。PE32+可以看做是对PE32的扩展,使其能够处理64位架构所需的更大地址和内存管理需求。


免责声明:

1. 一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。

2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。

3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
觉得有帮助可以赞赏本文哦~万分感谢!
文章:PE文件格式族
作者:沛旗
链接:https://www.peiqiblog.com/article/6155/
版权声明::本博客站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦~
暂无评论

发送评论(禁止发表一切违反法律法规的敏感言论) 编辑评论


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