├── preview.JPG ├── requirements.txt ├── archive ├── CHANGES_1.0.2-220427 ├── main-v1.0.0.py ├── main_v1.0.2-220427.py └── main_v1.0.3-220510.py ├── CHANGES ├── README.md ├── main.py └── LICENSE /preview.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/password123456/malwarescanner/HEAD/preview.JPG -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | python-magic 3 | python-magic-bin 4 | pefile 5 | cryptography 6 | asn1crypto 7 | netifaces 8 | -------------------------------------------------------------------------------- /archive/CHANGES_1.0.2-220427: -------------------------------------------------------------------------------- 1 | -*- coding: utf-8 -*- 2 | 3 | Changes with v1.0.2-220427 4 | 5 | 1) If an exception occurs while the program running, print the exception point line number and show what the error is. 6 | 7 | 2) Fixed some bugs. 8 | - comparion syntax and a few unexpected conditions. 9 | 10 | 3) If an infected found, the log will write the date of creation and modification of the infected file. 11 | 12 | 4) If an infected found, the log will scan the infected file and get it's file type and write the log. 13 | - it is very basical level. just get it's mime/type, 14 | 15 | 5) A few log field has been added. 16 | - ipaddress, OS name, hostname, file_type 17 | - So its function was added. 18 | 19 | 6) Log format has been changed. 20 | - The log format has been changed so you can handle more easily. 21 | - Just put it in the analysis-system and SIEM, there is no work to do. It will definitely recognized automatically. 22 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | -*- coding: utf-8 -*- 2 | 3 | 4 | Changes with v1.0.3-220510 5 | 6 | *) Digital Signature Extraction 7 | - When malware detected, scanner scan it's file format. 8 | - If file has digital signature(code signing), get the certificate information and write to the log and try to extract certificates to logs directory 9 | - If finished, you can see certficates in the log directory.export-certificate file format is DER. 10 | 11 | *) Code improvement 12 | - I improved the code, but it doesn't look clearer than the previous. 13 | 14 | 15 | Changes with v1.0.4-221219 16 | *) Code improvement 17 | - a little bit improvement 18 | 19 | *) Exclude scan directory variable 20 | - If you want to exclude directories from scanning, add them as a list to 'EXCLUDE_DIRS' 21 | 22 | *) Upcoming 23 | - Untrust Code Signing Certificate Blocklist Scan Function 24 | - It will be created/separate as a new program or add the features to current version. 25 | 26 | 27 | Changes with v1.0.5-221223 28 | *) Code improvement 29 | - multi-threading work added 30 | 31 | *) Remove digital signature exporting function temporary 32 | - coming soon.. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MalwareScanner 2 | ![made-with-python][made-with-python] 3 | ![Python Versions][pyversion-button] 4 | [![Hits](https://hits.sh/github.com/password123456/malwarescanner.svg?view=today-total)](https://hits.sh/github.com/password123456/malwarescanner/) 5 | 6 | [pyversion-button]: https://img.shields.io/pypi/pyversions/Markdown.svg 7 | [made-with-python]: https://img.shields.io/badge/Made%20with-Python-1f425f.svg 8 | 9 | - Very basic malware Scanner by hash comparison 10 | - Sometimes this can be needed when an incident response. 11 | - If you found new or suspicious files when you do response, you want to check out where these files exist in systems. so then you may need like this tool. this is a demo version. not complete. you have to change and modify code and make it yours. 12 | - Let me know if there are any changes required or additional features need it. 13 | - and press the "stars" if it helps. then it will continue to improvement. 14 | 15 | # Features 16 | - File inspection based on hash (free malware hashes) 17 | - Scanning include subdirectories 18 | - Multithreading Jobs 19 | - Define file extensions to scan 20 | - Define directories not to scan 21 | - Easy to attach scan_logs to the SIEM (e.g Splunk) 22 | - Easy to Handle and changeable code/function structure (if you interest) 23 | 24 | ## v1.0.5 25 | - https://github.com/password123456/malwarescanner/blob/main/CHANGES 26 | 27 | ## Preview 28 | 29 | 30 | 31 | ## Scan result_log 32 | - datetime, scan_id, os, hostname, ip, file.........hash, 33 | ``` 34 | datetime="2022-12-23 13:52:45",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\chrome.exe",sha256="45de9f5a25bf478c6a8d8625a984895c9b3fdea6eb12a55ddd088dcebd9df5ab",created_at="2022-05-02 21:33:38",modified_at="2022-04-14 06:17:04" 35 | datetime="2022-12-23 13:52:46",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\LineLauncher.exe",sha256="60fd9ceac8d1c40e835d3dc9aa77599d3f810a646199ecbdcb8003521ce7d502",created_at="2022-05-10 12:38:18",modified_at="2022-03-10 18:00:10" 36 | datetime="2022-12-23 13:52:46",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\LineUpdater.exe",sha256="6b663f9f7bf3b6c1a2ce898b2402b691fff6dced36e9b0196e152bbfc409db42",created_at="2022-05-10 12:38:22",modified_at="2022-04-06 10:06:28" 37 | datetime="2022-12-23 13:52:47",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\PCHunter32.exe",sha256="0544b99c52d607712d70351c6d9afb1fba6adb0de256baf662f06fdf6058aa3d",created_at="2022-05-10 11:36:04",modified_at="2017-08-14 02:27:08" 38 | datetime="2022-12-23 13:52:48",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\report.xlsx",sha256="dd1dc1cb4ffa3e1b91f00496436416807f6153d53ac45e67ce1287c842bd5e3e",created_at="2022-04-27 23:53:33",modified_at="2021-09-07 10:52:40" 39 | datetime="2022-12-23 13:52:48",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\TWOD_Launcher.exe",sha256="1d55ab0a703f2b8f5cc0d3cc93bedf7c03799b2b3e42defc83c175da8a125962",created_at="2022-04-27 23:54:50",modified_at="2022-04-07 09:14:08" 40 | datetime="2022-12-23 13:52:49",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\unins000.exe",sha256="2da3b504c2b68219c0b4c6f062867dae091560c3f1e0735f1a7b17b1d79b5a90",created_at="2022-04-27 23:54:53",modified_at="2022-04-26 12:19:01" 41 | datetime="2022-12-23 13:52:49",scan_id="4d7f0b1b-f771-4dd0-bb87-6453ff6341e8",os="Windows",hostname="DESKTOP-S5VJGLH",ip="192.168.0.23",infected_file="F:\code\pythonProject\malware_hash_scanner3\scan_test_dir\vmware-view.exe",sha256="17b21902170784d460fd0168e08f6e2075d6eb0a501093bc35184c550d73b5d8",created_at="2022-05-10 12:38:38",modified_at="2020-07-07 18:08:06" 42 | 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /archive/main-v1.0.0.py: -------------------------------------------------------------------------------- 1 | __author__ = 'https://github.com/password123456/' 2 | 3 | import os 4 | import sys 5 | import importlib 6 | import time 7 | from datetime import datetime 8 | import requests 9 | import hashlib 10 | from zipfile import ZipFile 11 | import argparse 12 | 13 | importlib.reload(sys) 14 | 15 | _today_ = datetime.today().strftime('%Y-%m-%d') 16 | _ctime_ = datetime.today().strftime('%Y-%m-%d %H:%M:%S') 17 | 18 | _home_path_ = '%s' % os.getcwd() 19 | 20 | _engine_zipfile_ = '%s/%s.zip' % (_home_path_, _today_) 21 | _engine_extract_file_ = '%s/engine.db' % _home_path_ 22 | 23 | _scan_result_logs_ = '%s/output/%s-infected.log' % (_home_path_, _today_) 24 | _scan_extension_list_ = ['.exe', '.dll', '.sys', '.doc', '.docx', '.xls', '.xlsx', '.py', '.xml', '.cfg', '.txt','.ppt', '.pptx', '.hwp'] 25 | 26 | 27 | class Bcolors: 28 | Black = '\033[30m' 29 | Red = '\033[31m' 30 | Green = '\033[32m' 31 | Yellow = '\033[33m' 32 | Blue = '\033[34m' 33 | Magenta = '\033[35m' 34 | Cyan = '\033[36m' 35 | White = '\033[37m' 36 | Endc = '\033[0m' 37 | BOLD = '\033[1m' 38 | UNDERLINE = '\033[4m' 39 | 40 | 41 | def download_engine(): 42 | _url = 'https://bazaar.abuse.ch/export/txt/sha256/full/' 43 | _header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) ' 44 | 'Chrome/49.0.2623.112 Safari/537.36', 'Connection': 'keep-alive'} 45 | try: 46 | with open(_engine_zipfile_, 'wb') as f: 47 | r = requests.get(_url, headers=_header, stream=True) 48 | download_file_length = r.headers.get('Content-Length') 49 | print('%s Downloading: %s / %.2f MB %s' 50 | % (Bcolors.Green, _engine_zipfile_, (float(download_file_length) / (1024.0 * 1024.0)), Bcolors.Endc)) 51 | 52 | if download_file_length is None: 53 | f.write(r.content) 54 | else: 55 | dl = 0 56 | total_length = int(download_file_length) 57 | start = time.perf_counter() 58 | for data in r.iter_content(chunk_size=8092): 59 | dl += len(data) 60 | f.write(data) 61 | done = int(100 * dl / total_length) 62 | sys.stdout.write('\r [%s%s] %s/%s (%s%%) - %.2f seconds ' 63 | % ('>' * done, ' ' * (100 - done), total_length, dl, 64 | done, (time.perf_counter() - start))) 65 | sys.stdout.flush() 66 | f.close() 67 | 68 | extract_gzip(_engine_zipfile_, _home_path_) 69 | 70 | except Exception as e: 71 | print('%s[-] Exception::%s%s' % (Bcolors.Yellow, e, Bcolors.Endc)) 72 | else: 73 | r.close() 74 | 75 | 76 | def extract_gzip(_engine_zipfile_, _home_path_): 77 | with ZipFile(_engine_zipfile_, 'r') as zipObj: 78 | file_list = zipObj.infolist() 79 | for file in file_list: 80 | if file.filename[-1] == '/': 81 | continue 82 | file.filename = os.path.basename(file.filename) 83 | if file.filename.lower() == 'full_sha256.txt'.lower(): 84 | zipObj.extract(file, _home_path_) 85 | _update_file = '%s/%s' % (_home_path_, file.filename) 86 | try: 87 | os.rename(_update_file, _engine_extract_file_) 88 | except OSError as e: 89 | print('%s can not be renamed' % _update_file) 90 | print('%s- Exception::%s%s' % (Bcolors.Yellow, e, Bcolors.Endc)) 91 | sys.exit(1) 92 | 93 | # Remove Engine zip 94 | try: 95 | os.remove(_engine_zipfile_) 96 | except OSError as e: 97 | print('%s can not be removed' % _engine_zipfile_) 98 | print('%s- Exception::%s%s' % (Bcolors.Yellow, e, Bcolors.Endc)) 99 | sys.exit(1) 100 | 101 | # Check Downloaded File 102 | if os.path.isfile(_engine_extract_file_): 103 | f = open(_engine_extract_file_, 'rb') 104 | file_read = f.read() 105 | f.close() 106 | 107 | file_hash = hashlib.sha256(file_read).hexdigest() 108 | file_info = '===> Extracted Size: %.2f MB\n===> Hash(SHA-256) : %s\n' \ 109 | % (int(os.path.getsize(_engine_extract_file_)) / (1024.0 * 1024.0), file_hash) 110 | 111 | print('\n\n%s===> Update Success: %s %s' % (Bcolors.Green, _engine_extract_file_, Bcolors.Endc)) 112 | print('%s%s%s' % (Bcolors.Green, file_info, Bcolors.Endc)) 113 | else: 114 | print('%s[-] %s not found. %s' % (Bcolors.Yellow, _engine_extract_file_, Bcolors.Endc)) 115 | sys.exit(1) 116 | 117 | 118 | def raw_count(filename): 119 | _n = 0 120 | with open(filename) as f: 121 | for line in f: 122 | if not line.startswith('#'): 123 | _n = _n + 1 124 | f.close() 125 | return n 126 | 127 | 128 | def engine_last_dated(filename): 129 | with open(filename) as f: 130 | for line in f: 131 | if 'Last updated' in line: 132 | line = line.replace('#', '') 133 | line = line.lstrip().strip('\n') 134 | line = line.split(' ') 135 | line = line[2:5] 136 | line = ' '.join(line) 137 | break 138 | f.close() 139 | return line 140 | 141 | 142 | def hash_exists_in_db(check_hash): 143 | _mode = 'r' 144 | _n = 0 145 | with open(_engine_extract_file_, _mode) as database: 146 | for line in database: 147 | _n = _n + 1 148 | if len(line.strip()) != 0: 149 | if not line.startswith('#'): 150 | if str(check_hash) in str(line): 151 | return True 152 | return False 153 | 154 | 155 | def scan_result_logs(_contents): 156 | _make_output_dir = '%s/output' % _home_path_ 157 | _mode = 'w' 158 | 159 | if os.path.exists(_make_output_dir): 160 | if os.path.exists(_scan_result_logs_): 161 | _mode = 'a' 162 | else: 163 | _mode = 'w' 164 | os.makedirs(_make_output_dir) 165 | 166 | with open(_scan_result_logs_, _mode) as fa: 167 | fa.write('%s' % _contents) 168 | fa.close() 169 | 170 | 171 | def make_hash(_file_name): 172 | _file_hash = '' 173 | if os.path.isfile(_file_name): 174 | f = open(_file_name, 'rb') 175 | filename_read = f.read() 176 | _file_hash = hashlib.sha256(filename_read).hexdigest() 177 | f.close() 178 | return _file_hash 179 | 180 | 181 | def check_file_extension(_file_name): 182 | if _file_name.endswith(tuple(_scan_extension_list_)): 183 | return True 184 | else: 185 | return False 186 | 187 | 188 | def check_file_size(_file_name): 189 | # 5MB = '5242880' 190 | _limit = 5242880 191 | 192 | f = os.stat(_file_name).st_size 193 | if f <= _limit: 194 | return True 195 | else: 196 | return False 197 | 198 | 199 | def check_engine(): 200 | _name = 'MALWARE' 201 | _get_download = True 202 | 203 | if os.path.exists(_engine_extract_file_): 204 | create_time = os.stat(_engine_extract_file_).st_mtime 205 | engine_file_datetime = datetime.fromtimestamp(create_time).strftime('%Y-%m-%d') 206 | today = datetime.now().date() 207 | if str(engine_file_datetime) == str(today): 208 | _get_download = True 209 | else: 210 | _get_download = False 211 | else: 212 | print('- Updating %s Signatures' % _name) 213 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 214 | download_engine() 215 | 216 | if not _get_download: 217 | print('- Updating %s Signatures' % _name) 218 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 219 | download_engine() 220 | 221 | 222 | def scanner(_scan_path): 223 | _scanned_file_count = 0 224 | _infected_file_count = 0 225 | _scandir_file_count = sum(len(files) for _, _, files in os.walk(_scan_path)) 226 | print('- Scan Directory : %s' % _scan_path) 227 | print('- Number of files : %d' % _scandir_file_count) 228 | print('- Scan Extensions : %s' % str(_scan_extension_list_)[1:-1]) 229 | print('%s------------------------------------->%s\n' % (Bcolors.Yellow, Bcolors.Endc)) 230 | print('\x1b[7;33;40m' + 'O.K Here We go.!' + '\x1b[0m') 231 | print('- Currently scanning...') 232 | 233 | start = time.perf_counter() 234 | _detected = '' 235 | 236 | for subdir, dirs, files in os.walk(_scan_path): 237 | for file in files: 238 | _scanned_file_count = _scanned_file_count + 1 239 | _f_name = '%s' % os.path.join(subdir, file) 240 | elapsed_time = time.perf_counter() - start 241 | if check_file_extension(file): 242 | if check_file_size(_f_name): 243 | scan_file_hash = make_hash(_f_name) 244 | if hash_exists_in_db(scan_file_hash): 245 | _infected_file_count = _infected_file_count + 1 246 | _contents = '%s %s %s %s\n' % (_infected_file_count, _ctime_, _f_name, scan_file_hash) 247 | _detected += _contents 248 | scan_result_logs(_contents) 249 | 250 | sys.stdout.write('\r- %d files scanned / %d infected [%s] (%s) ' 251 | % (_scanned_file_count, _infected_file_count, 252 | datetime.strftime(datetime.utcfromtimestamp(elapsed_time), '%H:%M:%S'), 253 | _f_name)) 254 | #time.sleep(0.01) 255 | sys.stdout.flush() 256 | 257 | if _infected_file_count >= 1: 258 | _vt_info = '\n##### For more information about infected file, search the Virustotal ###\n' \ 259 | 'https://www.virustotal.com/gui/file/$infected_file_hash\n\n' 260 | scan_result_logs(_vt_info) 261 | 262 | print('\n') 263 | print('- Result') 264 | 265 | if _detected: 266 | print('%s------O------M------G--------------->-%s' % (Bcolors.Red, Bcolors.Endc)) 267 | print('- Scanner Found %s%d%s infected files!' % (Bcolors.Yellow, _infected_file_count, Bcolors.Endc)) 268 | print('- See the %s%s%s \n' % (Bcolors.Cyan, _scan_result_logs_, Bcolors.Endc)) 269 | else: 270 | print('%s- OK.Good. No infection found.%s' % (Bcolors.Green, Bcolors.Endc)) 271 | 272 | 273 | def main(): 274 | print('\n') 275 | print('%s▌║█║▌│║▌│║▌║▌█║ %sSimple Basic Malware Scanner%s▌│║▌║▌│║║▌█║▌║█%s\n' 276 | % (Bcolors.Green, Bcolors.Red, Bcolors.Green, Bcolors.Endc)) 277 | opt = argparse.ArgumentParser(description='Simple Basic Malware Scanner') 278 | opt.add_argument('--path', help='ex) /var/www/html/upload') 279 | opt.add_argument('--update', action='store_true', help='AV Engine Update') 280 | 281 | if len(sys.argv) < 1: 282 | opt.print_help() 283 | sys.exit(1) 284 | else: 285 | options = opt.parse_args() 286 | 287 | if options.path: 288 | _scan_path = os.path.abspath(options.path) 289 | print('- Run time: %s' % _ctime_) 290 | print('- For questions contact github.com/password123456\t\t') 291 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 292 | check_engine() 293 | print('- Engine Updated : %s' % engine_last_dated(_engine_extract_file_)) 294 | engine_count = raw_count(_engine_extract_file_) 295 | print('- AV Signatures : %s' % engine_count) 296 | scanner(_scan_path) 297 | 298 | elif options.update: 299 | print('- Run time: %s' % _ctime_) 300 | print('- For questions contact github.com/password123456\t\t') 301 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 302 | check_engine() 303 | print('- Engine Updated : %s' % engine_last_dated(_engine_extract_file_)) 304 | engine_count = raw_count(_engine_extract_file_) 305 | print('- AV Signatures : %s' % engine_count) 306 | else: 307 | opt.print_help() 308 | sys.exit(1) 309 | 310 | 311 | if __name__ == '__main__': 312 | try: 313 | main() 314 | except KeyboardInterrupt: 315 | sys.exit(0) 316 | except Exception as e: 317 | print('%s- Exception::%s%s' % (Bcolors.Yellow, e, Bcolors.Endc)) 318 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | __author__ = 'https://github.com/password123456/' 2 | __version__ = '1.0.5-221223' 3 | 4 | import os 5 | import sys 6 | import platform 7 | import importlib 8 | import time 9 | import requests 10 | import hashlib 11 | import magic 12 | import argparse 13 | import uuid 14 | import netifaces 15 | 16 | from concurrent.futures import ThreadPoolExecutor, as_completed 17 | from datetime import datetime 18 | from zipfile import ZipFile 19 | 20 | importlib.reload(sys) 21 | 22 | SCAN_EXTENSIONS = ['.exe', '.dll', '.sys', '.doc', '.docx', '.xls', '.xlsx', '.py', '.xml', '.cfg', '.txt', '.ppt', '.pptx', '.hwp'] 23 | EXCLUDE_DIRS = ['venv', 'venv2', '.idea', 'lib'] 24 | 25 | _today_ = datetime.today().strftime('%Y-%m-%d') 26 | _ctime_ = datetime.today().strftime('%Y-%m-%d %H:%M:%S') 27 | 28 | #_home_path_ = 'F:/code/pythonProject/malware_hash_scanner3' 29 | _home_path_ = f'{os.getcwd()}' 30 | 31 | _engine_zipfile_ = f'{_home_path_}/{_today_}.zip' 32 | _engine_extract_file_ = f'{_home_path_}/engine.db' 33 | _scan_result_logs_ = f'{_home_path_}/output/{_today_}-infected.log' 34 | 35 | 36 | class Bcolors: 37 | Black = '\033[30m' 38 | Red = '\033[31m' 39 | Green = '\033[32m' 40 | Yellow = '\033[33m' 41 | Blue = '\033[34m' 42 | Magenta = '\033[35m' 43 | Cyan = '\033[36m' 44 | White = '\033[37m' 45 | Endc = '\033[0m' 46 | BOLD = '\033[1m' 47 | UNDERLINE = '\033[4m' 48 | 49 | 50 | def download_engine(): 51 | _url = 'https://bazaar.abuse.ch/export/txt/sha256/full/' 52 | _header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) ' 53 | 'Chrome/49.0.2623.112 Safari/537.36', 'Connection': 'keep-alive'} 54 | try: 55 | with open(_engine_zipfile_, 'wb') as f: 56 | r = requests.get(_url, headers=_header, stream=True) 57 | download_file_length = r.headers.get('Content-Length') 58 | print(f'{Bcolors.Green} Downloading: {_engine_zipfile_} / {(float(download_file_length) / (1024.0 * 1024.0)):.2f} MB {Bcolors.Endc}') 59 | 60 | if download_file_length is None: 61 | f.write(r.content) 62 | else: 63 | dl = 0 64 | total_length = int(download_file_length) 65 | start = time.perf_counter() 66 | for data in r.iter_content(chunk_size=8092): 67 | dl += len(data) 68 | f.write(data) 69 | done = int(100 * dl / total_length) 70 | print(f'[{">" * done}{" " * (100 - done)}] {total_length}/{dl} ({done}%) - {(time.perf_counter() - start):.2f} seconds ', end='\r') 71 | 72 | extract_gzip(_engine_zipfile_, _home_path_) 73 | 74 | except Exception as e: 75 | print(f'{Bcolors.Yellow}- ::Exception:: Func:[{download_engine.__name__}] Line:[{sys.exc_info()[-1].tb_lineno}] [{type(e).__name__}] {e}{Bcolors.Endc}') 76 | finally: 77 | r.close() 78 | 79 | 80 | def extract_gzip(_engine_zipfile_, _home_path_): 81 | with ZipFile(_engine_zipfile_, 'r') as zipObj: 82 | file_list = zipObj.infolist() 83 | for file in file_list: 84 | if file.filename[-1] == '/': 85 | continue 86 | file.filename = os.path.basename(file.filename) 87 | if file.filename.lower() == 'full_sha256.txt'.lower(): 88 | zipObj.extract(file, _home_path_) 89 | _update_file = f'{_home_path_}/{file.filename}' 90 | 91 | if os.path.isfile(_engine_extract_file_): 92 | os.remove(_engine_extract_file_) 93 | 94 | try: 95 | os.rename(_update_file, _engine_extract_file_) 96 | except OSError as e: 97 | print(f'{_update_file} can not be renamed') 98 | print(f'{Bcolors.Yellow}- ::Exception:: Func:[{extract_gzip.__name__}] Line:[{sys.exc_info()[-1].tb_lineno}] [{type(e).__name__}] {e}{Bcolors.Endc}') 99 | sys.exit(1) 100 | 101 | # Remove Engine zip 102 | try: 103 | os.remove(_engine_zipfile_) 104 | except OSError as e: 105 | print(f'{_update_file} can not be renamed') 106 | print(f'{Bcolors.Yellow}- ::Exception:: Func:[{extract_gzip.__name__}] Line:[{sys.exc_info()[-1].tb_lineno}] [{type(e).__name__}] {e}{Bcolors.Endc}') 107 | sys.exit(1) 108 | 109 | # Check Downloaded File 110 | if os.path.isfile(_engine_extract_file_): 111 | with open(_engine_extract_file_, 'rb') as f: 112 | file_read = f.read() 113 | file_hash = hashlib.sha256(file_read).hexdigest() 114 | file_info = f'===> Extracted Size: {int(os.path.getsize(_engine_extract_file_)) / (1024.0 * 1024.0):.2f} MB\n===> Hash(SHA-256) : {file_hash}\n' 115 | 116 | print(f'\n\n{Bcolors.Green}===> Update Success: {_engine_extract_file_} {Bcolors.Endc}') 117 | print(f'{Bcolors.Green}{file_info}{Bcolors.Endc}') 118 | else: 119 | print(f'{Bcolors.Yellow}[-] {_engine_extract_file_} not found. {Bcolors.Endc}') 120 | sys.exit(1) 121 | 122 | 123 | def raw_count(filename): 124 | n = 0 125 | with open(filename) as f: 126 | for line in f: 127 | if not line.startswith('#'): 128 | n = n + 1 129 | return n 130 | 131 | 132 | def get_engine_last_updated_date(filename): 133 | with open(filename) as f: 134 | for line in f: 135 | if 'Last updated' in line: 136 | line = line.replace('#', '') 137 | line = line.lstrip().strip('\n') 138 | line = line.split(' ') 139 | line = line[2:5] 140 | line = ' '.join(line) 141 | #print(line) 142 | break 143 | return line 144 | 145 | 146 | def hash_exists_in_db(check_hash): 147 | _mode = 'r' 148 | _n = 0 149 | with open(_engine_extract_file_, _mode) as database: 150 | for line in database: 151 | _n = _n + 1 152 | if len(line.strip()) != 0: 153 | if not line.startswith('#'): 154 | if str(check_hash) in str(line): 155 | return True 156 | return False 157 | 158 | 159 | def scan_result_logs(_contents): 160 | _make_output_dir = f'{_home_path_}/output' 161 | _mode = 'w' 162 | 163 | if os.path.exists(_make_output_dir): 164 | if os.path.exists(_scan_result_logs_): 165 | _mode = 'a' 166 | else: 167 | _mode = 'w' 168 | os.makedirs(_make_output_dir) 169 | 170 | with open(_scan_result_logs_, _mode) as fa: 171 | fa.write('%s' % _contents) 172 | 173 | 174 | def make_hash(_f_file_name): 175 | _file_hash = '' 176 | if os.path.isfile(_f_file_name): 177 | with open(_f_file_name, 'rb') as f: 178 | filename_read = f.read() 179 | _file_hash = hashlib.sha256(filename_read).hexdigest() 180 | return _file_hash 181 | 182 | 183 | def check_file_extension(_file_name): 184 | if _file_name.endswith(tuple(SCAN_EXTENSIONS)): 185 | return True 186 | else: 187 | return False 188 | 189 | 190 | def check_file_size(_f_file_name): 191 | # 10MB = '10485760' 192 | _limit = 10485760 193 | 194 | f = os.stat(_f_file_name).st_size 195 | if f <= _limit: 196 | return True 197 | else: 198 | return False 199 | 200 | 201 | def get_create_date(_f_file_name): 202 | if platform.system() == 'Windows': 203 | _result = os.path.getctime(_f_file_name) 204 | else: 205 | _result = os.path.getmtime(_f_file_name) 206 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 207 | 208 | 209 | def get_modify_date(_f_file_name): 210 | _result = os.path.getmtime(_f_file_name) 211 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 212 | 213 | 214 | def get_file_type(_file_name): 215 | return magic.from_buffer(open(_file_name, 'rb').read(2048)) 216 | 217 | 218 | def get_hostname(): 219 | return platform.node() 220 | 221 | 222 | def get_osver(): 223 | return platform.system() 224 | 225 | 226 | def get_ip_address(): 227 | gateways = netifaces.gateways() 228 | default_gateway = gateways['default'][netifaces.AF_INET] 229 | gateway_ip, interface = default_gateway[0], default_gateway[1] 230 | iface = netifaces.ifaddresses(interface) 231 | local_ip = iface[netifaces.AF_INET][0]['addr'] 232 | return local_ip 233 | 234 | 235 | def create_job_id(): 236 | return uuid.uuid4() 237 | 238 | 239 | def check_engine(): 240 | if os.path.exists(_engine_extract_file_): 241 | modify_filetime = os.stat(_engine_extract_file_).st_mtime 242 | today_num_ymd = datetime.today().strftime('%Y%m%d') 243 | _engine_file_date = datetime.fromtimestamp(modify_filetime).strftime('%Y%m%d') 244 | 245 | if not(int(_engine_file_date) == int(today_num_ymd)): 246 | _get_download = False 247 | else: 248 | _get_download = True 249 | 250 | if not _get_download: 251 | print(f'{Bcolors.Yellow}- Updating Engine Signatures.{Bcolors.Endc}') 252 | download_engine() 253 | else: 254 | print(f'{Bcolors.Yellow}- Up2date Engine : ^_^V {Bcolors.Endc}') 255 | else: 256 | print(f'{Bcolors.Yellow}- Updating Engine Signatures.{Bcolors.Endc}') 257 | download_engine() 258 | 259 | 260 | def scan_file(_f_file_name): 261 | _scan_result = '' 262 | if check_file_extension(_f_file_name): 263 | if check_file_size(_f_file_name): 264 | scan_file_hash = make_hash(_f_file_name) 265 | if hash_exists_in_db(scan_file_hash): 266 | _scan_result = f'{_f_file_name}|{scan_file_hash}|{get_create_date(_f_file_name)}|{get_modify_date(_f_file_name)}' 267 | return _scan_result 268 | 269 | 270 | def scan_directory(_scan_path): 271 | _log_ipaddr = get_ip_address() 272 | _log_hostname = get_hostname() 273 | _log_scan_id = create_job_id() 274 | _log_os_ver = get_osver() 275 | 276 | _count_submitted_file = 0 277 | _count_infected_file = 0 278 | 279 | _scan_start_time = time.perf_counter() 280 | 281 | with ThreadPoolExecutor(max_workers=50) as executor: 282 | # Submit a task for each file in the directory 283 | for subdir, dirs, files in os.walk(_scan_path): 284 | dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS] 285 | for file in files: 286 | _f_file_name = os.path.realpath(os.path.join(subdir, file)) 287 | result = [executor.submit(scan_file, _f_file_name)] 288 | for future in as_completed(result): 289 | result = future.result() 290 | _count_submitted_file += 1 291 | _scan_duration_time = time.perf_counter() + _scan_start_time 292 | if result: 293 | _count_infected_file += 1 294 | 295 | print(f'[{datetime.strftime(datetime.utcfromtimestamp(_scan_duration_time), "%H:%M:%S.%f")}] ' 296 | f'({_count_submitted_file} scanned / {_count_infected_file} infected) {result.split("|")[0].strip()}', end='\r') 297 | 298 | _contents = f'datetime="{datetime.today().strftime("%Y-%m-%d %H:%M:%S")}",scan_id="{_log_scan_id}",os="{_log_os_ver}",' \ 299 | f'hostname="{_log_hostname}",ip="{_log_ipaddr}",infected_file="{result.split("|")[0]}",sha256="{result.split("|")[1]}",' \ 300 | f'created_at="{result.split("|")[2]}",modified_at="{result.split("|")[3]}"\n' 301 | scan_result_logs(_contents) 302 | print('\n') 303 | 304 | if _count_infected_file >= 1: 305 | print(f'\x1b[0;43;43m Scan Completed.! \x1b[0m \n- O.M.G... \x1b[0;42;42m [{_count_infected_file}] \x1b[0m file infected.\n- See "{_scan_result_logs_}"\n') 306 | else: 307 | _contents = f'datetime="{datetime.today().strftime("%Y-%m-%d %H:%M:%S")}",scan_id="{_log_scan_id}",os="{_log_os_ver}",' \ 308 | f'hostname="{_log_hostname}",ip="{_log_ipaddr}",infected_file="None"\n' 309 | scan_result_logs(_contents) 310 | print(f'\x1b[0;43;43m Scan Completed.! \x1b[0m \n- no malware Found.! happy happy:)\n') 311 | 312 | 313 | def main(): 314 | print(f'\n') 315 | print(f'{Bcolors.Green}▌║█║▌│║▌│║▌║▌█║ {Bcolors.Red}Simple Basic Malware Scanner {Bcolors.White}v{__version__}{Bcolors.Green} ▌│║▌║▌│║║▌█║▌║█{Bcolors.Endc}\n') 316 | opt = argparse.ArgumentParser(description='Simple Basic Malware Scanner') 317 | opt.add_argument('--path', help='ex) /home/download') 318 | opt.add_argument('--update', action='store_true', help='AV Engine Update') 319 | 320 | if len(sys.argv) < 1: 321 | opt.print_help() 322 | sys.exit(1) 323 | else: 324 | options = opt.parse_args() 325 | print(f'- Run time: {_ctime_}') 326 | print('- For questions contact github.com/password123456\t\t') 327 | print('\n') 328 | 329 | if options.path: 330 | _scan_path = os.path.abspath(options.path) 331 | print(f'{Bcolors.Green}------------------------------------->{Bcolors.Endc}\n') 332 | check_engine() 333 | print(f'- Engine Updated : {get_engine_last_updated_date(_engine_extract_file_)}') 334 | print(f'- AV Signatures : {raw_count(_engine_extract_file_)}') 335 | print(f'-{Bcolors.Green} O.K Here We go.!{Bcolors.Endc}') 336 | scan_directory(_scan_path) 337 | 338 | elif options.update: 339 | print(f'{Bcolors.Green}——————————————————>{Bcolors.Endc}\n') 340 | check_engine() 341 | print(f'- Engine Updated : {get_engine_last_updated_date(_engine_extract_file_)}') 342 | print(f'- AV Signatures : {raw_count(_engine_extract_file_)}') 343 | else: 344 | opt.print_help() 345 | sys.exit(1) 346 | 347 | 348 | if __name__ == '__main__': 349 | try: 350 | main() 351 | except KeyboardInterrupt: 352 | sys.exit(0) 353 | except Exception as e: 354 | print(f'{Bcolors.Yellow}- ::Exception:: Func:[{__name__.__name__}] Line:[{sys.exc_info()[-1].tb_lineno}] [{type(e).__name__}] {e}{Bcolors.Endc}') 355 | -------------------------------------------------------------------------------- /archive/main_v1.0.2-220427.py: -------------------------------------------------------------------------------- 1 | __author__ = 'https://github.com/password123456/' 2 | 3 | import os 4 | import sys 5 | import platform 6 | import importlib 7 | import time 8 | from datetime import datetime 9 | import requests 10 | import hashlib 11 | import magic 12 | from zipfile import ZipFile 13 | import argparse 14 | import uuid 15 | from socket import getaddrinfo, gethostname 16 | 17 | importlib.reload(sys) 18 | 19 | _today_ = datetime.today().strftime('%Y-%m-%d') 20 | _ctime_ = datetime.today().strftime('%Y-%m-%d %H:%M:%S') 21 | 22 | _version_ = '1.0.2-220427' 23 | _home_path_ = '%s' % os.getcwd() 24 | 25 | _engine_zipfile_ = '%s/%s.zip' % (_home_path_, _today_) 26 | _engine_extract_file_ = '%s/engine.db' % _home_path_ 27 | 28 | _scan_result_logs_ = '%s/output/%s-infected.log' % (_home_path_, _today_) 29 | _scan_extension_list_ = ['.exe', '.dll', '.sys', '.doc', '.docx', '.xls', '.xlsx', '.py', '.xml', '.cfg', '.txt', '.ppt', '.pptx', '.hwp'] 30 | 31 | 32 | class Bcolors: 33 | Black = '\033[30m' 34 | Red = '\033[31m' 35 | Green = '\033[32m' 36 | Yellow = '\033[33m' 37 | Blue = '\033[34m' 38 | Magenta = '\033[35m' 39 | Cyan = '\033[36m' 40 | White = '\033[37m' 41 | Endc = '\033[0m' 42 | BOLD = '\033[1m' 43 | UNDERLINE = '\033[4m' 44 | 45 | 46 | def download_engine(): 47 | _url = 'https://bazaar.abuse.ch/export/txt/sha256/full/' 48 | _header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) ' 49 | 'Chrome/49.0.2623.112 Safari/537.36', 'Connection': 'keep-alive'} 50 | try: 51 | with open(_engine_zipfile_, 'wb') as f: 52 | #print('%s Download URL: %s %s' % (Bcolors.Green, url, Bcolors.Endc)) 53 | 54 | r = requests.get(_url, headers=_header, stream=True) 55 | download_file_length = r.headers.get('Content-Length') 56 | print('%s Downloading: %s / %.2f MB %s' 57 | % (Bcolors.Green, _engine_zipfile_, (float(download_file_length) / (1024.0 * 1024.0)), Bcolors.Endc)) 58 | 59 | if download_file_length is None: 60 | f.write(r.content) 61 | else: 62 | dl = 0 63 | total_length = int(download_file_length) 64 | start = time.perf_counter() 65 | for data in r.iter_content(chunk_size=8092): 66 | dl += len(data) 67 | f.write(data) 68 | done = int(100 * dl / total_length) 69 | sys.stdout.write('\r [%s%s] %s/%s (%s%%) - %.2f seconds ' 70 | % ('>' * done, ' ' * (100 - done), total_length, dl, 71 | done, (time.perf_counter() - start))) 72 | sys.stdout.flush() 73 | f.close() 74 | 75 | extract_gzip(_engine_zipfile_, _home_path_) 76 | 77 | except Exception as e: 78 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, download_engine.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 79 | else: 80 | r.close() 81 | 82 | 83 | def extract_gzip(_engine_zipfile_, _home_path_): 84 | with ZipFile(_engine_zipfile_, 'r') as zipObj: 85 | file_list = zipObj.infolist() 86 | for file in file_list: 87 | if file.filename[-1] == '/': 88 | continue 89 | file.filename = os.path.basename(file.filename) 90 | if file.filename.lower() == 'full_sha256.txt'.lower(): 91 | zipObj.extract(file, _home_path_) 92 | _update_file = '%s/%s' % (_home_path_, file.filename) 93 | 94 | if os.path.isfile(_engine_extract_file_): 95 | os.remove(_engine_extract_file_) 96 | 97 | try: 98 | os.rename(_update_file, _engine_extract_file_) 99 | except OSError as e: 100 | print('%s can not be renamed' % _update_file) 101 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, extract_gzip.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 102 | sys.exit(1) 103 | 104 | # Remove Engine zip 105 | try: 106 | os.remove(_engine_zipfile_) 107 | except OSError as e: 108 | print('%s can not be removed' % _engine_zipfile_) 109 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, extract_gzip.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 110 | sys.exit(1) 111 | 112 | # Check Downloaded File 113 | if os.path.isfile(_engine_extract_file_): 114 | f = open(_engine_extract_file_, 'rb') 115 | file_read = f.read() 116 | f.close() 117 | 118 | file_hash = hashlib.sha256(file_read).hexdigest() 119 | file_info = '===> Extracted Size: %.2f MB\n===> Hash(SHA-256) : %s\n' \ 120 | % (int(os.path.getsize(_engine_extract_file_)) / (1024.0 * 1024.0), file_hash) 121 | 122 | print('\n\n%s===> Update Success: %s %s' % (Bcolors.Green, _engine_extract_file_, Bcolors.Endc)) 123 | print('%s%s%s' % (Bcolors.Green, file_info, Bcolors.Endc)) 124 | else: 125 | print('%s[-] %s not found. %s' % (Bcolors.Yellow, _engine_extract_file_, Bcolors.Endc)) 126 | sys.exit(1) 127 | 128 | 129 | def raw_count(filename): 130 | n = 0 131 | with open(filename) as f: 132 | for line in f: 133 | if not line.startswith('#'): 134 | n = n + 1 135 | f.close() 136 | return n 137 | 138 | 139 | def get_engine_last_udated_date(filename): 140 | with open(filename) as f: 141 | for line in f: 142 | if 'Last updated' in line: 143 | line = line.replace('#', '') 144 | line = line.lstrip().strip('\n') 145 | line = line.split(' ') 146 | line = line[2:5] 147 | line = ' '.join(line) 148 | #print(line) 149 | break 150 | f.close() 151 | return line 152 | 153 | 154 | def hash_exists_in_db(check_hash): 155 | _mode = 'r' 156 | _n = 0 157 | with open(_engine_extract_file_, _mode) as database: 158 | for line in database: 159 | _n = _n + 1 160 | if len(line.strip()) != 0: 161 | if not line.startswith('#'): 162 | if str(check_hash) in str(line): 163 | return True 164 | return False 165 | 166 | 167 | def scan_result_logs(_contents): 168 | _make_output_dir = '%s/output' % _home_path_ 169 | _mode = 'w' 170 | 171 | if os.path.exists(_make_output_dir): 172 | if os.path.exists(_scan_result_logs_): 173 | _mode = 'a' 174 | else: 175 | _mode = 'w' 176 | os.makedirs(_make_output_dir) 177 | 178 | with open(_scan_result_logs_, _mode) as fa: 179 | fa.write('%s' % _contents) 180 | fa.close() 181 | 182 | 183 | def make_hash(_file_name): 184 | _file_hash = '' 185 | if os.path.isfile(_file_name): 186 | f = open(_file_name, 'rb') 187 | filename_read = f.read() 188 | _file_hash = hashlib.sha256(filename_read).hexdigest() 189 | f.close() 190 | return _file_hash 191 | 192 | 193 | def check_file_extension(_file_name): 194 | #print(file_name) 195 | if _file_name.endswith(tuple(_scan_extension_list_)): 196 | return True 197 | else: 198 | return False 199 | 200 | 201 | def check_file_size(_file_name): 202 | # 5MB = '5242880' 203 | _limit = 5242880 204 | 205 | f = os.stat(_file_name).st_size 206 | if f <= _limit: 207 | return True 208 | else: 209 | return False 210 | 211 | 212 | def get_create_date(_file_name): 213 | if platform.system() == 'Windows': 214 | _result = os.path.getctime(_file_name) 215 | else: 216 | _result = os.path.getmtime(_file_name) 217 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 218 | 219 | 220 | def get_modify_date(_file_name): 221 | _result = os.path.getmtime(_file_name) 222 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 223 | 224 | 225 | def get_file_type(_file_name): 226 | return magic.from_buffer(open(_file_name, 'rb').read(2048)) 227 | 228 | 229 | def get_hostname(): 230 | return platform.node() 231 | 232 | 233 | def get_osver(): 234 | if str(platform.system()) != 'Windows': 235 | _result = '%s' % platform.platform().split('-with-', 1)[1] 236 | else: 237 | _result = '%s' % platform.platform() 238 | return _result 239 | 240 | 241 | def get_ip_address(): 242 | system_ip_list = getaddrinfo(gethostname(), None, 2, 1, 0) 243 | _ip_list = '' 244 | for ip in system_ip_list: 245 | _ip_address = ip[4][0] 246 | _ip_list += '%s ' % _ip_address 247 | return _ip_list.strip() 248 | 249 | 250 | def create_job_id(): 251 | return uuid.uuid5(uuid.NAMESPACE_URL, platform.node()) 252 | 253 | 254 | def check_engine(): 255 | _name = 'MALWARE' 256 | _get_download = True 257 | 258 | if os.path.exists(_engine_extract_file_): 259 | create_time = os.stat(_engine_extract_file_).st_mtime 260 | 261 | _engine_file_date = datetime.fromtimestamp(create_time).strftime('%Y%m%d') 262 | _today_num = datetime.today().strftime('%Y%m%d') 263 | 264 | if int(_engine_file_date) == int(_today_num): 265 | _get_download = True 266 | else: 267 | _get_download = False 268 | else: 269 | print('- Updating %s Signatures' % _name) 270 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 271 | download_engine() 272 | 273 | if not _get_download: 274 | print('- Updating %s Signatures' % _name) 275 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 276 | download_engine() 277 | 278 | 279 | def scanner(_scan_path): 280 | _scanned_file_count = 0 281 | _infected_file_count = 0 282 | _scandir_file_count = sum(len(files) for _, _, files in os.walk(_scan_path)) 283 | print('- Scan Directory : %s' % _scan_path) 284 | print('- Number of files : %d' % _scandir_file_count) 285 | print('- Scan Extensions : %s' % str(_scan_extension_list_)[1:-1]) 286 | print('%s------------------------------------->%s\n' % (Bcolors.Yellow, Bcolors.Endc)) 287 | #print('-%s O.K Here We go.!%s' % (Bcolors.Magenta, Bcolors.Endc)) 288 | print('\x1b[7;33;40m' + 'O.K Here We go.!' + '\x1b[0m') 289 | print('- Currently scanning...') 290 | 291 | start = time.perf_counter() 292 | _detected = '' 293 | 294 | _ipaddress = get_ip_address() 295 | _hostname = get_hostname() 296 | _scan_id = create_job_id() 297 | _os_version = get_osver() 298 | 299 | for subdir, dirs, files in os.walk(_scan_path): 300 | for file in files: 301 | #print(os.path.join(subdir, file)) 302 | _scanned_file_count = _scanned_file_count + 1 303 | _f_name = '%s' % os.path.join(subdir, file) 304 | elapsed_time = time.perf_counter() - start 305 | #print(file) 306 | if check_file_extension(file): 307 | if check_file_size(_f_name): 308 | scan_file_hash = make_hash(_f_name) 309 | if hash_exists_in_db(scan_file_hash): 310 | _infected_file_count = _infected_file_count + 1 311 | _contents = 'datetime="%s",scan_id="%s",os="%s",hostname="%s",ip="%s",file_type="%s",file="%s",created_at="%s",modified_at="%s",hash="%s"\n' \ 312 | % (datetime.today().strftime('%Y-%m-%d %H:%M:%S'), _scan_id, _os_version, _hostname, _ipaddress, get_file_type(_f_name), 313 | _f_name, get_create_date(_f_name), get_modify_date(_f_name), scan_file_hash) 314 | _detected += _contents 315 | scan_result_logs(_contents) 316 | 317 | sys.stdout.write('\r- %d files scanned / %d infected [%s] (%s) ' 318 | % (_scanned_file_count, _infected_file_count, 319 | datetime.strftime(datetime.utcfromtimestamp(elapsed_time), '%H:%M:%S'), 320 | _f_name)) 321 | #time.sleep(0.01) 322 | sys.stdout.flush() 323 | 324 | if _infected_file_count >= 1: 325 | _vt_info = '\n##### For more information about infected file, search the Virustotal ###\n' \ 326 | 'https://www.virustotal.com/gui/file/$infected_file_hash\n\n' 327 | scan_result_logs(_vt_info) 328 | 329 | print('\n') 330 | print('- Result') 331 | 332 | if _detected: 333 | print('%s------O------M------G--------------->%s' % (Bcolors.Red, Bcolors.Endc)) 334 | print('- Scanner Found %s%d%s infected files!' % (Bcolors.Yellow, _infected_file_count, Bcolors.Endc)) 335 | print('- See the %s%s%s \n' % (Bcolors.Cyan, _scan_result_logs_, Bcolors.Endc)) 336 | else: 337 | print('%s- OK.Good. No infection found.%s' % (Bcolors.Green, Bcolors.Endc)) 338 | 339 | 340 | def main(): 341 | print('\n') 342 | print('%s▌║█║▌│║▌│║▌║▌█║ %sSimple Basic Malware Scanner %sv%s%s ▌│║▌║▌│║║▌█║▌║█%s\n' 343 | % (Bcolors.Green, Bcolors.Red, Bcolors.White, _version_, Bcolors.Green, Bcolors.Endc)) 344 | opt = argparse.ArgumentParser(description='Simple Basic Malware Scanner') 345 | opt.add_argument('--path', help='ex) /var/www/html/upload') 346 | opt.add_argument('--update', action='store_true', help='AV Engine Update') 347 | 348 | if len(sys.argv) < 1: 349 | opt.print_help() 350 | sys.exit(1) 351 | else: 352 | options = opt.parse_args() 353 | 354 | if options.path: 355 | _scan_path = os.path.abspath(options.path) 356 | print('- Run time: %s' % _ctime_) 357 | print('- For questions contact github.com/password123456\t\t') 358 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 359 | check_engine() 360 | # engine_date = engine_last_dated(_engine_extract_file_) 361 | print('- Engine Updated : %s' % get_engine_last_udated_date(_engine_extract_file_)) 362 | engine_count = raw_count(_engine_extract_file_) 363 | print('- AV Signatures : %s' % engine_count) 364 | scanner(_scan_path) 365 | 366 | elif options.update: 367 | print('- Run time: %s' % _ctime_) 368 | print('- For questions contact github.com/password123456\t\t') 369 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 370 | check_engine() 371 | # engine_date = engine_last_dated(_engine_extract_file_) 372 | print('- Engine Updated : %s' % get_engine_last_udated_date(_engine_extract_file_)) 373 | engine_count = raw_count(_engine_extract_file_) 374 | print('- AV Signatures : %s' % engine_count) 375 | else: 376 | opt.print_help() 377 | sys.exit(1) 378 | 379 | 380 | if __name__ == '__main__': 381 | try: 382 | main() 383 | except KeyboardInterrupt: 384 | sys.exit(0) 385 | except Exception as e: 386 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, __name__.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 387 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The GNU General Public License, Version 2, June 1991 (GPLv2) 2 | ============================================================ 3 | 4 | > Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | 11 | Preamble 12 | -------- 13 | 14 | The licenses for most software are designed to take away your freedom to share 15 | and change it. By contrast, the GNU General Public License is intended to 16 | guarantee your freedom to share and change free software--to make sure the 17 | software is free for all its users. This General Public License applies to most 18 | of the Free Software Foundation's software and to any other program whose 19 | authors commit to using it. (Some other Free Software Foundation software is 20 | covered by the GNU Lesser General Public License instead.) You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not price. Our 24 | General Public Licenses are designed to make sure that you have the freedom to 25 | distribute copies of free software (and charge for this service if you wish), 26 | that you receive source code or can get it if you want it, that you can change 27 | the software or use pieces of it in new free programs; and that you know you can 28 | do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid anyone to deny 31 | you these rights or to ask you to surrender the rights. These restrictions 32 | translate to certain responsibilities for you if you distribute copies of the 33 | software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether gratis or for a 36 | fee, you must give the recipients all the rights that you have. You must make 37 | sure that they, too, receive or can get the source code. And you must show them 38 | these terms so they know their rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and (2) offer 41 | you this license which gives you legal permission to copy, distribute and/or 42 | modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain that 45 | everyone understands that there is no warranty for this free software. If the 46 | software is modified by someone else and passed on, we want its recipients to 47 | know that what they have is not the original, so that any problems introduced by 48 | others will not reflect on the original authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software patents. We wish 51 | to avoid the danger that redistributors of a free program will individually 52 | obtain patent licenses, in effect making the program proprietary. To prevent 53 | this, we have made it clear that any patent must be licensed for everyone's free 54 | use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and modification 57 | follow. 58 | 59 | 60 | Terms And Conditions For Copying, Distribution And Modification 61 | --------------------------------------------------------------- 62 | 63 | **0.** This License applies to any program or other work which contains a notice 64 | placed by the copyright holder saying it may be distributed under the terms of 65 | this General Public License. The "Program", below, refers to any such program or 66 | work, and a "work based on the Program" means either the Program or any 67 | derivative work under copyright law: that is to say, a work containing the 68 | Program or a portion of it, either verbatim or with modifications and/or 69 | translated into another language. (Hereinafter, translation is included without 70 | limitation in the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not covered by 73 | this License; they are outside its scope. The act of running the Program is not 74 | restricted, and the output from the Program is covered only if its contents 75 | constitute a work based on the Program (independent of having been made by 76 | running the Program). Whether that is true depends on what the Program does. 77 | 78 | **1.** You may copy and distribute verbatim copies of the Program's source code 79 | as you receive it, in any medium, provided that you conspicuously and 80 | appropriately publish on each copy an appropriate copyright notice and 81 | disclaimer of warranty; keep intact all the notices that refer to this License 82 | and to the absence of any warranty; and give any other recipients of the Program 83 | a copy of this License along with the Program. 84 | 85 | You may charge a fee for the physical act of transferring a copy, and you may at 86 | your option offer warranty protection in exchange for a fee. 87 | 88 | **2.** You may modify your copy or copies of the Program or any portion of it, 89 | thus forming a work based on the Program, and copy and distribute such 90 | modifications or work under the terms of Section 1 above, provided that you also 91 | meet all of these conditions: 92 | 93 | * **a)** You must cause the modified files to carry prominent notices stating 94 | that you changed the files and the date of any change. 95 | 96 | * **b)** You must cause any work that you distribute or publish, that in whole 97 | or in part contains or is derived from the Program or any part thereof, to 98 | be licensed as a whole at no charge to all third parties under the terms of 99 | this License. 100 | 101 | * **c)** If the modified program normally reads commands interactively when 102 | run, you must cause it, when started running for such interactive use in the 103 | most ordinary way, to print or display an announcement including an 104 | appropriate copyright notice and a notice that there is no warranty (or 105 | else, saying that you provide a warranty) and that users may redistribute 106 | the program under these conditions, and telling the user how to view a copy 107 | of this License. (Exception: if the Program itself is interactive but does 108 | not normally print such an announcement, your work based on the Program is 109 | not required to print an announcement.) 110 | 111 | These requirements apply to the modified work as a whole. If identifiable 112 | sections of that work are not derived from the Program, and can be reasonably 113 | considered independent and separate works in themselves, then this License, and 114 | its terms, do not apply to those sections when you distribute them as separate 115 | works. But when you distribute the same sections as part of a whole which is a 116 | work based on the Program, the distribution of the whole must be on the terms of 117 | this License, whose permissions for other licensees extend to the entire whole, 118 | and thus to each and every part regardless of who wrote it. 119 | 120 | Thus, it is not the intent of this section to claim rights or contest your 121 | rights to work written entirely by you; rather, the intent is to exercise the 122 | right to control the distribution of derivative or collective works based on the 123 | Program. 124 | 125 | In addition, mere aggregation of another work not based on the Program with the 126 | Program (or with a work based on the Program) on a volume of a storage or 127 | distribution medium does not bring the other work under the scope of this 128 | License. 129 | 130 | **3.** You may copy and distribute the Program (or a work based on it, under 131 | Section 2) in object code or executable form under the terms of Sections 1 and 2 132 | above provided that you also do one of the following: 133 | 134 | * **a)** Accompany it with the complete corresponding machine-readable source 135 | code, which must be distributed under the terms of Sections 1 and 2 above on 136 | a medium customarily used for software interchange; or, 137 | 138 | * **b)** Accompany it with a written offer, valid for at least three years, to 139 | give any third party, for a charge no more than your cost of physically 140 | performing source distribution, a complete machine-readable copy of the 141 | corresponding source code, to be distributed under the terms of Sections 1 142 | and 2 above on a medium customarily used for software interchange; or, 143 | 144 | * **c)** Accompany it with the information you received as to the offer to 145 | distribute corresponding source code. (This alternative is allowed only for 146 | noncommercial distribution and only if you received the program in object 147 | code or executable form with such an offer, in accord with Subsection b 148 | above.) 149 | 150 | The source code for a work means the preferred form of the work for making 151 | modifications to it. For an executable work, complete source code means all the 152 | source code for all modules it contains, plus any associated interface 153 | definition files, plus the scripts used to control compilation and installation 154 | of the executable. However, as a special exception, the source code distributed 155 | need not include anything that is normally distributed (in either source or 156 | binary form) with the major components (compiler, kernel, and so on) of the 157 | operating system on which the executable runs, unless that component itself 158 | accompanies the executable. 159 | 160 | If distribution of executable or object code is made by offering access to copy 161 | from a designated place, then offering equivalent access to copy the source code 162 | from the same place counts as distribution of the source code, even though third 163 | parties are not compelled to copy the source along with the object code. 164 | 165 | **4.** You may not copy, modify, sublicense, or distribute the Program except as 166 | expressly provided under this License. Any attempt otherwise to copy, modify, 167 | sublicense or distribute the Program is void, and will automatically terminate 168 | your rights under this License. However, parties who have received copies, or 169 | rights, from you under this License will not have their licenses terminated so 170 | long as such parties remain in full compliance. 171 | 172 | **5.** You are not required to accept this License, since you have not signed 173 | it. However, nothing else grants you permission to modify or distribute the 174 | Program or its derivative works. These actions are prohibited by law if you do 175 | not accept this License. Therefore, by modifying or distributing the Program (or 176 | any work based on the Program), you indicate your acceptance of this License to 177 | do so, and all its terms and conditions for copying, distributing or modifying 178 | the Program or works based on it. 179 | 180 | **6.** Each time you redistribute the Program (or any work based on the 181 | Program), the recipient automatically receives a license from the original 182 | licensor to copy, distribute or modify the Program subject to these terms and 183 | conditions. You may not impose any further restrictions on the recipients' 184 | exercise of the rights granted herein. You are not responsible for enforcing 185 | compliance by third parties to this License. 186 | 187 | **7.** If, as a consequence of a court judgment or allegation of patent 188 | infringement or for any other reason (not limited to patent issues), conditions 189 | are imposed on you (whether by court order, agreement or otherwise) that 190 | contradict the conditions of this License, they do not excuse you from the 191 | conditions of this License. If you cannot distribute so as to satisfy 192 | simultaneously your obligations under this License and any other pertinent 193 | obligations, then as a consequence you may not distribute the Program at all. 194 | For example, if a patent license would not permit royalty-free redistribution of 195 | the Program by all those who receive copies directly or indirectly through you, 196 | then the only way you could satisfy both it and this License would be to refrain 197 | entirely from distribution of the Program. 198 | 199 | If any portion of this section is held invalid or unenforceable under any 200 | particular circumstance, the balance of the section is intended to apply and the 201 | section as a whole is intended to apply in other circumstances. 202 | 203 | It is not the purpose of this section to induce you to infringe any patents or 204 | other property right claims or to contest validity of any such claims; this 205 | section has the sole purpose of protecting the integrity of the free software 206 | distribution system, which is implemented by public license practices. Many 207 | people have made generous contributions to the wide range of software 208 | distributed through that system in reliance on consistent application of that 209 | system; it is up to the author/donor to decide if he or she is willing to 210 | distribute software through any other system and a licensee cannot impose that 211 | choice. 212 | 213 | This section is intended to make thoroughly clear what is believed to be a 214 | consequence of the rest of this License. 215 | 216 | **8.** If the distribution and/or use of the Program is restricted in certain 217 | countries either by patents or by copyrighted interfaces, the original copyright 218 | holder who places the Program under this License may add an explicit 219 | geographical distribution limitation excluding those countries, so that 220 | distribution is permitted only in or among countries not thus excluded. In such 221 | case, this License incorporates the limitation as if written in the body of this 222 | License. 223 | 224 | **9.** The Free Software Foundation may publish revised and/or new versions of 225 | the General Public License from time to time. Such new versions will be similar 226 | in spirit to the present version, but may differ in detail to address new 227 | problems or concerns. 228 | 229 | Each version is given a distinguishing version number. If the Program specifies 230 | a version number of this License which applies to it and "any later version", 231 | you have the option of following the terms and conditions either of that version 232 | or of any later version published by the Free Software Foundation. If the 233 | Program does not specify a version number of this License, you may choose any 234 | version ever published by the Free Software Foundation. 235 | 236 | **10.** If you wish to incorporate parts of the Program into other free programs 237 | whose distribution conditions are different, write to the author to ask for 238 | permission. For software which is copyrighted by the Free Software Foundation, 239 | write to the Free Software Foundation; we sometimes make exceptions for this. 240 | Our decision will be guided by the two goals of preserving the free status of 241 | all derivatives of our free software and of promoting the sharing and reuse of 242 | software generally. 243 | 244 | 245 | No Warranty 246 | ----------- 247 | 248 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 249 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 250 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 251 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 252 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 253 | PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 254 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 255 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 256 | 257 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 258 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 259 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 260 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 261 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 262 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 263 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 264 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 265 | -------------------------------------------------------------------------------- /archive/main_v1.0.3-220510.py: -------------------------------------------------------------------------------- 1 | __author__ = 'https://github.com/password123456/' 2 | 3 | import os 4 | import sys 5 | import platform 6 | import importlib 7 | import time 8 | import requests 9 | import hashlib 10 | import magic 11 | import argparse 12 | import uuid 13 | import pefile 14 | 15 | from datetime import datetime 16 | from zipfile import ZipFile 17 | from socket import getaddrinfo, gethostname 18 | from cryptography import x509 19 | from cryptography.hazmat.backends import default_backend 20 | from cryptography.x509.oid import NameOID 21 | from asn1crypto import cms 22 | 23 | 24 | importlib.reload(sys) 25 | 26 | _today_ = datetime.today().strftime('%Y-%m-%d') 27 | _ctime_ = datetime.today().strftime('%Y-%m-%d %H:%M:%S') 28 | 29 | _version_ = '1.0.3-220510' 30 | _home_path_ = '%s' % os.getcwd() 31 | 32 | _engine_zipfile_ = '%s/%s.zip' % (_home_path_, _today_) 33 | _engine_extract_file_ = '%s/engine.db' % _home_path_ 34 | 35 | _scan_result_logs_ = '%s/output/%s-infected.log' % (_home_path_, _today_) 36 | _scan_extension_list_ = ['.exe', '.dll', '.sys', '.doc', '.docx', '.xls', '.xlsx', '.py', '.xml', '.cfg', '.txt', '.ppt', '.pptx', '.hwp'] 37 | 38 | 39 | class Bcolors: 40 | Black = '\033[30m' 41 | Red = '\033[31m' 42 | Green = '\033[32m' 43 | Yellow = '\033[33m' 44 | Blue = '\033[34m' 45 | Magenta = '\033[35m' 46 | Cyan = '\033[36m' 47 | White = '\033[37m' 48 | Endc = '\033[0m' 49 | BOLD = '\033[1m' 50 | UNDERLINE = '\033[4m' 51 | 52 | 53 | def download_engine(): 54 | _url = 'https://bazaar.abuse.ch/export/txt/sha256/full/' 55 | _header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) ' 56 | 'Chrome/49.0.2623.112 Safari/537.36', 'Connection': 'keep-alive'} 57 | try: 58 | with open(_engine_zipfile_, 'wb') as f: 59 | #print('%s Download URL: %s %s' % (Bcolors.Green, url, Bcolors.Endc)) 60 | 61 | r = requests.get(_url, headers=_header, stream=True) 62 | download_file_length = r.headers.get('Content-Length') 63 | print('%s Downloading: %s / %.2f MB %s' 64 | % (Bcolors.Green, _engine_zipfile_, (float(download_file_length) / (1024.0 * 1024.0)), Bcolors.Endc)) 65 | 66 | if download_file_length is None: 67 | f.write(r.content) 68 | else: 69 | dl = 0 70 | total_length = int(download_file_length) 71 | start = time.perf_counter() 72 | for data in r.iter_content(chunk_size=8092): 73 | dl += len(data) 74 | f.write(data) 75 | done = int(100 * dl / total_length) 76 | sys.stdout.write('\r [%s%s] %s/%s (%s%%) - %.2f seconds ' 77 | % ('>' * done, ' ' * (100 - done), total_length, dl, 78 | done, (time.perf_counter() - start))) 79 | sys.stdout.flush() 80 | f.close() 81 | 82 | extract_gzip(_engine_zipfile_, _home_path_) 83 | 84 | except Exception as e: 85 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, download_engine.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 86 | else: 87 | r.close() 88 | 89 | 90 | def extract_gzip(_engine_zipfile_, _home_path_): 91 | with ZipFile(_engine_zipfile_, 'r') as zipObj: 92 | file_list = zipObj.infolist() 93 | for file in file_list: 94 | if file.filename[-1] == '/': 95 | continue 96 | file.filename = os.path.basename(file.filename) 97 | if file.filename.lower() == 'full_sha256.txt'.lower(): 98 | zipObj.extract(file, _home_path_) 99 | _update_file = '%s/%s' % (_home_path_, file.filename) 100 | 101 | if os.path.isfile(_engine_extract_file_): 102 | os.remove(_engine_extract_file_) 103 | 104 | try: 105 | os.rename(_update_file, _engine_extract_file_) 106 | except OSError as e: 107 | print('%s can not be renamed' % _update_file) 108 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, extract_gzip.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 109 | sys.exit(1) 110 | 111 | # Remove Engine zip 112 | try: 113 | os.remove(_engine_zipfile_) 114 | except OSError as e: 115 | print('%s can not be removed' % _engine_zipfile_) 116 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, extract_gzip.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 117 | sys.exit(1) 118 | 119 | # Check Downloaded File 120 | if os.path.isfile(_engine_extract_file_): 121 | f = open(_engine_extract_file_, 'rb') 122 | file_read = f.read() 123 | f.close() 124 | 125 | file_hash = hashlib.sha256(file_read).hexdigest() 126 | file_info = '===> Extracted Size: %.2f MB\n===> Hash(SHA-256) : %s\n' \ 127 | % (int(os.path.getsize(_engine_extract_file_)) / (1024.0 * 1024.0), file_hash) 128 | 129 | print('\n\n%s===> Update Success: %s %s' % (Bcolors.Green, _engine_extract_file_, Bcolors.Endc)) 130 | print('%s%s%s' % (Bcolors.Green, file_info, Bcolors.Endc)) 131 | else: 132 | print('%s[-] %s not found. %s' % (Bcolors.Yellow, _engine_extract_file_, Bcolors.Endc)) 133 | sys.exit(1) 134 | 135 | 136 | def raw_count(filename): 137 | n = 0 138 | with open(filename) as f: 139 | for line in f: 140 | if not line.startswith('#'): 141 | n = n + 1 142 | f.close() 143 | return n 144 | 145 | 146 | def get_engine_last_udated_date(filename): 147 | with open(filename) as f: 148 | for line in f: 149 | if 'Last updated' in line: 150 | line = line.replace('#', '') 151 | line = line.lstrip().strip('\n') 152 | line = line.split(' ') 153 | line = line[2:5] 154 | line = ' '.join(line) 155 | #print(line) 156 | break 157 | f.close() 158 | return line 159 | 160 | 161 | def hash_exists_in_db(check_hash): 162 | _mode = 'r' 163 | _n = 0 164 | with open(_engine_extract_file_, _mode) as database: 165 | for line in database: 166 | _n = _n + 1 167 | if len(line.strip()) != 0: 168 | if not line.startswith('#'): 169 | if str(check_hash) in str(line): 170 | return True 171 | return False 172 | 173 | 174 | def scan_result_logs(_contents): 175 | _make_output_dir = '%s/output' % _home_path_ 176 | _mode = 'w' 177 | 178 | if os.path.exists(_make_output_dir): 179 | if os.path.exists(_scan_result_logs_): 180 | _mode = 'a' 181 | else: 182 | _mode = 'w' 183 | os.makedirs(_make_output_dir) 184 | 185 | with open(_scan_result_logs_, _mode) as fa: 186 | fa.write('%s' % _contents) 187 | fa.close() 188 | 189 | 190 | def make_hash(_f_file_name): 191 | _file_hash = '' 192 | if os.path.isfile(_f_file_name): 193 | f = open(_f_file_name, 'rb') 194 | filename_read = f.read() 195 | _file_hash = hashlib.sha256(filename_read).hexdigest() 196 | f.close() 197 | return _file_hash 198 | 199 | 200 | def check_file_extension(_file_name): 201 | #print(file_name) 202 | if _file_name.endswith(tuple(_scan_extension_list_)): 203 | return True 204 | else: 205 | return False 206 | 207 | 208 | def check_file_size(_f_file_name): 209 | # 10MB = '10485760' 210 | _limit = 10485760 211 | 212 | f = os.stat(_f_file_name).st_size 213 | if f <= _limit: 214 | return True 215 | else: 216 | return False 217 | 218 | 219 | def get_create_date(_f_file_name): 220 | if platform.system() == 'Windows': 221 | _result = os.path.getctime(_f_file_name) 222 | else: 223 | _result = os.path.getmtime(_f_file_name) 224 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 225 | 226 | 227 | def get_modify_date(_f_file_name): 228 | _result = os.path.getmtime(_f_file_name) 229 | return datetime.fromtimestamp(_result).strftime('%Y-%m-%d %H:%M:%S') 230 | 231 | 232 | def get_file_type(_file_name): 233 | return magic.from_buffer(open(_file_name, 'rb').read(2048)) 234 | 235 | 236 | def get_hostname(): 237 | return platform.node() 238 | 239 | 240 | def get_osver(): 241 | if str(platform.system()) != 'Windows': 242 | _result = '%s' % platform.platform().split('-with-', 1)[1] 243 | else: 244 | _result = '%s' % platform.platform() 245 | return _result 246 | 247 | 248 | def get_ip_address(): 249 | system_ip_list = getaddrinfo(gethostname(), None, 2, 1, 0) 250 | _ip_list = '' 251 | for ip in system_ip_list: 252 | _ip_address = ip[4][0] 253 | _ip_list += '%s ' % _ip_address 254 | return _ip_list.strip() 255 | 256 | 257 | def create_job_id(): 258 | return uuid.uuid4() 259 | 260 | 261 | def extract_digital_signature(_file_name, _f_file_name): 262 | _result = '' 263 | 264 | pe = pefile.PE(_f_file_name) 265 | if hex(pe.DOS_HEADER.e_magic) == '0x5a4d': 266 | address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]].VirtualAddress 267 | size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]].Size 268 | 269 | if address == 0: 270 | _result = '** No Digital-Signed File **' 271 | else: 272 | with open(_f_file_name, 'rb') as fh: 273 | fh.seek(address) 274 | thesig = fh.read(size) 275 | signature = cms.ContentInfo.load(thesig[8:]) 276 | 277 | i = 0 278 | for cert in signature['content']['certificates']: 279 | i = i + 1 280 | _export_certificate = '%s/output/%s_%s.der' % (_home_path_, _file_name, i) 281 | with open(_export_certificate, 'wb+') as f: 282 | f.write(cert.dump()) 283 | 284 | x509_pem_cert = x509.load_der_x509_certificate(cert.dump(), default_backend()) 285 | _subject = x509_pem_cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value 286 | if 'root' not in _subject.lower(): 287 | _contents = '%s_%s_valid(%s-%s)|' % (x509_pem_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value, 288 | x509_pem_cert.issuer.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value, 289 | x509_pem_cert.not_valid_before, x509_pem_cert.not_valid_after) 290 | _result += _contents 291 | fh.close() 292 | f.close() 293 | pe.close() 294 | return _result 295 | 296 | 297 | def check_engine(): 298 | _name = 'MALWARE' 299 | _get_download = True 300 | 301 | if os.path.exists(_engine_extract_file_): 302 | create_time = os.stat(_engine_extract_file_).st_mtime 303 | 304 | _engine_file_date = datetime.fromtimestamp(create_time).strftime('%Y%m%d') 305 | _today_num = datetime.today().strftime('%Y%m%d') 306 | 307 | if int(_engine_file_date) == int(_today_num): 308 | _get_download = True 309 | else: 310 | _get_download = False 311 | else: 312 | print('- Updating %s Signatures' % _name) 313 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 314 | download_engine() 315 | 316 | if not _get_download: 317 | print('- Updating %s Signatures' % _name) 318 | print('%s------------------------------------->%s' % (Bcolors.Yellow, Bcolors.Endc)) 319 | download_engine() 320 | 321 | 322 | def scanner(_scan_path): 323 | _scanned_file_count = 0 324 | _infected_file_count = 0 325 | _scandir_file_count = sum(len(files) for _, _, files in os.walk(_scan_path)) 326 | print('- Scan Directory : %s' % _scan_path) 327 | print('- Number of files : %d' % _scandir_file_count) 328 | print('- Scan Extensions : %s' % str(_scan_extension_list_)[1:-1]) 329 | print('%s------------------------------------->%s\n' % (Bcolors.Yellow, Bcolors.Endc)) 330 | #print('-%s O.K Here We go.!%s' % (Bcolors.Magenta, Bcolors.Endc)) 331 | print('\x1b[7;33;40m' + 'O.K Here We go.!' + '\x1b[0m') 332 | print('- Currently scanning...') 333 | 334 | start = time.perf_counter() 335 | _detected = '' 336 | 337 | _ipaddress = get_ip_address() 338 | _hostname = get_hostname() 339 | _scan_id = create_job_id() 340 | _os_version = get_osver() 341 | 342 | for subdir, dirs, files in os.walk(_scan_path): 343 | for file in files: 344 | #print(os.path.join(subdir, file)) 345 | _scanned_file_count = _scanned_file_count + 1 346 | _f_file_name = '%s' % os.path.join(subdir, file) 347 | elapsed_time = time.perf_counter() - start 348 | #print(file) 349 | if check_file_extension(file): 350 | if check_file_size(_f_file_name): 351 | scan_file_hash = make_hash(_f_file_name) 352 | if hash_exists_in_db(scan_file_hash): 353 | _infected_file_count = _infected_file_count + 1 354 | _contents = 'datetime="%s",scan_id="%s",os="%s",hostname="%s",ip="%s",file_type="%s",file="%s",created_at="%s",modified_at="%s",hash="%s",code_sign="%s"\n' \ 355 | % (datetime.today().strftime('%Y-%m-%d %H:%M:%S'), _scan_id, _os_version, _hostname, _ipaddress, get_file_type(_f_file_name), 356 | _f_file_name, get_create_date(_f_file_name), get_modify_date(_f_file_name), scan_file_hash, extract_digital_signature(file, _f_file_name)) 357 | _detected += _contents 358 | scan_result_logs(_contents) 359 | 360 | sys.stdout.write('\r- %d files scanned / %d infected [%s] (%s) ' 361 | % (_scanned_file_count, _infected_file_count, 362 | datetime.strftime(datetime.utcfromtimestamp(elapsed_time), '%H:%M:%S'), 363 | _f_file_name)) 364 | sys.stdout.flush() 365 | 366 | if _infected_file_count >= 1: 367 | _vt_info = '\n##### For more information about infected file, search the Virustotal ###\n' \ 368 | 'https://www.virustotal.com/gui/file/$infected_file_hash\n\n' 369 | scan_result_logs(_vt_info) 370 | 371 | print('\n') 372 | print('- Result') 373 | 374 | if _detected: 375 | print('%s------O------M------G--------------->%s' % (Bcolors.Red, Bcolors.Endc)) 376 | print('- Scanner Found %s%d%s infected files!' % (Bcolors.Yellow, _infected_file_count, Bcolors.Endc)) 377 | print('- See the %s%s%s \n' % (Bcolors.Cyan, _scan_result_logs_, Bcolors.Endc)) 378 | else: 379 | print('%s- OK.Good. No infection found.%s' % (Bcolors.Green, Bcolors.Endc)) 380 | 381 | 382 | def main(): 383 | print('\n') 384 | print('%s▌║█║▌│║▌│║▌║▌█║ %sSimple Basic Malware Scanner %sv%s%s ▌│║▌║▌│║║▌█║▌║█%s\n' 385 | % (Bcolors.Green, Bcolors.Red, Bcolors.White, _version_, Bcolors.Green, Bcolors.Endc)) 386 | opt = argparse.ArgumentParser(description='Simple Basic Malware Scanner') 387 | opt.add_argument('--path', help='ex) /var/www/html/upload') 388 | opt.add_argument('--update', action='store_true', help='AV Engine Update') 389 | 390 | if len(sys.argv) < 1: 391 | opt.print_help() 392 | sys.exit(1) 393 | else: 394 | options = opt.parse_args() 395 | 396 | if options.path: 397 | _scan_path = os.path.abspath(options.path) 398 | print('- Run time: %s' % _ctime_) 399 | print('- For questions contact github.com/password123456\t\t') 400 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 401 | check_engine() 402 | # engine_date = engine_last_dated(_engine_extract_file_) 403 | print('- Engine Updated : %s' % get_engine_last_udated_date(_engine_extract_file_)) 404 | engine_count = raw_count(_engine_extract_file_) 405 | print('- AV Signatures : %s' % engine_count) 406 | scanner(_scan_path) 407 | 408 | elif options.update: 409 | print('- Run time: %s' % _ctime_) 410 | print('- For questions contact github.com/password123456\t\t') 411 | print('%s------------------------------------->%s\n' % (Bcolors.Green, Bcolors.Endc)) 412 | check_engine() 413 | # engine_date = engine_last_dated(_engine_extract_file_) 414 | print('- Engine Updated : %s' % get_engine_last_udated_date(_engine_extract_file_)) 415 | engine_count = raw_count(_engine_extract_file_) 416 | print('- AV Signatures : %s' % engine_count) 417 | else: 418 | opt.print_help() 419 | sys.exit(1) 420 | 421 | 422 | if __name__ == '__main__': 423 | try: 424 | main() 425 | except KeyboardInterrupt: 426 | sys.exit(0) 427 | except Exception as e: 428 | print('%s- ::Exception:: Func:[%s] Line:[%s] [%s] %s%s' % (Bcolors.Yellow, __name__.__name__, sys.exc_info()[-1].tb_lineno, type(e).__name__, e, Bcolors.Endc)) 429 | --------------------------------------------------------------------------------