Created
September 20, 2016 18:27
-
-
Save hasherezade/fd6c9fbb159a0e640615baba25503c5e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| # CC-BY: hasherezade | |
| """Decoder for 7even-HONE$T ransomware""" | |
| import argparse | |
| import os | |
| PREFIX = 'M' | |
| SUFFIX = '*' | |
| #for old version of 7ev3n: | |
| FNAME_KEY_R5A_OLD = "SHdbvntkorcfrertgejviotrgj689534hf4tuionc4IHDSbngjvkmrregtrhgfevtbtvnviotnrcejk4vnm30ij4t4" | |
| #for 7ev3n HONE$T: | |
| FNAME_KEY_R4A = 'ANOASudgfjfirtj4k504iojm5io5nm59uh5vob5mho5p6gf2u43i5hojg4mf4i05j6g594cn9mjg6h' | |
| FNAME_KEY_R5A = 'ASIBVbhciJ5hv6bjyuwetjykok7mbvtbvtiJ5h6jg54ifj0655iJ5hok7mbok7mbvtvtv6bjfib56j45fkmbvtiJ5hv6bokok7mbvt7mbvtj55nf4y8uhmvbi7knd4ium6iok7mbvtiiJ5hv6bjJ5hhigmubn56gfiok7mbvtjnmvu9bvtiJ5h6biok7mbvt7mbgi5fmuv65mg9fi4dm5v6iognfun5u6inguifbv5ibomlimmhnbjvfchbgnhugk5ybvtc3cty5' | |
| def decode(data, key, offset=0): | |
| maxlen = len(data) | |
| keylen = len(key) | |
| j = 0 #key index | |
| decoded = bytearray() | |
| for i in range(offset, maxlen): | |
| dec = data[i] ^ key[j % keylen] | |
| j += 1 | |
| decoded.append(dec) | |
| return decoded | |
| def search_suffix(filep): | |
| filep.seek(0, os.SEEK_END) | |
| size = filep.tell() | |
| pos = size - 1 | |
| filep.seek(pos, os.SEEK_SET) | |
| data = filep.read(1) | |
| if data != '\x0a': | |
| return None | |
| pos -= 1 | |
| buffer = "" | |
| prev_data = None | |
| while pos > 0: | |
| filep.seek(pos, os.SEEK_SET) | |
| data = filep.read(1) | |
| pos -= 1 | |
| if data == SUFFIX and prev_data == SUFFIX: | |
| break | |
| if data == SUFFIX: | |
| prev_data = SUFFIX | |
| continue | |
| else: | |
| if prev_data == SUFFIX: | |
| buffer = prev_data + buffer | |
| prev_data = None | |
| buffer = data + buffer | |
| return buffer | |
| def merge_win_path(path, filename): | |
| if path.endswith('\\') or path.endswith('/'): | |
| path = path[:len(path)-1] | |
| return path + '\\' + filename | |
| def extend_key(key, out_len): | |
| while len(key) < out_len: | |
| key = key + key | |
| return key[:out_len] | |
| class R5A_decoder(): | |
| """Decoder for R5A algorithm""" | |
| def __init__(self, f_path, data): | |
| self.f_path = f_path | |
| self.data = bytearray(data) | |
| self.size = len(data) | |
| self.quarter_size = self.size >> 2 | |
| self.half_size = self.quarter_size * 2 | |
| def decode(self, r5a_key, key_len): | |
| for i in (0,1): | |
| self.loop2(i) | |
| self.loop1(i) | |
| path_key = extend_key(self.f_path, key_len) | |
| hard_key = extend_key(r5a_key, key_len) | |
| self.data = decode(self.data, bytearray(path_key)) | |
| self.data = decode(self.data, bytearray(hard_key)) | |
| return self.data | |
| def loop2(self, index2): | |
| #process quarter of the content | |
| my_quarter = self.half_size * index2 + self.quarter_size | |
| end_quarter = my_quarter + self.quarter_size | |
| for i in range(my_quarter, my_quarter + self.quarter_size): | |
| dx = i % 255 | |
| self.data[i] = self.data[i] ^ dx | |
| return self.data | |
| def loop1(self, index2): | |
| #process quarter of the content | |
| my_quarter = self.half_size * index2 | |
| other_quarter = my_quarter + self.quarter_size | |
| for i in range(0, self.quarter_size): | |
| other_val = self.data[other_quarter + i] | |
| my_val = self.data[my_quarter + i] | |
| self.data[my_quarter + i] = my_val ^ other_val | |
| return self.data | |
| def decode_content(fp, is_r4a, fname_key, orig_file_name, path=None, r5a_key=None, r5a_keylen=None): | |
| suffix_len = len(orig_file_name) + len('**') + len('\x0a') + 1 | |
| data = read_encrypted(fp, suffix_len) | |
| if is_r4a: | |
| return decode(bytearray(data), bytearray(fname_key)) | |
| if (path is None): | |
| print "[-] R5A cannot be recover without knowing file's original path" | |
| return None | |
| f_path = merge_win_path(str(path), str(orig_file_name)) | |
| r5a_decoder = R5A_decoder(f_path, data) | |
| return r5a_decoder.decode(r5a_key, r5a_keylen) | |
| def read_encrypted(filep, suffix_len): | |
| filep.seek(0, os.SEEK_END) | |
| size = filep.tell() | |
| if size < suffix_len: | |
| return None | |
| filep.seek(0, os.SEEK_SET) | |
| data = filep.read(1) | |
| if data != PREFIX: | |
| print "encrypted not found" | |
| return None | |
| return filep.read(size - suffix_len) | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Data XOR") | |
| parser.add_argument('--file', dest="file", default=None, help="Input file", required=True) | |
| parser.add_argument('--path', dest='path', default=None, help="Original path - is neccessery to recover R5A") | |
| parser.add_argument('--old', dest="old", default=False, help="Is it old version of 7ev3n?", action='store_true') | |
| parser.add_argument('--r5a_key', dest="r5a_key", default="r5a_key.bin", help="File with the R5A key") | |
| parser.add_argument('--r5a_keylen', dest="r5a_keylen", type=int, default=None, help="Length of R5A key") | |
| args = parser.parse_args() | |
| fp = open(args.file, 'rb') | |
| fname = search_suffix(fp) | |
| if fname == None or len(fname) == 0: | |
| print "Failed to recover file name" | |
| exit (-1) | |
| is_r4a = False | |
| if args.file.endswith("R5A"): | |
| if args.old == True: | |
| fname_key = FNAME_KEY_R5A_OLD | |
| else: | |
| fname_key = FNAME_KEY_R5A | |
| print "R5A" | |
| else: | |
| fname_key = FNAME_KEY_R4A | |
| is_r4a = True | |
| print "R4A" | |
| orig_file_name = decode(bytearray(fname), bytearray(fname_key)) | |
| print "[+] Original name: " + orig_file_name | |
| dirname = os.path.dirname(args.file) | |
| orig_fname = os.path.join(dirname, orig_file_name) | |
| r5a_key = None | |
| if args.r5a_key is not None: | |
| keyfile = None | |
| try: | |
| keyfile = open(args.r5a_key, 'rb') | |
| except IOError: | |
| print "[-] Cannot open file with R5A key. Please supply the valid file as a parameter" | |
| return -1 | |
| if keyfile is None: | |
| print "[-] Cannot open the file with R5A key: %s" % args.r5a_key | |
| return -1 | |
| r5a_key = keyfile.read() | |
| keyfile.close() | |
| print "[+] R5A key fetched from file: %s" % args.r5a_key | |
| if not is_r4a and r5a_key is None: | |
| print "[-] You need to supply R5A key" | |
| return -1 | |
| r5a_keylen = len(r5a_key) | |
| if args.r5a_keylen is not None: | |
| r5a_keylen = args.r5a_keylen | |
| print "[+] Using R5A key length: %s" % r5a_keylen | |
| outdata = decode_content(fp, is_r4a, fname_key, orig_file_name, args.path, r5a_key, r5a_keylen) | |
| if outdata is None: | |
| print "[-] Decoding failed" | |
| return (-2) | |
| outfile = open(str(orig_fname), 'wb') | |
| if outfile is None: | |
| print "Error creating file: " + orig_fname | |
| return (-2) | |
| outfile.write(outdata) | |
| outfile.close() | |
| print "[+] Decoded to: " + orig_fname | |
| return 0 | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment