-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFormCrypt.py
185 lines (158 loc) · 7.1 KB
/
FormCrypt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import random
import argparse
import os
import shutil
RED = "\033[31m"
WHITE = "\033[37m"
BOLD = "\033[1m"
CYAN = "\033[36m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
RESET = "\033[0m"
MAGENTA = " \033[35m"
BANNER = f"""{MAGENTA}
____ ____ ____ _ ____ ____ _ ____ ____
| _\\| || . \\|\\/\\ | __\\| . \\||_/\\| . \\|_ _\\
| _\\ | . || <_| \\| \\__| <_| __/| __/ ||
|/ |___/|/\\_/|/v\\/|___/|/\\_/|/ |/ |/
|‾|
/‾‾‾\\ _________/‾‾‾\\
| O | | __ ___ O |
\\___/ |_| |_| \\___/
{RED}A string obfuscation tool for C/C++ malware.{RESET}
{WHITE}Author: {YELLOW}wizardy0ga
{WHITE}Version: {YELLOW}1.0.0
{WHITE}Github: {YELLOW}https://github.com/wizardy0ga/formcrypt
{RESET}
"""
class Rc4EncryptedBuffer(object):
"""
Defines an encrypted buffer (ENCRYPTED_BUFFER) structure for the C code.
"""
def __init__(self, data: str or bytes, key_size, name):
if not isinstance(data, str) and not isinstance(data, bytes):
raise Exception(f"Data parameter expects a byte_array_string or bytes object. Got type {type(data)}.")
self.key = self._new_key(key_size)
self.name = name
self.plaintext = data.encode() if isinstance(data, str) else data
self.ciphertext = self._rc4(self.key, self.plaintext)
log_message(f"Created encrypted buffer for: {highlight(self.plaintext.decode(), CYAN)}")
def _new_key(self, size: int) -> bytes:
return bytes(random.randint(0, 255) for _ in range(size))
def _rc4(self, key, data):
"""
RC4 encryption/decryption function.
:param key: The key used for encryption/decryption (bytes)
:param data: The plaintext/ciphertext data (bytes)
:return: The processed data (bytes)
"""
# Key Scheduling Algorithm (KSA)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# Pseudo-Random Generation Algorithm (PRGA)
i = j = 0
output = bytearray()
for byte in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
enc_byte = byte ^ K
if (enc_byte == 0x00):
return self._rc4(key, data)
output.append(enc_byte)
return bytes(output)
def to_c_byte_array_string(self, data) -> str:
byte_array_string = ""
c = 0
for i in data:
format_string = "0x%02X," if c != (len(data) - 1) else "0x%02X"
byte_array_string += format_string % ( i & 0xFF)
c += 1
return byte_array_string
def get_macro_definition(self):
macro_definition = f"#define {self.name.upper()}_TEXT {self.to_c_byte_array_string(self.ciphertext)}\n"
macro_definition += f"#define {self.name.upper()}_KEY {self.to_c_byte_array_string(self.key)}\n"
return macro_definition
def get_direct_definition(self):
direct_definition = f"NEW_BUFFER({self.name}, {self.name.upper()}_TEXT, {self.name.upper()}_KEY);\n"
return direct_definition
def parse_key_value_pairs(args):
key_value_pairs = {}
for arg in args:
if '=' in arg:
key, value = arg.split('=', 1)
key_value_pairs[key] = value
else:
raise ValueError(f"Invalid format for argument: {arg}. Use key=value.")
return key_value_pairs
def highlight(text: str, _format: str) -> str:
return (_format + text + RESET)
def log_message(text):
pointer = BOLD + WHITE + "==> " + GREEN
message = pointer + text + RESET
print(message)
def log_config():
pointer = BOLD + WHITE + "> "
print(f"{pointer}Keysize: {highlight(str(args.keysize), YELLOW)}\n{pointer}CPU Arch: {highlight(args.arch, YELLOW)}\n{pointer}Out Dir: {highlight(os.path.abspath(args.outdir), YELLOW)}\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="FormCrypt v1.0.0. A tool for creating encrypted string buffers in C/C++. Encrypt your strings & insert the code files into your malware source.\n")
input_data_arg_group = parser.add_mutually_exclusive_group()
input_data_arg_group.add_argument('--strings', type=str, nargs="+", help='a list of strings to encrypt')
input_data_arg_group.add_argument('--file', type=str, help='path to a file containing each string to encrypt, seperated by new line')
parser.add_argument('--keysize', default=12, type=int, help='size of the encryption key for all encrypted buffers')
parser.add_argument('--print_buffers', action='store_true', help='print the macro & buffer data')
parser.add_argument('--outdir', type=str, help='An alternative directory to write the source files to. Defaults to "Out"', default='Out')
parser.add_argument('--arch', type=str, choices=['x86', 'x64'], default='x64', help='sets the architecture for the output code [x86, x64]. defaults to x64')
args = parser.parse_args()
print(BANNER)
log_config()
# Collect strings to encrypt
strings = []
if args.strings:
strings = args.strings
elif args.file:
with open(args.file, 'r') as strings_input_file:
strings = strings_input_file.read().splitlines()
if strings == []:
log_message("No strings were received. Quitting.")
exit()
strings = parse_key_value_pairs(strings)
# Created encrypted buffer objects
EncryptedBufferDataObjects = []
Index = 0
string_dict_list = list(strings.items())
while (Index < len(string_dict_list)):
try:
EncryptedBufferDataObjects += [Rc4EncryptedBuffer(string_dict_list[Index][1], args.keysize, string_dict_list[Index][0])]
Index += 1
except RecursionError:
continue
# Create header file
HeaderFile = f"#define SHADOW_SPACE_SIZE 0x20\n#define KEY_SIZE {args.keysize}\n"
if args.arch == 'x86':
HeaderFile += "#define ARCH_X86\n"
with open('Template/FormCrypt.h') as header_template:
HeaderFile += header_template.read() + '\n'
# Add encrypted buffer definitions to header file
for encrypted_buffer in EncryptedBufferDataObjects:
HeaderFile += encrypted_buffer.get_macro_definition()
HeaderFile += "\n"
for encrypted_buffer in EncryptedBufferDataObjects:
HeaderFile += encrypted_buffer.get_direct_definition()
# Generate source code
if not os.path.exists('Out') and args.outdir == "Out":
os.mkdir('Out')
with open(os.path.join(args.outdir, 'FormCrypt.h'), 'w') as form_crypt_header:
form_crypt_header.write(HeaderFile)
shutil.copy(os.path.join('Source', 'FormCrypt.C'), os.path.join(args.outdir, 'FormCrypt.c'))
log_message(f"Saved code files to {os.path.abspath(args.outdir)}")
# Print buffers if instructed
if args.print_buffers:
for encrypted_buffer in EncryptedBufferDataObjects:
print(encrypted_buffer.get_macro_definitions())
for encrypted_buffer in EncryptedBufferDataObjects:
print(encrypted_buffer.get_macro_definitions())