Skip to main content

Overview

The global state is a core runtime structure that tracks execution context, tape positions, and runtime values throughout the obfuscated JavaScript execution. Understanding the global state is crucial for reverse engineering the control flow.

Global State Structure

The global state is implemented as an array (globalState) that serves as a central data store for the execution engine.
var globalState = [];

Key State Indices

globalState[35] - Execution Context

The execution context of the tape system. This is the most frequently accessed state index.Contains:
  • globalStateWriteIndex - Where to write the next value
  • globalStateReadIndex - Where to read the next value
  • Bit manipulation values
  • Array length tracking
  • Tape position pointers
  • Current execution context metadata
From README.md:170: “globalState[35]: is the execution context of the tape. This may contain the globalStateWriteIndex, globalStateReadIndex, bits for manipulation, length of the array, etc… basically all sorts of things that point to the tape for the current execution context.”
Example usage:
var executionContext = globalState[35];
var writeIndex = globalState[executionContext.writeIndexOffset];

State Tracking System

The pre-transform-code.js file provides utilities for tracking state changes during execution:

State Capture Functions

mitmproxy/src/javascript/pre-transform-code.js
var beforeFunctionState = function(functionName, globalState) {
    var stateContext = new Map([
        ["functionName", functionName],
        ["before", globalState.slice()]
    ]);
    return stateContext;
};

var afterFunctionState = function(stateContext, globalState) {
    var beforeFunctionGlobalState = stateContext.get("before");
    var changes = getGlobalStateChanges(beforeFunctionGlobalState, globalState);
    if (changes) {
        stateContext.delete("before");
        stateContext.set("change", changes);
        states.push(stateContext);
    }
};
functionName
string
required
Name of the function being tracked
globalState
Array
required
The current global state array

Change Detection

The getGlobalStateChanges function compares two state snapshots:
mitmproxy/src/javascript/pre-transform-code.js
var getGlobalStateChanges = function(globalState1, globalState2) {
    if (globalState1 === undefined || globalState2 === undefined) return undefined;
    var globalStateKeys = new Set();
    var changed = new Map();
    
    globalState1.forEach((value, i) => globalStateKeys.add(i));
    globalState2.forEach((value, i) => globalStateKeys.add(i));
    
    for (key of globalStateKeys) {
        var globalStateValue1 = globalState1[key];
        var globalStateValue2 = globalState2[key];
        if (globalStateValue1 !== globalStateValue2) {
            changed.set(key, [globalStateValue1, globalStateValue2]);
        }
    }
    return isChangeMapEmpty(changed) ? undefined : changed;
};
Returns a Map where:
  • Key: Global state index that changed
  • Value: [oldValue, newValue] tuple

Filtering Noise

mitmproxy/src/javascript/pre-transform-code.js
function isChangeMapEmpty(map) {
    if (map.size === 1 && map.get(35)) return true;
    return false;
}
Changes are considered “empty” if only globalState[35] changed, since it changes on every function call (execution context updates).

State Logging

Console Logger

mitmproxy/src/javascript/pre-transform-code.js
var consoleLogger = function(string) {
    if (console) {
        console.log(string);
    }
};

var print = function(label, input) {
    if (Array.isArray(input)) {
        var object = Object.assign({}, input);
        consoleLogger(label + ": " + toJsonString(object));
    } else if(typeof input === "object") {
        consoleLogger(label + ": " + toJsonString(input));
    } else {
        consoleLogger(label + ": " + input);
    }
};

Circular Reference Handling

mitmproxy/src/javascript/pre-transform-code.js
var circularObjectReferences = new Set([
    "_subscriptionsInitializedDelegate",
    "_pushBalanceReceivedDelegate",
    "_currentTransportMethod",
    "delegate_transportConnectedHandler",
    "document",
    "_active_element",
    "delegate_transportConnectionFailedHandler",
    "scope",
    "rules",
    "_element",
    "_module",
    "_delegateList",
    "_actualChildren",
    "parent",
    "widthStateWatcher",
    "pointerProcessor",
    "favouritesMechanics"
]);

var circularObjectReferenceToString = function(key, value) {
    if((value && value === value.window) || circularObjectReferences.has(key)) {
        return value.toString();
    }
    return value;
};
Prevents JSON serialization errors when logging state that contains circular references.

State Variables from Transform Pipeline

The deobfuscation process identifies these state-related variables:

Write Operations

  • globalStateWriteIndex - Primary write index
  • globalStateWriteStringValue - String value being written
  • Various specific write indices for operations

Read Operations

  • globalStateReadIndex - Primary read index
  • globalStateReadIndex1 - First operand read index
  • globalStateReadIndex2 - Second operand read index
  • globalStateReadValueIndex - Index for reading values
  • globalStateReadObjectKeyIndex - Index for reading object keys
  • globalStateReadObjectIndex - Index for reading objects

Context Management

  • globalStateContexts - Stack of execution contexts
  • globalStateContextValues - Values in current context
  • globalStateEvalStringIndex - Index for eval operations
  • globalStateErrorIndex - Error handling index
  • latestGlobalStateContext - Most recent context

Specific State Indices

// From refactor-obfuscated-code-jscodeshift-2.js
'_0xfe71f0': 'globalStateWriteIndex',
'_0x5d873e': 'globalStateWriteStringValue',
'_0x2da1b3': 'globalStateWriteIndex',
'_0x48a4e7': 'globalStateWriteIndex',
'_0x176fa3': 'globalStateWriteIndex',
'_0x551c19': 'globalStateReadIndex1',
'_0x20e93a': 'globalStateReadIndex2',
// ... over 50 state-related mappings

Debugging State

When Chrome is started with logging flags:
open -a "Google Chrome" --args \
  --proxy-server=http://localhost:8080 \
  --enable-logging --v=1 \
  --user-data-dir=$(pwd)/chrome-profile
You can monitor state changes without opening DevTools:
tail -f <user-data-dir>/chrome_debug.log | \
  sed -En "s/.*inside.*\\]: (.*)\", source\:  \(3\)/\1/p"
This filters the Chrome debug log to show only the JSON state dumps.

State Monitoring Best Practices

Avoid tracking globalState[35] changes alone - Since it changes on every function call, filter it out to see meaningful state changes.
var stateContext = beforeFunctionState("myFunction", globalState);
// ... function execution
afterFunctionState(stateContext, globalState);

Tape Keywords Tracking

The state system also tracks tape keywords:
mitmproxy/src/javascript/pre-transform-code.js
var tapeKeywords = {};
This object maps tape indices to keyword strings extracted during deobfuscation.
From README.md:179: “When the devtool is open the tapeKeywords[27269]: ”” is added, indicating the obfuscated code detects DevTools.

State Array Access Patterns

Writing Values

globalState[globalStateWriteIndex] = value;

Reading Values

var value = globalState[globalStateReadIndex];

Context Operations

var context = globalState[35];
globalState[context.writeOffset] = newValue;

Build docs developers (and LLMs) love