控制流平坦化


参考:https://security.tencent.com/index.php/blog/msg/112

控制流平坦化,简单来讲就是将代码块之间的关系打断,由一个分发器来控制代码块的跳转,找了个图如下

正常流程

经过混淆后:

本文也就拿这个例子做实验,原文的c语言代码变成js代码如下:

function check_pass(passwd) {var i=0;var sum=0;for(i=0;;i++){if(i==passwd.length){break;}sum=sum+passwd.charCodeAt(i);}if(i==4){if(sum==0x1a1 && passwd.charAt(3) > 'c' && passwd.charAt(3) < 'e' && passwd.charAt(0)=='b'){if((passwd.charCodeAt(3)^0xd)==passwd.charCodeAt(1)){return 1;}console.log("Orz..");}}else{console.log("len error")}return 0;
}function test()
{if(check_pass("bird")){alert( "congratulation!");}else{alert( "error!");}
}
test();

在网站中使用如下选项加密:

生成代码如下:

function check_pass(_0x57a7be) {var _0x252e28 = {'tPlEX': function (_0x52a315, _0x59fdfd) {return _0x52a315 == _0x59fdfd;},'TcjYB': function (_0x300e56, _0x2fe857) {return _0x300e56 + _0x2fe857;},'ZtFYf': function (_0x53b823, _0x136f17) {return _0x53b823 == _0x136f17;},'tPstu': function (_0x1607f2, _0x4a18be) {return _0x1607f2 > _0x4a18be;},'Vhxzy': function (_0x248a47, _0x5a2ca2) {return _0x248a47 < _0x5a2ca2;},'uuFIS': function (_0x3718bc, _0x3081f9) {return _0x3718bc == _0x3081f9;},'cRvgS': function (_0x56fd75, _0x1d2164) {return _0x56fd75 ^ _0x1d2164;},'GsTse': 'Orz..','ykyBq': 'len\x20error'};var _0x537fc8 = 0x0;var _0x3df4b0 = 0x0;for (_0x537fc8 = 0x0;; _0x537fc8++) {if (_0x252e28['tPlEX'](_0x537fc8, _0x57a7be['length'])) {break;}_0x3df4b0 = _0x252e28['TcjYB'](_0x3df4b0, _0x57a7be['charCodeAt'](_0x537fc8));}if (_0x252e28['ZtFYf'](_0x537fc8, 0x4)) {if (_0x252e28['ZtFYf'](_0x3df4b0, 0x1a1) && _0x252e28['tPstu'](_0x57a7be['charAt'](0x3), 'c') && _0x252e28['Vhxzy'](_0x57a7be['charAt'](0x3), 'e') && _0x252e28['uuFIS'](_0x57a7be['charAt'](0x0), 'b')) {if (_0x252e28['uuFIS'](_0x252e28['cRvgS'](_0x57a7be['charCodeAt'](0x3), 0xd), _0x57a7be['charCodeAt'](0x1))) {return 0x1;}console['log'](_0x252e28['GsTse']);}} else {console['log'](_0x252e28['ykyBq']);}return 0x0;
}
function test() {var _0x288152 = {'eOZRR': function (_0x3f5c8e, _0x24ced8) {return _0x3f5c8e(_0x24ced8);},'alzHn': 'bird','GyIol': function (_0x5ddbd5, _0x5cc507) {return _0x5ddbd5(_0x5cc507);},'FWSbx': 'congratulation!','tYizA': 'error!'};if (_0x288152['eOZRR'](check_pass, _0x288152['alzHn'])) {_0x288152['GyIol'](alert, _0x288152['FWSbx']);} else {_0x288152['GyIol'](alert, _0x288152['tYizA']);}
}
test();

还原代码如下:

