163 lines
6.7 KiB
Python
163 lines
6.7 KiB
Python
import tkinter as tk
|
|
from tkinter import filedialog, ttk, messagebox
|
|
import re
|
|
from typing import Tuple, List
|
|
from collections import Counter
|
|
|
|
class PSADecoder:
|
|
def __init__(self):
|
|
# Tabela de decodificação exata do PDF (Table 1)
|
|
self.decode_table = {
|
|
0xCF: '0', 0xCE: '1', 0xCD: '2', 0xCC: '3', 0xCB: '4',
|
|
0xCA: '5', 0xC9: '6', 0xC8: '7', 0xC7: '8', 0xC6: '9',
|
|
0xBE: 'A', 0xBD: 'B', 0xBC: 'C', 0xBB: 'D', 0xBA: 'E',
|
|
0xB9: 'F', 0xB8: 'G', 0xB7: 'H', 0xB6: 'I', 0xB5: 'J',
|
|
0xB4: 'K', 0xB3: 'L', 0xB2: 'M', 0xB1: 'N', 0xB0: 'O',
|
|
0xAF: 'P', 0xAE: 'Q', 0xAD: 'R', 0xAC: 'S', 0xAB: 'T',
|
|
0xAA: 'U', 0xA9: 'V', 0xA8: 'W', 0xA7: 'X', 0xA6: 'Y',
|
|
0xA5: 'Z'
|
|
}
|
|
|
|
def decode_bytes(self, data: bytes) -> str:
|
|
decoded = ''
|
|
for byte in data:
|
|
if byte in self.decode_table:
|
|
decoded += self.decode_table[byte]
|
|
else:
|
|
decoded += ' ' # Substitui caracteres não encontrados na tabela por espaço
|
|
return decoded
|
|
|
|
class VINPINFinder(tk.Tk):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.decoder = PSADecoder()
|
|
|
|
self.title("Decodificador BSI PSA - VIN/PIN Finder")
|
|
self.geometry("800x600")
|
|
|
|
# Criar o frame principal
|
|
main_frame = ttk.Frame(self, padding="10")
|
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
# Botão para selecionar arquivo
|
|
self.select_button = ttk.Button(
|
|
main_frame,
|
|
text="Selecionar Arquivo BSI",
|
|
command=self.select_file
|
|
)
|
|
self.select_button.grid(row=0, column=0, pady=10, sticky=tk.W)
|
|
|
|
self.select_buttonb = ttk.Button(main_frame, text="About", command=self.show_about)
|
|
self.select_buttonb.grid(row=0, column=1, pady=10, sticky=tk.W)
|
|
|
|
# Label para mostrar o arquivo selecionado
|
|
self.file_label = ttk.Label(main_frame, text="Nenhum arquivo selecionado")
|
|
self.file_label.grid(row=1, column=0, pady=5, sticky=tk.W)
|
|
|
|
# Frame para resultados
|
|
results_frame = ttk.LabelFrame(main_frame, text="Resultados da Decodificação", padding="5")
|
|
results_frame.grid(row=2, column=0, pady=10, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
# Texto para mostrar resultados
|
|
self.results_text = tk.Text(results_frame, height=20, width=80)
|
|
self.results_text.grid(row=0, column=0, pady=5)
|
|
|
|
# Scrollbar para resultados
|
|
scrollbar = ttk.Scrollbar(results_frame, orient=tk.VERTICAL, command=self.results_text.yview)
|
|
scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
|
self.results_text.configure(yscrollcommand=scrollbar.set)
|
|
|
|
def validate_vin_occurrences(self, vins: List[str]) -> List[str]:
|
|
"""Valida VINs que aparecem entre 2 e 3 vezes"""
|
|
counter = Counter(vins)
|
|
valid_vins = [vin for vin, count in counter.items() if 2 <= count <= 3]
|
|
return valid_vins
|
|
|
|
def find_isolated_pin(self, decoded_content: str) -> str:
|
|
"""
|
|
Procura por um PIN isolado (4 caracteres alfanuméricos com não-alfanuméricos ao redor)
|
|
Usa lookbehind e lookahead para garantir que o PIN está isolado
|
|
"""
|
|
# Padrão modificado:
|
|
# (?<![A-Z0-9]) - lookbehind negativo para garantir que não há alfanumérico antes
|
|
# [A-Z0-9]{4} - quatro caracteres alfanuméricos
|
|
# (?![A-Z0-9]) - lookahead negativo para garantir que não há alfanumérico depois
|
|
pin_pattern = r'(?<![A-Z0-9])([A-Z0-9]{4})(?![A-Z0-9])'
|
|
|
|
matches = re.finditer(pin_pattern, decoded_content)
|
|
for match in matches:
|
|
return match.group(1) # Retorna o primeiro PIN isolado encontrado
|
|
return ''
|
|
|
|
def select_file(self):
|
|
filename = filedialog.askopenfilename(
|
|
title="Selecione o arquivo BSI",
|
|
filetypes=[
|
|
("Arquivos binários", "*.bin"),
|
|
("Todos os arquivos", "*.*")
|
|
]
|
|
)
|
|
|
|
if filename:
|
|
self.file_label.config(text=f"Arquivo selecionado: {filename}")
|
|
self.process_file(filename)
|
|
|
|
def process_file(self, filename: str):
|
|
try:
|
|
# Ler o arquivo em modo binário
|
|
with open(filename, 'rb') as file:
|
|
content = file.read()
|
|
|
|
# Decodificar o conteúdo usando apenas a tabela PSA BSI
|
|
decoded_content = self.decoder.decode_bytes(content)
|
|
|
|
# Procurar por VINs (17 caracteres alfanuméricos)
|
|
all_vins = re.findall(r'[A-Z0-9]{17}', decoded_content)
|
|
valid_vins = self.validate_vin_occurrences(all_vins)
|
|
|
|
# Procurar por um PIN isolado
|
|
pin = self.find_isolated_pin(decoded_content)
|
|
|
|
# Limpar resultados anteriores
|
|
self.results_text.delete(1.0, tk.END)
|
|
|
|
# Mostrar resultados
|
|
self.results_text.insert(tk.END, "=== Resultados da Decodificação BSI ===\n\n")
|
|
|
|
# Mostrar VINs válidos (que aparecem 2-3 vezes)
|
|
self.results_text.insert(tk.END, "VIN encontrado:\n")
|
|
if valid_vins:
|
|
for vin in valid_vins:
|
|
count = all_vins.count(vin)
|
|
self.results_text.insert(tk.END, f"VIN: {vin} (encontrado {count} vezes)\n")
|
|
else:
|
|
self.results_text.insert(tk.END, "Nenhum VIN válido encontrado (deve aparecer 2-3 vezes)\n")
|
|
|
|
# Mostrar PIN (se encontrado)
|
|
self.results_text.insert(tk.END, "\nPIN encontrado:\n")
|
|
if pin:
|
|
self.results_text.insert(tk.END, f"PIN: {pin}\n")
|
|
else:
|
|
self.results_text.insert(tk.END, "Nenhum PIN isolado encontrado\n")
|
|
|
|
# Informações adicionais
|
|
self.results_text.insert(tk.END, f"\nTamanho do arquivo: {len(content)} bytes\n")
|
|
self.results_text.insert(tk.END, "\nNota: VINs são validados apenas se aparecem 2-3 vezes no arquivo.\n")
|
|
self.results_text.insert(tk.END, " PIN deve ser exatamente 4 caracteres isolados.\n")
|
|
|
|
except Exception as e:
|
|
self.results_text.delete(1.0, tk.END)
|
|
self.results_text.insert(tk.END, f"Erro ao processar arquivo: {str(e)}")
|
|
|
|
def show_about(self):
|
|
messagebox.showinfo(
|
|
"Sobre",
|
|
"BSI VALEO 24C128 PIN Decoder\n"
|
|
"Versão 1.0\n\n"
|
|
"Programa para decodificar PINs de BSI VALEO 24C128 E2PROM\n"
|
|
"Created by: @Godax"
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
app = VINPINFinder()
|
|
app.mainloop() |