Spaces:
Sleeping
Sleeping
| """ | |
| Inference script for Engine B (Malimg). | |
| This module provides functionality to read raw binary files, convert them | |
| to 2D grayscale image tensors, and classify their malware family using | |
| a pre-trained Convolutional Neural Network (CNN). | |
| """ | |
| import os | |
| import torch | |
| from torchvision import transforms | |
| from PIL import Image | |
| import numpy as np | |
| import json | |
| from src.engine_b.model import MalwareCNN | |
| import math | |
| def bytes_to_image(file_path): | |
| """ | |
| Reads a raw binary file and converts its bytes into a square grayscale image. | |
| If the file is a mock JSON profile, it reads the byte array from the JSON. | |
| Otherwise, it reads the raw bytes of the executable and reshapes them into | |
| a square 2D matrix, padding the end with zeros if necessary. | |
| Args: | |
| file_path (str): Path to the target binary file. | |
| Returns: | |
| PIL.Image: A grayscale (mode 'L') Image object representing the binary. | |
| """ | |
| # Check if this is our safe mock profile | |
| if file_path.endswith(".json"): | |
| try: | |
| with open(file_path, "r") as f: | |
| data = json.load(f) | |
| if data.get("is_mock_profile"): | |
| byte_array = np.array(data["malimg_bytes"], dtype=np.uint8) | |
| else: | |
| with open(file_path, "rb") as f: | |
| binary_data = f.read() | |
| byte_array = np.frombuffer(binary_data, dtype=np.uint8) | |
| except: | |
| with open(file_path, "rb") as f: | |
| binary_data = f.read() | |
| byte_array = np.frombuffer(binary_data, dtype=np.uint8) | |
| else: | |
| # Read raw binary | |
| with open(file_path, "rb") as f: | |
| binary_data = f.read() | |
| byte_array = np.frombuffer(binary_data, dtype=np.uint8) | |
| # Calculate image dimensions (square) | |
| length = len(byte_array) | |
| if length == 0: | |
| return Image.new("L", (128, 128), color=0) | |
| width = int(math.ceil(math.sqrt(length))) | |
| height = width | |
| # Pad array to form a perfect square | |
| padded_length = width * height | |
| padded_array = np.pad(byte_array, (0, padded_length - length), mode="constant") | |
| # Reshape and create PIL Image | |
| image_2d = padded_array.reshape((height, width)) | |
| img = Image.fromarray(image_2d, mode="L") | |
| return img | |
| class EngineBInfer: | |
| """ | |
| Inference Engine for Visual Malware Family Classification. | |
| Attributes: | |
| device (torch.device): CPU or CUDA device for inference. | |
| classes (list): Ordered list of malware family class names. | |
| model (MalwareCNN): The loaded PyTorch CNN. | |
| transform (transforms.Compose): Image preprocessing pipeline. | |
| """ | |
| def __init__( | |
| self, | |
| model_path="models/engine_b_model.pth", | |
| classes_path="models/engine_b_classes.json", | |
| ): | |
| """ | |
| Initializes the vision inference engine. | |
| Args: | |
| model_path (str): Path to the trained PyTorch state dictionary. | |
| classes_path (str): Path to the JSON list mapping indices to class names. | |
| """ | |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| self.classes = [] | |
| if os.path.exists(classes_path): | |
| with open(classes_path, "r") as f: | |
| self.classes = json.load(f) | |
| else: | |
| # Fallback dummy classes if not trained yet | |
| self.classes = [f"Class_{i}" for i in range(24)] | |
| num_classes = len(self.classes) | |
| self.model = MalwareCNN(num_classes=num_classes) | |
| if os.path.exists(model_path): | |
| self.model.load_state_dict( | |
| torch.load(model_path, map_location=self.device, weights_only=True) | |
| ) | |
| else: | |
| print(f"Warning: {model_path} not found. Using untrained weights.") | |
| self.model.to(self.device) | |
| self.model.eval() | |
| self.transform = transforms.Compose( | |
| [ | |
| transforms.Resize((128, 128)), | |
| transforms.ToTensor(), | |
| ] | |
| ) | |
| def predict(self, file_path): | |
| """ | |
| Converts the target file to an image and runs CNN inference. | |
| Args: | |
| file_path (str): Path to the target file. | |
| Returns: | |
| dict: Contains 'family' (str), 'confidence' (float), | |
| 'all_probabilities' (dict), and 'image' (PIL.Image upscaled). | |
| """ | |
| img = bytes_to_image(file_path) | |
| tensor = self.transform(img).unsqueeze(0).to(self.device) | |
| with torch.no_grad(): | |
| outputs = self.model(tensor) | |
| probabilities = torch.nn.functional.softmax(outputs, dim=1)[0] | |
| # Create a dictionary of all class probabilities | |
| all_probs = { | |
| self.classes[i]: probabilities[i].item() | |
| for i in range(len(self.classes)) | |
| } | |
| top_prob, top_class = torch.max(probabilities, 0) | |
| class_name = self.classes[top_class.item()] | |
| return { | |
| "family": class_name, | |
| "confidence": top_prob.item(), | |
| "all_probabilities": all_probs, | |
| "image": img.resize((512, 512), resample=Image.NEAREST), | |
| } | |