|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +import requests |
| 4 | +import hashlib |
| 5 | +import pefile |
| 6 | +import sys |
| 7 | +import os |
| 8 | + |
| 9 | +# --- INÍCIO DA CONFIGURAÇÃO --- |
| 10 | + |
| 11 | +# 1. Pega a chave da API a partir de uma variável de ambiente para mais segurança. |
| 12 | +VIRUSTOTAL_API_KEY = os.getenv('VIRUSTOTAL_API_KEY') |
| 13 | + |
| 14 | +# --- FIM DA CONFIGURAÇÃO --- |
| 15 | + |
| 16 | +def check_api_key(): |
| 17 | + """Verifica se a chave da API foi carregada corretamente.""" |
| 18 | + if not VIRUSTOTAL_API_KEY: |
| 19 | + print("\n[ERRO] A variável de ambiente 'VIRUSTOTAL_API_KEY' não foi definida.") |
| 20 | + print("Instruções:") |
| 21 | + print(" - Windows (CMD): set VIRUSTOTAL_API_KEY=\"sua_chave_aqui\"") |
| 22 | + print(" - Windows (PowerShell): $env:VIRUSTOTAL_API_KEY=\"sua_chave_aqui\"") |
| 23 | + print(" - Linux/macOS: export VIRUSTOTAL_API_KEY=\"sua_chave_aqui\"") |
| 24 | + sys.exit(1) # Encerra o script com um código de erro |
| 25 | + |
| 26 | +def calculate_sha256(file_path): |
| 27 | + """Calcula o hash SHA-256 de um arquivo de forma eficiente.""" |
| 28 | + sha256_hash = hashlib.sha256() |
| 29 | + try: |
| 30 | + with open(file_path, "rb") as f: |
| 31 | + for byte_block in iter(lambda: f.read(4096), b""): |
| 32 | + sha256_hash.update(byte_block) |
| 33 | + return sha256_hash.hexdigest() |
| 34 | + except IOError as e: |
| 35 | + print(f"Erro ao ler o arquivo: {e}") |
| 36 | + return None |
| 37 | + |
| 38 | +def check_virustotal(file_hash): |
| 39 | + """Verifica o hash no VirusTotal com tratamento de erros robusto.""" |
| 40 | + print("\n--- Verificando no VirusTotal ---") |
| 41 | + url = f"https://www.virustotal.com/api/v3/files/{file_hash}" |
| 42 | + headers = {"x-apikey": VIRUSTOTAL_API_KEY} |
| 43 | + |
| 44 | + try: |
| 45 | + # Adiciona um timeout de 15 segundos para evitar que o script trave |
| 46 | + response = requests.get(url, headers=headers, timeout=15) |
| 47 | + |
| 48 | + # O VirusTotal retorna 404 para hashes que nunca viu. Tratamos isso como "não encontrado". |
| 49 | + if response.status_code == 404: |
| 50 | + print("Hash não encontrado no banco de dados do VirusTotal.") |
| 51 | + return None, None |
| 52 | + |
| 53 | + # Lança uma exceção para outros erros HTTP (ex: 401 Unauthorized, 429 Rate Limit) |
| 54 | + response.raise_for_status() |
| 55 | + |
| 56 | + json_response = response.json() |
| 57 | + |
| 58 | + # Usamos .get() para evitar erros caso a estrutura da resposta mude |
| 59 | + stats = json_response.get("data", {}).get("attributes", {}).get("last_analysis_stats", {}) |
| 60 | + |
| 61 | + if not stats: |
| 62 | + print("Nenhuma análise encontrada para este hash.") |
| 63 | + return 0, 0 |
| 64 | + |
| 65 | + positives = stats.get("malicious", 0) |
| 66 | + total = sum(stats.values()) |
| 67 | + return positives, total |
| 68 | + |
| 69 | + except requests.exceptions.RequestException as e: |
| 70 | + print(f"Erro de rede ao consultar o VirusTotal: {e}") |
| 71 | + return None, None |
| 72 | + except Exception as e: |
| 73 | + print(f"Ocorreu um erro inesperado ao processar a resposta do VirusTotal: {e}") |
| 74 | + return None, None |
| 75 | + |
| 76 | +def analyze_pe_file(file_path): |
| 77 | + """Realiza análise estática em um executável PE, extraindo mais informações.""" |
| 78 | + print("\n--- Análise Estática do Arquivo PE ---") |
| 79 | + try: |
| 80 | + pe = pefile.PE(file_path) |
| 81 | + |
| 82 | + print(f"EntryPoint: {hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint)}") |
| 83 | + print(f"ImageBase: {hex(pe.OPTIONAL_HEADER.ImageBase)}") |
| 84 | + print(f"Número de Seções: {len(pe.sections)}") |
| 85 | + |
| 86 | + # Extrai as DLLs importadas, um forte indicador de funcionalidade |
| 87 | + print("\n[+] DLLs Importadas:") |
| 88 | + if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): |
| 89 | + for entry in pe.DIRECTORY_ENTRY_IMPORT: |
| 90 | + # Usamos .decode com 'errors=ignore' para evitar problemas com nomes de DLL malformados |
| 91 | + print(f" - {entry.dll.decode('utf-8', errors='ignore')}") |
| 92 | + else: |
| 93 | + print(" Nenhuma DLL importada encontrada (o arquivo pode ser 'packed').") |
| 94 | + |
| 95 | + except pefile.PEFormatError: |
| 96 | + print("Erro: O arquivo não parece ser um executável PE válido.") |
| 97 | + except Exception as e: |
| 98 | + print(f"Ocorreu um erro inesperado durante a análise do PE: {e}") |
| 99 | + |
| 100 | +def analyze_file(file_path): |
| 101 | + """Função principal que orquestra a análise completa do arquivo.""" |
| 102 | + if not os.path.isfile(file_path): |
| 103 | + print(f"Erro: Arquivo não encontrado em '{file_path}'") |
| 104 | + return |
| 105 | + |
| 106 | + print(f"Iniciando análise de: {os.path.basename(file_path)}") |
| 107 | + |
| 108 | + file_hash = calculate_sha256(file_path) |
| 109 | + if not file_hash: |
| 110 | + return # Encerra se não foi possível calcular o hash |
| 111 | + |
| 112 | + print(f"Hash SHA-256: {file_hash}") |
| 113 | + |
| 114 | + positives, total = check_virustotal(file_hash) |
| 115 | + if positives is not None: |
| 116 | + print(f"Resultado: {positives}/{total} detecções maliciosas.") |
| 117 | + if positives > 0: |
| 118 | + print("[ALERTA] O arquivo foi identificado como malicioso por um ou mais antivírus.") |
| 119 | + |
| 120 | + if file_path.lower().endswith(('.exe', '.dll', '.sys')): |
| 121 | + analyze_pe_file(file_path) |
| 122 | + else: |
| 123 | + print("\nAnálise estática de PE não aplicável (apenas para .exe, .dll, .sys).") |
| 124 | + |
| 125 | +if __name__ == "__main__": |
| 126 | + check_api_key() # Primeiro, verifica se a chave existe |
| 127 | + |
| 128 | + if len(sys.argv) != 2: |
| 129 | + print("\nUso: python seu_script.py <caminho_do_arquivo>") |
| 130 | + else: |
| 131 | + file_to_analyze = sys.argv[1] |
| 132 | + analyze_file(file_to_analyze) |
0 commit comments