分享
 
 
 

PE学习笔记(二) 选择自 rivershan 的 Blog

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

五、Section Table(节表)

节表是紧挨着 PE Header 的一结构数组。该数组成员的数目由 File Header (IMAGE_FILE_HEADER) 结构中 NumberOfSections 域的域值来决定。节表成员结构又命名为 IMAGE_SECTION_HEADER(四十字节)。其结构定义:

typedef struct _IMAGE_SECTION_HEADER {

BYTE Name[IMAGE_SIZEOF_SHORT_NAME];

union {

DWORD PhysicalAddress;

DWORD VirtualSize;

} Misc;

DWORD VirtualAddress;

DWORD SizeOfRawData;

DWORD PointerToRawData;

DWORD PointerToRelocations;

DWORD PointerToLinenumbers;

WORD NumberOfRelocations;

WORD NumberOfLinenumbers;

DWORD Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

IMAGE_SECTION_HEADER 结构成员含义:

1.IMAGE_SIZEOF_SHORT_NAME:不超过8字节的节名。节名仅是个标记,我们选择任何名字甚至空着也行,不能用null结束。命名不是一个ASCIIZ字符串,所以不用null结尾。

2.PhysicalAddress:指定文件地址。

3.VirtualSize:这个域的意义与程序类型有关。如果是EXE,代表当节被装入内存之后的大小总和,这是在它们被调整为最接近文件对齐粒度的倍数之前的大小。稍后的SizeOfRawData则是调整后的大小。对于OBJ文档,这个域没有意义。

4.VirtualAddress:本节的RVA(相对虚拟地址)。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址400000h处,那么本节就被载到401000h。微软把第一个Section 的此域值设为0x1000h。对于OBJ文档,此域没意义,总为0。

5.SizeOfRawData:经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数。 假设一个文件的文件对齐尺寸是0x200,如果前面的 VirtualSize 域指示本节长度是0x388字节,则本域值为0x400,表示本节是0x400字节长。在obj中,这个与表示有编译器或 組譯器 指定的真正的section 大小。

6.PointerToRawData:这是本节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。 如果你自己以内存映射的方式应设了一个PE程序(而不是由操作系统的装载器载入),那么你就必须根据此值找到本节的信息,而不是根据VirtualAddress 中的RVA值。

7.PointerToRelocations:在OBJs中,这是以程序开始为基准的偏移量,用来指向section 的重定位信息。每个OBJ section 的重定位信息紧跟在section 信息之后。在EXEs中,这个域(一记下一个域)没有意义,总是为0。但链接器产生一个EXE,它会决定大部分的待修正纪录(fixups),只剩下基址的重定位地址以及 imported 函数的重定位地址,留待载入时在解决。两份相同的信息放在 base relocation section 和imported function section 之中,所以EXEs 不需要在每一个 section 之后又有重定位信息。

8.PointerToLinenumbers:行号表的偏移量(以程序开始为基准)。行号表与源代码行号和其被映射到内存中的位置有关。在EXE文件中,行号信息被放在程序的最尾端。如果没有COFF行好,设为0。

9.NumberOfRelocations:重定位表格(由PointerToRelocations 指向)中的重定位项目的个数。此域只用于OBJ中。EXE中为0。

10.NumberOfLinenumbers:行号表格(由PointerToLinenumbers 指向)中的行号个数。

11.Characteristics:包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。 下面是一些标记:

IMAGE_SCN_TYPE_REG Reserved.

IMAGE_SCN_TYPE_DSECT Reserved.

IMAGE_SCN_TYPE_NOLOAD Reserved.

IMAGE_SCN_TYPE_GROUP Reserved.

IMAGE_SCN_TYPE_NO_PAD Reserved.

IMAGE_SCN_TYPE_COPY Reserved.

IMAGE_SCN_CNT_CODE Section contains executable code.

IMAGE_SCN_CNT_INITIALIZED_DATA Section contains initialized data.

IMAGE_SCN_CNT_UNINITIALIZED_DATA Section contains uninitialized data.

IMAGE_SCN_LNK_OTHER Reserved.

IMAGE_SCN_LNK_INFO Reserved.

IMAGE_SCN_TYPE_OVER Reserved.

IMAGE_SCN_LNK_COMDAT Section contains COMDAT data.

IMAGE_SCN_MEM_FARDATA Reserved.

IMAGE_SCN_MEM_PURGEABLE Reserved.

IMAGE_SCN_MEM_16BIT Reserved.

IMAGE_SCN_MEM_LOCKED Reserved.

IMAGE_SCN_MEM_PRELOAD Reserved.

IMAGE_SCN_ALIGN_1BYTES Align data on a 1-byte boundary.

IMAGE_SCN_ALIGN_2BYTES Align data on a 2-byte boundary.

IMAGE_SCN_ALIGN_4BYTES Align data on a 4-byte boundary.

IMAGE_SCN_ALIGN_8BYTES Align data on a 8-byte boundary.

IMAGE_SCN_ALIGN_16BYTES Align data on a 16-byte boundary.

IMAGE_SCN_ALIGN_32BYTES Align data on a 32-byte boundary.

IMAGE_SCN_ALIGN_64BYTES Align data on a 64-byte boundary.

IMAGE_SCN_LNK_NRELOC_OVFL Section contains extended relocations.

IMAGE_SCN_MEM_DISCARDABLE Section can be discarded as needed.

IMAGE_SCN_MEM_NOT_CACHED Section cannot be cached.

IMAGE_SCN_MEM_NOT_PAGED Section cannot be paged.

IMAGE_SCN_MEM_SHARED Section can be shared in memory.

IMAGE_SCN_MEM_EXECUTE Section can be executed as code.

IMAGE_SCN_MEM_READ Section can be read.

IMAGE_SCN_MEM_WRITE Section can be written to.

遍历节表的步骤:

1.PE文件有效性校验。

2.定位到 PE Header 的起始地址。

3.从 file Header 的 NumberOfSections域获取节数。

4.通过两种方法定位节表: ImageBase+SizeOfHeaders 或者 PE header的起始地址+ PE header结构大小。 (节表紧随 PE Header)。如果不是使用文件映射的方法,可以用SetFilePointer 直接将文件指针定位到节表。节表的文件偏移量存放在 SizeOfHeaders域里(SizeOfHeaders 是 IMAGE_OPTIONAL_HEADER 的结构成员) 。

5.处理每个 IMAGE_SECTION_HEADER 结构。

六、Import Table(导入表)

6.1、导入函数:

一个导入函数是被某模块调用的但又不在调用者模块中的函数,因而命名为"import(导入)"。导入函数实际位于一个或者更多的DLL里。调用者模块里只保留一些函数信息,包括函数名及其驻留的DLL名。

PE 程序被载入到内存之前,存放在 PE 文件的 .data 中的内容是给装载器用来决定函数位置并修补它们以便完成image 用的。而在被载入之后,.idata内含有的是指向 EXE/DLL 的导入函数的指针。

6.2、Data Directory:

Data Directory 是一个 IMAGE_DATA_DIRECTORY 结构数组,共有16个成员。Data Directory 包含了PE文件中各重要数据结构的位置和尺寸信息。 每个成员包含了一个重要数据结构的信息。

Data Directory 的每个成员都是 IMAGE_DATA_DIRECTORY 结构类型的,其定义如下所示:

typedef struct _IMAGE_DATA_DIRECTORY {

DWORD VirtualAddress;

DWORD Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

IMAGE_DATA_DIRECTORY 结构成员含义:

1.VirtualAddress: 实际上是数据结构的相对虚拟地址(RVA)。比如,如果该结构是关于Import Symbols的,该域就包含指向IMAGE_IMPORT_DESCRIPTOR 数组的RVA。

2.Size: 含有VirtualAddress所指向数据结构的字节数。

6.3、找寻PE文件中重要数据结构的一般方法:

1、从 DOS Header 定位到 PE Header。

2、从 Optional Header 读取 Data Directory 的地址。

3、IMAGE_DATA_DIRECTORY 结构尺寸乘上找寻结构的索引号:比如您要找寻Import Symbols的位置信息,必须用IMAGE_DATA_DIRECTORY 结构尺寸(8 bytes)乘上1(Import Symbols 在 Data Diectory 中的索引号)。

4、将上面的结果加上 Data Diectory 地址,我们就得到包含所查询数据结构信息的 IMAGE_DATA_DIRECTORY 结构项。

6.4、导入表:

Data Directory 数组第一项的 VirtualAddress 包含导入表地址。导入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组。每个结构包含PE文件导入函数的一个相关DLL的信息。该数组以一个全0的成员结尾。

IMAGE_IMPORT_DESCRIPTOR结构组成:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD Characteristics; // 0 for terminating null import descriptor

DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

};

DWORD TimeDateStamp; // 0 if not bound,

// -1 if bound, and real date\time stamp

// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)

// O.W. date/time stamp of DLL bound to (Old BIND)

DWORD ForwarderChain; // -1 if no forwarders

DWORD Name;

DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)

} IMAGE_IMPORT_DESCRIPTOR;

