fakebrowser
Version:
🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.
220 lines • 19.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserLauncher = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const URLToolkit = __importStar(require("url-toolkit"));
const http = __importStar(require("http"));
const url = __importStar(require("url"));
const axios_1 = __importDefault(require("axios"));
const https_1 = require("https");
const assert_1 = require("assert");
const Driver_js_1 = __importDefault(require("./Driver.js"));
const DeviceDescriptor_js_1 = __importDefault(require("./DeviceDescriptor.js"));
const PptrPatcher_1 = require("./PptrPatcher");
const FakeBrowser_1 = require("./FakeBrowser");
const kFakeDDFileName = '__fakebrowser_fakeDD.json';
const kInternalHttpServerHeartbeatMagic = '__fakebrowser__&88ff22--';
class BrowserLauncher {
static checkLaunchOptionsLegal(options) {
if (!options || !options.args || !options.args.length) {
return;
}
// These args are set by FakeBrowser and cannot be set externally:
const externalCannotSetArgs = [
'--user-data-dir',
'--lang',
'--window-position',
'--window-size',
];
if (options.args.filter(e => externalCannotSetArgs.includes(e.toLocaleLowerCase().split('=')[0])).length > 0) {
throw new TypeError(`${externalCannotSetArgs} cannot be set in options.args`);
}
}
static prepareFakeDeviceDesc(params) {
// Go to the userDataDir specified by the user and read the __fakebrowser_fakeDD.json file
// or create it if it does not exist.
const userDataDir = params.userDataDir;
(0, assert_1.strict)(userDataDir);
if (!fs.existsSync(userDataDir)) {
// may throw
fs.mkdirSync(userDataDir, { recursive: true });
}
// Read from existing files, or generate if not available.
const fakeDDPathName = path.resolve(userDataDir, `./${kFakeDDFileName}`);
let tempFakeDD = null;
try {
tempFakeDD = (fs.existsSync(fakeDDPathName)
? fs.readJsonSync(fakeDDPathName)
: params.deviceDesc);
DeviceDescriptor_js_1.default.checkLegal(tempFakeDD);
}
catch (ex) {
console.warn('FakeDD illegal');
// It is possible that some fields are missing due to the deviceDesc update and need to recreate fakeDD
const orgTempFakeDD = tempFakeDD;
tempFakeDD = params.deviceDesc;
if (orgTempFakeDD) {
tempFakeDD.fontSalt = orgTempFakeDD.fontSalt;
tempFakeDD.canvasSalt = orgTempFakeDD.canvasSalt;
}
}
const { fakeDeviceDesc, needsUpdate, } = DeviceDescriptor_js_1.default.buildFakeDeviceDescriptor(tempFakeDD);
if (needsUpdate) {
fs.writeJsonSync(fakeDDPathName, fakeDeviceDesc, { spaces: 2 });
}
params.fakeDeviceDesc = fakeDeviceDesc;
}
static async connect(params) {
await this.bootInternalHTTPServer();
this.prepareFakeDeviceDesc(params);
(0, assert_1.strict)(params.fakeDeviceDesc);
const uuid = DeviceDescriptor_js_1.default.deviceUUID(params.fakeDeviceDesc);
const { vanillaBrowser, pptrExtra, } = await Driver_js_1.default.connect(uuid, params);
const launchTime = new Date().getTime();
const fb = new FakeBrowser_1.FakeBrowser(params, vanillaBrowser, pptrExtra, launchTime, uuid);
// pages 0 cannot be hook, lets drop it
await fb._patchPages0Bug();
return fb;
}
static async launch(params) {
this.bootBrowserSurvivalChecker();
await this.bootInternalHTTPServer();
// deviceDesc, userDataDir cannot be empty
this.checkLaunchOptionsLegal(params.launchOptions);
this.prepareFakeDeviceDesc(params);
(0, assert_1.strict)(params.fakeDeviceDesc);
const uuid = DeviceDescriptor_js_1.default.deviceUUID(params.fakeDeviceDesc);
const { vanillaBrowser, pptrExtra, } = await Driver_js_1.default.launch(uuid, FakeBrowser_1.FakeBrowser.globalConfig.defaultLaunchArgs, params);
const launchTime = new Date().getTime();
const fb = new FakeBrowser_1.FakeBrowser(params, vanillaBrowser, pptrExtra, launchTime, uuid);
// pages 0 cannot be hook, lets drop it
await fb._patchPages0Bug();
// Manage surviving browsers and kill them if they time out
this._fakeBrowserInstances.push(fb);
return fb;
}
static async bootInternalHTTPServer() {
if (!this._httpServer) {
this._httpServer = http.createServer();
this._httpServer.on('request', async (req, res) => {
(0, assert_1.strict)(req.url);
const { query, pathname } = url.parse(req.url, true);
if (pathname === '/hb') {
res.write(kInternalHttpServerHeartbeatMagic);
res.end();
}
if (pathname === '/patchWorker') {
const relUrl = query['relUrl'];
const workerUrl = query['workerUrl'];
const uuid = query['uuid'];
const fullUrl = URLToolkit.buildAbsoluteURL(relUrl, workerUrl);
console.log('request worker content from: ', fullUrl);
// Object.fromEntries ES2019
const reqHeaders = Object.fromEntries(Object.entries(req.headers).map(e => ([e[0], e[1][0]])));
delete reqHeaders['host'];
// TODO: get through proxy
const jsResp = await axios_1.default.get(fullUrl, {
headers: reqHeaders,
httpsAgent: new https_1.Agent({
rejectUnauthorized: false,
}),
});
let jsContent = jsResp.data;
const browser = BrowserLauncher.getBrowserWithUUID(uuid);
if (browser) {
jsContent = await PptrPatcher_1.PptrPatcher.patchWorkerJsContent(browser, jsContent);
}
const respHeaders = jsResp.headers;
delete respHeaders['content-length'];
res.writeHead(jsResp.status, jsResp.statusText, respHeaders);
res.write(jsContent);
res.end();
}
});
// If the port listens to errors, determine if the heartbeat interface is successful
try {
this._httpServer.listen(FakeBrowser_1.FakeBrowser.globalConfig.internalHttpServerPort);
}
catch (ex) {
const hbUrl = `http://127.0.0.1:${FakeBrowser_1.FakeBrowser.globalConfig.internalHttpServerPort}/hb`;
try {
const hbData = (await axios_1.default.get(hbUrl)).data;
if (hbData === kInternalHttpServerHeartbeatMagic) {
try {
this._httpServer.close();
}
finally {
this._httpServer = null;
}
return;
}
}
catch (ignore) {
}
throw ex;
}
}
}
static bootBrowserSurvivalChecker() {
if (!this._checkerIntervalId) {
this._checkerIntervalId = setInterval(async () => {
const killThese = this._fakeBrowserInstances.filter(e => (e.launchParams.maxSurvivalTime > 0)
&& (new Date().getTime() > e.bindingTime + e.launchParams.maxSurvivalTime));
const p = [];
for (const fb of killThese) {
p.push(fb.shutdown());
}
await Promise.all(p);
}, 5 * 1000);
}
}
static getBrowserWithUUID(uuid) {
return this._fakeBrowserInstances.find(e => e.uuid === uuid);
}
static async _forceShutdown(fb) {
await Driver_js_1.default.shutdown(fb.vanillaBrowser);
const browserIndex = this._fakeBrowserInstances.indexOf(fb);
(0, assert_1.strict)(browserIndex >= 0);
this._fakeBrowserInstances.splice(browserIndex, 1);
// If all browsers have exited, close internal http service
if (this._fakeBrowserInstances.length === 0) {
// console.log('close appserver')
if (this._httpServer) {
try {
this._httpServer.close();
}
finally {
this._httpServer = null;
}
}
}
}
}
exports.BrowserLauncher = BrowserLauncher;
BrowserLauncher._fakeBrowserInstances = [];
BrowserLauncher._checkerIntervalId = null;
BrowserLauncher._httpServer = null;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJvd3NlckxhdW5jaGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvcmUvQnJvd3NlckxhdW5jaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSw2Q0FBOEI7QUFDOUIsMkNBQTRCO0FBQzVCLHdEQUF5QztBQUN6QywyQ0FBNEI7QUFFNUIseUNBQTBCO0FBRTFCLGtEQUF5QjtBQUN6QixpQ0FBMkI7QUFDM0IsbUNBQXVDO0FBRXZDLDREQUErRztBQUMvRyxnRkFBa0Y7QUFDbEYsK0NBQXlDO0FBQ3pDLCtDQUF5QztBQUV6QyxNQUFNLGVBQWUsR0FBRywyQkFBMkIsQ0FBQTtBQUNuRCxNQUFNLGlDQUFpQyxHQUFHLDBCQUEwQixDQUFBO0FBRXBFLE1BQWEsZUFBZTtJQU1oQixNQUFNLENBQUMsdUJBQXVCLENBQUMsT0FBOEI7UUFDakUsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNuRCxPQUFNO1NBQ1Q7UUFFRCxrRUFBa0U7UUFDbEUsTUFBTSxxQkFBcUIsR0FBRztZQUMxQixpQkFBaUI7WUFDakIsUUFBUTtZQUNSLG1CQUFtQjtZQUNuQixlQUFlO1NBQ2xCLENBQUE7UUFFRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUNuQixDQUFDLENBQUMsRUFBRSxDQUFDLHFCQUFxQixDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FDM0UsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1lBQ1YsTUFBTSxJQUFJLFNBQVMsQ0FBQyxHQUFHLHFCQUFxQixnQ0FBZ0MsQ0FBQyxDQUFBO1NBQ2hGO0lBQ0wsQ0FBQztJQUVPLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxN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