-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathautonuitkadecompiler.py
628 lines (517 loc) · 26 KB
/
autonuitkadecompiler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
import os
import logging
import subprocess
import shutil
import inspect
import string
import re
import ipaddress
import sys
from datetime import datetime
import time
import io
import pefile
import zstandard
from elftools.elf.elffile import ELFFile
import macholib.MachO
import macholib.mach_o
from typing import Optional, Tuple, BinaryIO, Dict, Any
import struct
# Set script directory
script_dir = os.getcwd()
# Define log directories and files
log_directory = os.path.join(script_dir, "log")
if not os.path.exists(log_directory):
os.makedirs(log_directory)
# Separate log files for different purposes
console_log_file = os.path.join(log_directory, "antivirusconsole.log")
application_log_file = os.path.join(log_directory, "antivirus.log")
# Configure logging for application log
logging.basicConfig(
filename=application_log_file,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
)
# Set the default encoding to UTF-8 for standard output and input, with error handling set to 'ignore'
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8', errors='ignore')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding='utf-8', errors='ignore')
sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding='utf-8', errors='ignore')
# Logging for application initialization
logging.info("Application started at %s", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
seven_zip_path = "C:\\Program Files\\7-Zip\\7z.exe" # Path to 7z.exe
detectiteasy_dir = os.path.join(script_dir, "detectiteasy")
detectiteasy_console_path = os.path.join(detectiteasy_dir, "diec.exe")
nuitka_source_code_dir = os.path.join(script_dir, "nuitkasourcecode")
nuitka_dir = os.path.join(script_dir, "nuitka")
general_extracted_dir = os.path.join(script_dir, "general_extracted")
# Ensure output directories exist
os.makedirs(nuitka_source_code_dir, exist_ok=True)
os.makedirs(nuitka_dir, exist_ok=True)
os.makedirs(general_extracted_dir, exist_ok=True)
def is_nuitka_file(file_path):
"""Check if the file is a Nuitka executable using Detect It Easy."""
try:
# Run the DIE console command to analyze the file
logging.info(f"Analyzing file: {file_path} using Detect It Easy...")
result = subprocess.run([detectiteasy_console_path, file_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# Check for Nuitka executable and OneFile
if "Packer: Nuitka[OneFile]" in result.stdout:
logging.info(f"File {file_path} is a Nuitka OneFile executable.")
return "Nuitka OneFile"
elif "Packer: Nuitka" in result.stdout:
logging.info(f"File {file_path} is a Nuitka executable.")
return "Nuitka"
else:
logging.info(f"File {file_path} is not a Nuitka executable. Result: {result.stdout}")
except subprocess.SubprocessError as ex:
logging.error(f"Error in {inspect.currentframe().f_code.co_name} while running Detect It Easy for {file_path}: {ex}")
return None
except Exception as ex:
logging.error(f"General error in {inspect.currentframe().f_code.co_name} while running Detect It Easy for {file_path}: {ex}")
return None
return None
def scan_directory_for_executables(directory):
"""
Recursively scan a directory for .exe, .dll, and other files, prioritizing Nuitka executables.
If an .exe file is found and confirmed as Nuitka, stop further scanning.
"""
found_executables = []
# Look for .exe files first
for root, _, files in os.walk(directory):
for file in files:
if file.lower().endswith('.exe'):
file_path = os.path.join(root, file)
nuitka_type = is_nuitka_file(file_path)
if nuitka_type:
found_executables.append((file_path, nuitka_type))
return found_executables # Stop scanning further as .exe is found
# If no .exe found, look for .dll files
for root, _, files in os.walk(directory):
for file in files:
if file.lower().endswith('.dll'):
file_path = os.path.join(root, file)
nuitka_type = is_nuitka_file(file_path)
if nuitka_type:
found_executables.append((file_path, nuitka_type))
return found_executables # Stop scanning further as .dll is found
# If no .exe or .dll found, check other files
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
if not file.lower().endswith(('.exe', '.dll')): # Skip .exe and .dll files
nuitka_type = is_nuitka_file(file_path)
if nuitka_type:
found_executables.append((file_path, nuitka_type))
return found_executables # Stop scanning further as Nuitka file is found
return found_executables
def extract_all_files_with_7z(file_path):
try:
counter = 1
base_output_dir = os.path.join(general_extracted_dir, os.path.splitext(os.path.basename(file_path))[0])
# Ensure output directory is unique
while os.path.exists(f"{base_output_dir}_{counter}"):
counter += 1
output_dir = f"{base_output_dir}_{counter}"
os.makedirs(output_dir, exist_ok=True)
logging.info(f"Attempting to extract file {file_path} into {output_dir}...")
# Run the 7z extraction
command = [seven_zip_path, "x", file_path, f"-o{output_dir}", "-y", "-snl", "-spe"]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
logging.error(f"7z extraction failed with return code {result.returncode}: {result.stderr}")
return []
logging.info(f"7z extraction successful for {file_path}.")
# Gather all files in the output directory after extraction
extracted_files = []
for root, _, files in os.walk(output_dir):
for name in files:
extracted_files.append(os.path.join(root, name))
if not extracted_files:
logging.warning(f"No files were extracted from {file_path}.")
else:
logging.info(f"Extracted {len(extracted_files)} files from {file_path}.")
return extracted_files
except Exception as ex:
logging.error(f"Error during 7z extraction: {ex}")
return []
def clean_text(input_text):
"""
Remove non-printable ASCII control characters from the input text.
:param input_text: The string to clean.
:return: Cleaned text with control characters removed.
"""
# Remove non-printable characters (ASCII 0-31 and 127)
cleaned_text = re.sub(r'[\x00-\x1F\x7F]+', '', input_text)
return cleaned_text
def is_local_ip(ip):
try:
ip_obj = ipaddress.ip_address(ip)
return ip_obj.is_private
except ValueError:
return False
# Function to scan code for links (domains, IPs, URLs, and Discord links)
def scan_code_for_links(code):
"""
Scan a given string of code for domains, IP addresses, URLs, and Discord webhook/Discord invite URLs,
removing duplicates.
"""
try:
# Regular expressions for different patterns
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
domain_pattern = r'\b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}\b'
url_pattern = r'https?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
discord_webhook_pattern = r'https://discord\.com/api/webhooks/[0-9]+/[A-Za-z0-9_-]+'
discord_canary_webhook_pattern = r'https://canary\.discord\.com/api/webhooks/[0-9]+/[A-Za-z0-9_-]+'
discord_invite_pattern = r'https://discord\.gg/[A-Za-z0-9]+'
# Perform scans and store results in sets to automatically remove duplicates
ip_matches = set(re.findall(ip_pattern, code))
domain_matches = set(re.findall(domain_pattern, code))
url_matches = set(re.findall(url_pattern, code))
discord_webhook_matches = set(re.findall(discord_webhook_pattern, code))
discord_canary_webhook_matches = set(re.findall(discord_canary_webhook_pattern, code))
discord_invite_matches = set(re.findall(discord_invite_pattern, code))
# Filter out local IP addresses
ip_matches = {ip for ip in ip_matches if not is_local_ip(ip)}
# Logging the findings
if ip_matches:
logging.info(f"IP addresses detected (excluding local IPs): {ip_matches}")
if domain_matches:
logging.info(f"Domains detected: {domain_matches}")
if url_matches:
logging.info(f"URLs detected: {url_matches}")
if discord_webhook_matches:
logging.warning(f"Discord webhook URLs detected: {discord_webhook_matches}")
if discord_canary_webhook_matches:
logging.warning(f"Discord Canary webhook URLs detected: {discord_canary_webhook_matches}")
if discord_invite_matches:
logging.info(f"Discord invite links detected: {discord_invite_matches}")
except Exception as ex:
logging.error(f"Error scanning code for links: {ex}")
def scan_rsrc_directory(extracted_files):
"""
Scans all files in the extracted_files list for .rsrc\\RCDATA, extracts
the full content, cleans it, and performs scans for domains, URLs,
IP addresses, and Discord webhooks.
:param extracted_files: List of files extracted by 7z.
"""
try:
for extracted_file in extracted_files:
# Check if the file path contains .rsrc\RCDATA
if ".rsrc\\RCDATA" in extracted_file or ".rsrc/RCDATA" in extracted_file:
logging.info(f"Processing RCDATA file: {extracted_file}")
# Ensure the path refers to an actual file
if os.path.isfile(extracted_file):
try:
# Read the full content of the file, handling invalid UTF-8 gracefully
with open(extracted_file, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
if lines:
# Clean each line by removing non-printable characters
cleaned_lines = [clean_text(line.strip()) for line in lines]
# Save the full cleaned content to a uniquely named file
base_name = os.path.splitext(os.path.basename(extracted_file))[0]
save_path = os.path.join(nuitka_source_code_dir, f"{base_name}_full_content.txt")
counter = 1
while os.path.exists(save_path):
save_path = os.path.join(
nuitka_source_code_dir, f"{base_name}_full_content_{counter}.txt"
)
counter += 1
# Write all cleaned lines to the file
with open(save_path, "w", encoding="utf-8") as save_file:
for line in cleaned_lines:
save_file.write(line + '\n')
logging.info(f"Saved full content from {extracted_file} to {save_path}")
# Join the full content for scanning purposes
rsrc_content = ''.join(lines)
# Perform the scans
scan_code_for_links(rsrc_content)
else:
logging.info(f"File {extracted_file} is empty.")
except Exception as ex:
logging.error(f"Error reading file {extracted_file}: {ex}")
else:
logging.warning(f"Path {extracted_file} is not a valid file.")
else:
logging.debug(f"Skipping non-RCDATA file: {extracted_file}")
except Exception as ex:
logging.error(f"Error during RCDATA file scanning: {ex}")
class FileType:
UNKNOWN = -1
ELF = 0
PE = 1
MACHO = 2
class CompressionFlag:
UNKNOWN = -1
NON_COMPRESSED = 0
COMPRESSED = 1
class PayloadError(Exception):
"""Custom exception for payload processing errors"""
pass
class NuitkaPayload:
MAGIC_KA = b'KA'
MAGIC_UNCOMPRESSED = ord('X')
MAGIC_COMPRESSED = ord('Y')
def __init__(self, data: bytes, offset: int, size: int):
self.data = data
self.offset = offset
self.size = size
self.compression = CompressionFlag.UNKNOWN
self._validate()
def _validate(self):
"""Validate payload magic and set compression flag"""
if not self.data.startswith(self.MAGIC_KA):
raise PayloadError("Invalid Nuitka payload magic")
magic_type = self.data[2]
if magic_type == self.MAGIC_UNCOMPRESSED:
self.compression = CompressionFlag.NON_COMPRESSED
elif magic_type == self.MAGIC_COMPRESSED:
self.compression = CompressionFlag.COMPRESSED
else:
raise PayloadError(f"Unknown compression magic: {magic_type}")
def get_stream(self) -> BinaryIO:
"""Get a file-like object for reading the payload"""
# Skip the 3-byte magic header
payload_data = self.data[3:]
stream = io.BytesIO(payload_data)
if self.compression == CompressionFlag.COMPRESSED:
try:
dctx = zstandard.ZstdDecompressor()
# Create a stream reader with a large read size
return dctx.stream_reader(stream, read_size=8192)
except zstandard.ZstdError as ex:
raise PayloadError(f"Failed to initialize decompression: {str(ex)}")
return stream
class NuitkaExtractor:
def __init__(self, filepath: str, output_dir: str):
self.filepath = filepath
self.output_dir = output_dir
self.file_type = FileType.UNKNOWN
self.payload: Optional[NuitkaPayload] = None
def _detect_file_type(self) -> int:
"""Detect the executable file type using Detect It Easy methods"""
if is_nuitka_file(self.filepath):
return FileType.PE # Assuming Nuitka files are detected as PE files
elif is_pe_file(self.filepath):
return FileType.PE
elif is_elf_file(self.filepath):
return FileType.ELF
elif is_macho_file(self.filepath):
return FileType.MACHO
return FileType.UNKNOWN
def _find_pe_resource(self, pe: pefile.PE) -> Tuple[Optional[int], Optional[int]]:
"""Find the Nuitka resource in PE file"""
try:
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if hasattr(entry, 'directory'):
for entry1 in entry.directory.entries:
if entry1.id == 27: # Nuitka's resource ID
if hasattr(entry1, 'directory'):
data_entry = entry1.directory.entries[0]
if hasattr(data_entry, 'data'):
offset = pe.get_offset_from_rva(data_entry.data.struct.OffsetToData)
size = data_entry.data.struct.Size
return offset, size
except Exception:
pass
return None, None
def _extract_pe_payload(self) -> Optional[NuitkaPayload]:
"""Extract payload from PE file"""
try:
pe = pefile.PE(self.filepath, fast_load=False)
# Find RT_RCDATA resource with ID 27
if not hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
raise PayloadError("No resource directory found")
offset, size = self._find_pe_resource(pe)
if offset is None or size is None:
raise PayloadError("No Nuitka payload found in PE resources")
# Read the payload data
with open(self.filepath, 'rb') as f:
f.seek(offset)
payload_data = f.read(size)
return NuitkaPayload(payload_data, offset, size)
except Exception as ex:
raise PayloadError(f"PE payload extraction failed: {str(ex)}")
def _extract_elf_payload(self) -> Optional[NuitkaPayload]:
"""Extract payload from ELF file"""
try:
with open(self.filepath, 'rb') as f:
elf = ELFFile(f)
# Find last section to locate appended data
last_section = max(elf.iter_sections(),
key=lambda s: s.header.sh_offset + s.header.sh_size)
# Read trailer for payload size
f.seek(-8, io.SEEK_END)
payload_size = struct.unpack('<Q', f.read(8))[0]
# Read payload
payload_offset = last_section.header.sh_offset + last_section.sh_size
f.seek(payload_offset)
payload_data = f.read(payload_size)
return NuitkaPayload(payload_data, payload_offset, payload_size)
except Exception as ex:
raise PayloadError(f"ELF payload extraction failed: {str(ex)}")
def _extract_macho_payload(self) -> Optional[NuitkaPayload]:
"""Extract payload from Mach-O file"""
try:
macho = macholib.MachO.MachO(self.filepath)
for header in macho.headers:
for cmd in header.commands:
if cmd[0].cmd in (macholib.mach_o.LC_SEGMENT, macholib.mach_o.LC_SEGMENT_64):
for section in cmd[1].sections:
if section[0].decode('utf-8') == 'payload':
offset = section[2]
size = section[3]
with open(self.filepath, 'rb') as f:
f.seek(offset)
payload_data = f.read(size)
return NuitkaPayload(payload_data, offset, size)
raise PayloadError("No payload section found in Mach-O file")
except Exception as ex:
raise PayloadError(f"Mach-O payload extraction failed: {str(ex)}")
def _read_string(self, stream: BinaryIO, is_wide: bool = False) -> Optional[str]:
"""Read a null-terminated string from the stream"""
result = bytearray()
while True:
char = stream.read(2 if is_wide else 1)
if not char or char == b'\0' * len(char):
break
result.extend(char)
if not result:
return None
try:
return result.decode('utf-16-le' if is_wide else 'utf-8')
except UnicodeDecodeError:
return None
def _extract_files(self, stream: BinaryIO):
"""Extract files from the payload stream"""
total_files = 0
os.makedirs(self.output_dir, exist_ok=True)
try:
while True:
# Read filename
filename = self._read_string(stream, is_wide=(self.file_type == FileType.PE))
if not filename:
break
# Read file flags for ELF
if self.file_type == FileType.ELF:
stream.read(1) # Skip flags
# Read file size
size_data = stream.read(8)
if not size_data or len(size_data) != 8:
break
file_size = struct.unpack('<Q', size_data)[0]
# Sanitize output path
safe_output_dir = str(self.output_dir).replace('..', '__')
outpath = os.path.join(safe_output_dir, filename)
os.makedirs(os.path.dirname(outpath), exist_ok=True)
# Extract file
try:
with open(outpath, 'wb') as f:
remaining = file_size
while remaining > 0:
chunk_size = min(remaining, 8192)
data = stream.read(chunk_size)
if not data:
logging.warning(f"Incomplete read for {filename}")
break
f.write(data)
remaining -= len(data)
total_files += 1
logging.info(f"[+] Extracted: {filename}")
except Exception as ex:
logging.error(f"Failed to extract {filename}: {ex}")
continue
except Exception as ex:
logging.error(f"Extraction error: {ex}")
return total_files
def extract(self):
"""Main extraction process"""
try:
# Detect file type using the new detection methods
self.file_type = self._detect_file_type()
if self.file_type == FileType.UNKNOWN:
raise PayloadError("Unsupported file type")
logging.info(f"[+] Processing: {self.filepath}")
logging.info(f"[+] Detected file type: {['ELF', 'PE', 'MACHO'][self.file_type]}")
# Extract payload based on file type
if self.file_type == FileType.PE:
self.payload = self._extract_pe_payload()
elif self.file_type == FileType.ELF:
self.payload = self._extract_elf_payload()
else: # MACHO
self.payload = self._extract_macho_payload()
if not self.payload:
raise PayloadError("Failed to extract payload")
logging.info(f"[+] Payload size: {self.payload.size} bytes")
logging.info(f"[+] Compression: {'Yes' if self.payload.compression == CompressionFlag.COMPRESSED else 'No'}")
# Extract files from payload
stream = self.payload.get_stream()
total_files = self._extract_files(stream)
logging.info(f"[+] Successfully extracted {total_files} files to {self.output_dir}")
except PayloadError as ex:
logging.error(f"[!] {str(ex)}")
except Exception as ex:
logging.error(f"[!] Unexpected error: {str(ex)}")
def extract_nuitka_file(file_path, nuitka_type):
"""
Detect Nuitka type, extract Nuitka executable content, and scan for additional Nuitka executables.
:param file_path: Path to the Nuitka executable file.
:param nuitka_type: Type of Nuitka executable ("Nuitka OneFile" or "Nuitka").
"""
try:
if nuitka_type == "Nuitka OneFile":
logging.info(f"Nuitka OneFile executable detected in {file_path}")
# Extract the file name (without extension) to include in the folder name
file_name_without_extension = os.path.splitext(os.path.basename(file_path))[0]
# Find the next available directory number for OneFile extraction
folder_number = 1
while os.path.exists(os.path.join(nuitka_dir, f"OneFile_{file_name_without_extension}_{folder_number}")):
folder_number += 1
# Create the new directory with the executable file name and folder number
nuitka_output_dir = os.path.join(nuitka_dir, f"OneFile_{file_name_without_extension}_{folder_number}")
os.makedirs(nuitka_output_dir, exist_ok=True)
logging.info(f"Extracting Nuitka OneFile {file_path} to {nuitka_output_dir}")
# Use NuitkaExtractor for extraction
extractor = NuitkaExtractor(file_path, nuitka_output_dir)
extractor.extract()
# Scan the extracted directory for additional Nuitka executables
logging.info(f"Scanning extracted directory for additional Nuitka executables...")
found_executables = scan_directory_for_executables(nuitka_output_dir)
# Process any found normal Nuitka executables
for exe_path, exe_type in found_executables:
if exe_type == "Nuitka":
logging.info(f"Found normal Nuitka executable in extracted files: {exe_path}")
extract_nuitka_file(exe_path, exe_type)
elif nuitka_type == "Nuitka":
logging.info(f"Nuitka executable detected in {file_path}")
# Extract the Nuitka executable
file_name_without_extension = os.path.splitext(os.path.basename(file_path))[0]
nuitka_output_dir = os.path.join(nuitka_dir, f"Nuitka_{file_name_without_extension}")
os.makedirs(nuitka_output_dir, exist_ok=True)
logging.info(f"Extracting Nuitka executable {file_path} to {nuitka_output_dir}")
# Use enhanced 7z extraction
extracted_files = extract_all_files_with_7z(file_path)
if extracted_files:
logging.info(f"Successfully extracted files from Nuitka executable: {file_path}")
# Scan for RSRC/RCDATA resources
scan_rsrc_directory(extracted_files)
else:
logging.error(f"Failed to extract normal Nuitka executable: {file_path}")
else:
logging.info(f"No Nuitka content found in {file_path}")
except PayloadError as ex:
logging.error(f"Payload error while extracting Nuitka file: {ex}")
except Exception as ex:
logging.error(f"Unexpected error while extracting Nuitka file: {ex}")
# Main script logic to ask for the user input (file path)
if __name__ == "__main__":
file_path = input("Enter the path to the Nuitka executable file: ")
# Check if the provided file exists
if os.path.exists(file_path):
nuitka_type = is_nuitka_file(file_path)
if nuitka_type:
extract_nuitka_file(file_path, nuitka_type)
else:
logging.info("The file is not a Nuitka executable.")
else:
logging.error(f"The file {file_path} does not exist.")