From cc83d145de472f185ec3e2db444d621aae06c831 Mon Sep 17 00:00:00 2001 From: sanex3339 Date: Fri, 17 Apr 2026 13:33:18 +0700 Subject: [PATCH] Fix self-defending obfuscated code hanging in Bun environments --- CHANGELOG.md | 4 + package.json | 7 +- .../AtobTemplate.ts | 19 ++- .../SelfDefendingTemplate.ts | 4 +- .../StringArrayCallsWrapperTemplate.spec.ts | 120 +++++++++++++++++- 5 files changed, 141 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35feddad8..839cef024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ Change Log +v5.4.2 +--- +* Fixed obfuscated code hanging in Bun when `selfDefending` is enabled. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1404 + v5.4.1 --- * Fixed `Utils.nodeRequire` causing `ReferenceError: require is not defined` in browser build by making it lazy-evaluated diff --git a/package.json b/package.json index 3f6f3f765..3c29944bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "javascript-obfuscator", - "version": "5.4.1", + "version": "5.4.2", "description": "JavaScript obfuscator", "keywords": [ "obfuscator", @@ -132,10 +132,7 @@ "author": { "name": "Timofei Kachalov" }, - "contributors": [ - "Timofei Kachalov (https://github.com/sanex3339)", - "Dmitry Zamotkin (https://github.com/zamotkin)" - ], + "contributors": ["Timofei Kachalov (https://github.com/sanex3339)", "Dmitry Zamotkin (https://github.com/zamotkin)"], "license": "BSD-2-Clause", "packageManager": "yarn@1.22.21+sha512.ca75da26c00327d26267ce33536e5790f18ebd53266796fbb664d2a4a5116308042dd8ee7003b276a20eace7d3c5561c3577bdd71bcb67071187af124779620a" } diff --git a/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts b/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts index 340d7b42a..f94a27569 100644 --- a/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts +++ b/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts @@ -12,8 +12,15 @@ export function AtobTemplate(selfDefending: boolean): string { let output = ''; let tempEncodedString = ''; - ${selfDefending ? 'let func = output + {atobFunctionName};' : ''} - + ${ + selfDefending + ? ` + let func = output + {atobFunctionName}; + let __ = ('' + function(){return 0;}).indexOf('\\n') !== -1; + ` + : '' + } + for ( let bc = 0, bs, buffer, idx = 0; buffer = input.charAt(idx++); @@ -21,7 +28,13 @@ export function AtobTemplate(selfDefending: boolean): string { ? output += ${((): string => { const basePart: string = 'String.fromCharCode(255 & bs >> (-2 * bc & 6))'; - return selfDefending ? `((func.charCodeAt(idx + 10) - 10 !== 0) ? ${basePart} : bc)` : basePart; + return selfDefending + ? ` + ((__ || func.charCodeAt(idx + 10) - 10 !== 0) + ? ${basePart} + : bc) + ` + : basePart; })()} : 0 ) { diff --git a/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/SelfDefendingTemplate.ts b/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/SelfDefendingTemplate.ts index def5c29e9..92abd981d 100644 --- a/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/SelfDefendingTemplate.ts +++ b/src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/SelfDefendingTemplate.ts @@ -57,6 +57,8 @@ export function SelfDefendingTemplate( return ${rc4BytesIdentifier}(this.${statesIdentifier}[0]); }; - new StatesClass({stringArrayCallsWrapperName}).${checkStateIdentifier}(); + if (('' + function(){return 0;}).indexOf('\\n') === -1) { + new StatesClass({stringArrayCallsWrapperName}).${checkStateIdentifier}(); + } `; } diff --git a/test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts b/test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts index 68a393509..393fd2e71 100644 --- a/test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts +++ b/test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts @@ -332,9 +332,9 @@ describe('StringArrayCallsWrapperTemplate', () => { decodedValue = Function(` ${stringArrayTemplate} - + ${stringArrayCallsWrapperTemplate} - + return ${stringArrayCallsWrapperName}(${index}); `)(); }); @@ -379,9 +379,9 @@ describe('StringArrayCallsWrapperTemplate', () => { decodedValue = Function(` ${stringArrayTemplate} - + ${stringArrayCallsWrapperTemplate} - + return ${stringArrayCallsWrapperName}(${index}); `)(); }); @@ -391,6 +391,118 @@ describe('StringArrayCallsWrapperTemplate', () => { }); }); }); + + describe('Variant #3: correct code evaluation when engine reformats Function.prototype.toString', () => { + const origToString = Function.prototype.toString; + + afterEach(() => { + Function.prototype.toString = origToString; + }); + + describe('Variant #1: long decoded string', () => { + const index: string = '0x0'; + + const indexShiftAmount: number = 0; + + const expectedDecodedValue: string = 'test1test1'; + + let decodedValue: string; + + before(() => { + const stringArrayTemplate = format(StringArrayTemplate(), { + stringArrayName, + stringArrayFunctionName, + stringArrayStorageItems: `'${cryptUtilsSwappedAlphabet.btoa('test1test1')}'` + }); + const atobPolyfill = format(AtobTemplate(selfDefendingEnabled), { + atobFunctionName + }); + const atobDecodeTemplate: string = format(StringArrayBase64DecodeTemplate(randomGenerator), { + atobPolyfill, + atobFunctionName, + selfDefendingCode: '', + stringArrayCacheName, + stringArrayCallsWrapperName + }); + const stringArrayCallsWrapperTemplate: string = format(StringArrayCallsWrapperTemplate(), { + decodeCodeHelperTemplate: atobDecodeTemplate, + indexShiftAmount, + stringArrayCacheName, + stringArrayCallsWrapperName, + stringArrayFunctionName + }); + + // Simulate Bun/JSC: Function.prototype.toString adds newlines + Function.prototype.toString = function () { + return origToString.call(this).replace('){', '){\n'); + }; + + decodedValue = Function(` + ${stringArrayTemplate} + + ${stringArrayCallsWrapperTemplate} + + return ${stringArrayCallsWrapperName}(${index}); + `)(); + }); + + it('should correctly return decoded value when engine reformats toString', () => { + assert.deepEqual(decodedValue, expectedDecodedValue); + }); + }); + + describe('Variant #2: 3-characters decoded string', () => { + const index: string = '0x0'; + + const indexShiftAmount: number = 0; + + const expectedDecodedValue: string = 'foo'; + + let decodedValue: string; + + before(() => { + const stringArrayTemplate = format(StringArrayTemplate(), { + stringArrayName, + stringArrayFunctionName, + stringArrayStorageItems: `'${cryptUtilsSwappedAlphabet.btoa('foo')}'` + }); + const atobPolyfill = format(AtobTemplate(selfDefendingEnabled), { + atobFunctionName + }); + const atobDecodeTemplate: string = format(StringArrayBase64DecodeTemplate(randomGenerator), { + atobPolyfill, + atobFunctionName, + selfDefendingCode: '', + stringArrayCacheName, + stringArrayCallsWrapperName + }); + const stringArrayCallsWrapperTemplate: string = format(StringArrayCallsWrapperTemplate(), { + decodeCodeHelperTemplate: atobDecodeTemplate, + indexShiftAmount, + stringArrayCacheName, + stringArrayCallsWrapperName, + stringArrayFunctionName + }); + + // Simulate Bun/JSC: Function.prototype.toString adds newlines + Function.prototype.toString = function () { + return origToString.call(this).replace('){', '){\n'); + }; + + decodedValue = Function(` + ${stringArrayTemplate} + + ${stringArrayCallsWrapperTemplate} + + return ${stringArrayCallsWrapperName}(${index}); + `)(); + }); + + it('should correctly return decoded value when engine reformats toString', () => { + assert.deepEqual(decodedValue, expectedDecodedValue); + }); + }); + }); }); });