Clamav杀毒软件源码分析笔记[九]
刺猬@http://blog.csdn.net/littlehedgehog
[数据流病毒扫描]
数据流病毒扫描,听上去貌似很牛逼的称呼,其实就是一个传送数据流,接收数据流,扫描病毒而已. 貌似扫描病毒代码亦是很牛逼的代码,其实也不然,不过调用扫描病毒的引擎API函数尔. 调API调久了,自然想去看看这个API究竟怎么回事,于是乎就去看kernel代码了,想刚哥早年做游戏开发,光是调用库函数,索然无味,便一门心思要开发3D引擎. 唉,看久了API自然想去实际操作,封封API,这也就是国家要禁止黄片的原因吧...
文章开篇我就决定暂时不要研究引擎API,一来里面难度比较大,二则我目前还没准备要从事病毒工作. 这里我们也就限于调用clamlib库API而已.
先贴代码,凑齐字数
- int scanstream(int odesc, unsigned long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt)
- {
- int ret, portscan = CL_DEFAULT_MAXPORTSCAN, sockfd, port = 0, acceptd;
- int tmpd, bread, retval, timeout, btread, min_port, max_port;
- long int size = 0, maxsize = 0;
- short bound = 0, rnd_port_first = 1;
- const char *virname;
- char buff[FILEBUFF];
- struct sockaddr_in server;
- struct hostent *he;
- struct cfgstruct *cpt;
- FILE *tmp = NULL;
-
- if ((cpt = cfgopt(copt, "StreamMinPort")))
- {
- if (cpt->numarg < 1024 || cpt->numarg > 65535)
- min_port = 1024;
- else
- min_port = cpt->numarg;
- }
- else
- min_port = 1024;
-
- if ((cpt = cfgopt(copt, "StreamMaxPort")))
- {
- if (cpt->numarg < min_port || cpt->numarg > 65535)
- max_port = 65535;
- else
- max_port = cpt->numarg;
- }
- else
- max_port = 2048;
-
- while (!bound && --portscan)
- {
- if (rnd_port_first)
- {
-
- port = min_port + cli_rndnum(max_port - min_port + 1);
- rnd_port_first = 0;
- }
- else
- {
-
- if (--port < min_port)
- port=max_port;
- }
- memset((char *) &server, 0, sizeof(server));
- server.sin_family = AF_INET;
- server.sin_port = htons(port);
- if ((cpt = cfgopt(copt, "TCPAddr")))
- {
- pthread_mutex_lock(&gh_mutex);
- if ((he = gethostbyname(cpt->strarg)) == 0)
- {
- logg("!gethostbyname(%s) error: %s/n", cpt->strarg);
- mdprintf(odesc, "gethostbyname(%s) ERROR/n", cpt->strarg);
- pthread_mutex_unlock(&gh_mutex);
- return -1;
- }
- server.sin_addr = *(struct in_addr *) he->h_addr_list[0];
- pthread_mutex_unlock(&gh_mutex);
- }
- else
- server.sin_addr.s_addr = INADDR_ANY;
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- continue;
- if (bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) == -1)
- close(sockfd);
- else
- bound = 1;
- }
- if ((cpt = cfgopt(copt, "ReadTimeout")))
- timeout = cpt->numarg;
- else
- timeout = CL_DEFAULT_SCANTIMEOUT;
- if (timeout == 0)
- timeout = -1;
-
- if (!bound && !portscan)
- {
- logg("!ScanStream: Can't find any free port./n");
- mdprintf(odesc, "Can't find any free port. ERROR/n");
- close(sockfd);
- return -1;
- }
- else
- {
- listen(sockfd, 1);
- mdprintf(odesc, "PORT %d/n", port);
- }
-
- switch (retval = poll_fd(sockfd, timeout))
- {
- case 0:
- mdprintf(odesc, "Accept timeout. ERROR/n");
- logg("!ScanStream: accept timeout./n");
- close(sockfd);
- return -1;
- case -1:
- mdprintf(odesc, "Accept poll. ERROR/n");
- logg("!ScanStream: accept poll failed./n");
- close(sockfd);
- return -1;
- }
- if ((acceptd = accept(sockfd, NULL, NULL)) == -1)
- {
- close(sockfd);
- mdprintf(odesc, "accept() ERROR/n");
- logg("!ScanStream: accept() failed./n");
- return -1;
- }
- logg("*Accepted connection on port %d, fd %d/n", port, acceptd);
-
- if ((tmp = tmpfile()) == NULL)
- {
- shutdown(sockfd, 2);
- close(sockfd);
- close(acceptd);
- mdprintf(odesc, "tempfile() failed. ERROR/n");
- logg("!ScanStream: Can't create temporary file./n");
- return -1;
- }
- tmpd = fileno(tmp);
- if ((cpt = cfgopt(copt, "StreamMaxLength")))
- maxsize = cpt->numarg;
- else
- maxsize = CL_DEFAULT_STREAMMAXLEN;
- btread = sizeof(buff);
-
-
- while ((retval = poll_fd(acceptd, timeout)) == 1)
- {
- bread = read(acceptd, buff, btread);
- if (bread <= 0)
- break;
- size += bread;
- if (writen(tmpd, buff, bread) != bread)
- {
- shutdown(sockfd, 2);
- close(sockfd);
- close(acceptd);
- mdprintf(odesc, "Temporary file -> write ERROR/n");
- logg("!ScanStream: Can't write to temporary file./n");
- if (tmp)
- fclose(tmp);
- return -1;
- }
- if (maxsize && (size + btread >= maxsize))
- {
- btread = (maxsize - size);
- if (btread <= 0)
- {
- logg("^ScanStream: Size limit reached ( max: %d)/n", maxsize);
- break;
- }
- }
- }
- switch (retval)
- {
- case 0:
- mdprintf(odesc, "read timeout ERROR/n");
- logg("!ScanStream: read timeout./n");
- case -1:
- mdprintf(odesc, "read poll ERROR/n");
- logg("!ScanStream: read poll failed./n");
- }
- lseek(tmpd, 0, SEEK_SET);
- ret = cl_scandesc(tmpd, &virname, scanned, root, limits, options);
- if (tmp)
- fclose(tmp);
- close(acceptd);
- close(sockfd);
- if (ret == CL_VIRUS)
- {
- mdprintf(odesc, "stream: %s FOUND/n", virname);
- logg("stream: %s FOUND/n", virname);
- virusaction("stream", virname, copt);
- }
- else if (ret != CL_CLEAN)
- {
- mdprintf(odesc, "stream: %s ERROR/n", cl_strerror(ret));
- logg("stream: %s ERROR/n", cl_strerror(ret));
- }
- else
- {
- mdprintf(odesc, "stream: OK/n");
- if (logok)
- logg("stream: OK/n");
- }
- return ret;
- }
scanstream 的功能就是:
分配临时端口─────从临时端口上读取客户端的数据流(Y的,就是病毒嫌疑文件)──────扫描病毒──────结果反馈给客户端。 很清晰很明了的过程,不过要明白, 我们分配临时端口,只是为了获取数据流,传完文件就关闭,其它的一切与客户端的通信都建立在我们的老端口上.比如后面我们通知客户端小心,有毒,我们就用的是
mdprintf(odesc,
"stream: %s FOUND/n"
, virname);
写这篇文章的时候,想起一个问题,INADDR_ANY,用这个代表什么? 网上断章取义的拿来一段博文:
INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。
例如MontiVista Linux中在/usr/include/netinet/in.h定义为:
/* Address to accept any incoming messages. */
#define INADDR_ANY ((in_addr_t) 0x00000000)
一般情况下,如果你要建立网络服务器应用程序,则你要通知服务器操作系统:请在某地址 xxx.xxx.xxx.xxx上的某端口 yyyy上进行侦听,并且把侦听到的数据包发送给我。这个过程,你是通过bind()系统调用完成的。——也就是说,你的程序要绑定服务器的某地址,或者 说:把服务器的某地址上的某端口占为已用。服务器操作系统可以给你这个指定的地址,也可以不给你。
如果你的服务器有多个网卡(每个网卡上有不同的 IP地址),而你的服务(不管是在udp端口上侦听,还是在tcp端口上侦听),出于某种原因:可能是你的服务器操作系统可能随时增减IP地址,也有可能 是为了省去确定服务器上有什么网络端口(网卡)的麻烦 —— 可以要在调用bind()的时候,告诉操作系统:“我需要在 yyyy 端口上侦听,所以发送到服务器的这个端口,不管是哪个网卡/哪个IP地址接收到的数据,都是我处理的。”
其实关于病毒command的这系列代码最好紧贴着客户端代码进行,看了服务端接收信息,我们打破书上限制,直接来看客户端.