IMAGE_IMPORT_DESCRIPTOR 结构成员含义:

1.结构第一项是一个union子结构。事实上,这个union子结构只是给 OriginalFirstThunk 增添了个别名,您也可以称其为"Characteristics"。该成员项含有指向一个 IMAGE_THUNK_DATA 结构数组的RVA。

2.TimeDateStamp:程序生成的时刻。此域通常为0。微软的 BIND 程序可以将此 IMAGE_IMPORT_DESCRIPTOR 所对应的dll的生成时刻写到这里来。

3.ForwarderChain:此域涉及到 forwarding(转交),意味着一个dll 函数在调用另一个 dll。例如,在 WINNT 中,Kernel32.dll 将它的某些输出函数转交给 NTDLL.dll。应用程序可能以为它调用 Kernel32.dll,而事实上它调用的事NTDLL.dll。这个域中含有一个索引,指向 FirstThunk 数组。被这个索引所指定的函数就是一个转交函数。

3.Name:含有指向DLL名字的RVA,即指向DLL名字的指针,也是一个ASCII字符串。

4.FirstThunk:与 OriginalFirstThunk 非常相似,它也包含指向一个 IMAGE_THUNK_DATA 结构数组的RVA(当然这是另外一个IMAGE_THUNK_DATA 结构数组)。

