This took some digging. samy.pl has several levels of indirection and obfuscation on top of this code. It uses a different version of the detection code than the GitHub repository found by JohanP. The code in samy.pl, unlike the GitHub repository, can detect the devtools when they are not docked.
It does so by using a short script that executes differently depending on whether devtools is open or closed.
Example script
Here's a self-contained example; open it in a browser and notice how the output changes as the devtools are opened and closed (whether it is docked or not):
<!DOCTYPE html>
<html>
<body>
<pre id="output"></pre>
<script type="text/javascript">
var element = new Image;
var devtoolsOpen = false;
element.__defineGetter__("id", function() {
devtoolsOpen = true; // This only executes when devtools is open.
});
setInterval(function() {
devtoolsOpen = false;
console.log(element);
document.getElementById('output').innerHTML += (devtoolsOpen ? "dev tools is open
" : "dev tools is closed
");
}, 1000);
</script>
</body>
</html>
How it works
The setInterval is executed every second. console.log
always executes, whether the devtools are open or closed: The console
object is always defined. However, the log
method only writes output to the console when the devtools are open. If the devtools are closed, console.log
is a no-op. That's the key that lets you detect if the devtools are open: detecting if the log operation is a no-op.
In the process of writing element
to the console, it gets the id of the element. That calls the function attached with __defineGetter__
. Therefore, console.log(element)
only calls that function when the devtools are open and console.log
is not a no-op. The flag is set in that function, giving us an updated view of the devtools state every second.
samy.pl uses some additional tricks to hide this: the console is also cleared every second, and this code is obfuscated with a whitespace (!) encoding.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…