const parser = require("@babel/parser");
const template = require("@babel/template").default;
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;   const path = require('path');
const fs = require('fs')let jscode= `function check_pass(_0x57a7be) {var _0x252e28 = {'tPlEX': function (_0x52a315, _0x59fdfd) {return _0x52a315 == _0x59fdfd;},'TcjYB': function (_0x300e56, _0x2fe857) {return _0x300e56 + _0x2fe857;},'ZtFYf': function (_0x53b823, _0x136f17) {return _0x53b823 == _0x136f17;},'tPstu': function (_0x1607f2, _0x4a18be) {return _0x1607f2 > _0x4a18be;},'Vhxzy': function (_0x248a47, _0x5a2ca2) {return _0x248a47 < _0x5a2ca2;},'uuFIS': function (_0x3718bc, _0x3081f9) {return _0x3718bc == _0x3081f9;},'cRvgS': function (_0x56fd75, _0x1d2164) {return _0x56fd75 ^ _0x1d2164;},'GsTse': 'Orz..','ykyBq': 'len\x20error'};var _0x537fc8 = 0x0;var _0x3df4b0 = 0x0;for (_0x537fc8 = 0x0;; _0x537fc8++) {if (_0x252e28['tPlEX'](_0x537fc8, _0x57a7be['length'])) {break;}_0x3df4b0 = _0x252e28['TcjYB'](_0x3df4b0, _0x57a7be['charCodeAt'](_0x537fc8));}if (_0x252e28['ZtFYf'](_0x537fc8, 0x4)) {if (_0x252e28['ZtFYf'](_0x3df4b0, 0x1a1) && _0x252e28['tPstu'](_0x57a7be['charAt'](0x3), 'c') && _0x252e28['Vhxzy'](_0x57a7be['charAt'](0x3), 'e') && _0x252e28['uuFIS'](_0x57a7be['charAt'](0x0), 'b')) {if (_0x252e28['uuFIS'](_0x252e28['cRvgS'](_0x57a7be['charCodeAt'](0x3), 0xd), _0x57a7be['charCodeAt'](0x1))) {return 0x1;}console['log'](_0x252e28['GsTse']);}} else {console['log'](_0x252e28['ykyBq']);}return 0x0;
}
function test() {var _0x288152 = {'eOZRR': function (_0x3f5c8e, _0x24ced8) {return _0x3f5c8e(_0x24ced8);},'alzHn': 'bird','GyIol': function (_0x5ddbd5, _0x5cc507) {return _0x5ddbd5(_0x5cc507);},'FWSbx': 'congratulation!','tYizA': 'error!'};if (_0x288152['eOZRR'](check_pass, _0x288152['alzHn'])) {_0x288152['GyIol'](alert, _0x288152['FWSbx']);} else {_0x288152['GyIol'](alert, _0x288152['tYizA']);}
}
test();`;// 将代码解析为AST树
const ast = parser.parse(jscode);traverse(ast, {VariableDeclarator: decrypt
})function decrypt(path) {var node = path.node;if (!node.id||!node.id.name)return;     var node_name = node.id.name// 筛选符合条件的节点// if (!t.isObjectExpression(node.init))//     return;// 获取propertiesvar obj_properties = node.init.propertiesif (!obj_properties||obj_properties.length == 0)return;for (var property of obj_properties) {//var property_name = property.key.name  var property_name = property.key.value;//console.log("[0]property_name:"+property_name);traverse(ast, {CallExpression: function(call_path) {// 遍历函数节点if (property.value.type==="FunctionExpression") {// 如果为函数var c_node = call_path.node;var call_params = c_node.arguments;// 确定是否是需要替换的节点if (!c_node.callee||!t.isMemberExpression(c_node.callee))return;if (!t.isIdentifier(c_node.callee.object)||c_node.callee.object.name != node_name)return;if (!t.isStringLiteral(c_node.callee.property)||c_node.callee.property.value != property_name)return;//console.log("property_name:"+property_name);// 构建新的Expression并替换var call_argument = property.value.body.body[0].argumentif (t.isBinaryExpression(call_argument) && call_params.length==2) {call_path.replaceWith(t.binaryExpression(call_argument.operator, call_params[0], call_params[1]));} else if (t.isLogicalExpression(call_argument) && call_params.length==2) {call_path.replaceWith(t.isLogicalExpression(call_argument.operator, call_params[0], call_params[1]));} else if(t.isCallExpression(call_argument) && t.isIdentifier(call_argument.callee)) {if (call_params.length == 1) {call_path.replaceWith(call_params[0])} else {call_path.replaceWith(t.callExpression(call_params[0], call_params.slice(1)))} }}},MemberExpression: function(member_path) {// 遍历属性节点if (t.isStringLiteral(property.value)) {// 如果为属性var m_node = member_path.nodevar property_value = property.value.valueif (!t.isIdentifier(m_node.object)||m_node.object.name != node_name){return;}if (!t.isStringLiteral(m_node.property)||m_node.property.value!=property_name)return;// 替换member_path.replaceWith(t.stringLiteral(property_value))}}})}path.remove();}//console['log']() 变 console.log()
function transform(path) {//path.node.property 获取到的是属性值  这是一个node类型//path.get('property') 获取到的是一个path(NodePath类型) 只有NodePath才有替换方法 let property=path.node.property;let property_path=path.get('property');if(t.isStringLiteral(property_path)){const val=property_path.node.value;path.node.computed=false;property_path.replaceWith(t.identifier(val));}
}traverse(ast, {MemberExpression: transform
})//变量重命名 这个例子里没有全局变量,所以可以这么搞,有全局变量与局部变量同名的要小心
function rename(path) {let name=path.node.name;if(name=="_0x537fc8"){path.node.name="i";} else if(name=="_0x3df4b0"){path.node.name="sum";}else if(name=="_0x57a7be"){path.node.name="passwd";}
}traverse(ast, {Identifier: rename
})let {code} = generator(ast);
console.log(code);

还原代码解析:

下图为变量声明的相关结构:

下图为调用方的相关结构:

对着上边的两张图再看例子就清晰多了。

另外一个例子:

var arr="3,0,1,2,4".split(",");
var cnt=0;
while(true){switch(arr[cnt++]){case "0":console.log("case 0");continue;case "1":console.log("case 1");continue;case "2":console.log("case 2");continue;case "3":console.log("case 3");continue;case "4":console.log("case 4");continue;}break;
}

const parser = require('@babel/parser');
const template = require('@babel/template').default;
const traverse = require('@babel/traverse').default;
const types = require('@babel/types');
const generator = require('@babel/generator').default;
const path = require('path');
const fs = require('fs');let jsStr =`var arr="3,0,1,2,4".split(",");
var cnt=0;
while(true){switch(arr[cnt++]){case "0":console.log("case 0");continue;case "1":console.log("case 1");continue;case "2":console.log("case 2");continue;case "3":console.log("case 3");continue;case "4":console.log("case 4");continue;}break;
}`;const ast = parser.parse(jsStr);
// 处理控制流平坦化
const decode_while = {WhileStatement(path) {let {test, body} = path.node;let swithchNode = body.body[0];//if (!types.isUnaryExpression(test) || !types.isSwitchStatement(swithchNode)) return;let {discriminant, cases} = swithchNode;if (!types.isMemberExpression(discriminant) || !types.isUpdateExpression(discriminant.property)) return;let arrayName = discriminant.object.name;//获得所有上方的兄弟节点  这里获取到的是两个变量声明的节点let per_bro_node = path.getAllPrevSiblings();let array = []per_bro_node.forEach(per_node => {const {declarations} = per_node.node;let {id, init} = declarations[0];if (arrayName === id.name) {array = init.callee.object.value.split(',');}per_node.remove();});let replace_body = [];array.forEach(index => {let case_body = cases[index].consequent;if (types.isContinueStatement(case_body[case_body.length - 1])) {case_body.pop();}replace_body = replace_body.concat(case_body);});path.replaceInline(replace_body);}
}traverse(ast, decode_while);/************************************处理完毕,生成新代码*************************************/
let {code} = generator(ast);
console.log(code);

关于此工具的原理及逆向方法的介绍到此结束,感谢各位的观看!

利用AST对抗js混淆(三) 控制流平坦化(Control Flow Flattening)的处理相关推荐

  1. 利用AST对抗js混淆(一) 基础知识

    准备工作: 1.网站及工具 JavaScript Obfuscator Tool   主要研究对象,主要是研究此网站的各种混淆方法及破解办法. JS NICE: Statistical renamin ...

  2. 跟着铁头干混淆4.1 ollvm控制流平坦化基本概念

    ollvm 4.1 控制流平坦化基本概念 控制流平坦化基本概念 编译器参数:-mllvm -fla 英文全称 简称 编译参数 控制流平坦化 Control Flow Flattening fla -m ...

  3. 利用符号执行去除控制流平坦化

    1. 背景 1.1 控制流平坦化 控制流平坦化(control flow flattening)的基本思想主要是通过一个主分发器来控制程序基本块的执行流程,例如下图是正常的执行流程 经过控制流平坦化后 ...

  4. 【转载】基于LLVM Pass实现控制流平坦化

    基于LLVM Pass实现控制流平坦化 文章目录 基于LLVM Pass实现控制流平坦化 0x00. 什么是LLVM和LLVM Pass 0x01. 首先写一个能跑起来的LLVM Pass 0x02. ...

  5. buu-[RoarCTF2019]polyre(控制流平坦化,虚假控制流程)

    这题一开始拿到人看麻了(不会),写篇wp记录新题型 这么一大大大串的函数图,是经过OLLVM 的控制流平坦化混肴. 控制流平坦化(Control Flow Flattening)的基本思想主要是通过一 ...

  6. 初识ollvm控制流平坦化

    app:B站, 版本:随便搞了个最新版 目标:分析sign算法 本文第一次发布2021-10-26,时隔两个月,感觉这篇文章写的似乎并不是很清晰,于是做了一篇重制版发在了公众号里.如果感觉本文看着不清 ...

  7. 某酷ckey签名生成算法系列--(三)ast代码控制流平坦化

    某酷ckey签名生成算法系列--(三)ast代码控制流平坦化 观察三个switch的值分别是Ci.mi和Ai.而这三个值又因为li的确定而确定的.也就是说已知li的值,就可以分别计算出Ci.mi和Ai ...

  8. AST混淆实战|仿obfuscator混淆控制流平坦化(超详细版)

    之前写过一篇这样的文章 : JavaScript 代码混淆实战(六):仿obfuscator混淆控制流平坦化,但并没有写过程,在这篇文章里面说明下! 依然以文章里的代码来说明怎么进行控制流平坦化. 混 ...

  9. AST还原技术专题:浅谈去控制流平坦化的思路及方法

    一. while-switch结构的控制流 这类平坦化代码很简单,常见于经过obfuscator在线工具混淆后的控制流平坦化.一般代码段不会很长,常见的 switch-case 基本都在10个分支以内 ...

最新文章

  1. 面向量产的3D目标与车道线检测方法
  2. Android开发环境搭建全程演示(jdk+eclip+android sdk)
  3. 家长会PPT教师的福利模板
  4. Mysql:mysql函数GROUP_CONCAT()
  5. 如何用postman发送post请求
  6. hausaufgabe--python 11-List slice
  7. python表格控件_python--excel操作插件openpyxl
  8. asp毕业设计—— 基于asp+access的网上论坛设计与实现(毕业论文+程序源码)——网上论坛
  9. Environment 注入service 报错 null
  10. 【庖丁解牛】成功解决nginx报错:bind() to 0.0.0.0:8090 failed (13: Permission denied)
  11. Win10/Win7 打印机 [有线局域网共享]
  12. 数据结构与算法---均摊时间复杂度
  13. Windows10下VirtualBox虚拟机的备份与迁移
  14. 《这就是搜索引擎》爬虫部分摘抄总结
  15. 组态王与单片机协议2
  16. 数学建模代码速成~赛前一个月~matlab~代码模板~吐血总结~三大模型代码(预测模型、优化模型、评价模型)
  17. IQA (图像质量评价)
  18. 机器人总动员主角简笔画_机器人总动员人物简介
  19. Kettle数据抽取实战之二:CSV文件抽取
  20. 如何设计mysql的表结构_数据库的数据表的结构是如何设计的?

热门文章

  1. WordPress彻底禁用上传媒体图片自动生成缩略图及多尺寸图片(亲测可用)
  2. ORACLE EBS
  3. Unity中PICO G2 4K开发环境配置说明
  4. 鄢陵一高2021高考成绩查询单,鄢陵县第一高级中学2020年高考喜报
  5. 10个需要警惕的BYOD常见陷阱
  6. 深入理解Flutter动画原理,已整理成文档
  7. 7.4 小团队、低成本的管理实践之路
  8. java set list 区别是什么_Java中的list和set有什么区别
  9. 教程get | K8S部署OpenStack容器云(下)
  10. android 爬虫获取邮箱,python爬虫源码学习:爬虫smtp邮件实时推送(2)