IMAGE_IMPORT_DESCRIPTOR 数组中,最重要的部分是 imported DLL 的名称以及两个 IMAGE_THUNK_DATA 数组。每个 IMAGE_THUNK_DATA 对应一个导入函数。在exe中,两个数组(分别由 Characteristics 和 FirstThunk 域指向)平行存在,并且都以 NULL 位结束符。

为什么需要两个平行数组?第一个数组(由 Characteristics 指向)从不被修改,有时它被称为 hint-name table。第二个数组(由 FirstThunk 指向)则被装载器改写。装载器一一检查每一个 IMAGE_THUNK_DATA 并且找出它所记录的函数的地址,然后把地址写入 IMAGE_THUNK_DATA 这个 DWORD 之中。由于这个 IMAGE_THUNK_DATA 数组内容已经被装载器改写为输入函数的地址,所以它又被叫做 Import Address Table(IAT)。IAT 是一个可写区域。API Hook 就利用到这一特性。PE装载器载入PE后,FirstThunk 指向的 IMAGE_THUNK_DATA 被改写,而 Characteristics 所指向的 IMAGE_THUNK_DATA 没有被改写。所以若还反过头来查找导入函数名,PE装载器还能够根据 Characteristics 所指向的 IMAGE_THUNK_DATA 找寻到函数名。

6.4、IMAGE_THUNK_DATA:

IMAGE_THUNK_DATA是一个DWORD类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针。注意 IMAGE_THUNK_DATA 包含了指向一个 IMAGE_IMPORT_BY_NAME 结构的指针,而不是结构本身。

IMAGE_THUNK_DATA 结构定义:

typedef struct _IMAGE_THUNK_DATA32 {

union {

PBYTE ForwarderString;

PDWORD Function;

DWORD Ordinal;

PIMAGE_IMPORT_BY_NAME AddressOfData;

} u1;

} IMAGE_THUNK_DATA32;

IMAGE_THUNK_DATA 实在PE被载入之后才被决定的。WIN32装载器使用 IMAGE_THUNK_DATA 的初始内容(可能是函数名称也可能是函数序号)来寻找输入函数的位置。然后装载器就以获得的地址改写 IMAGE_THUNK_DATA 的内容。

6.5、IMAGE_IMPORT_BY_NAME:

IMAGE_IMPORT_BY_NAME 结构定义:

