Skip to main content

Overview

The tape system is a bytecode-like instruction stream that dictates the control flow of the deobfuscated JavaScript. Think of it as a virtual machine where JavaScript functions read instructions from a base64-encoded tape to determine what operations to execute.
From README.md:183-184: “The current implementation only reverse engineers the JavaScript component but the control flow JavaScript is dictated by the tape. We will need to reverse engineer the tape for the complete reverse engineering.”

What is the Tape?

The tape is a base64-encoded binary instruction stream that contains:
  • Function indices to execute
  • Execution context information
  • Operation parameters
  • Control flow instructions
  • State manipulation directives
// From transform pipeline variable mappings
var tape = base64ToBytes(tapeBase64);

Tape Components

Tape Variables

From the transform pipeline (refactor-obfuscated-code-jscodeshift-2.js:42):
'_0x478891': 'tape',
'_0x501cef': 'tapeStringValue',
'_0x3fb717': 'tapeBytes',
'_0x169cb1': 'tapeValue',
'_0x1a170d': 'tapeLength'

Tape Parameters

From the parameter refactoring (refactor-obfuscated-code-jscodeshift-4.js:14):
'_0x343255': 'tapeBase64',
'_0x58916a': 'base64Tape',
'_0x27e170': 'base64Tape'

Tape Execution Flow

1. Tape Initialization

// Bootstrap module receives base64-encoded tape
bootstrapModule(tapeBase64);

// Tape is decoded from base64 to bytes
var tapeBytes = base64ToBytes(tapeBase64);

2. Execution Index Tracking

The tape maintains execution indices that point to the current instruction:
// From global state variables
'_0x524901': 'nextExecutionIndex',
'_0xdb013c': 'nextExecutionIndex',
'_0x2be870': 'nextExecutionIndex',
'_0x318d0d': 'nextExecutionIndex',
'_0x4044e8': 'currentExecutionIndex',
'_0x371e17': 'nextExecutionIndex'

3. Function Execution

// Execute function at specific tape position
executeFunctionAtExecutionIndex(nextExecutionIndex);

// Or execute function directly
executeFunction(functionIndex);

Tape and Global State Integration

The tape execution system is tightly coupled with the global state:
// From README.md:170-173
var executionContext = globalState[35];
// Contains:
// - globalStateWriteIndex
// - globalStateReadIndex  
// - bits for manipulation
// - tape pointers

Base64 Decoding

The tape arrives as base64 and must be decoded:
function base64ToBytes(base64Tape) {
    // Decode base64 string to binary
    var binaryString = atob(base64Tape);
    var tapeBytes = [];
    
    for (var i = 0; i < binaryString.length; i++) {
        var byteValue = binaryString.charCodeAt(i);
        tapeBytes.push(byteValue);
    }
    
    return tapeBytes;
}

Binary String Operations

The tape system includes utilities for binary string manipulation:
function createPaddedBinaryString(binaryString, bitLength, padValue) {
    // Pad binary string to specified length
    while (binaryString.length < bitLength) {
        binaryString = padValue + binaryString;
    }
    return binaryString;
}

Tape Keywords

The tape can reference keyword strings stored in a lookup table:
mitmproxy/src/javascript/pre-transform-code.js
var tapeKeywords = {};

Keyword Access Pattern

// From transform variables
'_0x1e16': 'keywords',
'_0x35ab': 'getKeywordName',
'_0x35ab73': 'shiftKeywords'
Example:
var keywordName = getKeywordName(keywordArray, index);
var value = tapeKeywords[keywordIndex];

Obfuscated Code Rotation

From README.md:131-140, bet365 rotates their obfuscated code, which includes different tape encodings:
The obfuscated code is manually tracked in mitmproxy/src/javascript/obfuscated/.Factors affecting tape version:
  • Location (IP address)
  • Time (rotates periodically)

String Extraction from Tape

Strings are encoded in the tape and extracted during execution:
// From variable mappings
'_0x469d52': 'stringFromTape',
'_0x5d873e': 'globalStateWriteStringValue'
Pattern:
var stringFromTape = extractString(tape, currentIndex);
globalState[globalStateWriteIndex] = stringFromTape;

Function Index Resolution

The tape contains indices to functions that should be executed:
// From variable mappings
'_0x13ccc7': 'functionRef',
'_0x31c854': 'functionRef',
'_0x3c9cb1': 'functionIndex',
'_0x54f086': 'functionIndex',
'_0xed3bc6': 'functionIndex',
'_0x19519d': 'functionIndex'
Execution:
var functionIndex = tape[executionIndex];
var functionRef = functions[functionIndex];
var functionResult = functionRef.apply(this, args);

Module System

The tape drives a module loading system:
// From function mappings
function bootstrapModule(tapeBase64) {
    var tape = base64ToBytes(tapeBase64);
    initialiseState(tape);
    // Start execution
}

function getModule(moduleKey) {
    return modules[moduleKey];
}
'_0x38dc1b': 'modules',
'_0x2418ef': 'exportedModules',
'_0x1f8e23': 'moduleKeys',
'_0x53d8ee': 'moduleKey',
'_0x21f1e2': 'getModule',
'_0x26ea4d': 'exportedModule'

Tape-Driven Control Flow

Conditional Execution

The tape determines which branches to execute:
var conditionResult = globalState[globalStateReadIndex];
var nextIndex = conditionResult ? tape[ifTrueIndex] : tape[ifFalseIndex];
executeFunctionAtExecutionIndex(nextIndex);

Loop Control

Loops are controlled by tape instructions:
var loopCount = tape[loopCountIndex];
for (var i = 0; i < loopCount; i++) {
    executeFunctionAtExecutionIndex(tape[loopBodyIndex]);
}

Error Handling

Error handlers are registered via the tape:
'_0x472240': 'errorFunctionIndex',
'_0x565949': 'finallyFunctionIndex',
'_0x33fb70': 'globalStateErrorIndex'

Tape Analysis Challenges

The tape is decoded at runtime, making static analysis difficult. The base64 encoding changes with each obfuscation rotation.
Tape instructions depend on globalState[35] execution context, which changes frequently during execution.
The meaning of specific byte sequences in the tape is not documented and must be reverse-engineered through observation.

Development Tools

Watchexec Development Loop

From README.md:143-150:
watchexec -e js "touch mitmproxy/src/python/download-payload.py && \
node mitmproxy/src/javascript/refactor-obfuscated-code-jscodeshift.js \
mitmproxy/src/javascript/obfuscated-original.js \
mitmproxy/src/javascript/deobfuscated-output.js && \
node mitmproxy/src/javascript/pre-transform-code.js"
This recompiles the deobfuscation transform when any JS file changes, allowing you to observe how tape operations are transformed.

Tape Logging

You can log tape operations by instrumenting the deobfuscated code:
function executeFunctionAtExecutionIndex(executionIndex) {
    console.log('Tape execution index:', executionIndex);
    console.log('Tape byte at index:', tape[executionIndex]);
    // ... rest of execution
}

Future Work

From README.md:189-193:
Current Status: Only the JavaScript component is reverse-engineered. The tape format itself remains largely undocumented.Next Steps:
  • Reverse engineer tape instruction format
  • Document tape opcodes and their meanings
  • Build a tape disassembler
  • Create tape execution tracer
  • Global State - Runtime state manipulated by tape instructions
  • Transform Scripts - How tape-related variables are renamed
  • See README.md:183-193 for current limitations and future work

Build docs developers (and LLMs) love