-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathmutate.py
144 lines (116 loc) · 3.69 KB
/
mutate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python3
import shutil
import random
import argparse
x64_no_instruction_sets = [
[
'nop'
],
[
'xchg eax, eax'
]
]
x86_no_instruction_sets = [
[
'nop'
],
[
'xchg eax, eax'
]
]
def tab(x):
result = []
for y in x:
result.append(f" {y}")
return result
x64_no_instruction_sets = list(map(tab, x64_no_instruction_sets))
x86_no_instruction_sets = list(map(tab, x86_no_instruction_sets))
assembly_instructions = [
'push',
'pop',
'ret',
'mov',
'xchg',
'xor',
'call',
'loop',
'test',
'jmp',
'jz',
'jnz',
'add',
'dec',
'shl',
'shr',
'cmp',
'loopnz',
]
def starts_with_pneumonic(line):
for i in assembly_instructions:
if line.strip().lower().startswith(f"{i} "):
return True
return False
def morph(shellcode_file, morph_percentage, verbose, x86_mode):
'''
Assumes max one empty line between each function
'''
with open(shellcode_file, 'r') as f:
shellcode_source = f.read().split('\n')
num_of_lines = len(shellcode_source)
print(f"Initial length: {num_of_lines}")
x = 0
started = False
while x < (num_of_lines - 2):
x += 1
if verbose:
print(f"Processing:\n{shellcode_source[x]}")
if shellcode_source[x].strip().startswith(";") or not starts_with_pneumonic(shellcode_source[x]):
if verbose:
print("Not instruction line")
continue
if verbose:
print(f"Rolling the dice...")
if random.random() > (1 - (morph_percentage / 100)):
if verbose:
print(f"Score! Picking instructions")
if x86_mode:
instructions = random.choice(x86_no_instruction_sets)
else:
instructions = random.choice(x64_no_instruction_sets)
for instruction in instructions:
if verbose:
print(f"Inserting instruction at offset {x}")
print(f"{instruction}")
shellcode_source.insert(x, instruction)
x += 1
num_of_lines += 1
elif verbose:
print(f"Not this time...")
with open(shellcode_file, 'w') as f:
print(f"Final length: {len(shellcode_source)} lines")
f.write('\n'.join(shellcode_source))
def create_arg_parser():
parser = argparse.ArgumentParser(description='Insert random no-instructions at random locations into assembly shellcode')
parser.add_argument("-t", "--shellcode-template", help="the template shellcode file to use", default='Source/ASM/shellcode-template.asm')
parser.add_argument("-s", "--shellcode-file", help="where to write the morphed file to", default='Source/ASM/shellcode.asm')
parser.add_argument("-m", "--morph-percentage", help="percentage increase of the number of instructions with no-instructions", type=int, default=15)
parser.add_argument("-v", "--verbose", help="enable verbose mode", action="store_true")
parser.add_argument("-x86", "--x86", help="x86 mode", action="store_true")
return parser
def main():
print("##################")
print("It's Morphin' Time")
print("##################")
parser = create_arg_parser()
args = parser.parse_args()
shellcode_file = args.shellcode_file
shellcode_template = args.shellcode_template
print("Copying template...")
shutil.copyfile(shellcode_template, shellcode_file)
print("Morphing shellcode...")
if args.x86:
print("x86 mode")
morph(shellcode_file, args.morph_percentage, args.verbose, args.x86)
print("Done")
if __name__ == '__main__':
main()