import struct import sys import argparse # The complete instruction set mapped from the VMContext dispatch table OPCODES = { 1: "BITWISE_NOT", 2: "VERIFY_FILE_HASH", 6: "PUSH_TEXT_BASE", 8: "JMP_IF_GTE", 12: "PUSH_RODATA_SIZE", 13: "PUSH_SIZE", 16: "VERIFY_HASH", 21: "STORE_VAR_NO_CLONE", 22: "AES_DECRYPT", 23: "BITVEC_COMPARE", 24: "POP", 25: "APPEND_IMM", 28: "JMP_IF_EQUAL", 29: "PUSH_IMM", 30: "CLONE_PUSH", 33: "STORE_TO_INPUT", 38: "NOP", 39: "ALIGN", 40: "DECRYPT_BLOCK", 44: "SLICE_SECOND_HALF", 45: "JMP_IF_NOT_FLAG", 47: "JMP_IF_NOT_EQUAL", 48: "JMP_IF_FLAG", 53: "COPY_VAR_TO_STACK", 57: "CONCAT", 59: "STORE_TO_INPUT", 61: "PUSH_RODATA_BASE", 63: "BIGINT_ADD", 64: "JMP", 66: "SHA256", 69: "NOP", 74: "PUSH_TEXT_SIZE", 75: "PUSH_VAR", 78: "NOP", 79: "READ_FILE", 80: "RSA_VERIFY", 81: "SHR1", 83: "HALT", 84: "SLICE_FIRST_HALF", 87: "PUSH_PTR", 88: "TRUNCATE", 89: "SLICE", 90: "STORE_VAR", 91: "PUSH_NULL", 94: "LOAD_FILE", } def disassemble(file_path, is_raw_file=False): try: with open(file_path, 'rb') as f: data = f.read() except FileNotFoundError: print(f"Error: Could not find file '{file_path}'") return # If parsing the raw connivance.bin directly from disk, we must skip # the 256-byte RSA signature header to reach the first instruction. # If parsing a memory dump of the decrypted 'v8' buffer, start at 0. start_offset = 256 if is_raw_file else 0 bytecode = data[start_offset:] print(f"{'Offset':<8} | {'Opcode':<6} | {'Mnemonic':<20} | {'Operand 1 (64-bit)':<20} | {'Operand 2 (32-bit)'}") print("-" * 82) # Read exactly 16 bytes at a time (sizeof VMInstruction) for ip in range(0, len(bytecode), 16): chunk = bytecode[ip : ip+16] # Stop if we hit a partial instruction at the end of the buffer if len(chunk) < 16: break # Unpack the struct using Little Endian ('<') # H: uint16 (opcode) # H: uint16 (padding) # I: uint32 (operand2) # Q: uint64 (operand1) opcode, pad, op2, op1 = struct.unpack('