JS逆向时碰到了恶心的死代码怎么办?手把手教你解决!

2024-05-15 13:32

本文主要是介绍JS逆向时碰到了恶心的死代码怎么办?手把手教你解决!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读本文大概需要 3 分钟。

你是否也曾有过「在逆向时看到一大坨代码,但自己却无从下手」的遭遇?

你是否也曾有过「跟着代码跳了很久之后,才发现那一大坨代码其实没有任何作用」的惨痛经历?

你是否也曾碰到过「代码量特别大、一格式化就卡死,但后来发现有很大一坨代码都没有任何用处」的狗血场景?

别担心,其实这些情况我们只需要静下心来好好分析一下代码,并将无用代码统统剔除,就能轻松解决掉。

本文将带你实际地分析一段被知名 Javascript 代码混淆工具 Obfuscator 混淆过的代码,并将混淆后的代码中的无用代码全部剔除,尽可能地将这段代码打回原形。

基础知识

在开始之前,我们先了解一下这种「在代码中插入大量无用代码以混淆视听」的混淆方式吧。这种混淆方式有两种叫法,或者说是两种做法,它们分别是「死代码」和「花指令」。

死代码

死代码一开始是被用来描述一些人写代码时写出的没有用到的代码的,为了编译后的文件尽可能地小,编译器通常会对死代码进行移除处理。

而在不知道什么时候开始,死代码被安全工作者们用来作为一种混淆机制,以将代码量变得极为庞大,使进行逆向工程的人难以找到主要逻辑

但死代码有个很明显的特征:它虽然看着代码量很大,但实际却完全不会在程序的正常代码中被调用

如果你有兴趣的话,可以对一些包含了死代码的代码进行聚类分析,你会发现死代码和正常代码之间泾渭分明,正常代码都是互相关联着的,而死代码却是孤零零的一块或者多块,并且正常代码还完全不会与死代码产生关联。

花指令

花指令是以前被大量运用在木马、病毒的免杀上的一种反反汇编手段,花指令中的“指令”通常指的是汇编中的 jmpcall 之类的调用、跳转指令,而攻击者们会将这些指令巧妙地插入到恶意代码的执行逻辑中,使得静态分析工具在分析到这个位置时无法正常反汇编。

花指令曾经的目的主要有两个,一个是使杀毒软件无法自动分析出恶意代码,达到瞒天过海的效果;一个是给安全工作者在分析恶意软件时设下层层阻拦,使安全工作者需要花费更多的时间才能理清代码逻辑,达到拖延时间的效果。

同样是不知道什么时候开始,花指令也被安全工作者们用来作为一种混淆机制。在这种应用场景下,花指令和死代码其实很类似,它们都是用了大量无用代码来混淆视听,但花指令和死代码最大的区别就是,花指令的无用代码是会被混在正常代码中进行执行的。相比于死代码而言,花指令会造成一些性能损失,但同时也会让进行逆向工程的人更加难以分析。

但花指令也不是无懈可击的,为了不影响程序正常的执行,花指令不能干扰到程序的原有逻辑,举个例子:

a = 1
b = 2
# 花指令开始,对变量进行了一通操作
a += 1
a += b
# 花指令结束,又把变量的值给变回去了
a -= b
a -= 1c(a, b)

所以其实只要你能看出这一通操作没有任何意义,花指令也自然就没法影响到你了。

小结

不管是死代码还是花指令,其实都只需要我们仔细观察就能将其剔除,它们并不是什么很难搞的东西,见得多了之后你甚至都不需要细看就能快速排除掉一些明显不是正常代码的部分,毕竟常见的混淆器中用到的代码其实重合度是很高的,同样的套路见多了之后自然很容易分辨。

更何况,代码混淆是需要考虑性能损耗的,对方不可能为了防你逆向工程而无止尽地对代码进行混淆,要不然人家正常业务也没办法进行了。

实战

基础知识了解完了,我们来进入实战环节。

