ob混淆原理与破解方案(1)

一. 混淆简介

1. 为什么要进行混淆

对于网页来说,其逻辑是依赖于JavaScript来实现的,JavaScript 有如下特点:

  • JavaScript 代码运行于客户端,也就是它必须要在用户浏览器端加载并运行。

  • JavaScript 代码是公开透明的,也就是说浏览器可以直接获取到正在运行的 JavaScript 的源码。

2. 压缩、混淆、加密技术
  • 代码压缩:即去除JavaScript 代码中的不必要的空格、换行等内容,使源码都压缩为几行内容,降低代码可读性,当然同时也能提高网站的加载速度。

  • 代码混淆:使用变量替换、字符串阵列化、控制流平坦化、多态变异、僵尸函数、调试保护等手段,使代码变得难以阅读和分析,达到最终保护的目的。但这不影响代码原有功能。是理想、实用的JavaScript保护方案

  • 代码加密:可以通过某种手段将 JavaScript 代码进行加密,转成人无法阅读或者解析的代码,如将代码完全抽象化加密,如 eval 加密。另外还有更强大的加密技术,可以直接将 JavaScript 代码用 C/C++ 实现,JavaScript 调用其编译后形成的文件来执行相应的功能,如Emscripten 还有 WebAssembly

二.ob混淆

OB 混淆全称 Obfuscator,Obfuscator 其实就是混淆的意思,官网:JavaScript Obfuscator Tool ,其作者是一位叫 Timofey Kachalov 的俄罗斯JavaScript开发工程师,早在 2016 年就发布了第一个版本。

1. ob混淆的特点
  1. 一般由一个大数组或者含有大数组的函数、一个自执行函数、解密函数和加密后的函数四部分组成;

  2. 函数名和变量名通常以 _0x 或者 0x 开头,后接 1~6 位数字或字母组合;

  3. 自执行函数,进行移位操作,有明显的 push、shift 关键字;

混淆之前的代码

source code
function hi() {
  console.log("Hello World!");
}
hi();

混淆之后的代码

var _0x3ed0 = ['1241023ikpdYM', 'Hello\x20World!', '291190xIUkft', '1251274vQVPdI', '124952hgHyOi', '1983KQSSIW', '247DipWFn', '7354VgseoG', '49680CQWPxl', '1ZTWTUo', '648lISKkF'];
 
function _0x4ed9(_0x475ec5, _0x372034) {
  return _0x4ed9 = function (_0x3ed0df, _0x4ed9c4) {
    _0x3ed0df = _0x3ed0df - 0x96;
    var _0x5a22f3 = _0x3ed0[_0x3ed0df];
    return _0x5a22f3;
  }, _0x4ed9(_0x475ec5, _0x372034);
}
 
(function (_0xa942b4, _0x57410c) {
  var _0x4e4980 = _0x4ed9;
 
  while (!![]) {
    try {
      var _0x1e86fa = parseInt(_0x4e4980(0x9b)) + parseInt(_0x4e4980(0x9e)) + -parseInt(_0x4e4980(0x97)) + -parseInt(_0x4e4980(0x9c)) * -parseInt(_0x4e4980(0xa0)) + -parseInt(_0x4e4980(0x98)) * parseInt(_0x4e4980(0x9d)) + -parseInt(_0x4e4980(0x96)) + parseInt(_0x4e4980(0x99)) * parseInt(_0x4e4980(0x9a));
 
      if (_0x1e86fa === _0x57410c) break;else _0xa942b4['push'](_0xa942b4['shift']());
    } catch (_0x178fbf) {
      _0xa942b4['push'](_0xa942b4['shift']());
    }
  }
})(_0x3ed0, 0xb3f61);
 
function hi() {
  var _0x81b55a = _0x4ed9;
  console['log'](_0x81b55a(0x9f));
}
 
hi();
2. ob混淆结构

一般obfuscator混淆的代码结构分为以下几部分:

第一部分: 定义一个数组

  • 数组里面保存了混淆或加密的变量,例如上面的_0x3ed0这个大数组,只是一个简单的形式数组,还可以再次混淆到看不出是一个数组。数组的位置也不一定在第一行,复杂一点的obfuscator混淆,会把这个数组放在比较隐藏的位置。

