mirror of
https://github.com/Karmaz95/Snake_Apple.git
synced 2026-03-30 14:00:16 +02:00
1971 lines
72 KiB
Python
1971 lines
72 KiB
Python
import os
|
|
import sys
|
|
import lief
|
|
import plistlib
|
|
from uuid import UUID
|
|
from io import StringIO
|
|
from CrimsonUroboros import *
|
|
|
|
# HOW TO TEST
|
|
'''
|
|
Each TestSnake class should have a method for each option in the Snake class being tested.
|
|
During setup phase for each TestSnake class, we prepare tests samples and assert that they exists.
|
|
Then, we run the testing methods and assert the outputs for each CrimsonUroboros option.
|
|
Finally, we purge the test samples.
|
|
We do it for each TestSnake class.
|
|
|
|
.vscode/settings.json:
|
|
{
|
|
"python.testing.pytestArgs": [
|
|
".",
|
|
"--disable-warnings",
|
|
"-vv",
|
|
"--rootdir=.",
|
|
],
|
|
"python.testing.unittestEnabled": false,
|
|
"python.testing.pytestEnabled": true,
|
|
"python.testing.cwd": "${workspaceFolder}/tests",
|
|
"python.REPL.enableREPLSmartSend": false,
|
|
"python.testing.autoTestDiscoverOnSaveEnabled": true
|
|
}
|
|
'''
|
|
|
|
class Compiler:
|
|
"""
|
|
A class for compiling C code using clang.
|
|
|
|
Usage:
|
|
compiler = Compiler()
|
|
compiler.compileIt("../I. Mach-O/custom/hello.c", "hello", ["-arch", "arm64"])
|
|
compiler.purgeCompiledFiles()
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.compiled_files = [] # Stores the paths to the compiled files.
|
|
|
|
def compile(self, cmd):
|
|
"""
|
|
Compile C code using the given compile command.
|
|
|
|
Args:
|
|
cmd (str): The compile command to run.
|
|
|
|
Returns:
|
|
int: Return code of the compilation process.
|
|
"""
|
|
result = os.system(cmd)
|
|
|
|
def buildClangCommand(self, source, output, flags=None):
|
|
"""
|
|
Build a clang compile command string based on the source file, output file, and optional flags.
|
|
|
|
Args:
|
|
source (str): Path to the source file.
|
|
output (str): Path to the output file.
|
|
flags (list, optional): List of additional flags. Defaults to None.
|
|
|
|
Returns:
|
|
str: Compiled clang command string.
|
|
"""
|
|
cmd_parts = ["clang"]
|
|
cmd_parts.append(source)
|
|
cmd_parts.extend(["-o", output])
|
|
if flags:
|
|
cmd_parts.extend(flags)
|
|
|
|
return ' '.join(cmd_parts)
|
|
|
|
def compileIt(self, source, output, flags=None):
|
|
"""
|
|
Compile the given source file using clang.
|
|
|
|
Args:
|
|
source (str): The path to the source file.
|
|
output (str): The path to the output file.
|
|
flags (list, optional): Additional compilation flags. Defaults to None.
|
|
|
|
Returns:
|
|
int: The exit code of the compilation process.
|
|
"""
|
|
cmd = self.buildClangCommand(source, output, flags)
|
|
self.compile(cmd)
|
|
self.compiled_files.append(output)
|
|
|
|
def purgeCompiledFiles(self):
|
|
"""
|
|
Remove all compiled files.
|
|
"""
|
|
for file in self.compiled_files:
|
|
os.remove(file)
|
|
|
|
class CodeSigner:
|
|
"""
|
|
# Example usage:
|
|
code_signer = CodeSigner()
|
|
entitlements = {
|
|
"com.apple.security.app-sandbox": "true",
|
|
"com.apple.security.cs.allow-jit": "true"
|
|
}
|
|
certificate = '-'
|
|
code_signer.signBinary("../../../snake_apple/simple/hello", certificate, entitlements)
|
|
"""
|
|
def __init__(self):
|
|
self.signed_files = []
|
|
|
|
def writeEntitlementsToFile(self, entitlements, filename):
|
|
"""Write entitlements dictionary to a plist file."""
|
|
with open(filename, 'wb') as f:
|
|
plistlib.dump(entitlements, f)
|
|
|
|
def signBinary(self, binary_path, certificate_name=None, entitlements=None):
|
|
"""
|
|
Sign binary using codesign.
|
|
(optional) Add entitlements to a binary using codesign. Example:
|
|
entitlements = {
|
|
"com.apple.security.app-sandbox": "true",
|
|
"com.apple.security.cs.allow-jit": "true"
|
|
}
|
|
|
|
Parameters:
|
|
- binary_path: Path to the binary file.
|
|
- certificate_name (optional): Name of the certificate to sign the binary.
|
|
- entitlements (optional): Dictionary of entitlements to add to the binary.
|
|
"""
|
|
# Write entitlements to a plist file
|
|
if entitlements:
|
|
entitlements_file = "unpredictable_entitlements_1029384756.plist"
|
|
self.writeEntitlementsToFile(entitlements, entitlements_file)
|
|
else:
|
|
entitlements_file = None
|
|
|
|
# Construct the codesign command
|
|
command = " ".join([
|
|
"codesign",
|
|
"-f",
|
|
"--entitlements",
|
|
entitlements_file,
|
|
"-s",
|
|
certificate_name,
|
|
binary_path
|
|
])
|
|
# Execute the codesign command
|
|
os.system(command)
|
|
|
|
def argumentWrapper(args_list):
|
|
"""
|
|
Wrapper function to parse command line arguments.
|
|
|
|
Args:
|
|
args_list (list): List of command line arguments.
|
|
|
|
Returns:
|
|
tuple: A tuple containing the parsed arguments and the absolute file path.
|
|
"""
|
|
sys.argv[1:] = args_list # Example: ['-p', 'hello', '--file_type']
|
|
arg_parser = ArgumentParser()
|
|
args = arg_parser.parseArgs()
|
|
file_path = os.path.abspath(args.path)
|
|
return args, file_path
|
|
|
|
def executeCodeBlock(func):
|
|
"""
|
|
Executes the provided function and captures its output.
|
|
|
|
Args:
|
|
func: The function to be executed.
|
|
|
|
Returns:
|
|
The captured output of the function.
|
|
"""
|
|
# Redirect stdout and stderr to capture the output
|
|
captured_output = StringIO()
|
|
sys.stdout = sys.stderr = captured_output
|
|
|
|
# Execute the provided function
|
|
func()
|
|
|
|
# Restore stdout and stderr
|
|
sys.stdout = sys.__stdout__
|
|
sys.stderr = sys.__stderr__
|
|
|
|
# Get the captured output
|
|
output = captured_output.getvalue().strip()
|
|
|
|
return output
|
|
|
|
def decompressKernelcache():
|
|
command = 'ipsw kernel dec $(ls /System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache) -o kernelcache'
|
|
return os.system(command)
|
|
|
|
def run_and_get_stdout(command):
|
|
command_with_stdout = f"{command} 2>&1"
|
|
return os.popen(command_with_stdout).read().strip()
|
|
|
|
class TestSnakeI():
|
|
'''Testing I. MACH-O'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_1", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_1") # Check if the file exists after compilation
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_1") # Check if the file is removed after purging
|
|
|
|
def test_MachOProcessor(self):
|
|
'''Test the initialization of MachOProcessor.
|
|
|
|
This test checks if the snake_instance is created by using the MachOProcessor class.
|
|
It sets up arguments for testing, defines the code block to be executed, executes the code block,
|
|
and asserts that there are no errors by checking the output.
|
|
'''
|
|
|
|
# Set up arguments for testing
|
|
args_list = ['-p', 'hello_1']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
# Define the code block to be executed
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
# Execute the code block using the wrapper function and capture the output
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
|
|
# Assert there are no errors by checking the output - when using only --path there is no output if file exists and is valid arm64 Mach-O file
|
|
expected_output = ''
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_file_type(self):
|
|
'''Test the --file_type flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--file_type']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'File type: EXECUTE'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_header_flags(self):
|
|
'''Test the --header_flags flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--header_flags']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Header flags: TWOLEVEL NOUNDEFS DYLDLINK PIE'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_endian(self):
|
|
'''Test the --endian flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--endian']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Endianess: little'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_header(self):
|
|
'''Test the --header flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--header']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'ARM64'
|
|
expected_output_2 = 'EXECUTE'
|
|
expected_output_3 = 'NOUNDEFS DYLDLINK TWOLEVEL PIE'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
|
|
def test_load_commands(self):
|
|
'''Test the --load_commands flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--load_commands']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Load Commands: SEGMENT_64 SEGMENT_64 SEGMENT_64 SEGMENT_64 DYLD_CHAINED_FIXUPS DYLD_EXPORTS_TRIE SYMTAB DYSYMTAB LOAD_DYLINKER UUID BUILD_VERSION SOURCE_VERSION MAIN LOAD_DYLIB FUNCTION_STARTS DATA_IN_CODE CODE_SIGNATURE'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_has_cmd(self):
|
|
'''Test the --has_cmd flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--has_cmd', 'LC_MAIN']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'hello_1 has LC_MAIN'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_segments(self):
|
|
'''Test the --segments flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--segments']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = '__PAGEZERO ---/--- VM: 0x0000000000000000-0x0000000100000000 FILE: 0x0-0x0'
|
|
expected_output_2 = '__TEXT r-x/r-x VM: 0x0000000100000000-0x0000000100004000 FILE: 0x4000-0x8000'
|
|
expected_output_3 = '__DATA_CONST rw-/rw- VM: 0x0000000100004000-0x0000000100008000 FILE: 0x4000-0x8000 (SG_READ_ONLY)'
|
|
expected_output_4 = '__LINKEDIT r--/r-- VM: 0x0000000100008000-0x000000010000c000 FILE: 0x298-0x530'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
|
|
def test_has_segment(self):
|
|
'''Test the --has_segment flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--has_segment', '__TEXT']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'hello_1 has __TEXT'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_sections(self):
|
|
'''Test the --sections flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--sections']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = '__TEXT __text REGULAR 0x100003f58-0x100007eb0 0x3f58-0x3f8c (SOME_INSTRUCTIONS PURE_INSTRUCTIONS)'
|
|
expected_output_2 = '__TEXT __stubs SYMBOL_STUBS 0x100003f8c-0x100007f18 0x3f8c-0x3f98 (SOME_INSTRUCTIONS PURE_INSTRUCTIONS)'
|
|
expected_output_3 = '__TEXT __cstring CSTRING_LITERALS 0x100003f98-0x100007f30 0x3f98-0x3fa7 ()'
|
|
expected_output_4 = '__TEXT __unwind_info REGULAR 0x100003fa8-0x100007f50 0x3fa8-0x4000 ()'
|
|
expected_output_5 = '__DATA_CONST __got NON_LAZY_SYMBOL_POINTERS 0x100004000-0x100008000 0x4000-0x4008 ()'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
assert expected_output_5 in uroboros_output
|
|
|
|
def test_has_section(self):
|
|
'''Test the --has_section flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--has_section', '__TEXT,__text']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'hello_1 has __TEXT,__text'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_symbols(self):
|
|
'''Test the --symbols flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--symbols']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = '__mh_execute_header'
|
|
expected_output_2 = '_main'
|
|
expected_output_3 = '_printf'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
|
|
def test_imported_symbols(self):
|
|
'''Test the --imported_symbols flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--imported_symbols']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '_printf : /usr/lib/libSystem.B.dylib'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_chained_fixups(self):
|
|
'''Test the --chained_fixups flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--chained_fixups']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '_DATA_CONST0x100004000: _printf (libSystem.B.dylib) addend: 0x0'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_exports_trie(self):
|
|
'''Test the --exports_trie flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--exports_trie']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '_main{addr: 0x3f58, flags: 0}'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_uuid(self):
|
|
'''Test the --uuid flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--uuid']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
try:
|
|
is_valid = UUID(uroboros_output.split('UUID: ')[-1], version=4) # this returns the UUID string if it is valid and stores it in is_valid
|
|
except ValueError:
|
|
is_valid = False
|
|
|
|
assert is_valid
|
|
|
|
def test_main(self):
|
|
'''Test the --main flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--main']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'Entry point: 0x3f58'
|
|
expected_output_2 = 'Stack size: 0x0'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
|
|
def test_encryption_info(self):
|
|
'''Test the --encryption_info flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--encryption_info']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'hello_1 binary does not have encryption info.'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_strings_section(self):
|
|
'''Test the --strings_section flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--strings_section']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'Hello, World!'
|
|
expected_output_2 = '__cstring'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
|
|
def test_all_strings(self):
|
|
'''Test the --all_strings flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--all_strings']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'Hello, World!'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
|
|
def test_save_strings(self):
|
|
'''Test the --save_strings flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--save_strings', 'testing_strings.txt']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('testing_strings.txt')
|
|
|
|
with open('testing_strings.txt', 'r') as file:
|
|
file_output = file.read()
|
|
expected_output = 'Hello, World'
|
|
|
|
assert expected_output in file_output
|
|
os.remove('testing_strings.txt')
|
|
|
|
def test_info(self):
|
|
'''Test the --info flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--info']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
|
|
expected_output_1 = 'Entry point: 0x3f58'
|
|
expected_output_2 = '__mh_execute_header'
|
|
expected_output_3 = '__PAGEZERO ---/--- VM: 0x0000000000000000-0x0000000100000000 FILE: 0x0-0x0'
|
|
expected_output_4 = '__DATA_CONST0x100004000: _printf (libSystem.B.dylib) addend: 0x0'
|
|
expected_output_5 = 'Command : SEGMENT_64'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
assert expected_output_5 in uroboros_output
|
|
|
|
def test_dump_data(self):
|
|
'''Test the --dump_data flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--dump_data', '0x00,0x08,hello_1_header_dump.bin']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('hello_1_header_dump.bin')
|
|
|
|
with open('hello_1_header_dump.bin', 'rb') as file:
|
|
file_output = file.read()
|
|
expected_output = b'\xcf\xfa\xed\xfe\x0c\x00\x00\x01'
|
|
|
|
assert expected_output in file_output
|
|
os.remove('hello_1_header_dump.bin')
|
|
|
|
def test_calc_offset(self):
|
|
'''Test the --calc_offset flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--calc_offset', "0x0000000100003f20"]
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = "0x0000000100003f20 : 0x3f20"
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_constructors(self):
|
|
'''Test the --constructors flag of SnakeI.'''
|
|
args_list = ['-p', 'hello_1', '--constructors']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = ""
|
|
# todo - this is only negative test, I should also check the file with valid constructors.
|
|
assert expected_output in uroboros_output
|
|
|
|
class TestSnakeII():
|
|
'''Testing II. CODE SIGNING'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../III.\ Checksec/custom/hello.c", "hello_2", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_2") # Check if the file exists after compilation
|
|
|
|
# Prepare signed binary for test_remove_sig
|
|
cls.compiler.compileIt("../III.\ Checksec/custom/hello.c", "hello_2_test_remove_sig", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_2_test_remove_sig") # Check if the file exists after compilation
|
|
|
|
# Prepare unsigned binary for test_sign_binary
|
|
binary2 = lief.parse('hello_2')
|
|
binary2.remove_signature()
|
|
binary2.write('hello_2_unsigned_binary')
|
|
cls.compiler.compiled_files.append("hello_2_unsigned_binary")
|
|
assert os.path.exists("hello_2_unsigned_binary")
|
|
|
|
# Code sign and set entitlements
|
|
cls.code_signer = CodeSigner()
|
|
cls.entitlements = {
|
|
"com.apple.security.app-sandbox": "true",
|
|
"com.apple.security.cs.allow-jit": "true"
|
|
}
|
|
cls.certificate = '-'
|
|
cls.code_signer.signBinary("hello_2", cls.certificate, cls.entitlements)
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_2")
|
|
assert not os.path.exists("hello_2_unsigned_binary")
|
|
|
|
# Purge entitlements file
|
|
os.remove('unpredictable_entitlements_1029384756.plist')
|
|
assert not os.path.exists('unpredictable_entitlements_1029384756.plist')
|
|
|
|
def test_verify_signature(self):
|
|
'''Test the --verify_signature flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--verify_signature']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Valid Code Signature (matches the content)'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
args_list = ['-p', 'hello_2_unsigned_binary', '--verify_signature']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Invalid Code Signature (does not match the content)'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_cd_info(self):
|
|
'''Test the --cd_info flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--cd_info']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'flags=0x2(adhoc)'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_cd_requirements(self):
|
|
'''Test the --cd_requirements flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--cd_requirements']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'designated'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_entitlements(self):
|
|
'''Test the --entitlements flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--entitlements']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'com.apple.security.app-sandbox'
|
|
expected_output_2 = 'com.apple.security.cs.allow-jit'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
|
|
def test_extract_cms(self):
|
|
'''Test the --extract_cms flag of SnakeII.''' # TODO - this test can be improved, but we need to create a security-identity for tests
|
|
args_list = ['-p', 'hello_2', '--extract_cms', 'hello_2_unpredictable_name.cms']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('hello_2_unpredictable_name.cms')
|
|
os.remove('hello_2_unpredictable_name.cms')
|
|
|
|
def test_extract_certificates(self):
|
|
'''Test the --extract_certificates flag of SnakeII.'''
|
|
args_list = ['-p', '/usr/lib/dyld', '--extract_certificates', 'hello_2_unpredictable_name']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
for i in range(3):
|
|
assert os.path.exists(f'hello_2_unpredictable_name_{i}')
|
|
os.remove(f'hello_2_unpredictable_name_{i}')
|
|
|
|
def test_remove_sig(self):
|
|
'''Test the --remove_code_signature flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--remove_sig', 'hello_2_test_remove_sig']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('hello_2_test_remove_sig')
|
|
binary1 = lief.parse('hello_2_test_remove_sig')
|
|
assert not binary1.has_code_signature
|
|
|
|
"""# sign_binary is problematic, because of race condition on codesign tool, but we do not need to test that since we are testing verify_signature. The test can be reimplemented when I rewrite codesigning utility in python.
|
|
|
|
def test_sign_binary(self):
|
|
'''Test the --sign_binary flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2_unsigned_binary', '--sign_binary']
|
|
args, file_path = argumentWrapper(args_list)
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
# Sign it
|
|
executeCodeBlock(code_block)
|
|
|
|
binary3 = lief.parse('hello_2_unsigned_binary')
|
|
assert binary3.has_code_signature"""
|
|
|
|
def test_cs_offset(self):
|
|
'''Test the --cs_offset flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--cs_offset']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Code Signature offset: 0x8100'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_cs_flags(self):
|
|
'''Test the --cs_flags flag of SnakeII.'''
|
|
args_list = ['-p', 'hello_2', '--cs_flags']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
code_signing_processor = CodeSigningProcessor()
|
|
code_signing_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'CS_FLAGS: 0x2'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
class TestSnakeIII():
|
|
'''Testing III. CHECKSEC'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_3", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_3")
|
|
|
|
# ARC binary
|
|
cls.compiler.compileIt("../III.\ Checksec/custom/example.m", "hello_3_arc", ["-arch", "arm64", "-fobjc-arc", "-framework", "Foundation"])
|
|
assert os.path.exists("hello_3_arc")
|
|
|
|
# Stripped binary
|
|
os.system('strip hello_3_arc -o hello_3_stripped')
|
|
assert os.path.exists("hello_3_stripped")
|
|
cls.compiler.compiled_files.append("hello_3_stripped")
|
|
|
|
# Stack guard binary
|
|
cls.compiler.compileIt("../III.\ Checksec/custom/example.m", "hello_3_sc", ["-arch", "arm64", "-fstack-protector-all", "-framework", "Foundation"])
|
|
assert os.path.exists("hello_3_sc")
|
|
|
|
# Code sign and set entitlements
|
|
cls.code_signer = CodeSigner()
|
|
cls.entitlements = {
|
|
"com.apple.security.app-sandbox": "true",
|
|
"com.apple.security.cs.allow-jit": "true"
|
|
}
|
|
cls.certificate = '-'
|
|
cls.code_signer.signBinary("hello_3", cls.certificate, cls.entitlements)
|
|
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_3") # Check if the file is removed after purging
|
|
|
|
# Purge entitlements file
|
|
os.remove('unpredictable_entitlements_1029384756.plist')
|
|
assert not os.path.exists('unpredictable_entitlements_1029384756.plist')
|
|
|
|
def test_has_pie(self):
|
|
'''Test the --has_pie flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_pie']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'PIE: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_arc(self):
|
|
'''Test the --has_arc flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_arc']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'ARC: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
args_list = ['-p', 'hello_3_arc', '--has_arc']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'ARC: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_stripped(self):
|
|
'''Test the --is_stripped flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_stripped']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'STRIPPED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
args_list = ['-p', 'hello_3_stripped', '--is_stripped']
|
|
args, file_path = argumentWrapper(args_list)
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'STRIPPED: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_canary(self):
|
|
'''Test the --has_canary flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_canary']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'CANARY: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
args_list = ['-p', 'hello_3_sc', '--has_canary']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'CANARY: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_nx_stack(self):
|
|
'''Test the --has_nx_stack flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_nx_stack']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'NX STACK: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_nx_heap(self):
|
|
'''Test the --has_nx_heap flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_nx_heap']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'NX HEAP: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_xn(self):
|
|
'''Test the --has_xn flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_xn']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'eXecute Never: False'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_is_notarized(self):
|
|
'''Test the --is_notarized flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_notarized']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'NOTARIZED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_encrypted(self):
|
|
'''Test the --is_encrypted flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_encrypted']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'ENCRYPTED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_restricted(self):
|
|
'''Test the --is_restricted flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_restricted']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'RESTRICTED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_hr(self):
|
|
'''Test the --is_hr flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_hr']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'HARDENED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_as(self):
|
|
'''Test the --is_as flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_as']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'APP SANDBOX: True'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_is_fort(self):
|
|
'''Test the --is_fort flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--is_fort']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'FORTIFIED: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_rpath(self):
|
|
'''Test the --has_rpath flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_rpath']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'RPATH: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_has_lv(self):
|
|
'''Test the --has_lv flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--has_lv']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'LIBRARY VALIDATION: False'
|
|
|
|
assert uroboros_output == expected_output
|
|
|
|
def test_checksec(self):
|
|
'''Test the --checksec flag of SnakeIII.'''
|
|
args_list = ['-p', 'hello_3', '--checksec']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
checksec_processor = ChecksecProcessor()
|
|
checksec_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
|
|
expected_output_1 = 'PIE: True'
|
|
expected_output_2 = 'ARC: False'
|
|
expected_output_3 = 'STRIPPED: False'
|
|
expected_output_4 = 'CANARY: False'
|
|
expected_output_5 = 'NX STACK: True'
|
|
expected_output_6 = 'NX HEAP: False'
|
|
expected_output_7 = 'XN'
|
|
expected_output_8 = 'NOTARIZED: False'
|
|
expected_output_9 = 'ENCRYPTED: False'
|
|
expected_output_10 = 'RESTRICTED: False'
|
|
expected_output_11 = 'HARDENED: False'
|
|
expected_output_12 = 'APP SANDBOX: True'
|
|
expected_output_13 = 'FORTIFIED: False'
|
|
expected_output_14 = 'RPATH: False'
|
|
expected_output_15 = 'LV: False'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
assert expected_output_5 in uroboros_output
|
|
assert expected_output_6 in uroboros_output
|
|
assert expected_output_7 in uroboros_output
|
|
assert expected_output_8 in uroboros_output
|
|
assert expected_output_9 in uroboros_output
|
|
assert expected_output_10 in uroboros_output
|
|
assert expected_output_11 in uroboros_output
|
|
assert expected_output_12 in uroboros_output
|
|
assert expected_output_13 in uroboros_output
|
|
assert expected_output_14 in uroboros_output
|
|
assert expected_output_15 in uroboros_output
|
|
|
|
class TestSnakeIV():
|
|
'''Testing IV. DYLIBS'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_4", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_4")
|
|
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/mylib.c", "mylib", ["-arch", "arm64", "-dynamiclib"])
|
|
os.system('install_name_tool -add_rpath "lc_rpath_test" "mylib"')
|
|
assert os.path.exists("mylib")
|
|
|
|
# Code sign and set entitlements
|
|
cls.code_signer = CodeSigner()
|
|
cls.entitlements = {
|
|
"com.apple.security.app-sandbox": "true",
|
|
"com.apple.security.cs.allow-jit": "true"
|
|
}
|
|
cls.certificate = '-'
|
|
cls.code_signer.signBinary("hello_4", cls.certificate, cls.entitlements)
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_4") # Check if the file is removed after purging
|
|
|
|
# Purge entitlements file
|
|
os.remove('unpredictable_entitlements_1029384756.plist')
|
|
assert not os.path.exists('unpredictable_entitlements_1029384756.plist')
|
|
|
|
def test_dylibs(self):
|
|
'''Test the --dylibs flag of SnakeIV.'''
|
|
args_list = ['-p', 'hello_4', '--dylibs']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'libSystem.B.dylib'
|
|
|
|
assert expected_output in uroboros_output
|
|
assert len(uroboros_output.splitlines()) == 2
|
|
|
|
def test_rpaths(self):
|
|
'''Test the --rpaths flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--rpaths']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'lc_rpath_test'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_rpaths_u(self):
|
|
'''Test the --rpaths_u flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--rpaths_u']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'lc_rpath_test'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_dylibs_paths(self):
|
|
'''Test the --dylibs_paths flag of SnakeIV.'''
|
|
args_list = ['-p', 'hello_4', '--dylibs_paths']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '/usr/lib/libSystem.B.dylib'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_broken_relative_paths(self):
|
|
'''Test the --broken_relative_paths flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--broken_relative_paths']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = ''
|
|
|
|
assert expected_output == uroboros_output
|
|
|
|
def test_dylibtree(self):
|
|
'''Test the --dylibtree flag of SnakeIV.'''
|
|
args_list = ['-p', 'hello_4', '--dylibtree']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'hello_4'
|
|
expected_output_2 = '/usr/lib/libSystem.B.dylib'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
os.system('rm -rf extracted_dyld_share_cache')
|
|
assert not os.path.exists('extracted_dyld_share_cache')
|
|
|
|
def test_dylib_id(self):
|
|
'''Test the --dylib_id flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--dylib_id']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'mylib'
|
|
|
|
assert expected_output == uroboros_output
|
|
|
|
def test_reexport_paths(self):
|
|
'''Test the --reexport_paths flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--reexport_paths']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = ''
|
|
|
|
assert expected_output == uroboros_output
|
|
|
|
def test_hijack_sec(self):
|
|
'''Test the --hijack_sec flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--hijack_sec']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
exepected_output = 'DYLIB HIJACKIG PROTECTION: False'
|
|
|
|
assert exepected_output == uroboros_output
|
|
|
|
def test_dylib_hijacking(self):
|
|
'''Test the --dylib_hijacking flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--dylib_hijacking']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'ROOT BINARY NOT PROTECTED'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_dylib_hijacking_a(self):
|
|
'''Test the --dylib_hijacking_a flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--dylib_hijacking_a']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = ''
|
|
|
|
assert expected_output == uroboros_output
|
|
|
|
def test_prepare_dylib(self):
|
|
'''Test the --prepare_dylib flag of SnakeIV.'''
|
|
args_list = ['-p', 'mylib', '--prepare_dylib']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dylibs_processor = DylibsProcessor()
|
|
dylibs_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('m.dylib')
|
|
assert os.path.exists('m.c')
|
|
os.remove('m.dylib')
|
|
os.remove('m.c')
|
|
|
|
class TestSnakeV():
|
|
'''Testing V. DYLD'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_5", ["-arch", "arm64", "-Wl,-dyld_env,DYLD_LIBRARY_PATH='@executable_path/dylibs'"])
|
|
assert os.path.exists("hello_5")
|
|
|
|
cls.compiler.compileIt("../V.\ Dyld/custom/interpose.c", "libinterpose.dylib", ["-dynamiclib", "-arch", "arm64"])
|
|
assert os.path.exists("libinterpose.dylib")
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_5")
|
|
assert not os.path.exists("libinterpose.dylib")
|
|
|
|
def test_is_built_for_sim(self):
|
|
'''Test the --is_built_for_sim flag of SnakeV.'''
|
|
args_list = ['-p', 'hello_5', '--is_built_for_sim']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'PLATFORM_MACOS'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_get_dyld_env(self):
|
|
'''Test the --get_dyld_env flag of SnakeV.'''
|
|
args_list = ['-p', '/usr/lib/dyld', '--get_dyld_env']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'DYLD_SHARED_CACHE_DIR'
|
|
expected_output_2 = 'DYLD_IN_CACHE'
|
|
expected_output_3 = 'DYLD_PRINT_SEGMENTS'
|
|
expected_output_4 = 'DYLD_AMFI_FAKE'
|
|
expected_output_5 = 'DYLD_FALLBACK_FRAMEWORK_PATH'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
assert expected_output_5 in uroboros_output
|
|
|
|
def test_compiled_with_dyld_env(self):
|
|
'''Test the --compiled_with_dyld_env flag of SnakeV.'''
|
|
args_list = ['-p', 'hello_5', '--compiled_with_dyld_env']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'DYLD_LIBRARY_PATH'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_has_interposing(self):
|
|
'''Test the --has_interposing flag of SnakeV.'''
|
|
args_list = ['-p', 'hello_5', '--has_interposing']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'INTERPOSING: False'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
args_list = ['-p', 'libinterpose.dylib', '--has_interposing']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'INTERPOSING: True'
|
|
|
|
def test_interposing_symbols(self):
|
|
'''Test the --interposing_symbols flag of SnakeV.'''
|
|
args_list = ['-p', 'libinterpose.dylib', '--interposing_symbols']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
dyld_processor = DyldProcessor()
|
|
dyld_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '_my_printf'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
class TestSnakeVI():
|
|
'''Testing VI. AMFI'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_6", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_6")
|
|
|
|
# Create copies for some tests
|
|
os.system("cp hello_6 hello_6_s")
|
|
os.system("chmod +s hello_6_s")
|
|
assert os.path.exists("hello_6_s")
|
|
|
|
os.system("cp hello_6 hello_6_g")
|
|
os.system("chmod g+s hello_6_g")
|
|
assert os.path.exists("hello_6_g")
|
|
|
|
os.system("cp hello_6 hello_6_sticky")
|
|
os.system("chmod +t hello_6_sticky")
|
|
assert os.path.exists("hello_6_sticky")
|
|
|
|
# Decompress KernelCache
|
|
result = decompressKernelcache()
|
|
assert result == 0
|
|
assert os.path.exists("kernelcache")
|
|
cls.kernelcache_path = run_and_get_stdout('ls kernelcache/System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.decompressed')
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_6")
|
|
|
|
# Remove samples
|
|
os.system("rm hello_6_s")
|
|
assert not os.path.exists("hello_6_s")
|
|
os.system("rm hello_6_g")
|
|
assert not os.path.exists("hello_6_g")
|
|
os.system("rm hello_6_sticky")
|
|
assert not os.path.exists("hello_6_sticky")
|
|
|
|
# Purge kernelcache directory
|
|
os.system("rm -rf kernelcache")
|
|
assert not os.path.exists("kernelcache")
|
|
|
|
def test_dump_prelink_info(self):
|
|
'''Test the --dump_prelink_info flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--dump_prelink_info']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('PRELINK_info.txt')
|
|
os.remove('PRELINK_info.txt')
|
|
|
|
def test_dump_prelink_text(self):
|
|
'''Test the --dump_prelink_text flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--dump_prelink_text']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('PRELINK_text.txt')
|
|
os.remove('PRELINK_text.txt')
|
|
|
|
def test_dump_prelink_kext(self):
|
|
'''Test the --dump_prelink_kext flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--dump_prelink_kext', 'amfi']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
executeCodeBlock(code_block)
|
|
|
|
assert os.path.exists('prelinked_amfi.bin')
|
|
os.remove('prelinked_amfi.bin')
|
|
|
|
def test_kext_prelinkinfo(self):
|
|
'''Test the --kext_prelinkinfo flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--kext_prelinkinfo', 'amfi']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '_PrelinkBundlePath: /System/Library/Extensions/AppleMobileFileIntegrity.kext'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_kmod_info(self):
|
|
'''Test the --kmod_info flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--kmod_info', 'amfi']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'name : com.apple.driver.AppleMobileFileIntegrity'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_kext_entry(self):
|
|
'''Test the --kext_entry flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--kext_entry', 'amfi']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'amfi entrypoint:'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_kext_exit(self):
|
|
'''Test the --kext_exit flag of SnakeVI.'''
|
|
args_list = ['-p', self.kernelcache_path, '--kext_exit', 'amfi']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'amfi exitpoint:'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_mig(self):
|
|
'''Test the --mig flag of SnakeVI.'''
|
|
args_list = ['-p', '/usr/libexec/amfid', '--mig']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output_1 = 'MIG_subsystem_1000:'
|
|
expected_output_2 = 'MIG_msg_1000'
|
|
expected_output_3 = 'MIG_msg_1001'
|
|
expected_output_4 = 'MIG_msg_1002'
|
|
expected_output_5 = 'MIG_msg_1003'
|
|
expected_output_6 = 'MIG_msg_1004'
|
|
expected_output_7 = 'MIG_msg_1005'
|
|
expected_output_8 = 'MIG_msg_1006'
|
|
expected_output_9 = 'MIG_msg_1007'
|
|
|
|
assert expected_output_1 in uroboros_output
|
|
assert expected_output_2 in uroboros_output
|
|
assert expected_output_3 in uroboros_output
|
|
assert expected_output_4 in uroboros_output
|
|
assert expected_output_5 in uroboros_output
|
|
assert expected_output_6 in uroboros_output
|
|
assert expected_output_7 in uroboros_output
|
|
assert expected_output_8 in uroboros_output
|
|
assert expected_output_9 in uroboros_output
|
|
|
|
def test_has_suid(self):
|
|
'''Test the --has_suid flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6_s', '--has_suid']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'SUID: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_has_sgid(self):
|
|
'''Test the --has_sgid flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6_g', '--has_sgid']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'SGID: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_has_sticky(self):
|
|
'''Test the --has_sticky flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6_sticky', '--has_sticky']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'STICKY: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_injectable_dyld(self):
|
|
'''Test the --injectable_dyld flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6', '--injectable_dyld']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'Injectable DYLD_INSERT_LIBRARIES: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_test_insert_dylib(self):
|
|
'''Test the --test_insert_dylib flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6', '--test_insert_dylib']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'DYLD_INSERT_LIBRARIES is allowed: True'
|
|
assert expected_output in uroboros_output # todo - I should also test for the false case (need to modify pytests to be thread aware).
|
|
|
|
def test_test_prune_dyld(self):
|
|
'''Test the --test_prune_dyld flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6', '--test_prune_dyld']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'DEV Pruned: False'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_test_dyld_print_to_file(self):
|
|
'''Test the --test_dyld_print_to_file flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6', '--test_dyld_print_to_file']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'DYLD_PRINT_TO_FILE allowed: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_test_dyld_SLC(self):
|
|
'''Test the --test_dyld_SLC flag of SnakeVI.'''
|
|
args_list = ['-p', 'hello_6', '--test_dyld_SLC']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
amfi_processor = AMFIProcessor()
|
|
amfi_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'DYLD_SHARED_CACHE_DIR allowed: True'
|
|
assert expected_output in uroboros_output
|
|
|
|
class TestSnakeVII():
|
|
'''Testing VII. Antivirus'''
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# Set up the compilation process
|
|
cls.compiler = Compiler()
|
|
cls.compiler.compileIt("../I.\ Mach-O/custom/hello.c", "hello_7", ["-arch", "arm64"])
|
|
assert os.path.exists("hello_7")
|
|
os.system("xattr -w com.apple.quarantine '0083;00000000;Safari' hello_7")
|
|
|
|
# Decompress KernelCache
|
|
#result = decompressKernelcache()
|
|
#assert result == 0
|
|
#assert os.path.exists("kernelcache")
|
|
#cls.kernelcache_path = run_and_get_stdout('ls kernelcache/System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.decompressed')
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# Purge the compiled files
|
|
cls.compiler.purgeCompiledFiles()
|
|
assert not os.path.exists("hello_6")
|
|
|
|
# Purge kernelcache directory
|
|
#os.system("rm -rf kernelcache")
|
|
#assert not os.path.exists("kernelcache")
|
|
|
|
def test_xattr(self):
|
|
'''Test the --xattr flag of SnakeVII.'''
|
|
args_list = ['-p', "hello_7", '--xattr']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
antivirus_processor = AntivirusProcessor()
|
|
antivirus_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'com.apple.quarantine'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_xattr_value(self):
|
|
'''Test the --xattr_value flag of SnakeVII.'''
|
|
args_list = ['-p', "hello_7", '--xattr_value', 'com.apple.quarantine']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
antivirus_processor = AntivirusProcessor()
|
|
antivirus_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '30 30 38 33 3b 30 30 30 30 30 30 30 30 3b 53 61'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_xattr_all(self):
|
|
'''Test the --xattr_all flag of SnakeVII.'''
|
|
args_list = ['-p', "hello_7", '--xattr_all']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
antivirus_processor = AntivirusProcessor()
|
|
antivirus_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = '30 30 38 33 3b 30 30 30 30 30 30 30 30 3b 53 61'
|
|
|
|
assert expected_output in uroboros_output
|
|
|
|
def test_has_quarantine(self):
|
|
'''Test the --has_quarantine flag of SnakeVII.'''
|
|
args_list = ['-p', "hello_7", '--has_quarantine']
|
|
args, file_path = argumentWrapper(args_list)
|
|
|
|
def code_block():
|
|
macho_processor = MachOProcessor(file_path)
|
|
macho_processor.process(args)
|
|
antivirus_processor = AntivirusProcessor()
|
|
antivirus_processor.process(args)
|
|
|
|
uroboros_output = executeCodeBlock(code_block)
|
|
expected_output = 'QUARANTINE: True'
|
|
|
|
assert expected_output in uroboros_output
|
|
|