import time
import random
import string
import subprocess
import sys
import glob
from colorama import Back, Fore, Style
import fade
MIN_PYTHON = (3, 10)
REQUIRED_MODULES = ["colorama", "fade", "glob"]
class ModuleCheckError(Exception):
pass
class InvalidInputError(Exception):
pass
def check_python_version():
if sys.version_info < MIN_PYTHON:
raise ModuleCheckError(f"BatchShield requires Python {MIN_PYTHON[0]}.{MIN_PYTHON[1]} or higher. You are using {sys.version_info[0]}.{sys.version_info[1]}.")
def check_required_modules():
missing_modules = [module for module in REQUIRED_MODULES if module not in sys.modules]
if missing_modules:
raise ModuleCheckError(f"The following required modules are missing: {', '.join(missing_modules)}")
def install_missing_modules(missing_modules):
for module in missing_modules:
subprocess.call(['pip', 'install', module])
print("All missing modules have been installed, please re-run BatchShield!")
time.sleep(5)
exit()
def main():
try:
check_python_version()
check_required_modules()
except ModuleCheckError as e:
print(f"{Fore.RED}[-] {e}{Style.RESET_ALL}")
if input("Would you like to automatically install any missing modules? [y/n] : ").lower() == "y":
install_missing_modules(e.args[0].split(', '))
exit()
auto_detect = input('{:<27}: '.format(f"{Fore.YELLOW}[+] AutoDetect .Bat File [Y/N] {Style.RESET_ALL}"))
if auto_detect.lower() not in ['y', 'n']:
raise InvalidInputError("Invalid Input!")
path = auto_detect_path(auto_detect)
level = int(input('{:<27}: '.format(f'{Fore.YELLOW}[+] Obfuscation Level [1/2] {Style.RESET_ALL}')))
if level not in [1, 2]:
raise InvalidInputError("Invalid Input!")
confirm = input('{:<27}: '.format(f"{Fore.YELLOW}[+] Are You Sure You Would Like To Obfuscate {path}? [Y/N] {Style.RESET_ALL}"))
if confirm.lower() not in ['y', 'n']:
raise InvalidInputError("Invalid Input!")
obfuscate_code(path, level, confirm)
def auto_detect_path(auto_detect):
path = ''
if auto_detect.lower() == "y":
os.getcwd()
bat_files = glob.glob("*.bat")
if not bat_files:
raise FileNotFoundError("No .Bat Files Found!")
elif len(bat_files) > 1:
print(f"{Fore.GREEN}[+] Multiple .Bat Files Found!{Style.RESET_ALL}")
path = choose_bat_file(bat_files)
else:
path = bat_files[0]
print(f"{Fore.GREEN}[+] AutoDetected .Bat File - {path}{Style.RESET_ALL}")
elif auto_detect.lower() == "n":
path = input('{:<27}: '.format(f"{Fore.YELLOW}[+] File Path Including (.bat) {Style.RESET_ALL}"))
os.getcwd()
bat_files = glob.glob(path)
if not bat_files:
raise FileNotFoundError("File Not Found!")
path = bat_files[0]
print(f"{Fore.GREEN}[+] Found .Bat File - {path}{Style.RESET_ALL}")
return path
def choose_bat_file(bat_files):
for idx, file in enumerate(bat_files):
print(f"{Fore.RED}[{idx+1}] {file}{Style.RESET_ALL}")
try:
selection = int(input('{:<27}: '.format(f"{Fore.YELLOW}[+] Select File [1-{len(bat_files)}] {Style.RESET_ALL}")))
if selection < 1 or selection > len(bat_files):
raise ValueError
path = bat_files[selection-1]
except ValueError:
raise InvalidInputError("Invalid Input!")
return path
def obfuscate_code(path, level, confirm):
with open(path, 'r', encoding='utf-8') as f:
code = f.read()
if confirm.lower() == "y":
try:
if level == 1:
with open(f'obfuscated_{os.path.basename(path)}', 'w+', encoding='utf-8') as f:
f.write(Methods.LevelOne(code))
elif level == 2:
with open(f'obfuscated_{os.path.basename(path)}', 'w+', encoding='utf-8') as f:
f.write(Methods.LevelTwo(code, path))
print(f"{Fore.GREEN}[+] Successfully Obfuscated {path}!{Style.RESET_ALL}")
print(f"{Fore.RED}[-] Exiting...{Style.RESET_ALL}")
time.sleep(5)
exit()
except Exception as e:
print(f"{Fore.RED}[-] Error: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}[-] Exiting...{Style.RESET_ALL}")
time.sleep(5)
exit()
class Methods:
@staticmethod
def LevelOne(code: str) -> str:
if not isinstance(code, str):
raise TypeError("Input 'code' Parameter Must Be A String Type!")
if not code.strip():
raise ValueError("Input 'code' Parameter Cannot Be Empty!")
obfuscated = ''.join(f'%{random_str()}%' if char == '%' else char for char in code)
if not obfuscated.strip():
raise ValueError("Output From 'LevelOne()' Method Cannot Be Empty!")
return obfuscated
@staticmethod
def random_str():
return ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(5, 15)))
@staticmethod
def LevelTwo(code: str, path: str) -> bytes:
if not isinstance(code, str):
raise TypeError("Input 'code' Parameter Must Be A String Type!")
if not code.strip():
raise ValueError("Input 'code' Parameter Cannot Be Empty!")
obfuscated = bytearray(b'\xFF\xFE\n\r')
encoded = Methods.LevelOne(code)
try:
code_bytes = encoded.encode('utf-8', 'strict')
except UnicodeEncodeError:
raise ValueError("Cannot Encode Obfuscated Code In UTF-8!")
if not code_bytes:
raise ValueError("Output From 'encode()' Method In 'LevelTwo()' Method Cannot Be Empty!")
if not all(c in string.printable for c in encoded):
raise ValueError("Input 'code' Parameter Contains Non-Printable Characters!")
if not all(ord(c) < 128 for c in encoded):
raise ValueError("Input 'code' Parameter Contains Characters Not Supported By The UTF-8 Encoding!")
with open(f'obfuscated_{os.path.basename(path)}', 'wb') as f:
f.write(obfuscated + code_bytes)
if not obfuscated:
raise ValueError("Output From 'LevelTwo()' Method Cannot Be Empty!")
return bytes(obfuscated)
if __name__ == '__main__':
try:
main()
except InvalidInputError as e:
print(f"{Fore.RED}[-] {e}{Style.RESET_ALL}")
print(f"{Fore.RED}[-] Exiting...{Style.RESET_ALL}")
time.sleep(5)
exit()
class Methods:
@staticmethod
def random_str():
return ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(5, 15)))
@staticmethod
def obfuscate_with_nonprintable(code: str) -> str:
if not isinstance(code, str):
raise TypeError("Input 'code' Parameter Must Be A String Type!")
if not code.strip():
raise ValueError("Input 'code' Parameter Cannot Be Empty!")
# Map every character to non-printable ones
mapping = {chr(i): chr(1 + i % 32) for i in range(33, 127)}
# Obfuscate the code using the mapping
obfuscated = ''.join(mapping.get(c, c) for c in code)
# Encapsulate the obfuscated code into a self-extracting batch script
decoder = f'''
@echo off
setlocal EnableDelayedExpansion
set "obfuscated_code={obfuscated}"
for %%A in ({' '.join(f'"{k}" "{v}"' for k, v in mapping.items())}) do (
set "obfuscated_code=!obfuscated_code:%%A=%%~B!"
)
%obfuscated_code%
'''
return decoder