首先,我们打开 https://obfuscator.io/,这是 Obfuscator 的网页版本,可以快速在网页上进行混淆参数的配置,并且一键生成并导出混淆后的代码。

顺带一提,Obfuscator 是一款非常优秀的 JavaScript 代码混淆工具,但代码结构都是固定的,如果想要更好的混淆效果,可将混淆后的代码进行修改,从而让别人更难分析和调试

现在,我们用它给出的样例代码来进行混淆。样例代码如下:

// Paste your JavaScript code here
function hi() {console.log("Hello World!");
}
hi();

注意,我们需要勾选以下选项:

这三个选项的效果分别是:

•Compact code将代码中的换行符全部去掉,使得代码看起来毫无结构性。也就是所谓的代码压缩。•Self Defending在代码中插入自检代码,用来干扰逆向工程的人对代码进行格式化、变量重命名操作,如果代码被格式化了就会无法正常运行。•Dead Code Injection在代码中插入死代码,也就是本文的重点。

配置好参数后点击 Obfuscate 按钮,即可生成按配置混淆后的代码,我生成的代码是这样的(长图警告⚠️):

可以看到,原本短短的几行代码,在经过混淆后变成了这么多。而且这个代码还是经过压缩的,完全看不出层级。

当然,这个代码是可以正常运行的,我们用NodeJS跑一遍看看:

看起来混淆并没有影响到正常的代码逻辑,我们再把这一坨代码给格式化一下看看:

果不其然,格式化后的代码直接就没法运行了。在平时我们遇到这种情况时要记住,原代码可以正常运行但格式化之后不行,那么这个报错肯定是跟格式化代码有关系的,至于它报错的内容具体是啥意思其实并不重要。

那么怎么办呢?我们来静态分析一下它的代码就知道了。

先来看看第一段代码:

定义了一个数组并初始化,显然不可能造成什么问题。

接着看看第二段代码(长图警告⚠️):

这是一个自执行的函数,没有返回值。但是注意,它的第一个实参是 _0x2831,也就是之前定义的那个数组,对应的形参是 _0x528cba。我们可以根据这个来判断它对 _0x2831 做了些什么。

现在我们来一段一段地分析这第二大段代码中的每一段代码,首先是第一段代码:

var _0x1b0e99 = function(_0x5beb46) {while (--_0x5beb46) {_0x528cba['push'](_0x528cba['shift']());}};

这么短的代码相信大家都应该能看懂,是对 _0x528cba 进行 shift 操作,而 _0x528cba 是自执行函数的形参,实参是 _0x2831。换句话说,它就是对实参进行 shift 操作。不过这里它只是声明,并没有调用,所以还不会去改变实参。

然后是第二段代码和第三段代码,这里因为代码量太大就不整个贴出来了,之前已经贴过完整代码了。

第二段代码是定义了一个函数,而第三段代码则是调用这个函数,因此我们主要分析这第二段代码即可。

如果你不会分析,可以跳过它声明的语句,它真正开始执行的是这行代码:

var _0x53c9b6 = _0x1d1bc5['updateCookie']();

不要看它这段代码里面既有 setCookie,又有 getCookie,其实它跟 cookie 没有半毛钱关系,它只是一个 object 的 key 值,仅此而已。

因此,我们只需要关注它有没有改变实参,有没有改变全局变量。整个代码全局变量只有一个 _0x2831,它也是实参,也就是说只需要关心这个 _0x2831 即可。

通过上面的分析我们可以知道,它的第一段代码定义了一个函数,确实改变了实参,但是没有调用。因此,我们得找找看它在哪里被调用的,直接搜函数名 _0x1b0e99,定位到这里:

_0x4c51d1(_0x1b0e99, _0x283138);

这时,_0x1b0e99是第一个实参,第二个实参 _0x283138 则是自执行函数的形参,它对应的实参是 0x1bf,是一个整形的数值。这下,我们只需要看看 _0x4c51d1 的函数声明即可:

