On a sunny day, you suddenly need to run a piece of code and open the console. At this point, it happens to be on a page. But as SOON as I opened the console, something caught my eye. It was actually the page’s watermark

Obsessive-compulsive curiosity

After running my code, I cut back to the Element section and tried to delete it. Click to select the div, then click delete

“Pa!” I guess I didn’t press it. “Snap!” , huh? Div flashed, right? “Pa!” Damn it, I can’t delete it! ?

Okay, I’ll change the style. Display: None, arrange! When I typed, div flashed again and all the changes were gone

At this point, the object of suspicion soon appeared — the MutationObserver

MutationObserver: Provides the ability to monitor changes made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which is part of the DOM3 Events specification. For details, see MDN

The general logic, then, is that MutationObserver listens for changes to the watermark, such as deleting, modifying attr, adding child nodes, and then directly re-renders an element that is exactly the same as the original, achieving the effect that “you can’t change this node even if you open the console”

Source search research

In the Source section, find the js file associated with the page, search MutationObserver, and find a function that looks like this:

  function observeSelector(e) {
    if (e) {
      var t = e.cloneNode(!0)
        , n = e.parentNode || document.body;
      new MutationObserver(function (r) {
        e && r.forEach(function (r) {
          var o = r.target
            , i = Array.prototype.slice.call(r.removedNodes)[0];
          if (o === e) {
            var a = t.cloneNode(!0);
            n.replaceChild(a, e),
              e = a
          } else
            i === e && (e = e.cloneNode(!0),
              n.appendChild(e))
        })
      }
      ).observe(document.body, {
        attributes:!0.childList:!0.subtree:!0}}})Copy the code

Modified to enhance readability:

  function observeSelector(element) {
    if (element) {
      const parentNode = element.parentNode || document.body;
      // Why do you do that? Because this is the original node
      // If you take element directly to replace, you can only get the node with the latest attribute
      const newClonedNode = element.cloneNode(true);
      new MutationObserver(mutations= > {
        mutations.forEach(mutationRecord= > {
          const currentTarget = mutationRecord.target;
          const removedNode = mutationRecord.removedNodes[0];
          When modifying an attribute, target is the current element
          if (currentTarget === element) {
            const replaceNode = newClonedNode.cloneNode(true);
            parentNode.replaceChild(replaceNode, element);
            element = replaceNode;
          } else {
            When removing an element, removedNodes is an array. Delete only one of them, the first of which is the current element
            if (removedNode === element) {
              element = element.cloneNode(true); parentNode.appendChild(element); }}}); }).observe(document.body, {
        attributes: true.childList: true.subtree: true.// Listen for descendant node changes}); }}Copy the code
  • Mutations target the target on mutationRecord for each element is the current node when you modify an attribute (this callback can only be triggered if the attributes are true). The idea is: replace back

  • When a node is deleted, the removedNodes array in the mutationRecord is an array of all the nodes that are currently being deleted. Of course, we only deleted one node here, so that’s the only node left. The idea is: delete a append back

This function can be used directly to “protect elements” by adding a MutationObserver to an element, preventing other technical people from opening the console and modifying the element to do other shady things (fake screenshots, overpass permissions, exposed data but watermarked).

This function can be taken out to be used as a protection element to prevent some front end from opening the console to modify the element and then taking screenshots. Of course, there are things to consider in the requirements if they need to be used: timely removal of the Observer, extensibility, and tolerable compatibility

How to Beat it

Magic to change the style

It can be solved by changing the style of the parent node, but the watermarking parent node of this page is body. If the body is changed, the browsing page will be affected. We can change the Angle and give the before element of the watermark a transparent background style to make it look similar to the watermark color

Canvas getimageData knows 137
    var str = '. Watermark div class::before {content: '; width: 100vw; height: 100vh; position: fixed; top: 0; left: 0; z-index: 10000; Background: rgba(137, 137, 137, 0.95); pointer-events: none; } `;

    var style = document.createElement('style');
    style.textContent = str;
    document.head.appendChild(style);
    // Adjust the fliter, such as contrast, brightness, saturation, etc
    document.body.style.filter = 'contrast (6.5)'
Copy the code

However, this will make the page hazy layer

Remove the main content

We know that interfering with the style of its parent is enough, but we are afraid of killing the content by mistake. So how about we take the content away and put the body behind it.

// Console selects the main content, i.e. Document. querySelector(' Watermark element selector ')
document.documentElement.appendChild($0)
Copy the code

Then, add display: None to the body, and an unwatermarked white page appears!

document.body.style.display = 'none';
Copy the code

While the priest climbs a post, the devil climbs ten. Document.documentelement is a watermark element, and appends it back to the body.

    ((targetNode) = > {
      new MutationObserver((mutations) = > {
        mutations.forEach(({ addedNodes }) = > {
          addedNodes.forEach(node= > {
            if (node === targetNode) {
              document.body.appendChild(targetNode)
            }
          })
        });
      }).observe(document.documentElement, {
        childList: true}); }) (document.querySelector('Watermark Element selector'));
Copy the code

em… Have you ever wondered what happens to nesting dolls? Watch the HTML add a target node and move it under the body; Watch the body add a target node, then move it to HTML, then cause the HTML to add a new node

    ((targetNode) = > {
      new MutationObserver((mutations) = > {
        mutations.forEach(({ addedNodes }) = > {
          addedNodes.forEach(node= > {
            if (node === targetNode) {
              document.body.appendChild(targetNode)
            }
          })
        });
      }).observe(document.documentElement, {
        childList: true}); }) (document.querySelector('Watermark Element selector'));
    // Add body observe
    ((targetNode) = > {
      new MutationObserver((mutations) = > {
        mutations.forEach(({ addedNodes }) = > {
          addedNodes.forEach(node= > {
            if (node === targetNode) {
              document.documentElement.appendChild(targetNode)
            }
          })
        });
      }).observe(document.body, {
        childList: true}); }) (document.querySelector('Watermark Element selector'));
Copy the code

Stop it, my computer is too hot, I guess its health code has turned red, it needs to be isolated from me. Dead loops do happen and need to be used with care

If you want to solve the problem of the MutationObserver listening to document.documentElement to prevent watermarking elements, you can also create a new div under the documentElement and move the watermarking elements inside the div

MutationObserver disallows all childList and subtree elements, removes them if they are not watermark elements, and puts them back in the body if they are

All so unfeeling, then I will write a Google Browser plug-in injection script, directly modify the global MutationObserver see you how to play…… That’s it. It’s always a battle of wits on the service side

Pay attention to the public account “different front-end”, learn front-end from a different perspective, grow fast, play with the latest technology, explore all kinds of black technology, with rich imagination to do things