Obfuscator 混淆学习

 前言

          最近正式工作了,这段时间比较忙,上篇android脱壳机可能要等一段时间,不过看雪已经有人公布android7.1的脱壳机的源码了。之前验证了android8.1的脱壳,感觉android8.1增加了参数解引用的问题,会有一些坑点导致程序会crash。所以暂时先放弃android8.1,过段时间先复现android7.1的脱壳机。然后工作这段时间,有遇到js混淆,打算学习一下。

js混淆比较出名的工具就有Obfuscator,但是Obfuscator缺点在于平坦化做的一般,有些企业会自己做一下平坦化,再用Obfuscator来混淆。所以先学习Obfuscator很有必要性。

js混淆学习前置知识

        建议先学习了解AST树和解析AST树的框架,可以先看下面的参考链接,补充前置知识。

Obfuscator混淆解析

         Obfuscator 在线混淆网站https://obfuscator.io/ 底下有介绍相关混淆内容,这里直接看提供例子来解析的代码。

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();

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

暂时可以看出Obfuscator有如下特点:

1.大数组变量

2.位移大数组函数

3.自解密函数

由于代码较短,还未看出平坦化,不过不着急,先尝试把这个还原。

解析ast推荐在线网站: https://astexplorer.net/

通过分析ast和混淆代码的分析,现在去混淆思路:

1.抠出自解密函数和解密函数的name

2.遍历调用函数和初始化赋值操作,获取解密函数的结果并且替代节点

3.修复函数调用方式,比如console['log']修复成console.log

修复办法:

1.使用ast来解析出body,转化成相关代码,获取到解密函数

2.遍历并判断调用解密函数的点,来进行获取最后结果

3.使用ast在线解析网站,对比二者不同,来进行修复

去混淆脚本如下

const fs=require('fs');
const parser=require('@babel/parser');
const traverse=require('@babel/traverse').default;
const types =require('@babel/types');
const generator=require('@babel/generator').default;
const { type } = require('os');
const { exit } = require('process');

var arguments=process.argv;
if(arguments.length<4)
{
  console.log("need input file and output file path");
  exit();
}
const inputFilePath = arguments[2];
const outputFilePath=arguments[3];

const jscode=fs.readFileSync(inputFilePath,
{
    encoding:"utf-8"
});

var globalArrayName;
var globalDecode;
var DecodeFunName;
var globalDecodeList=new Array();


/*

function getVariableName(path)
{
    var node = path.node;
    if(!types.isArrayExpression(node.init))
    {
      return;
    }
    //console.log(node.id.name);
    globalArrayName=node.id.name;
    
}
*/

function parserAst(ast)
{
    globalArrayName=ast.program.body[0].declarations[0].id.name;
    DecodeFunName=ast.program.body[1].id.name;
    globalDecodeList.push(DecodeFunName);
    var runCode=ast.program.body.slice(3,);
    ast.program.body=ast.program.body.slice(0,3);
    globalDecode=generator(ast).code;
    ast.program.body=runCode;
    return ast;
}

function getDecodeList(path) {
    var node=path.node;
    if(!types.isIdentifier(node.id)||!types.isIdentifier(node.init)||node.init.name!=DecodeFunName)
    {
        return;
    }
    globalDecodeList.push(node.id.name);
    path.remove();
}

function funToStr(path)
{
    var node =path.node;
    if (!types.isIdentifier(node.callee)||globalDecodeList.indexOf(node.callee.name)==-1)
    {
                return;
    }
    //console.log(path.toString());
    node.callee.name=DecodeFunName;
    let value = eval(globalDecode+path.toString());
    //console.log(value);
    path.replaceWith(types.valueToNode(value));
    
}

function fixFunCall(path)
{
    var node =path.node;
    if(!types.isIdentifier(node.property))
    {
        return;
    }
    let name =node.property.name;
    path.node.property=types.stringLiteral(name);
    path.node.computed=true;
}

function solveOb(ast)
{
    //eval(globalDecode);

    traverse(ast,{
        VariableDeclarator:getDecodeList,
        CallExpression:funToStr,
        MemberExpression:fixFunCall
      });
      return ast;
}

let ast=parser.parse(jscode);
ast=parserAst(ast);
ast=solveOb(ast);

let code=generator(ast).code;
console.log(code);

总结

        多在ast在线网站查看相关数据,多加对比。后续会写长的代码来去混淆,使其体现出平坦化,并且让脚本尽量通用化。

参考链接

Js Ast一部曲:高完整度还原某V5的加密

https://bbs.nightteam.cn/thread-417.htm

  

        

   

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
obfuscator是一种用于混淆JavaScript代码的工具。混淆JavaScript代码是为了增加代码的复杂性和可读性,从而增加代码的安全性和保护知识产权。以下是一些常用的obfuscator混淆JavaScript代码的方法。 1. 变量和函数重命名:obfuscator将变量和函数名称替换为随机选择的短名称,使其难以理解和分析。例如,将变量名称"username"替换为"a",将函数名称"submitForm"替换为"b"等。 2. 混淆控制流:obfuscator通过改变代码中的逻辑流程,增加不必要的代码和控制语句,使代码更加复杂和难以理解。例如,插入冗余的if语句、循环和条件判断,使代码的流程变得模糊和混乱。 3. 字符串加密:obfuscator将代码中的字符串进行加密或编码,使其难以直接理解。例如,将字符串"Hello World!"加密为一串特殊字符,只有在运行时通过解密算法才能还原。 4. 压缩和优化代码:obfuscator通常会对代码进行压缩和优化,去除不必要的空格、换行和注释,并将代码压缩为一行或一段较短的字符串,减小代码的体积和复杂性。 5. 隐藏关键代码片段:obfuscator可以将关键的代码片段进行隐藏或删除,使其变得更加难以察觉和理解。这可以包括服务器端的验证逻辑、关键算法和敏感数据等。 需要注意的是,obfuscator混淆JavaScript代码虽然可以增加代码的安全性,但也会给代码的调试和维护带来一定的困难。因此,在使用obfuscator进行代码混淆时,需要权衡安全性和可维护性之间的平衡。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值