【调试逆向】极x壁纸 - 图片请求数据解密
2021-07-29 09:32:00 Author: mp.weixin.qq.com(查看原文) 阅读量:129 收藏

作者坛账号:jixun66

  • 极◯壁纸:https://bz.zzzmh.cn/index

  • 使用 Selenium 爬图 by @lihu5841314

这个网站不知道从哪找的加密程序,强行开开发者工具会比较吃内存/CPU,只好想别的办法

准备操作

  1. 安装并配置好 Charles 反代工具,启动并确保能正常抓取流量。

  2. 安装好编辑器,如 VSCode。

  3. NodeJS,用来本地运行代码片段。


访问主页并等待加载完毕,将所有 JS 资源文件都保存下来:

  1. 右键 https://bz.zzzmh.cn/ 下的 js 目录

  2. 选择 “Save All...”,保存到一个目录下。

了。


使用 Map Local 把文件都改为从本地获取:

  1. 右键 https://bz.zzzmh.cn/ 下的 js 目录

  2. 选择 Map Local

  3. 点击 Local Path 的 Choose 按钮,选择 Charles 保存资源的 js 目录。


原始网页引入的代码存档:

 js_原始.7z (236.71 KB, 论坛下载)


现在可以使用编辑器打开这个 js 目录了。

因为不是很想解决这个反调试功能,只好自己加一些用来帮助调试的“调试器”了:

直接将下述代码随便塞到一个文件的顶部:

 复制代码 隐藏代码
(function() {
  const container = document.createElement('div');
  const console = document.createElement('pre');
  const input = document.createElement('textarea');
  const exec = document.createElement('button');

  const log = (text) => {
    console.appendChild(document.createTextNode(text));
  };

  const clear = () => {
    console.textContent = '';
  };

  input.cols = 60;
  input.rows = 8;
  exec.textContent = 'exec';
  exec.type = 'button';
  exec.onclick = () => {
    eval(input.value);
  };
  container.appendChild(input);
  container.appendChild(exec);
  container.appendChild(console);
  document.body.insertAdjacentElement('afterbegin', container);
})();

然后刷新浏览器就可以看到一个输入框和执行按钮,可以直接运行一些简单的代码并获取结果。

比如遇到加密字符串的时候,直接复制过来就能看到解密后的文字了:

随后观察 Charles 抓包到的内容,可以发现这么一则请求:

 复制代码 隐藏代码
POST https://api.zzzmh.cn/bz/v3/getData
 复制代码 隐藏代码
{
  "size": 23,
  "current": 0,
  "sort": 0,
  "category": 0,
  "resolution": 0,
  "color": 0,
  "categoryId": 0
}

{
  "msg": "success",
  "result": "ak+...省略...wE=",
  "code": 0,
  "ts": 123456789
}

感觉这个是我们要找的回应,所以直接检索 getData 看看能找到什么:

(VSCode 左侧的第二个图标是全局搜索,搜索到结果后点进去后进行格式化代码处理)

看起来是被 Babel 处理过的代码(async/await 降级),而下面的处理代码却可以直接访问 ["data"]["result"] 对象的成员,怀疑这个接口对返回值进行了处理之后才返回。

“Ctrl-左键” _0x3b1f45 跳转到定义,可以看到很多类似 xxx = yyy("1234") 的代码:

看起来很像是 Webpack 打包后的 require 调用,分别对他们选中、按下 F2 并更名为 httpLib

随后进行全局检索关键字 8e44,进入第一个找到的内容:

看起来第一个函数就是我们想找的,直接改一改变量名看看:

可以看到又调用了一个叫 ed08 的包,暂命名为 decipherLib,继续重复查找过程…

找到后下拉可以看到一串像是密钥一样的内容。因为我们知道上图中的关键字 decipher,于是一路折叠这些函数,直到模块底部:

可以看到导出了一些函数,在这里就可以很方便的更名/查找函数了。

直接改名与左侧的名称相同,然后进去看看:

稍微看一看这三个函数,感觉第一层的 4e50b7 只是简单的 base64 解码,4d16fb 是编码,而中间的 58af99 则像是对内容进行解密。没有看到其他依赖,直接复制进去并传入抓包得到的返回值进去看看:

得到结果了,可以试试移植到本地了。

移植

复制粘贴出来,然后进行一点点小清理就能得到类似下述的代码:

 decoder.src.7z (3.56 KB, 论坛下载)

 复制代码 隐藏代码
