malware_analysis / src /engine_b /inference.py
RohanSardar's picture
updated
461e792 verified
Raw
History Blame Contribute Delete
5.19 kB
"""
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),
}