js代码安全-反编译初试

要想了解代码如何保护,那就先要知道怎么被破解,最开始了解代码保护,是eletron项目,因此从吾爱破解上找到过一个electron项目-NeatReader,尝试反编译。

1. 相关工具

2. Electron程序解包

以macos 为例说明。

  1. 找到Finder​ - 应用程序​ - NeatReader​,右键显示包内容。

  2. 找到Resource​ 目录中的app.asar​ 文件。复制文件到工作目录。

  3. 全局安装asar,已安装可以跳过这一步

    npm install asar -g
  4. 进入到工作目录,解压app.asar​文件

    npm extract app.asar app
  5. app​ 目录即NeatReader​ 解压源码

  6. 修改源码

    很简单,源码只进行了简单的代码压缩,部分变量做了简化,还是拥有比较强的可读性的。

    1. 首先我想修改一下NeatReader​ 登录的用户名。VsCode 打开app 目录,全局搜索用户名以乌漆嘛黑​ 为例,找到这块代码

      // /app/build-app/static/js/4.912926e6.chunk.js 
      
      n.handleLoginOrRegisterSuccess = function(e) {
          var t = {
              token: e,
              deviceId: d.default.getDeviceId()
          };
      
        e = JSON.parse('{"code":1,"msg":"获取所有信息成功","userName":"乌漆嘛黑","nickName":"小黑","avatar":null,"registerTime":"1999-09-15T09:09:09.000Z","registerSource":0,"qqId":null,"weixinId":null,"weiboId":null,"googleId":null,"facebookId":null,"membershipExpireTime":4104748800000,"membershipType":1,"trial":0}');
          d.default.saveUserName(e.userName),
              d.default.saveNickName(e.nickName),
              d.default.saveMembershipType(e.membershipType),
              d.default.saveMembershipExpireTime(e.membershipExpireTime),
              d.default.saveMembershipIsTrial(e.trial),
              n.autoCloseWindow()
        
          return
        
          f.a.post(p.a.build(2, "getUserProfile"), t, function(e) {
              console.log("===Server User Profile===", e),
              e.code > 0 ? (d.default.saveUserName(e.userName),
              d.default.saveNickName(e.nickName),
              d.default.saveMembershipType(e.membershipType),
              d.default.saveMembershipExpireTime(e.membershipExpireTime),
              d.default.saveMembershipIsTrial(e.trial),
              n.autoCloseWindow()) : n.autoCloseWindow()
          })
      }

      从上述代码可以看出吾爱的破解应该是拦截了登录成功的回调,伪造了用户信息。设置了很久的会员过期时间。从而破解了NeatReader的会员。因此需要修改用户信息,在这块代码修改即可。

    1. 接着吾爱破解后,在每个书籍的标题栏,增加了吾爱破解的标识,很长一段话,影响看图书标题,想将这段话去掉。

      全局搜索这段话结果搜不到,推测应该是吾爱对字符进行了编码, 所以对这段话分别进行了Unicode编码以及16进制转换,均搜索不到这段内容。

      由此推断,应该是用了其他加密的手段,要想找到这块代码逻辑。首先想到的是设置document.title​ 全局搜索这块代码搜索不到。electron​ 也可以在打开新窗口的时候在主进程设置title,找到eletron-main.js​ 文件,发现并没有设置title

      但是,从eletron-main.js​ 中发现了,他可以预留了devtools​ 快捷键。所以手动修改了env 为dev,重新打包。这时候打开阅读窗口,按快捷键,就可以唤起chrome的 devtools

      // /app/electron-main.js
      // 注册快捷键:打开当前Window的DevTool
      globalShortcut.register('CommandOrControl+Shift+7', () => {
         console.log('快捷键:打开当前页面DevTool');
         let focusWeb = webContents.getFocusedWebContents();
      
         focusWeb.openDevTools({mode: 'detach'});
      });

      接下来使用chrome devtools​ 自带的Dom Breakpoint​,调试网页title的变化。首先在控制台执行document.title = ''​,然后找到元素下title标签右键break on​ 选择attribute modifications subtree modifications​ 进行元素变更断点,然后刷新页面。进入断点。

      // /app/build-app/static/js/1.cc07b31a.chunk.js
      window["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]['\x74\x69\x74\x6c\x65']=t['\x62\x6f\x6f\x6b\x4e\x61\x6d\x65']+(function uncompileStr(VkoPsQuL1){VkoPsQuL1=window["\x75\x6e\x65\x73\x63\x61\x70\x65"](VkoPsQuL1);var jjftUbi2=window["\x53\x74\x72\x69\x6e\x67"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](VkoPsQuL1['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](0)-VkoPsQuL1['\x6c\x65\x6e\x67\x74\x68']);for(var fzqD$3=1;fzqD$3<VkoPsQuL1['\x6c\x65\x6e\x67\x74\x68'];fzqD$3++){jjftUbi2+=window["\x53\x74\x72\x69\x6e\x67"]['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](VkoPsQuL1['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](fzqD$3)-jjftUbi2['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](fzqD$3-1))}return jjftUbi2})("\x25\x75\x33\x30\x34\x43\x25\x75\x36\x30\x30\x30\x25\x75\x36\x30\x30\x30\x25\x75\x36\x30\x30\x30\x25\x75\x36\x30\x30\x30\x25\x75\x36\x30\x30\x30\x25\x75\x36\x30\x30\x30\x25\x75\x33\x30\x33\x35\x67\x25\x41\x32\x25\x44\x46\x25\x44\x39\x25\x44\x33\x25\x43\x45\x25\x39\x33\x25\x39\x31\x25\x44\x31\x25\x38\x45\x40\x25\x75\x35\x39\x41\x32\x25\x75\x43\x31\x31\x45\x25\x75\x46\x31\x36\x35\x25\x75\x45\x39\x36\x30\x25\x75\x45\x42\x37\x43\x25\x75\x31\x42\x35\x34\x25\x75\x44\x45\x36\x35\x25\x75\x41\x38\x37\x33\x25\x75\x43\x45\x41\x35\x25\x75\x30\x31\x31\x46\x25\x75\x46\x31\x32\x36\x25\x75\x43\x38\x33\x30\x25\x75\x43\x45\x36\x34\x25\x75\x44\x44\x41\x42\x25\x75\x37\x31\x35\x34\x25\x75\x38\x41\x46\x31\x25\x75\x31\x34\x34\x41\x25\x75\x44\x36\x36\x36\x25\x75\x39\x43\x43\x36\x25\x75\x45\x35\x31\x35\x25\x75\x30\x42\x37\x38\x25\x75\x43\x33\x42\x36\x25\x75\x41\x39\x46\x34\x25\x75\x41\x39\x43\x36\x25\x75\x41\x32\x45\x43\x25\x75\x43\x43\x41\x30\x25\x75\x46\x32\x38\x41\x25\x75\x46\x31\x36\x34\x25\x75\x45\x44\x37\x32\x25\x75\x37\x35\x39\x46\x25\x75\x34\x44\x32\x38\x25\x75\x41\x44\x41\x34\x25\x75\x42\x42\x39\x44\x25\x75\x45\x37\x45\x42\x25\x75\x31\x34\x34\x41\x25\x75\x44\x36\x36\x36\x25\x75\x43\x33\x32\x39\x25\x75\x43\x33\x42\x36\x25\x75\x41\x33\x44\x34\x25\x75\x41\x33\x36\x30\x25\x75\x42\x30\x33\x30\x25\x75\x45\x32\x31\x42\x25\x75\x31\x37\x36\x33\x25\x75\x30\x34\x33\x33\x25\x75\x45\x31\x46\x44\x25\x75\x30\x35\x33\x43\x25\x75\x38\x46\x32\x30\x25\x75\x35\x33\x33\x32\x25\x75\x41\x36\x33\x46\x25\x75\x35\x31\x32\x35\x25\x75\x34\x44\x30\x43\x25\x75\x41\x30\x30\x37\x25\x75\x41\x36\x31\x35\x25\x75\x42\x42\x41\x41\x25\x75\x45\x39\x38\x36\x25\x75\x30\x46\x30\x39\x25\x75\x38\x43\x32\x30")

      找到了修改标题的代码。果然是关键子转换到了16进制,但是是关键词documnt​ 、title​ 之类的进行了16进制转换。文本文档还有一层code码偏移。

      为了方便阅读我们使用代码反编译工具进行解密,简单使用在线工具解密后得到, 代码之后进行了关键词替换得到以下代码。

      window["document"]['title'] = t['bookName'] + (function uncompileStr(params) {
          params = window["unescape"](params);
          var str = window["String"]['fromCharCode'](params['charCodeAt'](0) - params['length']);
          for (var index = 1; index < params['length']; index++) {
              str += window["String"]['fromCharCode'](params['charCodeAt'](index) - str['charCodeAt'](index - 1))
          }
          return str
      })("%u304C%u6000%u6000%u6000%u6000%u6000%u6000%u3035g%A2%DF%D9%D3%CE%93%91%D1%8E@%u59A2%uC11E%uF165%uE960%uEB7C%u1B54%uDE65%uA873%uCEA5%u011F%uF126%uC830%uCE64%uDDAB%u7154%u8AF1%u144A%uD666%u9CC6%uE515%u0B78%uC3B6%uA9F4%uA9C6%uA2EC%uCCA0%uF28A%uF164%uED72%u759F%u4D28%uADA4%uBB9D%uE7EB%u144A%uD666%uC329%uC3B6%uA3D4%uA360%uB030%uE21B%u1763%u0433%uE1FD%u053C%u8F20%u5332%uA63F%u5125%u4D0C%uA007%uA615%uBBAA%uE986%u0F09%u8C20")

      由此可见吾爱对这段文字先进行了Unicode编码, 然后进行了位数偏移,转义,最后有转换为了16进制。不过,我们只是想去掉这段文本,不用关心,他加密的逻辑,只需要去掉bookName​ 之后的代码即可。代码

3. 重新将打包成asar,替换app.asar文件

asar pack ./app app.asar

备份之前的app.asar​ 文件,将修改过重新打包的app.asar​ 文件放回到Finder​ - 应用程序​ - NeatReader​-Resource​ 目录中。重启NeatReader

至此完成了对于NeatReader 代码的修改,并应用到了应用程序之上。