import re import zlib import binascii import base64 import struct import sys from pathlib import Path import cs_payload_parser as csp ### Constants VERSION = 1.0 # Regex patterns for default CS encoding FILE_TYPE_PATTERNS = [ ['raw_payload', b'\xFC\xE8\x89\x00\x00\x00\x60\x89|\xFC\x48\x83\xE4\xF0\xE8\xC8\x00'], ['xored_payload', b'\x10[\x00-\xFF]{1}\x00\x00[\x00-\xFF]{3}\x00[\x00-\xFF]{4}\x61\x61\x61\x61'], ['xored_beacon', b'\xFC\xE8.\x00\x00\x00.{,32}\xEB[\x27\x2B].\x8B.\x00?\x83.\x04\x55?\x8B.\x00?\x31.\x83.\x04|\xFC\x48\x83\xE4\xF0\xEB\x33\x5D\x8B\x45\x00\x48\x83\xC5\x04\x8B\x4D\x00\x31\xC1\x48'], ['raw_hex', b'[a-fA-F0-9]{255,}'], ['raw_hex_array', b'(0x[a-fA-F0-9]{2}([;,\.]\s)?){255,}'], ['raw_hex_veil', rb'(\\x[a-fA-F0-9]{2}){255,}'], ['raw_dec_array',rb'([0-9\-]{1,4},(\s_\n)?[0-9\-]{1,4},?){255,}'], ['raw_chr_array', b'([aArR"&y\s]{5,})?(Chr\([0-9\-]{1,4}\)&("[a-zA-Z0-9\s]{1,}"&)?(\s_\n)?){32,}'], ['raw_base64', b'(?:[A-Za-z0-9+/]{4}){128,}(?:[A-Za-z0-9+/][AQgw]==|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=)?'] ] # Default XOR key used in encoding postprocess DEFAULT_CS_XOR_KEY = 0x23 # decorations HR = '-'*80 ### Helper functions # Check regex pattern def check_pattern(patterns, buff): data = ('unknown', b'') for pattern in patterns: r = re.compile(pattern[1], re.IGNORECASE) m = re.search(r,buff) if m: data = (pattern[0],m.group()) return data return data # Return dword value from offset def dword(buff, p_offset): return struct.unpack_from(' 32 and 0x10 <= enc_offset-start_offset <= 0x100: init_key = dword(buff, enc_offset) size = dword(buff, enc_offset+4) ^ init_key if enc_offset+size > len(buff) or size % 4 !=0: print('[!] Payload size is wrong..') return dexored_buff # Xor with rolling dword key enc_offset += 8 for i in range(0,size,4): enc_dw = dword(buff, enc_offset+i) dec = enc_dw ^ init_key dexored_buff += struct.pack("= 0: p_offset,p_size,p_xor_key,p_junk = struct.unpack_from('= 0: dexored_buff = buff[header_offset:header_offset+4096] return dexored_buff ### Extract payloads from encoded or binary files def extract_payload(p): decoded_buff = b'' output_path = '' try: with open(p,'rb') as f: buff = f.read() except OSError as e: print('%s\n[!] File: %s\n[!] Error: %s\n%s' % (HR,p.name,e.strerror,HR)) return False buff = fix_utf16(buff) ft, enc = check_pattern(FILE_TYPE_PATTERNS,buff) if ft in ['raw_hex', 'raw_hex_array', 'raw_hex_veil', 'raw_dec_array','raw_base64']: decoded_buff = globals()[ft](enc) elif ft in ['raw_chr_array', 'xored_payload', 'raw_payload', 'xored_beacon']: decoded_buff = globals()[ft](buff) else: print('%s\n[!] File: %s\n[!] Error: Payload pattern not found.\n%s' % (HR,p.name,HR)) return False print('%s\nFilename:\t%s\nPayload type:\t%s\n%s' % (HR,p.name,ft,HR)) decoded_buff = post_process(decoded_buff,buff) if len(decoded_buff) == 0: print('[!] Error: Extracting payload fails.\n%s' % HR) return False output_path = p.with_name(p.name+'_payload.bin') try: with open(output_path,'wb') as o: o.write(decoded_buff) print('Saved as:\t%s\n%s' % (p.name+'_payload.bin',HR)) except OSError as e: print('%s\n[!] File: %s\n[!] Error: %s\n%s' % (HR,p.name,e.strerror,HR)) return False return True, ft, output_path ### Main def main(): print('%s\nCS Payload extractor v%.02f%sAvast Software s.r.o\n%s' % (HR,VERSION,' '*34,HR)) if len(sys.argv) < 2: print('%s\n[!] Please specify input file or directory.' % HR) sys.exit() p = Path(sys.argv[1]) # extract and parse file if p.is_file(): print('[*] Extracting file..') d = extract_payload(p) if d: print('[*] Parsing file..') csp.raw_parser(d[2]) # extract and parse files from directory elif p.is_dir(): for fp in p.iterdir(): if fp.is_file(): print('\n%s\n[*] Extracting file..' % HR) d = extract_payload(fp) if d: print('[*] Parsing file..') csp.raw_parser(d[2]) else: print('%s\n[!] Please specify input file or directory.' % HR) sys.exit() if __name__ == "__main__": main()