Binary patch Firefox to always return false
for the navigator.webdriver
property.
First set up the debugging symbols for Firefox and install the dependencies.
Enter Gforth interpreter as root:
$ sudo gforth
Type the following commands into Gforth's interpreter.
include patch.4th
s" /usr/lib/firefox-esr/libxul.so" filepath!
find-offset
apply-patch
Assuming all went well, your Firefox is now patched!
Assuming you didn't get any errors while patching, you should test that the patch was successful.
- close Firefox
- open Firefox from the command line with marionette enabled
firefox --marionette
, this normally setsnavigator.webdriver
totrue
- open the inspector
- type
navigator.webdriver
into the console - verify that it is
false
This patcher was developed and tested on Debian 12 but it may also work on other distributions.
For this patcher to work, you need to have gdb, gforth, Firefox and the debugging symbols for Firefox installed.
See the debian wiki.
$ sudo apt install gdb gforth firefox-esr-dbgsym
You can use Violentmonkey with my script to proxy navigator.webdriver
to return false
.
This will work for many websites, but more sophisticated bot detection methods will be able to tell that the navigator.webdriver
property is being proxied.
The only reason I wrote this patcher is because I didn't want to keep a separate copy of Firefox or have to compile Firefox from source just for this really tiny change.
However, if you're willing to compile Firefox from source, then this is a trivial modification.
In Firefox's source directory open ./dom/base/Navigator.cpp
and find the Navigator::Webdriver
method.
It should look like this:
bool Navigator::Webdriver() {
#ifdef ENABLE_WEBDRIVER
nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
if (marionette) {
bool marionetteRunning = false;
marionette->GetRunning(&marionetteRunning);
if (marionetteRunning) {
return true;
}
}
nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID);
if (agent) {
bool remoteAgentRunning = false;
agent->GetRunning(&remoteAgentRunning);
if (remoteAgentRunning) {
return true;
}
}
#endif
return false;
}
Modify it to return false:
bool Navigator::Webdriver() {
return false;
}
Now compile and install Firefox. navigator.webdriver
will always be false
.
Here I'll describe how the patching works.
$ gdb /usr/lib/firefox-esr/libxul.so
after loading, gdb should tell us that debugging symbols were loaded for libxul.so
:
Reading symbols from /usr/lib/firefox-esr/libxul.so...
Reading symbols from /usr/lib/debug/.build-id/16/249d01b5f11eaf47fceedf77b9a846bdcc3c6f.debug...
If gdb says it can't find debugging symbols for libxul.so then see Setting up Debugging Symbols.
(gdb) break Navigator::Webdriver
Breakpoint 1 at 0x1a04810: file ./dom/base/Navigator.cpp, line 2290.
gdb found the offset for the method at 0x1a04810
, which is 27281424
in decimal. This offset will likely be different for you. Now we just need to write the bytes for the instructions starting at that offset into libxul.so.
In x86_64 assembly on GNU/Linux the instructions to return false from a function are:
xor rax, rax
ret
The patcher uses Gforth's built-in x86_64 assembler to generate the bytes for the instructions. You can just use an online assembler like here and copy the resulting bytes.
You should get 48 31 C0 C3
for the above mentioned assembly instructions. Replace METHOD_OFFSET
with the offset you found.
$ METHOD_OFFSET=27281424
$ echo -n -e '\x48\x31\xC0\xC3' > patch.bin
$ sudo dd if=patch.bin of=/usr/lib/firefox-esr/libxul.so seek=$METHOD_OFFSET bs=1 conv=notrunc