var _0x4c51d1 = function(_0x3d5743, _0x3c21e0) {_0x3d5743(++_0x3c21e0);
};

这么大一段代码,其实真正改变实参的只有这里。我们将这两行代码结合一下,就会变成这样:

_0x1b0e99(++_0x283138);

所以,第二大段代码这个自执行函数中的第二段代码只有这一句是真正改变实参的地方,其他的全部是垃圾代码,直接删除即可。删除后,这个自执行函数就变成了这样:

(function(_0x528cba, _0x283138) {var _0x1b0e99 = function(_0x5beb46) {while (--_0x5beb46) {_0x528cba['push'](_0x528cba['shift']());}};_0x1b0e99(++_0x283138);
}(_0x2831, 0x1bf));

这样看起来就清爽多了,运行试试看:

还是报同样的错误,接着往下分析第三段代码(长图警告⚠️):

这是一个函数,可以看到,引用全局变量 _0x2831 的只有这一行:

var _0x1b0e99 = _0x2831[_0x528cba];

这是一个赋值语句,但是不会改变 _0x2831 这个变量,因此我们只需要重点关注它的返回值 _0x1b0e99 就好。

再来看看它最后赋值的地方,有两处,一处在 if 语句里面:

_0x1b0e99 = _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);

另外一处在 else 语句里面:

_0x1b0e99 = _0x309846;

那它到底是执行的那行代码呢,来看看 if 语句的条件:

_0x309846 === undefined

继续分析上面的代码:

var _0x309846 = _0x1b0e['jZzRvK'][_0x528cba];

以及 _0x1b0e['jZzRvK'] 最近的定义的地方:

_0x1b0e['jZzRvK'] = {};

这样就清楚了,_0x309846 === undefined 这个条件是成立的,所以 _0x1b0e99 最后赋值的地方是这里:

_0x1b0e99 = _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);

函数 _0x1b0e['SmClCt'] 赋值在这里:

_0x1b0e['SmClCt'] = _0x5beb46;

而实参,则是在 _0x5beb46 函数之前定义过,因此只需要这两行代码提到 _0x5beb46 函数之后即可。注意,这里为了清楚一点,可以这样操作:

if (_0x1b0e['DVdkAf'] === undefined) {
............................_0x1b0e['SmClCt'] = _0x5beb46;_0x1b0e['jZzRvK'] = {};_0x1b0e['DVdkAf'] = !![];
}
_0x1b0e99 = _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);
return _0x1b0e99;

根据上面的思路继续分析 hi 函数,同样也注入了一些不会改变全局变量 _0x2831 的垃圾代码,真正有效的代码只有这一行:

console[_0x1b0e('0xc', '^G6o')](_0x1b0e('0xd', 'Bi36'));

删除掉这些垃圾代码后,我们就可以知道这份代码原来长什么样了。

BOOM!结果就是这么三行代码:

function hi() {console[_0x1b0e('0xc', '^G6o')](_0x1b0e('0xd', 'Bi36'));
}

最后我们再运行一下试试看吧:

没有报错,代码成功运行了~

总结

碰到大段代码时不要慌,先试着分析一下,把无用代码剔除掉之后其实最后剩下的可能就只有几行而已。当然实际情况中你往往会碰到混淆参数更复杂的代码,你需要让程序来帮助你进行分析,而想要写出这种程序又会使用到 AST 操作,所以说掌握 AST 操作还是很有必要的,建议学习一下。

推荐阅读

1

一些我日常使用的 Python 技巧分享

2

用它 5 分钟以后,我放弃用了四年的 Flask

3

精品连载丨安卓 App 逆向课程之二逆向神器 frida 的介绍

4‍‍

精品连载丨安卓 App 逆向课程一之环境配置

好文和朋友一起看~

这篇关于JS逆向时碰到了恶心的死代码怎么办?手把手教你解决!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991946

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