├── README.md
├── requirements.txt
└── scan.py
/README.md:
--------------------------------------------------------------------------------
1 | # Antiscan
2 | This script makes use of the Antiscan.me service, a site that allows you to scan your binaries with multiple antivirus engines without distributing them. It is meant to make the scanning process easier and more convenient.
3 | ## Requirements
4 | Required packages:
5 | ```txt
6 | requests
7 | colorama
8 | beautifulsoup4
9 | ```
10 | You also need a working Antiscan.me **API Key** to perform scans.
11 | ## Usage
12 | ```txt
13 | usage: scan.py [-h] [-f FILE] [-img] key
14 |
15 | Antiscan.me automatization script
16 |
17 | positional arguments:
18 | key Your Antiscan.me API Key
19 |
20 | optional arguments:
21 | -h, --help show this help message and exit
22 | -f FILE, --file FILE File to scan
23 | -img, --image Save the scan result as an image
24 | ```
25 | ## Supported file types
26 | Antiscan.me supports the following file types:
27 |
28 | | Extension | Description |
29 | |-----------|---------------------------------|
30 | | exe | Executable files |
31 | | dll | Dynamic Link Libraries |
32 | | bin | Binary files |
33 | | msi | Windows Installer Packages |
34 | | doc, docx | Microsoft Word Documents |
35 | | rtf | Rich Text Format files |
36 | | xls, xlsx | Microsoft Excel Spreadsheets |
37 | | pdf | Printable Document Format Files |
38 | | js | JavaScript files |
39 | | vbs, vbe | VBScript files |
40 | | ico | Icon files |
41 |
42 | If you want to scan files of a different type, rename them (*.bin) before scanning.
43 | ## Notes
44 | All credit for the scanning service goes to Antiscan.me.
45 |
46 | Free daily scans are not currently supported.
47 |
48 | This script uses the **AVCHECK API**.
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.31.0
2 | colorama==0.4.1
3 | beautifulsoup4==4.8.2
4 |
--------------------------------------------------------------------------------
/scan.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import argparse
3 | from bs4 import BeautifulSoup
4 | import json
5 | from pathlib import Path
6 | from colorama import init, Fore, Back, Style
7 |
8 | d = 0 # Number of detections
9 | t = 0 # Total number of AVs
10 |
11 | def printResult(av, det): # Function to print and count individual detections
12 | global d
13 | print('{av: <30}'.format(av = av), end = '')
14 | if det == 'Clean':
15 | print(Fore.GREEN + det, end = '')
16 | else:
17 | print(Fore.RED + det, end = '')
18 | d += 1
19 | print(Fore.RESET)
20 |
21 | p = argparse.ArgumentParser(description = "Antiscan.me automatization script")
22 | p.add_argument('key', help = 'Your Antiscan.me API Key')
23 | p.add_argument('-f', '--file', help = 'File to scan')
24 | p.add_argument('-img', '--image', action = 'store_true', help = 'Save the scan result as an image')
25 | args = p.parse_args()
26 |
27 | init()
28 | session = requests.Session() # Initialize a new session
29 |
30 | # Login
31 | res = session.get('https://antiscan.me/login')
32 | soup = BeautifulSoup(res.content, 'html.parser')
33 | csrf = soup.find('meta', attrs={'name': 'csrf-token'})['content']
34 |
35 | data = {
36 | '_csrf': csrf,
37 | 'LoginForm[auth_key]': args.key,
38 | 'login-button': ''
39 | }
40 |
41 | res = session.post('https://antiscan.me/login', data = data)
42 | if res.url == 'https://antiscan.me/login':
43 | print('Invalid API Key!')
44 | quit()
45 |
46 | # Print account balance
47 | soup = BeautifulSoup(res.content, 'html.parser')
48 | bal = soup.find('a', attrs = {'href': '/user/balance'}).text.strip()
49 | bal = bal[bal.find(' ')+1:]
50 | bal = bal[:bal.find(' ')]
51 | print(Fore.CYAN + 'Account stats for', args.key, Fore.RESET)
52 | print('\tBalance:', bal, 'USD')
53 | print('\tRemaining scans:', int(float(bal)*10))
54 | print()
55 |
56 | # Scan file
57 | if args.file is None:
58 | quit()
59 |
60 | print(Fore.CYAN + 'Scanning', args.file, Fore.RESET)
61 | files = {
62 | 'FileForm[file]': open(args.file, 'rb')
63 | }
64 | csrf = soup.find('meta', attrs={'name': 'csrf-token'})['content']
65 | data = {
66 | '_csrf': csrf
67 | }
68 | headers = {
69 | 'X-CSRF-Token': csrf,
70 | 'X-Requested-With': 'XMLHttpRequest',
71 | 'Origin': 'https://antiscan.me',
72 | 'Referer': 'https://antiscan.me/'
73 | }
74 |
75 | res = session.post('https://antiscan.me/scan/new/check', headers=headers, data=data, files=files)
76 | obj = json.loads(res.content)
77 |
78 | # Check for errors
79 | if (obj['status'] == False) or (res.status_code != 200):
80 | print(Fore.RED + 'Scan failed!')
81 | print('\tStatus code:', res.status_code)
82 | print('\tError: ', obj['error'])
83 | quit()
84 |
85 | print('\tScan ID:', Fore.MAGENTA + obj['id'], Fore.RESET)
86 |
87 | # Parse and print results
88 | res = session.get('https://antiscan.me/scan/new/result?id='+obj['id'])
89 | soup = BeautifulSoup(res.content, 'html.parser')
90 | csrf = soup.find('meta', attrs={'name': 'csrf-token'})['content']
91 |
92 | if args.image: # Save results as an image
93 | headers = {
94 | 'X-CSRF-Token': csrf,
95 | 'X-Requested-With': 'XMLHttpRequest',
96 | 'Referer': 'https://antiscan.me/scan/new/result?id='+obj['id']
97 | }
98 | res1 = session.get('https://antiscan.me/scan/new/image?id='+obj['id'], headers=headers) # Generate image
99 | res1 = session.get('https://antiscan.me/images/result/'+obj['id']+'.png') # Download image
100 | f = open(Path(args.file).stem+'.png', 'wb')
101 | f.write(res1.content)
102 | f.close()
103 | print('\tImage saved.')
104 |
105 | print()
106 |
107 | s = str(res.content).replace('', '') # Antiscan.me generates malformed HTML by default, we need to fix that
108 | s = s.replace('\\n', '\n')
109 | soup = BeautifulSoup(s, 'html.parser')
110 | line = soup.find('div', attrs = {'class': 'flatLineScanResult'}).text # First line of results
111 | line += soup.find('div', attrs = {'class': 'adjustLineScanResult'}).text # Second line of results
112 | for l in line.splitlines():
113 | l = l.strip()
114 | if len(l) == 0:
115 | continue # Ignore whitespace
116 | t += 1
117 | av, det = l.split(': ', 1)
118 | printResult(av, det)
119 | print()
120 | print('Detected by ', end = '')
121 | if d == 0:
122 | print(Fore.GREEN, end = '')
123 | else:
124 | print(Fore.RED, end = '')
125 | print(d, '/', t, ' (', '{0:.1f}'.format(d*100/t), '%)', Fore.RESET, sep = '')
126 | print()
--------------------------------------------------------------------------------