67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
import struct
|
|
import argparse
|
|
import os
|
|
from Crypto.Cipher import AES
|
|
|
|
def derive_aes_key():
|
|
# 1. The hardcoded 64-bit key chunks from the VM bytecode
|
|
chunk1 = 0x034898EFB531EE6F
|
|
chunk2 = 0x2A6F20B991CAA263
|
|
|
|
# 2. Pack them into a 16-byte array (Little-Endian to match the x86/VM architecture)
|
|
key_bytes = struct.pack('<QQ', chunk1, chunk2)
|
|
|
|
# 3. Apply the VM's BITWISE_NOT operation to invert every byte
|
|
final_key = bytes(~b & 0xFF for b in key_bytes)
|
|
|
|
return final_key
|
|
|
|
def decrypt_payload(input_path, output_path):
|
|
if not os.path.exists(input_path):
|
|
print(f"[-] Error: Could not find '{input_path}'")
|
|
return
|
|
|
|
key = derive_aes_key()
|
|
print(f"[*] Derived 128-bit AES Key: {key.hex()}")
|
|
|
|
# Initialize AES in ECB mode (standard for VMs lacking an explicit IV instruction)
|
|
# Note: If the first 16 bytes decode correctly but the rest is garbage,
|
|
# change this to AES.MODE_CBC with iv=b'\x00'*16
|
|
cipher = AES.new(key, AES.MODE_ECB)
|
|
|
|
print(f"[*] Starting block decryption (512-byte chunks)...")
|
|
|
|
with open(input_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
|
|
bytes_processed = 0
|
|
|
|
while True:
|
|
# The VM processes exactly 512 bytes (0x200) per loop iteration
|
|
chunk = f_in.read(512)
|
|
if not chunk:
|
|
break
|
|
|
|
# AES requires 16-byte alignment. 512 is perfectly divisible by 16.
|
|
# If the very last chunk is cut off, we trim it to prevent crypto crashes.
|
|
if len(chunk) % 16 != 0:
|
|
print(f"[!] Warning: Truncating unaligned trailing bytes at EOF.")
|
|
chunk = chunk[:-(len(chunk) % 16)]
|
|
if not chunk:
|
|
break
|
|
|
|
# Decrypt the block and write it to disk
|
|
decrypted_chunk = cipher.decrypt(chunk)
|
|
f_out.write(decrypted_chunk)
|
|
|
|
bytes_processed += len(chunk)
|
|
|
|
print(f"[+] Success! Decrypted {bytes_processed} bytes.")
|
|
print(f"[+] Unpacked payload saved to: {output_path}")
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Static Decryptor for Connivance Payload")
|
|
parser.add_argument("input_file", help="Path to the encrypted 'blob' file")
|
|
parser.add_argument("-o", "--output", default="decrypted_payload.bin", help="Output file path")
|
|
|
|
args = parser.parse_args()
|
|
decrypt_payload(args.input_file, args.output)
|