Open Bug 1578220 Opened 5 years ago Updated 10 months ago

Add a toggle to enable/disable all debugger statements (separately from the breakpoint toggle)

Categories

(DevTools :: Debugger, enhancement, P3)

enhancement

Tracking

(Not tracked)

People

(Reporter: nbp, Unassigned)

References

(Blocks 2 open bugs)

Details

(Keywords: dev-doc-needed)

Attachments

(1 file)

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

Priority: P5 → --
No longer blocks: firebug-gaps, dbg-control, dbg-70
No longer depends on: 925269

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.

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.

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?

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.

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.

Flags: needinfo?(nicolas.b.pierron)

I think this is doable, but Jim is likely to know better than me.

Flags: needinfo?(nicolas.b.pierron) → needinfo?(jimb)

Can't we just do this in the server, today, by not setting an onDebuggerStatement handler in the first place?

Flags: needinfo?(jimb)
Priority: -- → P3

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)

... but it does work on v67, as-advertised. You may need to "stay with that version" though.

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.

There is a new feature which is present in Firefox 98 and would help as selectively ignore debugger statements:

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.

(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 ...

Attached image image.png (deleted) —

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?

(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

I see, thank you Sebastian.

@stdedos+mozilla: what's your use case?

  1. Disable all (breakpoints + debugger; keywords)
  2. Disable just the debugger; keywords (all occurrences)
  3. Something else?

Honza

Flags: needinfo?(stdedos+mozilla)

(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?

  1. Disable all (breakpoints + debugger; keywords)
  2. Disable just the debugger; keywords (all occurrences)
  3. 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

(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

Flags: needinfo?(stdedos+mozilla)

(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #13)

Created attachment 9265126 [details]
image.png

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 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.

(In reply to Jan Honza Odvarko [:Honza] (always need-info? me) from comment #15)

@stdedos+mozilla: what's your use case?

  1. Disable all (breakpoints + debugger; keywords)
  2. Disable just the debugger; keywords (all occurrences)
  3. 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.

Severity: normal → S3

I think having the ability to make "debugger" statements a no-op is a good first start.

To clarify, I mean all debugger statements.

Blocks: eviltraps
Summary: Add a toggle to enable/disable all debugger statements → Add a toggle to enable/disable all debugger statements (separately from the breakpoint toggle)
Duplicate of this bug: 1846239

Here's a blog post describing how they patched SpiderMonkey/Firefox to rename the "debugger" keyword, effectively disabling it:

https://www.nullpt.rs/evading-anti-debugging-techniques

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.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: