Skip to content

Instantly share code, notes, and snippets.

@anon767
Last active October 16, 2022 12:29
Show Gist options
  • Save anon767/d481d8b225511201658ebfb0007f50bf to your computer and use it in GitHub Desktop.
Save anon767/d481d8b225511201658ebfb0007f50bf to your computer and use it in GitHub Desktop.
Reproduce Antivir Fuzzing
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Overxfl0w13 - 2015 - https://github.com/overxfl0w - #
# For anything -> https://github.com/overxfl0w/CObfuscator #
import re
import sys
from random import randint
""" Se compilarán las expresiones antes del finditer """
non_start_text = "(?!(?:\/\/|\"|\'|\/\*\*))" # Arreglar #
comments = "(\/\/.*\n|\/\*\s*\n*\s*(?:.*)\s*\n*\s*\*\/)" # Arreglar #
identifier = "[a-zA-Z][\w|_]*"
declarator = "(?:\*?)\s*" + "(" + identifier + ")"
storage_class_specifiers = "auto|register|static|extern|typedef"
type_qualifiers = "const|volatile"
type_specifiers = "void|char|short|int|long|float|double|signed|unsigned|struct|union" # Anyadir soporte enum (identifier) #
storage_class_specifiers_functions = "auto|static|extern"
type_qualifiers_functions = type_qualifiers
type_specifiers_functions = "char|short|int|long|float|double|signed|unsigned"
declaration_specifiers = "(?:" + storage_class_specifiers + ")?\s*(?:" + type_qualifiers + ")?\s*(?:" + type_specifiers + ")\*?\s+"
function_definition = non_start_text + declaration_specifiers + declarator + "\s*\("
start_preprocessor = "(?#)"
non_start_preprocessor = "(?!#)"
preprocessor_defines = "define|ifdef|ifndef|undef"
preprocessor = non_start_text + start_preprocessor + "\s*(?:" + preprocessor_defines + ")\s*" + declarator + "\s*\(?"
variable_definition = non_start_text + declaration_specifiers + declarator + "\s*(?:[=|;|,])"
separated_variables = non_start_text + identifier + "\s*,\s*" + declarator + "\s*(?:[=|;|,])" # Arreglar para variables declaradas con , #
token_numbers = non_start_text + "((?:0x|0b|0x%)?[0-9]+)"
MAX_VALID_IDENTIFIER = 10
JUNK_MAX_FUNCTIONS = 15
JUNK_MAX_PARAMS_PER_FUNCTION = 5
JUNK_MAX_INSTRUCTIONS = 10
def addJunk(source, functions):
""" Añade código basura con instrucciones que alteren el contenido binario sin alterar el resultado final.
Devuelve el código fuente con basura añadida
Parámetros:
source -- código fuente
Excepciones:
...
"""
## Arreglar ##
def addJunkInstructions(source, functions):
""" Añade código basura con instrucciones que alteren el contenido binario sin alterar el resultado final.
Devuelve el código fuente con basura añadida
Parámetros:
source -- código fuente
Excepciones:
...
"""
function_block = None
for function in functions: function_block = non_start_text + "(?:.*)" + function + "(?:.*)\){"
return source
def addJunkFunctions(source):
""" Añade código basura con instrucciones que alteren el contenido binario sin alterar el resultado final.
Devuelve el código fuente con basura añadida
Parámetros:
source -- código fuente
Excepciones:
...
"""
numFunctions = randint(1, JUNK_MAX_FUNCTIONS)
astorage_specifiers = storage_class_specifiers_functions.split("|")
astorage_specifiers.append("")
atype_qualifiers = type_qualifiers_functions.split("|")
atype_qualifiers.append("")
atype_specifiers = type_specifiers_functions.split("|")
for i in range(numFunctions):
my_storage_specifier = astorage_specifiers[randint(0, len(astorage_specifiers) - 1)]
my_type_qualifier = atype_qualifiers[randint(0, len(atype_qualifiers) - 1)]
my_type_specifier = atype_specifiers[randint(0, len(atype_specifiers) - 1)]
len_name = randint(3, MAX_VALID_IDENTIFIER)
my_name = generateValidIdentifier(len_name)
num_params = randint(1, JUNK_MAX_PARAMS_PER_FUNCTION)
source_function = my_storage_specifier + " " + my_type_qualifier + " " + my_type_specifier + " " + my_name + "("
source_function += atype_specifiers[randint(0, len(atype_specifiers) - 1)] + " " + generateValidIdentifier(
randint(0, MAX_VALID_IDENTIFIER))
for x in range(num_params - 1): source_function += "," + atype_specifiers[
randint(0, len(atype_specifiers) - 1)] + " " + generateValidIdentifier(randint(0, MAX_VALID_IDENTIFIER))
source_function += "){}" + "\n" * randint(1, 6)
source += source_function
return source
source = addJunkFunctions(source)
source = addJunkInstructions(source, functions)
return source
""" Number codifications functions (Documentar e implementar) """
def convertDecimal(number):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
return number
def convertBinary(number):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
try:
return bin(int(number, 0))
except:
return number
def convertHexadecimal(number):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
try:
return hex(int(number, 0))
except:
return number
def convertOctal(number):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
try:
return oct(int(number, 0))
except:
return number
def neutralOpNumber(number):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
# Generar todas las combinaciones #
neutralOps = [" | 0b0", " | 0b00", " | 0b000", " | 0b0000", " | 0x0", " | 0x00", " | 0", " & 0xF", " & 0xFF",
" & 0b11111111", " ^ " + str(number),
" + 0b0", " + 0", " + 0b00", " - 0", " - 0b00", " * 0b1", " * 0b01", " / 0b01", " / 0b1"]
return number + neutralOps[randint(0, len(neutralOps) - 1)]
""" File functions """
def getSource(fileName):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
with open(fileName, "r") as fd: yield fd.read()
fd.close()
def saveSource(source, fileName):
""" Almacena en disco el código fuente modificado.
No devuelve ningún valor
Parámetros:
source -- código fuente
fileName -- nombre del fichero donde almacenar el código fuente
Excepciones:
...
"""
with open(fileName, "w") as fd: fd.write(source)
fd.close()
""" Replace functions """
def replaceFunctions(source, functions):
""" Reemplazar funciones haciéndolas ilegibles.
Devuelve el codigo fuente con las funciones cambiadas
Parámetros:
source -- código fuente
functions -- conjunto de funciones del código fuente
Excepciones:
...
Sentencia:
storage_class_identifier type_qualifier type_specifier
"""
for function in functions: source = re.sub(r'\b' + function + r'\b', functions[function], source)
return source
def replaceDeclarations(source, declarations):
""" Reemplazar variables|funciones definidas con llamadas al preprocesador.
Devuelve el código fuente con las declaraciones cambiadas
Parámetros:
source -- código fuente
declarations -- conjunto de declaraciones a reemplazar
Excepciones:
...
"""
for declaration in declarations: source = re.sub(r'\b' + declaration + r'\b', declarations[declaration], source)
return source
def replaceVariables(source, variables):
""" Cambia el nombre de todas las variables haciéndolas ilegibles.
Devuelve el código fuente con las variables cambiadas
Parámetros:
source -- código fuente
variables -- conjunto de variables a reemplazar
Excepciones:
...
"""
for variable in variables: source = re.sub(r'\b' + variable + r'\b', variables[variable], source)
return source
def replaceNumberCodification(source, numbers):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
for number in numbers: source = re.sub(r'\b' + number + r'\b', numbers[number], source)
return source
""" Remove functions """
def removeLR(source):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
source = source.split("\n")
for x in range(len(source)):
if source[x].strip().startswith("#") == True: source[x] += "\n"
return "".join(source)
def removeComments(source):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
source = re.sub(comments, "", source)
return source
""" Search functions """
def searchFunctions(source, lengthValidIdentifiers):
""" Buscar las funciones y almacenarlas en una tabla hash.
Devuelve una tabla hash con todas las funciones del código como clave y su token aleatorio como valor
Parámetros:
source -- código fuente
lengthValidIdentifiers -- longitud de los identificadores aleatorios
Excepciones:
1) La expresion regular no encuentra ocurrencias, match.group(0) = None (AttributeError)
2) Se sobrepasa el indice del grupo (IndexError)
...
"""
try:
functions = {} # Nombre función : aleatorio #
# print non_start_text+function_definition
reCompiled = re.compile(function_definition)
for match in reCompiled.finditer(source):
funcToken = match.group(1)
if funcToken != "main":
if funcToken not in functions: functions[funcToken] = generateValidIdentifier(lengthValidIdentifiers)
except AttributeError as ae:
print("AttributeError")
except IndexError as ie:
print("Index error")
finally:
return functions
def searchDeclarations(source, lengthValidIdentifiers):
""" Buscar las declaraciones
Devuelve una tabla hash con todas las declaraciones del código como clave y su token aleatorio como valor.
Parámetros:
source -- código fuente
lengthValidIdentifiers -- longitud de los identificadores aleatorios
Excepciones:
1) La expresion regular no encuentra ocurrencias, match.group(0) = None (AttributeError)
2) Se sobrepasa el indice del grupo (IndexError)
...
"""
try:
declarations = {} # Nombre declaracion : aleatorio #
# print preprocessor
reCompiled = re.compile(preprocessor)
for match in reCompiled.finditer(source):
decToken = match.group(1)
if decToken not in declarations: declarations[decToken] = generateValidIdentifier(lengthValidIdentifiers)
except AttributeError as ae:
print("AttributeError")
except IndexError as ie:
print("Index error")
finally:
return declarations
def searchVariables(source, lengthValidIdentifiers):
""" Buscar las variables y almacenarlas en una tabla hash.
Devuelve una tabla hash con todas las variablesdel código como clave y su token aleatorio como valor
Parámetros:
source -- código fuente
lengthValidIdentifiers -- longitud de los identificadores aleatorios
Excepciones:
1) La expresion regular no encuentra ocurrencias, match.group(0) = None (AttributeError)
2) Se sobrepasa el indice del grupo (IndexError)
...
"""
try:
variables = {} # Nombre declaracion : aleatorio #
# print variable_definition,separated_variables
reCompiled = re.compile(variable_definition)
for match in reCompiled.finditer(source):
varToken = match.group(1)
if varToken not in variables: variables[varToken] = generateValidIdentifier(lengthValidIdentifiers)
## Arreglar ##
"""for match in re.finditer(separated_variables,source):
varToken = match.group(1)
if varToken not in variables: variables[varToken] = generateValidIdentifier(lengthValidIdentifiers)"""
except AttributeError as ae:
print("AttributeError")
except IndexError as ie:
print("Index error")
finally:
return variables
def searchNumbers(source):
""" Obtiene el código fuente del fichero.
Devuelve el código fuente con las declaraciones cambiadas.
Parámetros:
source -- código fuente
Excepciones:
...
"""
try:
numbers = {} # Nombre función : aleatorio #
convertFunctions = [convertDecimal, convertBinary, convertHexadecimal, convertOctal, neutralOpNumber]
reCompiled = re.compile(token_numbers)
for match in reCompiled.finditer(source):
numToken = match.group(1)
if numToken not in numbers:
randomFunction = randint(0, len(convertFunctions) - 1)
numbers[numToken] = convertFunctions[randomFunction](numToken)
except AttributeError as ae:
print("AttributeError")
except IndexError as ie:
print("Index error")
finally:
return numbers
def permuteSource(source):
""" Permuta grupos de instrucciones sin dependencias de flujo ni antidependencias.
Devuelve el código fuente con grupos de instrucciones con el orden cambiado aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
def permuteFunctions(source):
""" Permuta grupos de instrucciones sin dependencias de flujo ni antidependencias.
Devuelve el código fuente con grupos de instrucciones con el orden cambiado aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
pass
def permuteInstructions(source):
""" Permuta grupos de instrucciones sin dependencias de flujo ni antidependencias.
Devuelve el código fuente con grupos de instrucciones con el orden cambiado aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
pass
pass
def addJumps(source):
""" Cambia el flujo de ejecución añadiendo saltos sin alterar el resultado final.
Devuelve el código fuente con saltos añadidos aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
pass
def transposeSource(source):
""" Transponer el código manteniendo el flujo de ejecución.
Devuelve el código fuente transpuesto aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
def replaceInstructions(source):
""" Reemplaza instrucciones por otras equivalentes.
Devuelve el código fuente con las instrucciones reemplazadas aleatoriamente
Parámetros:
source -- código fuente
Excepciones:
...
"""
pass
""" Utils functions """
def generateValidIdentifier(length):
""" Generar un identificador valido para las sustituciones.
Devuelve un identificador aleatorio x / |x| = length, iniciado por un caracter alfabético seguido de length-1 caracteres alfanuméricos
Parámetros:
length -- longitud del identificador
Excepciones:
...
"""
return "".join([chr(randint(65, 90)) if randint(0, 2) >= 1 else chr(randint(97, 122))] + [
chr(randint(65, 90)) if randint(0, 2) == 2 else chr(randint(97, 122)) if randint(0, 2) == 1 else chr(
randint(48, 57)) for x in range(length)])
if __name__ == "__main__":
## source = getSource("mutation_motor.c").next() ##
""" Código de ejemplo hardcodeado """
source = open(sys.argv[1]).read()
numbers = searchNumbers(source)
source = replaceNumberCodification(source, numbers)
functions = searchFunctions(source, 10)
source = replaceFunctions(source,functions)
declarations = searchDeclarations(source,10)
source = replaceDeclarations(source,declarations)
variables = searchVariables(source,10)
source = replaceVariables(source,variables)
source = addJunk(source, functions)
source = removeComments(source)
source = removeLR(source)
print(source)
import os
import random
import subprocess
styles = ["LLVM", "GNU", "Google", "Chromium", "Mozilla", "WebKit", "Microsoft"]
def get_random_cpp(path):
return random.choice(os.listdir(path))
def apply_codestyle(file):
stream = os.popen('/usr/local/opt/llvm/bin/clang-format --style {} {}'.format(random.choice(styles), file))
output = stream.read()
return output
def apply_py_obfuscator(file):
stream = os.popen('/usr/local/bin/python3 obfuscator.py {}'.format(file))
output = stream.read()
return output
def apply_cobfuscate(file):
stream = os.popen('/usr/local/bin/python3 cobfuscator.py {}'.format(file))
output = stream.read().replace(file, "")
return output
mutators = [apply_codestyle, apply_py_obfuscator, apply_cobfuscate]
def test_against_environment():
compstream = subprocess.Popen(['/Users/user/Projects/VirScan/Lilith_obfuscated/build.sh', ],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=r'/Users/user/Projects/VirScan/Lilith_obfuscated')
# print(compstream.stdout.read().decode("utf-8"))
# print(compstream.stderr.read().decode("utf-8"))
stream = os.popen(
'/usr/local/bin/http -f localhost:3996/scan malware@/Users/user/Projects/VirScan/Lilith_obfuscated/hello_windows.exe')
output = stream.read()
print(output)
return "false" not in output
if __name__ == '__main__':
path = "Lilith_obfuscated/Lilith/"
test = True
while test:
file = path + get_random_cpp(path)
if not file.endswith(".cpp"):
continue
print("Obfuscating " + file)
mutator = random.choice(mutators)
obfuscated_content = mutator(file)
if "PASS" in obfuscated_content:
continue
myFile = open(file, "w")
myFile.write(obfuscated_content)
myFile.close()
test = test_against_environment()
"""
obfuscator
Description: A basic python script to aid in the obfuscation of c and c++ source code files
Authors: Sam "Alice" Blair, Winston Howard, Chance Sweetser
Created Date: 05/04/20
"""
import random
import re
import string
import sys
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
def variable_renamer(given_string):
"""
Function to rename all variables and fuctions.
given_string is a string of C/C++ code
"""
# Variable declarations:
variable_dictionary = {}
special_cases = {"typedef", "unsigned"}
index = 0
new_string = ""
# Split the code to indicate when it enters/exits a string
split_code = re.split('\"', given_string)
# REGEX to find all function and variable declarations ignoring main
filtered_code = re.findall(
"(?:\w+\s+)(?!main)(?:\*)*([a-zA-Z_][a-zA-Z0-9_]*)", given_string)
# For loop to add examples found from running a REGEX to a dictionary object
# Ignores special cases and repeats
# When a value is entered it is also assigned a random string of length 12
for found_example in filtered_code:
if (found_example not in special_cases):
if (found_example not in variable_dictionary):
variable_dictionary[found_example] = random_string(12)
# For each even section in split code (odd indicates that it is in a string)
# replace all of the varaible and function names with what is defined in the dictionary
for section in split_code:
if (index % 2 == 0):
for entry in variable_dictionary:
# Used \W because we dont want to replace a variable if it is inside another word.
re_string = r"\W{}\W".format(entry)
# While loop to go through every entry and replace it
# Breaks when it cannot find another instance
while True:
first_found_entry = re.search(re_string, section)
if (not first_found_entry):
break
# Gets the iterator start and enndpoints of the searched re_string
# Then replaces the the information inbetween with the dictionary value
start = first_found_entry.start(0)
end = first_found_entry.end(0)
section = section[:start + 1] + variable_dictionary[entry] + section[end - 1:]
# Add the current section back to make the original string but with obfuscated names
# Accounts for adding a quote everytime except for the first scenario
if (index >= 1):
new_string = new_string + "\"" + section
else:
new_string = new_string + section
index += 1
# Return the obfuscated code
return new_string
def random_string(stringLength=8):
"""
Function to generate a random string.
Can pass it an integer string length to make it that size else it will be 8
"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
def whitespace_remover(a):
"""
Function to remove all whitespace, except for after functions, variables, and imports
"""
splits = re.split('\"', a)
code_string = "((\w+\s+)[a-zA-Z_*][|a-zA-Z0-9_]*|#.*|return [a-zA-Z0-9_]*| [[.].]|else)"
index = 0
a = ""
for s in splits:
# If its not the contents of a string, remove spaces of everything but code
if (index % 2 == 0):
s_spaceless = re.sub("[\s]", "", s) # Create a spaceless version of s
s_code = re.findall(code_string, s) # find all spaced code blocks in s
for code in s_code:
old = re.sub("[\s]", "", code[0])
new = code[0]
if (code[0][0] == '#'):
new = code[0] + "\n" # Adding a newline for preprocesser commands
elif ("unsigned" in code[0] or "else" in code[0]):
new = code[0] + " "
s_spaceless = s_spaceless.replace(old,
new) # Replace the spaceless code blocks in s with their spaced equivilents
else:
s_spaceless = s
if (index >= 1):
a = a + "\"" + s_spaceless
else:
a = a + s_spaceless
index += 1
return a
def comment_remover(given_string):
"""
Function to (currently) remove C++ style comments
given_string is a string of C/C++ code
"""
# This does not take into account if a C++ style comment happens within a string
# i.e. "Normal String // With a C++ comment embedded inside"
cpp_filtered_code = re.findall(
r"\/\/.*", given_string)
for entry in cpp_filtered_code:
given_string = given_string.replace(entry, "")
# This is a barebones start for C style block comments
# Current issue is it is only single line C style comments
# It also finds C style comments in strings
c_filtered_code = re.findall(
r"\/\*.*\*\/", given_string)
for entry in c_filtered_code:
given_string = given_string.replace(entry, "")
return given_string
def main():
with open(sys.argv[1]) as file_data:
file_string = file_data.read()
print("PASS\n")
file_string = comment_remover(file_string)
file_string = variable_renamer(file_string)
file_string = whitespace_remover(file_string)
# f = open("obfuscated_"+filename, "w+")
# f.write(file_string)
print(file_string)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment