Clamav杀毒软件源码分析笔记[九]admin管理员组文章数量:1598602
刺猬@http://blog.csdn/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;
- /* get min port 获取最小端口号*/
- if ((cpt = cfgopt(copt, "StreamMinPort")))
- {
- if (cpt->numarg < 1024 || cpt->numarg > 65535)
- min_port = 1024;
- else
- min_port = cpt->numarg;
- }
- else
- min_port = 1024;
- /* get max port 获取最大端口号*/
- 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;
- /* bind to a free port */
- while (!bound && --portscan) //这里portscan默认是1000,也就是这里最多尝试1000个端口
- {
- if (rnd_port_first) //第一次分配端口 是最小端口+随机产生的数字(当然保证在正确的范围之类)
- {
- /* try a random port first */
- port = min_port + cli_rndnum(max_port - min_port + 1);
- rnd_port_first = 0;
- }
- else //第一次分配失败后后面就是依次尝试了
- {
- /* try the neighbor ports */
- 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) //这里通常我们设置的是127.0.0.1
- {
- 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); //这里队列长度只为1
- mdprintf(odesc, "PORT %d/n", port); //这里把新看上的端口告诉客户端
- }
- //retval如果是正数表示有多少文件描述符准备好了 --我老是忘字 -_-!
- switch (retval = poll_fd(sockfd, timeout))
- {
- case 0: /* timeout */
- 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);
- /* 下面是建立临时文件,tmpfile其实是一个库函数,真没想到*/
- 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);
- /* 下面主要是通过才绑定的socket获取客户端传来的文件,然后我们扫描,前面我们绑定端口忙活了一大阵子就是为了打开通道获取文件*/
- while ((retval = poll_fd(acceptd, timeout)) == 1) //大于0说明有文件描述符就绪了,好,我们开始行动
- {
- bread = read(acceptd, buff, btread);
- if (bread <= 0)
- break;
- size += bread;
- if (writen(tmpd, buff, bread) != bread) //writen是作者自己编写的函数 因为这里我们需要一个函数很卖命的给我们写数据,可以说了往死里写了
- {
- 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)) //tmp文件我们默认10M
- {
- btread = (maxsize - size); /* only read up to max 要保证不能超过文件最大值*/
- if (btread <= 0)
- {
- logg("^ScanStream: Size limit reached ( max: %d)/n", maxsize);
- break; /* Scan what we have */
- }
- }
- }
- switch (retval)
- {
- case 0: /* timeout */
- 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) //有毒 注意这里odesc是我们和客户端通信的唯一通道
- {
- 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;
- }
写这篇文章的时候,想起一个问题,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的这系列代码最好紧贴着客户端代码进行,看了服务端接收信息,我们打破书上限制,直接来看客户端.
版权声明:本文标题:Clamav杀毒软件源码分析笔记[九] 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728309106a1152991.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论