第二部分:重构数组

  • 把数组0x3ed0再次重构,例如,把数组的顺序重新排列等,这一部分一般用两个函数来处理,比如,上面的函数0x4ed9和自执行函数,有内存泄漏的风险,不建议格式化这部分代码。

第三部分:自解密函数

  • 解密函数是用来解密的,有可能有定时器,由于本例子简单没有解密函数,其实一般网站不会使用这么简单的混淆,本文只是为了讲解方便才使用此示例代码,有内存泄漏的风险,不建议格式化这部分代码。

第四部分:真实代码

  • 整个ob混淆的难度取决于此部分,这里面是加密前的逻辑,主要由真实代码(我们想要的代码)+ 控制流平坦化构成。

  • 这一部分就是示例中的hi()函数及其调用,这是我们真正想要的代码,分析代码就是为了找出这一部分。

第五部分:可以删除的垃圾代码

  • 这部分主要由“控制流平坦化+无限debugger自执行函数+死代码注入”构成,一般不涉及业务逻辑,既然一般也有特殊情况,不要看到有debugger的,有控制流平坦化的就删除,具体问题具体分析。

注意:obfuscator混淆后的这五部分顺序不一定按上面列出的顺序,例如,有可能第三部分在在第一部分前面,第一部分会有可能在第三部分位置;另外,也不是每个obfuscator混淆的代码都包括这五部分,本列子中就只有三部分。

3.ob混淆介绍

JavaScript 混淆完全是在 JavaScript 上面进行的处理,它的目的就是使得 JavaScript 变得难以阅读和分析,大大降低代码可读性,是一种很实用的 JavaScript 保护方案。

JavaScript 混淆技术主要有以下几种:

  • 变量混淆 将带有含意的变量名、方法名、常量名随机变为无意义的类乱码字符串,降低代码可读性,如转成单个字符或十六进制字符串。

  • 字符串混淆 将字符串阵列化集中放置、并可进行 MD5 或 Base64 加密存储,使代码中不出现明文字符串,这样可以避免使用全局搜索字符串的方式定位到入口点。

  • 属性加密 针对 JavaScript 对象的属性进行加密转化,隐藏代码之间的调用关系。

  • 控制流平坦化 打乱函数原有代码执行流程及函数调用关系,使代码逻变得混乱无序。

  • 僵尸代码 随机在代码中插入无用的僵尸代码、僵尸函数,进一步使代码混乱。

  • 调试保护 基于调试器特性,对当前运行环境进行检验,加入一些强制调试 debugger 语句,使其在调试模式下难以顺利执行 JavaScript 代码。

  • 多态变异 使 JavaScript 代码每次被调用时,将代码自身即立刻自动发生变异,变化为与之前完全不同的代码,即功能完全不变,只是代码形式变异,以此杜绝代码被动态分析调试。

  • 锁定域名 使 JavaScript 代码只能在指定域名下执行。

  • 反格式化 如果对 JavaScript 代码进行格式化,则无法执行,导致浏览器假死。

  • 特殊编码 将 JavaScript 完全编码为人不可读的代码,如表情符号、特殊表示内容等等。

总之,以上方案都是 JavaScript 混淆的实现方式,可以在不同程度上保护 JavaScript 代码。

4. 实现ob混淆

安装ob混淆库

npm install javascript-obfuscator -g

安装完成后,javascript-obfuscator就是一个独立的可执行命令了。

1. 代码压缩

这里javascript-obfuscator也提供了代码压缩的功能,使用其参数 compact即可完成JavaScript 代码的压缩,输出为一行内容。默认是 true,如果定义为 false,则混淆后的代码会分行显示。

const JavaScriptObfuscator = require('javascript-obfuscator');

const sourceCode = `
function myFunction() {
  var myVariable = 'Hello, World!';
  console.log(myVariable);
}
myFunction();
`;

const options = {
  compact: true, // 启用压缩
  controlFlowFlattening: true, // 控制流 平坦化
  controlFlowFlatteningThreshold: 0.75, // 控制流平坦化阈值
  deadCodeInjection: true, // 使死代码注入
  deadCodeInjectionThreshold: 0.4, // 死代码注入阈值
  identifierNamesGenerator: 'hexadecimal', // 生成标识符的方式
  log: false, // 关闭日志
  rotateStringArray: true, // 旋转字符串数组
  selfDefending: true, // 启用自我防御
  stringArray: true, // 启用字符串数组
  stringArrayThreshold: 0.75, // 字符串数组阈值
  // domainLock: ['example.com'], // 锁定域名
  debugProtection: true, // 调试保护
  disableConsoleOutput: true, // 禁用控制台输出
  unicodeEscapeSequence: false, // 禁用 Unicode 转义序列
  rotateUnicodeArray: true, // 旋转 Unicode 数组
};

const obfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options).getObfuscatedCode();

console.log(obfuscatedCode);

三.项目实战

1. 案例1
1. 逆向目标
2. 逆向分析
  • 解除无线debugger

Function.prototype.__constructor_back = Function.prototype.constructor;
Function.prototype.constructor = function() {
    if(arguments && typeof arguments[0]==='string'){
        if("debugger" === arguments[0]){
            return
        }
    }
   return Function.prototype.__constructor_back.apply(this,arguments);
}
  • 当前加载的数据为动态数据加密,我们需要定位到解密的位置

  • 找到发送ajax的位置,找到请求的回调位置,混淆的位置比较难找,需要一个个定位

  • 或者通过hookJSON.parse的方式来进行定位,js是一定会去转换这个json数据的

  • 可以看出数据是通过_0xf79b3e['a']['decipher']() 来进行解密的,我们需要跟进到方法里去找

  • 进函数之后可以看到就是三个函数来进行解密的操作

import requests
import execjs


class JiJian():
    def __init__(self):
        self.headers = {
            "referer": "https://bz.zzzmh.cn/",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
        }
        self.url = "https://api.zzzmh.cn/bz/v3/getData"
        self.js = execjs.compile(open('demo.js', encoding='utf-8').read())

    def get_data(self):
        data = {"size":24,"current":11,"sort":0,"category":0,"resolution":0,"color":0,"categoryId":0,"ratio":0}
        response = requests.post(self.url, headers=self.headers, json=data)
        return response.json()

    def parse_data(self, response):
        res = self.js.call('_0x563330', response['result'])
        # print(res)
        for i in res['list']:
            res = requests.get('https://api.zzzmh.cn/bz/v3/getUrl/' + i['i'] + '29', headers=self.headers, allow_redirects=False)
            self.save_data(res.headers['Location'], i['i'])


    def save_data(self, img_url, name):
        with open('壁纸/' + name + '.jpg', 'wb')as f:
            result = requests.get(img_url, headers=self.headers)
            f.write(result.content)
            print('正在下载{}'.format(name))


    def main(self):
        res = self.get_data()
        self.parse_data(res)

if __name__ == '__main__':
    jj = JiJian()
    jj.main()

需要本期js源码关注评论后私信作者获取哦

ob混淆 ast工具是一种用于混淆源代码的工具,主要针对Python语言编写的代码。AST是Abstract Syntax Tree的缩写,即抽象语法树,它是Python解释器在分析源代码时生成的一种表示方式。ob混淆 ast工具通过操作抽象语法树来混淆源代码,使其难以被逆向工程分析和理解。 ob混淆 ast工具的原理是通过修改和替换抽象语法树中的节点和结构,使得源代码的结构、语义和逻辑变得混乱和困惑。它可以对变量名、函数名、类名等进行重命名,使得原本可读性强的代码变得难以阅读和理解。同时,还可以对源代码中的函数调用关系、控制流等进行修改和隐藏,增加解析和分析的复杂度。 ob混淆 ast工具的优点是能够在不改变源代码的功能和逻辑的前提下,提高源代码的保护性。通过混淆源代码,可以有效地防止源代码被反编译、逆向工程和非法盗用。对于需要加密和保护源代码的应用场景,ob混淆 ast工具是一种非常有效的安全保护措施。 然而,ob混淆 ast工具也存在一些缺点。首先,混淆后的源代码可读性较差,给后续的维护和调试带来不便。其次,由于混淆是通过修改和替换抽象语法树实现的,因此在一些复杂和庞大的项目中,可能会导致混淆过程失败或引入新的错误。 总之,ob混淆 ast工具是一种在保护源代码安全性方面非常有用的工具。通过操作和修改抽象语法树,可以有效地混淆和加密源代码,使得其难以被逆向工程分析,增加了源代码的安全性。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值