Skip to main content
This guide covers debugging techniques for bet365-re-js, including state monitoring, console logging, and development workflows.

Development Workflow

Watch Mode for Rapid Development

For ease of development, you can automatically recompile transforms when source files change:
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 command watches for changes in any *.js files (excluding obfuscated-original.js and deobfuscated.js) and automatically recompiles the deobfuscation transform.

Working with Test Files

Place the obfuscated JavaScript in mitmproxy/src/javascript/obfuscated-original.js to test your transforms in isolation.

Debugging State

Chrome Debug Logs

If you start Chrome with the --enable-logging --v=1 flags, you can view console output without opening the browser:
open -a "Google Chrome" --args \
  --proxy-server=http://localhost:8080 \
  --enable-logging --v=1 \
  --user-data-dir=$(pwd)/chrome-profile
The console output is written to <user-data-dir>/chrome_debug.log.

Viewing JSON Output

To extract just the JSON output from debug logs:
tail -f <user-data-dir>/chrome_debug.log | \
  sed -En "s/.*inside.*\\]: (.*)\", source\:  \(3\)/\1/p"
This filters the log file to show only the structured JSON output from your debugging code.

Pre-Transform Debugging Utilities

The pre-transform-code.js file contains several debugging utilities that are injected into the deobfuscated code:

Console Logger

var consoleLogger = function(string) {
    if (console) {
        console.log(string);
    }
};
Safe wrapper for console logging that checks for console availability.

State Printer

var print = function(label, input) {
    if (Array.isArray(input)) {
        var object = Object.assign({}, input);
        Object.keys(object).forEach((key, value) => {
            try {
                object[key] = object[key];
            } catch (exception) {
                consoleLogger(exception)
            }
        });
        consoleLogger(label + ": " + toJsonString(object));
    } else if(typeof input === "object") {
        consoleLogger(label + ": " + toJsonString(input));
    } else {
        consoleLogger(label + ": " + input);
    }
};
Prints labeled debug output with support for arrays, objects, and primitives.

Circular Reference Handling

The debugging utilities handle circular object references automatically:
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;
};

Function State Tracking

1

Capture Before State

Record the global state before a function executes:
var beforeFunctionState = function(functionName, globalState) {
    var stateContext = new Map([
        ["functionName", functionName],
        ["before", globalState.slice()]
    ]);
    return stateContext;
};
2

Compare After State

Compare state after execution and log changes:
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);
    }
};
3

Print State Changes

Output the accumulated state changes:
var printStates = function(functionName, states, additionalPredicates) {
    if(states && states.length != 0 && printFunctions.has(functionName)) {
        var firstGlobalState = states[0];
        var lastGlobalState = states[states.length - 1];
        var changes = getGlobalStateChanges(firstGlobalState, lastGlobalState);
        var printInput = {
            "functionName": functionName,
            "states": states,
            "changes": changes
        }
        print(`inside ${functionName}`, printInput);
    }
};

Global State Debugging

Understanding Global State

The globalState[35] is the execution context of the tape, containing:
  • globalStateWriteIndex
  • globalStateReadIndex
  • Bits for manipulation
  • Array length
  • Pointers to the tape for the current execution context

Detecting Global State Changes

The debugging utilities can detect and report changes in global state:
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;
};
The function ignores changes to globalState[35] alone, as this changes frequently during normal execution.

AST Debugging

Using AST Explorer

For manipulating JavaScript AST, use https://astexplorer.net/ to:
  • Visualize AST structure
  • Test jscodeshift transforms
  • Understand node types and properties

Debugger Statements

The ChainedTransformer includes a debugger statement for stepping through transforms:
performTransform() {
    let ast = this.jscodeshiftAst;
    debugger;  // Breakpoint for debugging
    ast = new Void0Transformer(0, ast, this.output, this.outputBaseName).transform();
    ast = new UnaryExpressionTransformer(1, ast, this.output, this.outputBaseName).transform();
    // ... more transforms
}
Run Node.js with the inspector to use this breakpoint:
node --inspect-brk mitmproxy/src/javascript/refactor-obfuscated-code-jscodeshift.js \
  input.js output.js

Browser Recommendations

Create a dedicated Chrome profile for mitmproxy interception to keep debugging logs separate from your main profile.

Useful Extensions

  • Clear Cache Extension: Quickly clear cache from toolbar or with keyboard shortcuts
  • Avoid using obfuscated-code-logger.js excessively as it adds significant noise to console logs

Common Debugging Scenarios

Scenario 1: Transform Not Working

Enable intermediate file output to see the result of each transform step:
const outputIntermediateSteps = true;
This creates files like deobfuscated-output-0.js, deobfuscated-output-1.js, etc.
Use AST Explorer to verify your node matching patterns are correct. The actual AST structure may differ from expectations.

Scenario 2: Global State Changes

Insert beforeFunctionState and afterFunctionState calls around suspicious functions to track state changes.
Modify isChangeMapEmpty to ignore specific global state indices that change frequently but aren’t relevant to your investigation.

Scenario 3: DevTools Detection

When DevTools is open, tapeKeywords[27269]: "" is added to the tape. This is bet365’s anti-debugging mechanism.

Output Directory Structure

The /output directory contains:
  • <timestamp>-received-<index>.js: Original obfuscated code intercepted
  • <timestamp>-deobfuscated-<index>.js: Deobfuscated code
  • <timestamp>-sent-<index>.js: Final beautified code sent to browser
  • <timestamp>-received-<index>.css: CSS files

Testing Your Changes

After making changes to transforms:
  1. Clear the output directory
  2. Restart mitmproxy
  3. Reload the bet365 page
  4. Check the generated files in /output
  5. Verify the deobfuscated code is valid JavaScript
# Validate the generated JavaScript
node --check output/*-deobfuscated-*.js

Build docs developers (and LLMs) love