分享

sojson本地反调试原理解析

 小小明代码实体 2022-07-08 发布于广东

打开sojson的网址:https://www./jsobfuscator.html

sojson的js加密位于:

image-20220707115617206

下面我们拿默认代码进行测试:

(function(w, d) { 
 w.info = "这是一个一系列js操作。"; 
 d.warning = "如果您的JS里嵌套了PHP,JSP标签,等等其他非JavaScript的代码,请提取出来再加密。这个工具不能加密php、jsp等模版内容"; 
 d.intro = "本工具由 www.jsjiami.com 提供接口。"; 
})(window, document);

下面我们将加密系数全部设置为牛X模式,并启动绝对不可逆配置后,执行JS混淆加密:

image-20220707115803736

点击复制结果,复制混淆后的JS代码进行调试:

image-20220707115930592

动态调试重置方法过无限debug

下面我们尝试调试上述代码,在游览器控制台粘贴并运行:

image-20220707131227224

代码添加的属性可以顺利访问,但是代码本身已经进入了无限debug状态,格式化代码后如上。

点击两次跳出当前函数后,发现已经出现递归的无限调用,不断调用这两个方法:

image-20220707144258527

如果我们设置条件断点flase或永不在此处暂停,可以跳过这些断点,但是函数却仍然处于无效递归调用中,最终导致控制台崩溃。

在还未设置断点时右键:

image-20220707144609044

已在此处设置断点时右键,只能编辑断点设置false:

image-20220707144812203

为了跳出这个无限的递归,我们可以动态修改某个方法,下面我们先定位到这个无限递归调用的起始发起位置:

image-20220707145218384

可以看到,本质就是_0x4c01b6函数不断的自己调用自己实现了无限递归:

image-20220707145800308

下面我们执行如下代码:

_0x4c01b6=function(){}

再次点击恢复脚本执行后,已成功跳出这次的递归调用。

但是过了几秒之后,又再次进入了无限debug状态:

image-20220707150927965

不过从堆栈信息我们可以看到这次调用是定时器发起的。

这个定时器依然使用_0x4c01b6函数实现无限递归,我们再次将_0x4c01b6函数置空。

image-20220707151654951

然后给setInterval定时器函数执行的位置打上断点:

image-20220707151828749

此时由于定时器已经开始执行,所以执行过程已经固定而且无法取消,我们只能将执行过程所执行的方法进行修改会置空,从而实现检测的跳过。

观察上述代码可以直达,定时器本质上就是执行了_0x5e82cc方法,下面我们将_0x5e82cc方法置空:

_0x5e82cc=function(){}

然后就可以取消全部断点并恢复脚本执行。

此时已经通过全部检测,没有再进入debug状态。

sojson防代码格式化原理分析

首先复制游览器格式化之后的代码到本地文本编辑器,根据前面的分析,我们可以删除setInterval定时器,并将_0x4c01b6函数的定义置空跳过无限debug:

image-20220707165150226

下面我们将修改完的代码复制粘贴到游览器控制台执行,几秒钟过后代码已经崩溃:

image-20220707165404808

但是如果我们将代码压缩到一行复制粘贴到控制台却可以顺利执行,sojson到底是如何实现不让格式化之后的代码执行的呢?

我们可以再代码头部加上debugger,再刷新页面并执行代码,代码就会立马被中断,我们可以不断按下F11研究一下执行过程。

前面崩溃时,我们还能看到崩溃时堆栈的位置,我们先让代码运行到对应的位置:

image-20220707181149819

可以看到崩溃前是因为_0x2c2f51这个带有正则校验的方法返回了false。

这个正则校验了一个函数的文本内容:

image-20220707181706690

原来如此,当代码没有被格式化时,这个函数的文本就不会存在缩进和空格,而格式化之后就多出了很多缩进和空格,于是正则校验不通过返回了false,然后就会执行setCookie方法:

image-20220707182743227

这个方法在执行过程中_0x5f5bb8变量会不断递增,这个for循环永远无法满足结束条件,从而形成死循环导致卡死。

