基于「ClamAv」通过python进行病毒检测(2)-- p
介绍pyClamd模块一般用法和常用方法等。
我们可以使用python来控制clamd
,从而操控ClamAv
,需要引入第三方模块:pyClamd
。
使用pyClamd
控制clamd
之前,必须先正确安装带有clamd
的clamav
。可以参考之前的文章:
一、pyClamd简介
PyClamd
是clamd
的python接口。clamd
是ClamAv
杀毒工具的守护进程。
通过使用pyClamd
,我们可以以一种高效而简单的方式,为python程序添加病毒检测功能。
二、安装pyClamd
使用pip
快速安装:
pip install pyClamd
模块官方文档:
http://xael.org/pages/pyclamd-en.html
tip1:英文ok的朋友,可以直接阅读官方文档,当然也可以快速浏览本文后,深入研究官方文档。
tip2:
pypi
上pyClamd
最后更新时间是2017年,作者可能没有继续维护这个项目了,且行且珍惜吧~
三、使用
3.1 常用方法示例
pyClamd
提供了一些常用方法,包括简单测试clamd
是否可用、字节流扫描、文件/文件夹扫描等。下面,我们在python终端来测试这些方法。
第一步:打开python3命令行终端:
[root@程序员的一天 home]# python3
Python 3.7.4 (default, Sep 18 2020, 14:36:11)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
第二步:导入pyClamd
:
>>> import pyclamd
>>>
第三步:实例化一个clamd
控制对象:
>>> cd = pyclamd.ClamdAgnostic()
>>>
tip:此处为实例化的第一种方式,后文会继续介绍另外两种方式,包括通过网络连接
clamd
。
第四步:测试clamd
是否可连接:
如果连接成功返回True
。
>>> cd = pyclamd.ClamdAgnostic()
>>> cd.ping()
True
>>>
第五步:查看ClamAv
版本:
>>> cd.version()
'ClamAV 0.103.0/25929/Sun Sep 13 21:53:46 2020'
>>>
第六步:生成测试病毒样本:
pyClamd
提供了一个EICAR()
方法,该方法可以快速生成病毒样本,用于测试。我们先来看看样本具体内容:
>>> cd.EICAR()
b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'
>>>
tip:可以看到输出的是一个python字节串。虽然是病毒样本,但是我们并没有去执行它,所以是无害的,大家不要担心机器中毒!!
第七步:scan_stream()
方法检测bytes
流:
scan_stream()
方法用于检测bytes
流。
包括网络字节流,也是可以检测的。比如,在邮箱系统前,对所有的邮件字节流进行检测,查杀恶意邮件。
我们可以利用EICAR()
方法,生成字节流来测试:
>>> cd.scan_stream(cd.EICAR())
{'stream': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
>>> cd.scan_stream(b'pythonbytes')
>>>
>>> cd.scan_stream(b'pythonbytes') is None
True
>>>
tip:如果检测到病毒返回字典,否则返回None。
第八步:scan_file()
文件/文件夹扫描:
scan_file()
方法,用于检测文件或者文件夹。
scan_file()
方法,一旦检测到病毒,会结束扫描,立马返回检测到的第一个病毒结果。
如果,ClamAv
采用的源码编译方式安装,那么在ClamAV
的源码解压包中有一个test
文件夹,里面全部都是ClamAv
提供的病毒测试样本:
[root@程序员的一天 clamAv]# ls
clamav-0.103.0 clamav-0.103.0.tar.gz
[root@程序员的一天 clamAv]#
[root@程序员的一天 clamAv]# ls clamav-0.103.0/test
clam.7z clam_cache_emax.tgz clam.exe.bz2 clam.impl.zip clam.mail clam-pespin.exe clam-upx.exe Makefile.am
clam.arj clam.chm clam.exe.html clam_IScab_ext.exe clam-mew.exe clam-petite.exe clam-v2.rar Makefile.in
clam-aspack.exe clam.d64.zip clam.exe.mbox.base64 clam_IScab_int.exe clam.newc.cpio clam.ppt clam-v3.rar README
clam.bin-be.cpio clam.ea05.exe clam.exe.mbox.uu clam_ISmsi_ext.exe clam-nsis.exe clam.sis clam-wwpack.exe
clam.bin-le.cpio clam.ea06.exe clam.exe.rtf clam_ISmsi_int.exe clam.odc.cpio clam.tar.gz clam-yc.exe
clam.bz2.zip clam.exe clam.exe.szdd clam.iso clam.ole.doc clam.tnef clam.zip
clam.cab clam.exe.binhex clam-fsg.exe clamjol.iso clam.pdf clam-upack.exe Makefile
[root@程序员的一天 clamAv]#
tip:不要轻易持续扫描
test
文件夹,因为全部是病毒样本,扫描会非常慢(也许可以用于测试ClamAv扫描性能)。可以指定其中的具体某个文件测试,或者重新生成新的样本文件测试。
我们可以用这些文件来测试,也可以利用EICAR()
方法,将病毒字节串写入文件,来生成新的样本文件测试。我这里使用生成新文件测试。
在tmp
目录下生成EICAR
和NO_EICAR
文件:
>>> void = open('/tmp/EICAR','w').write(cd.EICAR().decode())
>>> void = open('/tmp/NO_EICAR','w').write('no virus in this file')
>>>
tip:这里在
/tmp
目录下生成了EICAR
和NO_EICAR
文件,分别代表病毒样本文件和正常文件。
为scan_file()
方法传入具体文件路径,进行文件扫描:
>>> cd.scan_file('/tmp/EICAR')
{'/tmp/EICAR': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
>>> cd.scan_file('/tmp/NO_EICAR') is None
True
>>>
tip:一旦检测到病毒,立马返回字典,
key
为具体文件,value
为检测结果。否则返回None
。
同样,也可以传入一个文件夹路径作为scan_file()
方法参数,进行文件夹扫描:
>>> cd.scan_file('/tmp')
{'/tmp/EICAR': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
tip:扫描到第一个病毒文件后,结束扫描,返回结果。
第九步:contscan_file()
文件/文件夹扫描:
contscan_file()
方法,同样用于文件/文件夹扫描。
与scan_file()
方法扫描不同的是,contscan_file()
方法会在整个文件夹全部扫描完后,返回检测到的所有病毒文件。
为了方便测试,我们在/tmp
目录下再生成一个EICAR_1
样本文件。然后扫描/tmp
目录:
>>> void = open('/tmp/EICAR_1','w').write(cd.EICAR().decode())
>>>
>>> cd.contscan_file('/tmp/')
{'/tmp/EICAR': ('FOUND', 'Win.Test.EICAR_HDB-1'), '/tmp/EICAR_1': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
>>> cd.scan_file('/tmp')
{'/tmp/EICAR': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
tip:可以看到
scan_file()
方法只返回了一个结果,而contscan_file()
方法会扫描完整个文件目录后,返回检测到的所有威胁文件。
同样的,contscan_file()
也可以用于单个文件扫描:
>>> cd.contscan_file('/tmp/EICAR_1')
{'/tmp/EICAR_1': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
>>> cd.contscan_file('/tmp/EICAR')
{'/tmp/EICAR': ('FOUND', 'Win.Test.EICAR_HDB-1')}
>>>
3.2 pyClamd
连接clamd
方式总结
在源码编译安装ClamAv
,配置clamd
时,我们介绍过:如果要允许clamd
和其他应用程序通信,至少启用以下选项之一:
# 本地套接字通信
LocalSocket
# 网络套接字通信
TCPSocket
比如,这样配置:
# LocalSocket /tmp/clamd.socket
TCPSocket 3310
tip:通过tcp套接字,在3310端口通信。
同样的,pyClamd
与clamd
通信,也对应了这两种方式。
在pyClamd
中,提供了两个类,来连接clamd
。分别为:
ClamdUnixSocket()
ClamdNetworkSocket()
上面我们使用的ClamdAgnostic()
是一个自动选择的封装方法。
tip:需要注意的是,不管哪种方法连接,创建实例化对象后,所有的常用方法都一样。
接下来,我们分别对它们进行介绍。
3.2.1、ClamdAgnostic()
方法
ClamdAgnostic()
方法,自动选择连接方法。
ClamdAgnostic()
方法,首先尝试使用ClamdUnixSocket
连接到clamd
。如果失败,尝试
使用ClamdNetworkSocket
连接。最后返回相应的实例化对象。
在pyclamd.py中ClamdAgnostic()
方法定义为:
tip:
pyclamd.py
是pyClamd
库的主要模块,我们甚至可以直接把这个文件放入到程序中,而不使用pip
安装pyClamd
。
############################################################################
def ClamdAgnostic():
"""
Tries to connect to clamd using ClamdUnixSocket or if it fails, tries
with ClamdNetworkSocket and return the corresponding object.
Of course, it tries to connect with default settings...
"""
try:
# Create object for using unix socket
cd = ClamdUnixSocket()
except ConnectionError:
# if failed, test for network socket
try:
cd = ClamdNetworkSocket()
except ConnectionError:
raise ValueError("could not connect to clamd server either by unix or network socket")
return cd
############################################################################
如果,我们在同一台机器上,通过python控制clamd
,那么使用这个方法就足够了。具体实例化示例如下:
>>> cd = pyclamd.ClamdAgnostic()
>>>
3.2.2、ClamdUnixSocket()
类
ClamdUnixSocket()
使用本地套接字,用于同一台计算机下进程通信。
需要clamd.conf
中配置LocalSocket
选项:
# 指定本地套接字路径
LocalSocket /tmp/clamd.socket
ClamdUnixSocket()
部分源码如下:
class ClamdUnixSocket(_ClamdGeneric):
"""
Class for using clamd with an unix socket
"""
def __init__(self, filename=None, timeout=None):
"""
Unix Socket Class initialisation
filename (string) : unix socket filename or None to get the socket from /etc/clamav/clamd.conf or /etc/clamd.conf
timeout (float or None) : socket timeout
"""
# try to get unix socket from clamd.conf
if filename is None:
for clamdpath in ['/etc/clamav/clamd.conf', '/etc/clamd.conf']:
if os.path.isfile(clamdpath):
break
else:
raise ConnectionError('Could not find clamd unix socket from /etc/clamav/clamd.conf or /etc/clamd.conf')
可以看到,ClamdUnixSocket()
能够接收两个传入参数:
# 指定本地套接字
filename
# 设置连接超时
timeout
如果,同一台计算机上,我们希望指定本地套接字,来控制clamd
,那么可以实例化ClamdUnixSocket()
类:
>>> cd = pyclamd.ClamdUnixSocket(filename='/tmp/clamd.socket', timeout=10)
>>>
tip:需要对应的本地套接字存在。
3.2.3、ClamdNetworkSocket()
类
ClamdNetworkSocket()
类,允许通过网络套接字来控制clamd
。
需要clamd.conf
中配置TCPSocket
选项:
# 配置监听端口
TCPSocket 3310
# 配置监听地址,默认为0.0.0.0
TCPAddr 127.0.0.1
ClamdNetworkSocket()
部分源码如下:
class ClamdNetworkSocket(_ClamdGeneric):
"""
Class for using clamd with a network socket
"""
def __init__(self, host='127.0.0.1', port=3310, timeout=None):
"""
Network Class initialisation
host (string) : hostname or ip address
port (int) : TCP port
timeout (float or None) : socket timeout
"""
可以看到,ClamdNetworkSocket()
能够接收三个传入参数:
# 指定clamd监听地址
host
# 指定clamd监听端口
port
# 设置连接超时
timeout
通过ClamdNetworkSocket()
,我们可以跨机器,通过网络套接字来远程控制clamd
。实例化ClamdNetworkSocket()
类:
>>> cd = pyclamd.ClamdNetworkSocket(host='127.0.0.1', port=3310, timeout=10)
>>>
到此,pyClamd
介绍完毕!
END.
我的部分文章会首发在公众号上。微信读者,可以搜一搜:【程序员的一天】,感兴趣的朋友可以关注,支持一下,谢谢!
每一个关注、点赞,都是极大的支持和鼓励。最后,非常感谢阅读。