function decrypt(input) {
  const key = [
    0x91, 0x34, 0x5b, 0x41, 0xbf, 0x74, 0x77, 0x6a, 0x87, 0xae, 0xfb, 0x50,
    0x33, 0x61, 0x44, 0xad, 0x90, 0xcd, 0x17, 0xd2, 0xde, 0x8e, 0xc9, 0xf5,
    0x81, 0x5a, 0x21, 0x16, 0xe1, 0x32, 0xef, 0x14, 0xd4, 0x0f, 0xa2, 0x85,
    0x76, 0xe9, 0xc3, 0x72, 0x47, 0x98, 0x82, 0x8b, 0xaf, 0xca, 0xee, 0x92,
    0xfc, 0xa1, 0xa5, 0x5e, 0xb0, 0xf2, 0x78, 0x69, 0x55, 0x68, 0xaa, 0x94,
    0x43, 0x19, 0x65, 0x6c, 0x10, 0x97, 0x6f, 0xf6, 0x75, 0xb7, 0x4d, 0x59,
    0xe3, 0x9e, 0xbc, 0x70, 0x6b, 0xff, 0x56, 0x79, 0x58, 0x9b, 0x84, 0x45,
    0xe2, 0xf8, 0x8f, 0xb6, 0x8a, 0x39, 0xe7, 0x0c, 0x8d, 0x96, 0x5f, 0x7f,
    0x54, 0x7c, 0x9a, 0xe4, 0x49, 0x2b, 0xc4, 0x1c, 0x2e, 0x73, 0x1e, 0x7a,
    0xb5, 0x7d, 0xbd, 0xb3, 0x03, 0xf9, 0xcb, 0xf3, 0x35, 0x4e, 0xb8, 0x01,
    0x0b, 0xb9, 0xd9, 0xb1, 0xfd, 0x13, 0x29, 0x7e, 0xd5, 0x83, 0xe5, 0x22,
    0x3f, 0x08, 0x48, 0xdd, 0xd7, 0xc1, 0x3c, 0xe8, 0x66, 0x2f, 0x89, 0x99,
    0xea, 0x2d, 0x3b, 0x40, 0xa0, 0x31, 0x53, 0x95, 0x88, 0xc7, 0xba, 0x00,
    0xda, 0xac, 0xd8, 0x18, 0x0e, 0x30, 0x1d, 0x2c, 0xdc, 0xd1, 0x38, 0xa4,
    0x26, 0x25, 0x04, 0xce, 0x67, 0x0a, 0xa7, 0x37, 0x71, 0xe6, 0x6e, 0x36,
    0x24, 0xec, 0xb2, 0xf4, 0x8c, 0x46, 0xdb, 0x05, 0xc2, 0xb4, 0xd0, 0xc0,
    0x4f, 0x64, 0x28, 0x06, 0xc6, 0xa6, 0xed, 0xf7, 0x27, 0x5d, 0x9d, 0x15,
    0x07, 0x1a, 0xfe, 0x1b, 0xd3, 0x51, 0x3a, 0x86, 0x4c, 0xbe, 0x02, 0x5c,
    0xd6, 0x62, 0xf0, 0x09, 0x3d, 0x3e, 0xf1, 0x63, 0xeb, 0x1f, 0xc8, 0x57,
    0x11, 0xcc, 0xbb, 0xdf, 0xc5, 0xab, 0x42, 0x4a, 0x12, 0xa3, 0x80, 0xa9,
    0xe0, 0x2a, 0x20, 0xa8, 0x6d, 0x60, 0x0d, 0xfa, 0x4b, 0x9c, 0xcf, 0x23,
    0x9f, 0x52, 0x93, 0x7b
  ];
  let idxA = 0;
  let idxB = 0;
  let result = [];
  for (let i = 0; i < input.length; i++) {
    idxA = (idxA + 0x1) & 0xff;
    idxB = (key[idxA] + idxB) & 0xff;

    let tmp = key[idxA];
    key[idxA] = key[idxB];
    key[idxB] = tmp;

    const realKeyIdx = (key[idxA] + key[idxB]) & 0xff;
    result.push(input[i] ^ key[realKeyIdx]);
  }
  return result;
}
function fromUTF8Array(input) {
  return Buffer.from(input).toString("utf-8");
}

function b64Decode(input) {
  return new Int8Array(Buffer.from(input, "base64"));
}
function decipherFn(_0x37db15) {
  return fromUTF8Array(decrypt(b64Decode(_0x37db15)));
}

module.exports = {
  decipherFn,
};

另外这个 decrypt 函数看起来像是预先初始化密钥盒的 rc4,具体就不知道了。

注意:使用到的 Buffer 为 node.js 平台专有 API,浏览器无法正常执行。

如果需要移植到 python,直接拿这个代码改改就好。

拼接链接

这个直接正常点击下载然后观察解密后数据与下载链接的关系即可。

如果你还是想知道怎么生成的话,参考下述整理好的代码:

 复制代码 隐藏代码
var urlBase = "https://doge.zzzmh.cn";

function getUrl(imageId, isPng, imgType) {
  var end = "";
  if (0x0 == imgType) {
    end = "/thumbs";
  } else if (0x1 == imgType) {
    end = "/fhd";
  } else if (0x2 == imgType) {
    end = "?response-content-disposition=attachment";
  }

  const ext = isPng ? ".png" : ".jpg";
  return urlBase + "/wallpaper/origin/" + imageId + ext + end;
}

后记

现在对抗 devtools 的工具已经越来越多了…

本次实验基本上是绕过了 devtools(更改代码与注入一个 “简陋” devtools 到页面进行调试)。

实际研究的时候走了一些弯路,包括尝试干掉反调试;不知道是 devtools 的问题还是网页做了一些检测,一开就占满 CPU 与内存。

最后:仅供学习交流之用,勿做商业用途。

--

www.52pojie.cn

--

pojie_52


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5Mjc3MDM2Mw==&mid=2651136212&idx=1&sn=ebab77b4dfb04e511f371e1fd064d3b7&chksm=bd50b0808a273996d7e5eca2a89ebf6b70a2c833c84fd3dd2950da3da6a28d3551f4133b2be1#rd
如有侵权请联系:admin#unsafe.sh