/
vidar_string_decryptor.py
236 lines (190 loc) · 6.18 KB
/
vidar_string_decryptor.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import idautils
import idc
import idaapi
import ida_bytes
def decrypt(str, key):
"""Aims to decrypt VIDAR's XOR encryption
Args:
str (list): list containing string to decrypt in hex form
key (list): list containing key to decrypt in hex form
Returns:
str: returns decrypted value in a readable way
"""
buf = []
i = 0
result = ""
while i <= (len(str) - 1):
str[i] = convert_to_ord(str[i])
key[i] = convert_to_ord(key[i])
decrypted_char = (str[i] ^ key[i])
buf.append(chr(decrypted_char))
i += 1
result = result.join(buf)
return result
def get_current_address():
"""Get the cursor address of a call to the decryption routine, if the cursor is not on this function, it prints an error and exits
Returns:
ea: current screen address
"""
ca = idc.get_screen_ea()
if idc.print_insn_mnem(ca) == "call":
addr = idc.get_operand_value(ca, 0)
else:
print("Please place your cursor on the decryption function call !")
exit(0)
return addr
def get_xrefs_addr(addr):
"""Gets all xrefs of a function
Args:
addr (ea): address of the function to get xrefs
Returns:
list: xrefs addresses
"""
xrefs = []
for xref in idautils.XrefsTo(addr):
xrefs.append(xref.frm)
return xrefs
def get_key(addr,size):
"""Gather the XOR key value
Args:
addr (ea): address of value
size (ea): size of bytes to capture
Returns:
list: key in list of hex bytes
"""
key = idaapi.get_bytes(addr, size)
key = slice_val(key.hex())
return key
def get_value(addr,icsize):
"""Gather the encrypted data
Args:
addr (ea): address of value
icsize (int): size arg of function, in case of all values are "unk" in ida
Returns:
list: value in list of hex bytes
int: size of bytes to capture
int: return size of data if value was "unk"
"""
size = get_size(addr) - 1
if size > 0:
value = idaapi.get_bytes(addr, size)
value = slice_val(value.hex())
return value, size;
else:
value = idaapi.get_bytes(addr, icsize)
value = slice_val(value.hex())
return value, icsize;
def get_size(addr):
"""Get the bytes size of an value
Args:
addr (ea): adress of value
"""
size = ida_bytes.get_item_size(addr)
return(size)
def scan_args(func_addr):
"""Get all args of a function
Args:
func_addr (ea): address of call to decryption routine
Returns:
ea: address of encrypted string arg
ea: address of key arg
int: size arg
"""
args = []
i = 0
while i < 3:
func_addr = idc.prev_head(func_addr)
mnem = idc.print_insn_mnem(func_addr)
if mnem == "push" and i < 3:
args.append(idc.get_operand_value(func_addr, 0))
i += 1
icsize = args[2] #! In case of unk for value data, try to take size arg (which can be a register)
encrypted = args[1]
key = args[0]
return encrypted, key, icsize;
def convert_to_ord(value):
"""Convert an hex value to decimal
Args:
value (hex): hex byte to convert
Returns:
int: decimal byte
"""
value = str('0x') + str(value) # Converted to hex string
value = int(value, 16) # Convert to ord
return value
def slice_val(str):
"""Slice value into hex bytes of 2
Args:
str (hex): hex string
Returns:
list : list of 2 digit hex values
"""
sliced = []
while str:
sliced.append(str[:2])
str = str[2:]
return sliced
def comment_result(addr, comm):
"""Comment addresses where the dwords are referenced
Args:
addr (ea): address of call function
comm (str): decrypted value to comment
"""
addr = idc.next_head(addr)
while idc.print_insn_mnem(addr) != "call":
if idc.print_insn_mnem(addr) == "mov" and idc.get_operand_value(addr,0) > 10:
dword_address = idc.get_operand_value(addr,0)
xrefs = get_xrefs_addr(dword_address)
for xref in xrefs:
idc.set_cmt(xref, comm, 0)
addr = idc.next_head(addr)
def main():
"""Main function of program
1. Get address of decryption function
2. Check xrefs
3. Gather all args of each function
4. Gather key and encrypted string values
5. Decrypt values
6. Comment decrypted string on each reference
7. Write strings in a txt file for other purposes
"""
num_str = 0
ca = get_current_address()
xrefs = get_xrefs_addr(ca)
str_list = []
breakpoint()
for addr in xrefs:
val_addr, key_addr, icsize = scan_args(addr)
val, size = get_value(val_addr,icsize)
key = get_key(key_addr,size)
result = decrypt(val,key)
if result:
comment_result(addr, result)
str_list.append(result)
num_str += 1
print("Work done !")
print(str(num_str) + " strings were decrypted")
write_to_log_file(str_list)
def test_single_function(addr):
"""If you figure out that an xref if causing an error, you can test the script with this address only
#! FOR DEBUG ONLY
Args:
addr (ea): address of call
"""
val_addr, key_addr, icsize = scan_args(addr)
val, size = get_value(val_addr, icsize)
key = get_key(key_addr,size)
result = decrypt(val,key)
comment_result(addr, result)
print(result)
def write_to_log_file(vals):
"""Write all decrypted strings in a file
Args:
vals (list): list of decrypted values
"""
file = open("VSD_log.txt", 'w') #* Change the w value to a if u want to decrypt multiple samples in the same file
for val in vals:
file.write(val + "\n")
file.close()
print("Done writing to " + file.name)
main()