Add a toggle to enable/disable all debugger statements (separately from the breakpoint toggle)
Categories
(DevTools :: Debugger, enhancement, P3)
Tracking
(relnote-firefox 121+, firefox121 fixed)
People
(Reporter: nbp, Assigned: ochameau)
References
(Blocks 2 open bugs)
Details
(Keywords: dev-doc-needed)
Attachments
(3 files)
Today some JavaScript Obfuscator are using the debugger;
keyword to prevent reverse engineering the code, or editing value with a debugger. Unfortunately these mitigations cause performance issues which appear only in Firefox.
By offering a toggle to enable/disable debugger statements, and communicating about it, we will not fix this performance issue, but suggest to developers of these tools that this kind of mitigation is now useless. Thus, hopefully reducing the number of these from appearing on the Web.
The performance problem comes from the way the the debugger
statement is used.
It is used in a newly created function, called by a function which is calling it-self recursively, and executed in a try-catch. The performance problem then appears from the fact that Firefox seems to have a bigger JavaScript stack than Chrome[1] in this specific case, thus crashing later rather than sooner. (see Bug 1475013)
[1] In the debugger console:
var i = 0;
function f() { i++; f() }
f();
On my system:
Firefox: i == 45706
Chromium: i == 15728
Reporter | ||
Updated•5 years ago
|
Reporter | ||
Updated•5 years ago
|
Comment 1•5 years ago
|
||
I believe that the "disable all debugger statements" button already exists. There is a button in the top right of the debugger that disables breakpoints, debugger statements and other types of pausing.
Comment 2•5 years ago
|
||
That doesn't address the goal of this bug though, which would be to allow people to debug normally, while basically skipping the part where debugger;
statements cause a pause, thus removing the encouragement for projects to use them as an obfuscation mechanism.
Comment 3•5 years ago
|
||
hmm, i see. so breakpoints should still work, but debugger statements should be disabled...
could we address this by making it easy to disable specific debugger statements?
Comment 4•5 years ago
|
||
The hard part is evalled code, since that will create a brand new snippet with a new debugger that isn't disabled. Disabled-by-default seems to be the only way.
Comment 5•5 years ago
|
||
Could JS engine provide a config pref to solve this edge use case – disabling debugger statements? We probably need more time to evaluate our approach on how to expose this in DevTools.
Reporter | ||
Comment 6•5 years ago
|
||
I think this is doable, but Jim is likely to know better than me.
Comment 7•5 years ago
|
||
Can't we just do this in the server, today, by not setting an onDebuggerStatement handler in the first place?
Updated•5 years ago
|
Updated•4 years ago
|
Comment 8•3 years ago
|
||
The patch offered here https://github.com/firefox-devtools/debugger/issues/8228#issuecomment-508776592 does not work anymore
cp -n libxul.so{,.prePatch}; dd of=libxul.so if=<(printf "dbbbgggr") obs=1 conv=notrunc seek=$(strings -t d libxul.so | egrep "^[0-9]* (debugger|padStart)$"| grep padStart -A 0 -B 1 | head -n 1|cut -d" " -f1); diff <(xxd libxul.so) <(xxd libxul.so.prePatch)
Comment 9•3 years ago
|
||
... but it does work on v67, as-advertised. You may need to "stay with that version" though.
Comment 10•3 years ago
|
||
Are there any hopes about this feature?
Debugger call is nowadays abused everywhere to break debugging attempts and the only way to stop this requires a custom fix in the source code as explained here https://nullpt.rs/evading-anti-debugging-techniques/ .
This can be solved by adding a new option to disable/enable debugger call and set a customizable call name to trigger the breakpoint and bypass this anti debugger method.
Updated•3 years ago
|
Reporter | ||
Comment 11•3 years ago
|
||
There is a new feature which is present in Firefox 98 and would help as selectively ignore debugger statements:
- It is behind the about:config flag:
devtools.debugger.features.blackbox-lines
. - This feature is illustrated here: https://twitter.com/FirefoxDevTools/status/1486025607007948811
- Tracked by Bug 875034.
This does not answer specifically about this specific bugs, as this does not disable all debugger statements, but this can be used as a work-around in the mean time.
Comment 12•3 years ago
|
||
(In reply to Nicolas B. Pierron [:nbp] from comment #11)
This does not answer specifically about this specific bugs, as this does not disable all debugger statements, but this can be used as a work-around in the mean time.
I am not sure if your claim is actually true.
as explained here https://nullpt.rs/evading-anti-debugging-techniques/
there is eval-usage. I am not sure how can you blackbox a line which is techincally not a line, but a string to be eval'ed.
And then there are "other techniques" too ...
Comment 13•3 years ago
|
||
There is also a button available on Debugger toolbar allowing the user to disable all breakpoints including debugger;
keywords.
See the attached screenshot for the location.
Does that work for you?
Comment 14•3 years ago
|
||
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #13)
There is also a button available on Debugger toolbar allowing the user to disable all breakpoints including
debugger;
keywords.See the attached screenshot for the location.
Does that work for you?
To answer for stdedos+mozilla, it does disable all breakpoints including debugger;
statements, yes. Though doesn't address the use case described in comment 2.
A simple script for that is setInterval(function() { debugger; }, 0);
, though there are more sophisticated ones out there with the goal to block people from debugging and reverse-engineering foreign code.
Sebastian
Comment 15•3 years ago
|
||
I see, thank you Sebastian.
@stdedos+mozilla: what's your use case?
- Disable all (breakpoints +
debugger;
keywords) - Disable just the
debugger;
keywords (all occurrences) - Something else?
Honza
Comment 16•3 years ago
|
||
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #15)
I see, thank you Sebastian.
@stdedos+mozilla: what's your use case?
- Disable all (breakpoints +
debugger;
keywords)- Disable just the
debugger;
keywords (all occurrences)- Something else?
Honza
Apologies, I didn't get a chance to re-test the suggestion yesterday (I remember I didn't plain come here to cry about it, but I am not sure what was the case 2 months ago).
My use case is that I don't want "whoever decided" to while(sleep(0.1)) ; do debugger ; done
the life out of a page will get it their way :-p
Even if I don't want to mess with the js itself, DevTools being open means "automatic" debugger
hit & activation.
pls clear "my" needinfo flag yourself, if my answer satisfies your request
Comment 17•3 years ago
|
||
(In reply to stdedos+mozilla from comment #16)
My use case is that I don't want "whoever decided" to
while(sleep(0.1)) ; do debugger ; done
the life out of a page will get it their way :-p
Even if I don't want to mess with the js itself, DevTools being open means "automatic"debugger
hit & activation.
I see, make sense, thank you for the clarification.
pls clear "my" needinfo flag yourself, if my answer satisfies your request
Done
Comment 18•3 years ago
|
||
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #13)
Created attachment 9265126 [details]
image.pngThere is also a button available on Debugger toolbar allowing the user to disable all breakpoints including
debugger;
keywords.See the attached screenshot for the location.
Does that work for you?
To answer this question (after so much time, sorry about that!), it seems that this "cannot" apply to my case.
The "testbed" in question, is able to detect the DevTools being open in the tab, halt the operations of the page, and render the DevTools half-inoperable - be it at the start or any other time in the tab's timeline.
I don't even get to the point where I could possibly test the feature in question (and it doesn't matter if I pre-set it).
Chrome has no such problem - after it's armed with an anti-anti-debugger, that is (didn't test Firefox).
Tested on Firefox 97.0.2 (64-bit) @ Ubuntu 20.04.
Comment 19•2 years ago
|
||
(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #15)
@stdedos+mozilla: what's your use case?
- Disable all (breakpoints +
debugger;
keywords)- Disable just the
debugger;
keywords (all occurrences)- Something else?
The debugger;
keyword is used in obfuscation protection loops to prevent users from opening the DevTools console, as explained in this page.
Here's an example of such code being used in the wild, see function check()
at line #2:
var tryCount = 0;var minimalUserResponseInMiliseconds = 200;
function check(){console.clear();before = new Date().getTime();debugger;after = new Date().getTime();
if(after - before > minimalUserResponseInMiliseconds){document.write(" Dont open Developer Tools. ");self.location.replace(window.location.protocol + window.location.href.substring(window.location.protocol.length));
}else{before = null;after = null;delete before;delete after;}setTimeout(check, 100);}check();
window.onload = function(){document.addEventListener("contextmenu", function(e){e.preventDefault();}, false);document.addEventListener("keydown", function(e) {
if(e.ctrlKey && e.shiftKey && e.keyCode == 73){disabledEvent(e);}
if(e.ctrlKey && e.shiftKey && e.keyCode == 74){disabledEvent(e);}
if(e.keyCode == 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {disabledEvent(e);}
if(e.ctrlKey && e.keyCode == 85) {disabledEvent(e);}
if(event.keyCode == 123) {disabledEvent(e);}
}, false);function disabledEvent(e){if(e.stopPropagation){e.stopPropagation();} else if(window.event){window.event.cancelBubble = true;}e.preventDefault();return false;}};
Disabling the debugger;
keyword is not enough to resume execution in these cases.
So perhaps a new feature, being able to "silence" any function in the call stack (i.e. temporarily replacing its code with a no-op), so that when the first debugger;
call is made by the obfuscation code and pauses everything, the user can go to the call stack and mark the deepest (or second or third etc deepest) function in the call stack to be silenced, so that when you resume execution that function will not queue up another recursive call.
I think this might be an alternative to what's suggested in comment 11 about blackboxing, because to blackbox you need to be able to see and select the code lines, and in lots of cases this anti-debugging trick is hidden behind sophisticated obfuscation + inside an anonymous function, so the call stack is the only way to figure out which function this defensive debugger;
call is coming from.
Updated•2 years ago
|
Comment 20•2 years ago
|
||
I think having the ability to make "debugger" statements a no-op is a good first start.
Comment 21•2 years ago
|
||
To clarify, I mean all debugger statements.
Updated•1 year ago
|
Comment 23•1 year ago
|
||
Here's a blog post describing how they patched SpiderMonkey/Firefox to rename the "debugger" keyword, effectively disabling it:
Reporter | ||
Comment 24•1 year ago
|
||
As a hacky solution, I was thinking we could add an environment variable to avoid generating the code associated with debugger statement when such environment variable is set.
Comment 25•1 year ago
|
||
Same thing is abused by sites.
Payload:
function _0x39426c(e) {
function t(e) {
if ("string" == typeof e) return function(e) {}.constructor("while (true) {}").apply("counter");
1 !== ("" + e / e).length || e % 20 == 0 ? function() {
return !0
}.constructor("debugger").call("action") : function() {
return !1
}.constructor("debugger").apply("stateObject"), t(++e)
}
try {
if (e) return t;
t(0)
} catch (e) {}
}
setInterval(function() {
_0x39426c()
}, 4e3), console.log("%cSTOP! %s", css, "Great power comes great responsibility!");
Possible explanation(It's in French but Firefox translate works): https://www.stashofcode.fr/contourner-la-protection-debugger-des-sites-de-streaming/
Real world usage(check last few lines of this script): https://sflix.se/js/group_11/app.min.js?v=6.3
Comment 26•1 year ago
|
||
In my opinion the debugger
statement as well as any other calls which act (introspectively) on the execution environment (like clearing the console, or even writing to the console!) should urgently be put into question as a whole. Urgently, because this and related "features" are successfully exploited by many webpages, with the expressed goal of curtailing the user of their browser's intended functionality!
The very, principal idea of debugging is analyzing the code without affecting it. Code should not be given the opportunity to deliberately become "Heisenberg", and much less so against the wish of the user! And much, much less so if the code isn't trusted (i.e. the user trusts a promise about what the code will do) in the first place!
I don't know what thought process, which disrespected these principles, led to the introduction of the debugger
statement, but being able to deactivate this should be a no-brainer.
Updated•1 year ago
|
Assignee | ||
Comment 27•1 year ago
|
||
This toggle is checked by default so that debugger statements are still enabled by default.
As for other settings, this is preserved on restart so that it is handy for users willing to
always keep them disabled. But it may be an annoyance for others forgetting that they once disabled them.
Updated•1 year ago
|
Updated•1 year ago
|
Assignee | ||
Comment 28•1 year ago
|
||
Current patch suggests introducing a Pause on debugger statement
checkbox in the breakpoints panel.
This will be checked by default, so that we don't change current behavior.
It will be persisted exactly like any other debugger settings.
So for people willing to turns this off for ever, you have to click this once.
For the others, you will have to remember to re-enable this if you want to debugger statement to work again!
Comment 30•1 year ago
|
||
I'm excited to see progress on this!
I posted this in the time-reversed-duplicate bug 1300934, but I wanted to resuggest it here: I would ideally like to see debugger statements handled as a per-host permission, with the same UX that Firefox uses for existing per-host permissions like web notifications and camera/microphone. I think it makes sense for debugger statements to be disabled by default; they have no benefit to the end user unless that user is a developer, in which case they are only useful on those sites that the developer is actively debugging, which will probably be a relatively small list of hosts like localhost
and intranet-dev-server-4
and maybe qa.company.com
. For those sites, Firefox should make it easy to enable the preference (‘Will you allow intranet-dev-server-4
to pause the debugger?’) and persist it; ideally, it's not something I'd have to toggle on and off manually whenever I start debugging for real versus inspecting a random website, and it also needn't be something that I have to dig for in layers of advanced devtools preferences.
Grouping this with privacy permissions also makes some sense because websites can exploit debugger statements to detect whether devtools is open, which could be seen as a privacy leak.
Comment 31•1 year ago
|
||
Comment 32•1 year ago
|
||
bugherder |
Comment 33•1 year ago
|
||
Is this something we should add to the Fx121 relnotes? Please nominate if so.
Release Note Request (optional, but appreciated)
[Why is this notable]: New feature for web developers in Firefox DevTools.
[Affects Firefox for Android]: n/a
[Suggested wording]: The Firefox Debugger now includes a new feature: an option to disable the debugger;
keyword on the current page. This feature is accessible via a new checkbox in the Breakpoints side panel, labeled Pause on debugger statement, located next to the existing Pause on exceptions checkbox. By default, this option is enabled, meaning that the debugger statements are active unless manually disabled.
[Links (documentation, blog post, etc)]: Screenshot available in comment #28
Updated•1 year ago
|
Description
•