那么对于上述检测我们可以将那个正则检测代码相关的代码修改为直接返回true,我根据代码逻辑将:

var _0x2c2f51 = function() {
	var _0x14443b = new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');
	return _0x14443b['test'](_0x38d6b4['removeCookie']['toString']());
};
_0x38d6b4['updateCookie'] = _0x2c2f51;
var _0x5d1737 = '';
var _0x43d0b9 = _0x38d6b4['updateCookie']();
if (!_0x43d0b9) {
	_0x38d6b4['setCookie'](['*'], 'counter', 0x1);
} else if (_0x43d0b9) {
	_0x5d1737 = _0x38d6b4['getCookie'](null, 'counter');
} else {
	_0x38d6b4['removeCookie']();
}

直接修改为:

_0x5d1737 = _0x38d6b4['getCookie'](null, 'counter');

因为通过这一层检验后必将只执行这一行代码。

当然sojson并不只有一处对代码格式化的检测,我们可以搜索new RegExp关键字将所有可能存在格式化检测的代码都找出来。

注意:可以直接在原始代码对应位置加入debugger,不需要一定要在执行时再加断点。

重启游览器后,将上述修改后的代码粘贴到控制台执行,然后搜索new RegExp,将每个涉及正则并且后续使用了test的代码都打上断点,点击继续执行,定位到断点处:

var _0x55f2e1 = function(_0x54d96d) {
	this['rc4Bytes'] = _0x54d96d;
	this['states'] = [0x1, 0x0, 0x0];
	this['newState'] = function() {
		return 'newState';
	}
	;
	this['firstState'] = '\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*';
	this['secondState'] = '[\x27|\x22].+[\x27|\x22];?\x20*}';
};
_0x55f2e1['prototype']['checkState'] = function() {
	var _0x978b9f = new RegExp(this['firstState'] + this['secondState']);
	return this['runState'](_0x978b9f['test'](this['newState']['toString']()) ? --this['states'][0x1] : --this['states'][0x0]);
};
_0x55f2e1['prototype']['runState'] = function(_0x5ec77f) {
	if (!Boolean(~_0x5ec77f)) {
		return _0x5ec77f;
	}
	return this['getState'](this['rc4Bytes']);
};
_0x55f2e1['prototype']['getState'] = function(_0x5c8d2b) {
	for (var _0x1277d0 = 0x0, _0x125c5f = this['states']['length']; _0x1277d0 < _0x125c5f; _0x1277d0++) {
		this['states']['push'](Math['round'](Math['random']()));
		_0x125c5f = this['states']['length'];
	}
	return _0x5c8d2b(this['states'][0x0]);
}

正则的实际内容为:

'\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*'+'[\x27|\x22].+[\x27|\x22];?\x20*}'

\w+ *\(\) *{\w+ *['|"].+['|"];? *}

上面一大堆代码的核心在于,runState必须传入-1才能判断通过,否则就会执行getState方法进入无限循环。

上述代码可以直接修改为:

var _0x55f2e1 = function(_0x54d96d) {};
_0x55f2e1['prototype']['checkState'] = function() {
	return -1;
};

修改完毕后,刷新页面重新运行代码,顺利执行通过全部代码格式化检测。

其实还有一套最复杂的正则检测,但由于我一开始就清空了_0x4c01b6函数,导致检测直接通过,无需继续分析:

image-20220707220152021

hook获取真实代码的位置

我们在代码前加上如下代码:

(function() {
	var temp = "";
    Object.defineProperty(window, 'info', {
		//hook set方法也就是赋值的方法 
		set: function(val) {
				console.log('Hook捕获到info设置->', val);
				debugger;
				temp = val;
				return val;
		},
		//hook get方法也就是取值的方法 
		get: function()
		{
			return temp;
		}
    });
})();

重启游览器后执行代码,就定位到了实际给windows对象设置属性的代码位置:

image-20220707223331378

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多