2 ### Submit a sample to VirusTotal and check the result.
3 ### Based on example code by Bryce Boe, downloaded from:
4 ### http://www.bryceboe.com/2010/09/01/submitting-binaries-to-virustotal/
6 ### Needs Python 2.6 or 2.7
9 ### - Put your VirusTotal API key in the file virus-scan.key in the current
10 ### working directory (obtain from http://www.virustotal.com/advanced.html#publicapi)
11 ### - Run PATH/TO/PYTHON virus-scan.py FILE_TO_SCAN
13 ### Copyright 2010 Steven J. Murdoch <http://www.cl.cam.ac.uk/users/sjm217/>
14 ### See LICENSE for licensing information
17 import hashlib
, httplib
, mimetypes
, os
, pprint
, json
, sys
, urlparse
19 DEFAULT_TYPE
= 'application/octet-stream'
21 REPORT_URL
= 'https://www.virustotal.com/api/get_file_report.json'
22 SCAN_URL
= 'https://www.virustotal.com/api/scan_file.json'
24 API_KEY_FILE
= 'virus-scan.key'
26 # The following function is modified from the snippet at:
27 # http://code.activestate.com/recipes/146306/
28 def encode_multipart_formdata(fields
, files
=()):
30 fields is a dictionary of name to value for regular form fields.
31 files is a sequence of (name, filename, value) elements for data to be
33 Return (content_type, body) ready for httplib.HTTP instance
35 BOUNDARY
= '----------ThIs_Is_tHe_bouNdaRY_$'
38 for key
, value
in fields
.items():
39 L
.append('--' + BOUNDARY
)
40 L
.append('Content-Disposition: form-data; name="%s"' % key
)
43 for (key
, filename
, value
) in files
:
44 L
.append('--' + BOUNDARY
)
45 L
.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
47 content_type
= mimetypes
.guess_type(filename
)[0] or DEFAULT_TYPE
48 L
.append('Content-Type: %s' % content_type
)
51 L
.append('--' + BOUNDARY
+ '--')
54 content_type
= 'multipart/form-data; boundary=%s' % BOUNDARY
55 return content_type
, body
57 def post_multipart(url
, fields
, files
=()):
59 url is the full to send the post request to.
60 fields is a dictionary of name to value for regular form fields.
61 files is a sequence of (name, filename, value) elements for data to be
63 Return body of http response.
65 content_type
, data
= encode_multipart_formdata(fields
, files
)
66 url_parts
= urlparse
.urlparse(url
)
67 if url_parts
.scheme
== 'http':
68 h
= httplib
.HTTPConnection(url_parts
.netloc
)
69 elif url_parts
.scheme
== 'https':
70 h
= httplib
.HTTPSConnection(url_parts
.netloc
)
72 raise Exception('Unsupported URL scheme')
73 path
= urlparse
.urlunparse(('', '') + url_parts
[2:])
74 h
.request('POST', path
, data
, {'content-type':content_type
})
75 return h
.getresponse().read()
77 def scan_file(filename
, api_key
):
78 files
= [('file', filename
, open(filename
, 'rb').read())]
79 json_result
= post_multipart(SCAN_URL
, {'key':api_key
}, files
)
80 return json
.loads(json_result
)
82 def get_report(filename
, api_key
):
83 md5sum
= hashlib
.md5(open(filename
, 'rb').read()).hexdigest()
84 json_result
= post_multipart(REPORT_URL
, {'resource':md5sum
, 'key':api_key
})
85 data
= json
.loads(json_result
)
86 if data
['result'] != 1:
87 print 'Result not found, submitting file.'
88 data
= scan_file(filename
, api_key
)
89 if data
['result'] == 1:
90 print 'Submit successful.'
91 print 'Please wait a few minutes and try again to receive report.'
94 print 'Submit failed.'
98 #pprint.pprint(data['report'])
99 scan_date
, result_dict
= data
['report']
100 print "Scanned on:", scan_date
103 for av_name
, result
in result_dict
.items():
106 print " %20s: %s"%(av_name
, result
)
108 print 'SUCCESS: no AV detection triggered'
111 print 'FAIL: %s AV detection(s)'%failures
114 if __name__
== '__main__':
115 if len(sys
.argv
) != 2:
116 print 'Usage: %s filename' % sys
.argv
[0]
120 key_fh
= open(API_KEY_FILE
, "rt")
121 api_key
= key_fh
.readline().strip()
124 print 'Failed to open API key file %s: %s' % (API_KEY_FILE
, e
)
127 filename
= sys
.argv
[1]
128 if not os
.path
.isfile(filename
):
129 print '%s is not a valid file' % filename
132 exit_status
= get_report(filename
, api_key
)
133 sys
.exit(exit_status
)