typedef struct _IMAGE_IMPORT_BY_NAME {

WORD Hint;

BYTE Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

1.Hint:指示本函数在其所驻留DLL的导出表中的索引号。该域被PE装载器用来在DLL的导出表里快速查询函数。该值不是必须的,一些连接器将此值设为0。

2.Name:含有导入函数的函数名。函数名是一个ASCII字符串。注意这里虽然将Name的大小定义成字节,其实它是可变尺寸域,只不过我们没有更好方法来表示结构中的可变尺寸域。这个结构被提供用于查阅描述名字的结构。

有些情况下一些函数仅由序数导出,也就是说不能用函数名来调用它们,只能用它们的位置来调用。此时,调用者模块中就不存在该函数的 IMAGE_IMPORT_BY_NAME 结构。不同的,对应该函数的 IMAGE_THUNK_DATA 值的低位字指示函数序数,而最高二进位 (MSB)设为1。例如,如果一个函数只由序数导出且其序数是1234h,那么对应该函数的 IMAGE_THUNK_DATA 值是80001234h。Microsoft提供了一个方便的常量来测试dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值为80000000h。

6.6、列出某个PE文件的所有导入函数步骤:

1、校验文件是否是有效的PE。

2、从 DOS Header 定位到 PE Header。

3、获取位于 OptionalHeader 数据目录地址。

4、转至数据目录的第二个成员提取其VirtualAddress值。

5、利用上值定位第一个 IMAGE_IMPORT_DESCRIPTOR 结构。

6、检查 OriginalFirstThunk值。若不为0,顺着 OriginalFirstThunk 里的RVA值转入那个RVA数组。若 OriginalFirstThunk 为0,就改用FirstThunk值。有些连接器生成PE文件时会置OriginalFirstThunk值为0,这应该算是个bug。不过为了安全起见,我们还是检查 OriginalFirstThunk值先。

7、对于每个数组元素,我们比对元素值是否等于IMAGE_ORDINAL_FLAG32。如果该元素值的最高二进位为1,那么函数是由序数导入的,可以从该值的低字节提取序数。

8、如果元素值的最高二进位为0,就可将该值作为RVA转入 IMAGE_IMPORT_BY_NAME 数组,跳过 Hint 就是函数名字了。

9、再跳至下一个数组元素提取函数名一直到数组底部(它以null结尾)。现在我们已遍历完一个DLL的导入函数,接下去处理下一个DLL。

10、即跳转到下一个 IMAGE_IMPORT_DESCRIPTOR 并处理之,如此这般循环直到数组见底。(IMAGE_IMPORT_DESCRIPTOR 数组以一个全0域元素结尾)。

6.7、Bound Import:

当PE装载器装入PE文件时,检查导入表并将相关DLLs映射到进程地址空间。然后象我们这样遍历IMAGE_THUNK_DATA 数组并用导入函数的真实地址替换IMAGE_THUNK_DATAs 值。这一步需要很多时间。如果程序员能事先正确预测函数地址,PE装载器就不用每次装入PE文件时都去修正IMAGE_THUNK_DATAs 值了。Bound import就是这种思想的产物。

Microsoft 出品的类似Visual Studio的编译器多提供了bind.exe这样的工具,由它检查PE文件的导入表并用导入函数的真实地址替换IMAGE_THUNK_DATA 值。当文件装入时,PE装载器必定检查地址的有效性,如果DLL版本不同于PE文件存放的相关信息,或则DLLs需要重定位,那么装载器认为原先计算的地址是无效的,它必定遍历OriginalFirstThunk指向的数组以获取导入函数新地址。

七、Export Table(导出表)

当PE装载器执行一个程序,它将相关DLLs都装入该进程的地址空间。然后根据主程序的导入函数信息,查找相关DLLs中的真实函数地址来修正主程序。PE装载器搜寻的是DLLs中的导出函数。PE 程序把它的导出函数相关信息放在.edata 中。

DLL/EXE要导出一个函数给其他DLL/EXE使用,有两种实现方法: 通过函数名导出或者仅仅通过序数导出。比如某个DLL要导出名为"GetSysConfig"的函数,如果它以函数名导出,那么其他DLLs/EXEs若要调用这个函数,必须通过函数名,就是GetSysConfig。另外一个办法就是通过序数导出。序数是唯一指定DLL中某个函数的16位数字,在所指向的DLL里是独一无二的。例如在上例中,DLL可以选择通过序数导出,假设是16,那么其他DLLs/EXEs若要调用这个函数必须以该值作为GetProcAddress调用参数。这就是所谓的仅仅靠序数导出。

7.1 导出表是数据目录的第一个成员,又可称为 IMAGE_EXPORT_DIRECTORY。结构定义:

typedef struct _IMAGE_EXPORT_DIRECTORY {

DWORD Characteristics;

DWORD TimeDateStamp;

WORD MajorVersion;

WORD MinorVersion;

DWORD Name;

DWORD Base;

DWORD NumberOfFunctions;

DWORD NumberOfNames;

DWORD AddressOfFunctions; // RVA from base of image

DWORD AddressOfNames; // RVA from base of image

DWORD AddressOfNameOrdinals; // RVA from base of image

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

IMAGE_EXPORT_DIRECTORY 结构成员含义:

1.Characteristics:此域没有用途,总是为0。

2.TimeDateStamp:程序被生成的时刻。

3.MajorVersion/MinorVersion:无实际用途,0。

4.Name:一个 RVA 值,指向一个 ASCIIZ 字串(dll 名称,如MYDLL.dll)。模块的真实名称。本域是必须的,因为文件名可能会改变。这种情况下,PE装载器将使用这个内部名字。

3.Base:基数,加上序数就是函数地址数组的索引值了。

4.NumberOfFunctions:模块导出的函数/符号总数。

5.NumberOfNames:通过名字导出的函数/符号数目。该值不是模块导出的函数/符号总数,这是由上面的NumberOfFunctions给出。本域可以为0,表示模块可能仅仅通过序数导出。如果模块根本不导出任何函数/符号,那么数据目录中导出表的RVA为0。

6.AddressOfFunctions:模块中有一个指向所有函数/符号的RVAs数组,本域就是指向该RVAs数组的RVA。简言之,模块中所有函数的RVAs都保存在一个数组里,本域就指向这个数组的首地址。

7.AddressOfNames:类似上个域,模块中有一个指向所有函数名的RVAs数组,本域就是指向该RVAs数组的RVA。

9.AddressOfNameOrdinals:RVA,指向包含上述 AddressOfNames数组中相关函数之序数的16位数组。

导出表的设计是为了方便PE装载器工作。

首先,模块必须保存所有导出函数的地址以供PE装载器查询。模块将这些信息保存在AddressOfFunctions域指向的数组中,而数组元素数目存放在NumberOfFunctions域中。 因此,如果模块导出40个函数,则AddressOfFunctions指向的数组必定有40个元素,而NumberOfFunctions值为40。

现在如果有一些函数是通过名字导出的,那么模块必定也在文件中保留了这些信息。这些名字的RVAs存放在一数组中以供PE装载器查询。该数组由AddressOfNames指向,NumberOfNames包含名字数目。考虑一下PE装载器的工作机制,它知道函数名,并想以此获取这些函数的地址。至今为止,模块已有两个模块: 名字数组和地址数组,但两者之间还没有联系的纽带。因此我们还需要一些联系函数名及其地址的东东。PE参考指出使用到地址数组的索引作为联接,因此PE装载器在名字数组中找到匹配名字的同时,它也获取了指向地址表中对应元素的索引。而这些索引保存在由AddressOfNameOrdinals域指向的另一个数组(最后一个)中。由于该数组是起了联系名字和地址的作用,所以其元素数目必定和名字数组相同,比如,每个名字有且仅有一个相关地址,反过来则不一定: 每个地址可以有好几个名字来对应。因此我们给同一个地址取"别名"。为了起到连接作用,名字数组和索引数组必须并行地成对使用,譬如,索引数组的第一个元素必定含有第一个名字的索引,以此类推。

7.2 如果我们有了导出函数名并想以此获取地址,可以这么做:

1、定位到PE Header。

2、从数据目录读取导出表的虚拟地址。

3、定位导出表获取名字数目(NumberOfNames)。

4、并行遍历AddressOfNames和AddressOfNameOrdinals指向的数组匹配名字。如果在AddressOfNames 指向的数组中找到匹配名字,从AddressOfNameOrdinals 指向的数组中提取索引值。例如,若发现匹配名字的RVA存放在AddressOfNames 数组的第77个元素,那就提取AddressOfNameOrdinals数组的第77个元素作为索引值。如果遍历完NumberOfNames 个元素,说明当前模块没有所要的名字。

5、从AddressOfNameOrdinals 数组提取的数值作为AddressOfFunctions 数组的索引。也就是说,如果值是5,就必须读取AddressOfFunctions 数组的第5个元素,此值就是所要函数的RVA。

7.3 假设我们只有函数的序数,那么怎样获取函数地址呢,可以这么做:

1、定位到PE Header。

2、从数据目录读取导出表的虚拟地址。

3、定位导出表获取nBase值。

4、减掉nBase值得到指向AddressOfFunctions 数组的索引。

5、将该值与NumberOfFunctions作比较,大于等于后者则序数无效。

6、通过上面的索引就可以获取AddressOfFunctions 数组中的RVA了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有