mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-22 01:03:01 +02:00
6073964768
This is BN15. It is a really big change; see the PR for all the details.
239 lines
9.8 KiB
TypeScript
239 lines
9.8 KiB
TypeScript
import { commonPasswordDictionary, letters, packetSniffPhrases } from "./dictionaryData";
|
|
import { generateSimpleArithmeticExpression, getPassword, romanNumeralEncoder } from "../controllers/ServerGenerator";
|
|
import { generateDarknetServerName, isPasswordResponse, type PasswordResponse } from "./DarknetServerOptions";
|
|
import { LocationName } from "@enums";
|
|
import { getServerState, LogEntry } from "./DarknetState";
|
|
import { ModelIds } from "../Enums";
|
|
import { getDarknetServer } from "../utils/darknetServerUtils";
|
|
import { getAllMovableDarknetServers } from "../utils/darknetNetworkUtils";
|
|
import { getExactCorrectChars, getTwoCharsInPassword } from "../utils/darknetAuthUtils";
|
|
import type { DarknetServer } from "../../Server/DarknetServer";
|
|
import { isLabyrinthServer, isLocationStatus } from "../effects/labyrinth";
|
|
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
|
|
|
const MAX_LOG_LINES = 200;
|
|
|
|
export const capturePackets = (server: DarknetServer) => {
|
|
const BASE_PASSWORD_INCLUSION_RATE = 0.18;
|
|
const DIFFICULTY_MODIFIER = 0.88;
|
|
const difficulty = server.difficulty * 1.3;
|
|
const vulnerability = server.modelId === ModelIds.packetSniffer ? 8 : 1;
|
|
const passwordInclusionChance = BASE_PASSWORD_INCLUSION_RATE * vulnerability * DIFFICULTY_MODIFIER ** difficulty;
|
|
|
|
if (Math.random() < passwordInclusionChance) {
|
|
const intro = Math.floor(Math.random() * 124);
|
|
return `${getRandomData(server, intro)}${server.password}${getRandomData(
|
|
server,
|
|
124 - intro - server.password.length,
|
|
)}`;
|
|
}
|
|
if (Math.random() < passwordInclusionChance) {
|
|
const connectedServerName = server.serversOnNetwork[Math.floor(Math.random() * server.serversOnNetwork.length)];
|
|
const connectedServer = getDarknetServer(connectedServerName);
|
|
if (connectedServer) {
|
|
const intro = Math.floor(Math.random() * 124);
|
|
return `${getRandomData(server, intro)} ${connectedServerName}:${connectedServer.password} ${getRandomData(
|
|
server,
|
|
124 - intro - connectedServer.password.length - connectedServerName.length,
|
|
)}`;
|
|
}
|
|
}
|
|
|
|
return `${getRandomData(server, 124)}`;
|
|
};
|
|
|
|
const getRandomData = (server: DarknetServer, length: number) => {
|
|
const password = server.password;
|
|
let result = "";
|
|
while (result.length < length) {
|
|
if (Math.random() < 0.1) {
|
|
result += " " + packetSniffPhrases[Math.floor(Math.random() * packetSniffPhrases.length)] + " ";
|
|
} else if (Math.random() < 0.25) {
|
|
result += commonPasswordDictionary[Math.floor(Math.random() * commonPasswordDictionary.length)];
|
|
} else if (Math.random() < 0.2) {
|
|
result += " " + getRandomCharsInPassword(password);
|
|
} else if (Math.random() < 0.8) {
|
|
result += getPassword(password.length, !!password.split("").find((c) => letters.includes(c)));
|
|
} else if (Math.random() < 0.3) {
|
|
result += generateSimpleArithmeticExpression(Math.floor(Math.random() * 5 + 2));
|
|
} else if (Math.random() < 0.33) {
|
|
const mostRecentAuthLog = getMostRecentAuthLog(server.hostname);
|
|
if (mostRecentAuthLog) {
|
|
result += " " + getExactCharactersHint(mostRecentAuthLog.passwordAttempted, password);
|
|
}
|
|
} else if (Math.random() < 0.6) {
|
|
result += " " + generateDarknetServerName() + " ";
|
|
} else if (Math.random() < 0.15) {
|
|
result += "/" + Object.keys(LocationName)[Math.floor(Math.random() * Object.keys(LocationName).length)] + "/";
|
|
} else if (Math.random() < 0.05) {
|
|
const servers = getAllMovableDarknetServers();
|
|
const randomServer = servers[Math.floor(Math.random() * servers.length)];
|
|
return `--${randomServer.password}--`;
|
|
} else {
|
|
result += romanNumeralEncoder(Math.floor(Math.random() * 5000));
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
const getRandomCharsInPassword = (password: string) => {
|
|
if (!password) {
|
|
return "There's definitely nothing in that password...";
|
|
}
|
|
const [containedChar1, containedChar2] = getTwoCharsInPassword(password);
|
|
const hints = [
|
|
`There's definitely a ${containedChar1} and a ${containedChar2}...`,
|
|
`I can see a ${containedChar1} and a ${containedChar2}.`,
|
|
`I must use ${containedChar1} & ${containedChar2}!`,
|
|
`Did it have a ${containedChar1} and a ${containedChar2}?`,
|
|
`Note to self: ${containedChar1} and ${containedChar2} are important.`,
|
|
`I think ${containedChar1} with ${containedChar2} is key.`,
|
|
`I need to remember ${containedChar1} 'n ${containedChar2}.`,
|
|
`Theres a ${containedChar1}, and maybe a ${containedChar2}...`,
|
|
];
|
|
return hints[Math.floor(Math.random() * hints.length)];
|
|
};
|
|
|
|
const getExactCharactersHint = (lastPassword: string, realPassword: string) => {
|
|
const correctCharPlacement = getExactCorrectChars(realPassword, lastPassword);
|
|
const rightChars = realPassword
|
|
.split("")
|
|
.filter((c, i) => correctCharPlacement[i])
|
|
.slice(0, 2);
|
|
if (rightChars.length === 0) {
|
|
return "No characters are in the right place.";
|
|
}
|
|
return `The characters ${rightChars.join(", ")} are in the right place. `;
|
|
};
|
|
|
|
export const logPasswordAttempt = (server: DarknetServer, passwordResponse: PasswordResponse, pid: number) => {
|
|
const serverState = getServerState(server.hostname);
|
|
const serverLogs = serverState.serverLogs;
|
|
populateServerLogsWithNoise(server);
|
|
|
|
let message = passwordResponse;
|
|
|
|
// buffer overflow servers have special logging: any characters beyond the password length start to overwrite the
|
|
// response code in the log, which can turn it into a 200
|
|
if (server.modelId === ModelIds.BufferOverflow) {
|
|
if (isLocationStatus(passwordResponse.data)) {
|
|
exceptionAlert(
|
|
new Error(
|
|
`Invalid password response data of model: ${ModelIds.BufferOverflow}. Got a location status instead of a ` +
|
|
`string or undefined. Server: ${server.hostname}. passwordAttempted: ${
|
|
passwordResponse.passwordAttempted
|
|
}. data: ${JSON.stringify(passwordResponse.data)}`,
|
|
),
|
|
true,
|
|
);
|
|
return;
|
|
}
|
|
const [passwordInBuffer, overflow] = (passwordResponse.data ?? "").split(",");
|
|
message = {
|
|
code: passwordResponse.code,
|
|
passwordAttempted: passwordInBuffer,
|
|
passwordExpected: overflow,
|
|
message: passwordResponse.message,
|
|
} satisfies PasswordResponse;
|
|
}
|
|
|
|
const logMessage = {
|
|
message,
|
|
pid,
|
|
};
|
|
serverState.serverLogs = [logMessage, ...serverLogs].slice(0, MAX_LOG_LINES);
|
|
};
|
|
|
|
export const populateServerLogsWithNoise = (server: DarknetServer) => {
|
|
if (isLabyrinthServer(server.hostname) || server.logTrafficInterval === -1) return;
|
|
|
|
const serverState = getServerState(server.hostname);
|
|
const interval = server.logTrafficInterval;
|
|
if (!serverState.lastLogTime) {
|
|
serverState.serverLogs = [
|
|
getLogNoise(server, new Date(new Date().getTime() - interval * 1000)),
|
|
getLogNoise(server, new Date(new Date().getTime() - interval * 2000)),
|
|
];
|
|
serverState.lastLogTime = new Date();
|
|
return;
|
|
}
|
|
|
|
const lastLogTime = new Date(serverState.lastLogTime ?? new Date()).getTime();
|
|
const millisecondsSinceLastLog = new Date().getTime() - lastLogTime;
|
|
const missingLogs = Math.floor(millisecondsSinceLastLog / (interval * 1000));
|
|
if (missingLogs > 0) {
|
|
const noiseArray = Array(missingLogs)
|
|
.fill("")
|
|
.map((__: unknown, i: number) => getLogNoise(server, new Date(lastLogTime + interval * 1000 * (i + 1))));
|
|
serverState.serverLogs = [...noiseArray, ...serverState.serverLogs].slice(0, MAX_LOG_LINES);
|
|
// set the last log date at when the prior log would have been generated, as if they are added live
|
|
serverState.lastLogTime = new Date(lastLogTime + missingLogs * interval * 1000);
|
|
}
|
|
};
|
|
|
|
const getLogNoise = (server: DarknetServer, logDate: Date): LogEntry => {
|
|
if (Math.random() < 0.2) {
|
|
return log(packetSniffPhrases[Math.floor(Math.random() * packetSniffPhrases.length)]);
|
|
}
|
|
if (Math.random() < 0.05 * (1 / (server.difficulty + 1))) {
|
|
const connectedServerName = server.serversOnNetwork[Math.floor(Math.random() * server.serversOnNetwork.length)];
|
|
const connectedServer = getDarknetServer(connectedServerName);
|
|
if (connectedServer) {
|
|
return log(`Connecting to ${connectedServerName}:${connectedServer.password} ...`);
|
|
}
|
|
}
|
|
if (Math.random() < 0.05) {
|
|
const connectedServerName = server.serversOnNetwork[Math.floor(Math.random() * server.serversOnNetwork.length)];
|
|
return log(`[sending transaction details to ${connectedServerName}.]`);
|
|
}
|
|
if (Math.random() < 0.1) {
|
|
const mostRecentAuthLog = getMostRecentAuthLog(server.hostname);
|
|
if (mostRecentAuthLog) {
|
|
return log(getExactCharactersHint(mostRecentAuthLog.passwordAttempted, server.password));
|
|
}
|
|
}
|
|
|
|
if (Math.random() < 0.1) {
|
|
return log(getRandomCharsInPassword(server.password));
|
|
}
|
|
|
|
if (Math.random() < 0.05) {
|
|
const servers = getAllMovableDarknetServers();
|
|
const randomServer = servers[Math.floor(Math.random() * servers.length)];
|
|
return log(`--${randomServer.password}--`);
|
|
}
|
|
|
|
if (server.modelId === ModelIds.packetSniffer && Math.random() < 0.5 / (server.difficulty + 1)) {
|
|
return log("Authentication successful: " + server.password);
|
|
}
|
|
|
|
return log(`${logDate.toLocaleTimeString()}: ${server.hostname} - heartbeat check (alive)`);
|
|
};
|
|
|
|
const log = (message: string, pid = -1) => ({
|
|
message,
|
|
pid,
|
|
});
|
|
|
|
export const getMostRecentAuthLog = (hostname: string) => {
|
|
for (const log of getServerState(hostname).serverLogs) {
|
|
if (!isPasswordResponse(log.message)) {
|
|
continue;
|
|
}
|
|
return log.message;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const getServerLogs = (server: DarknetServer, logLines: number, peek = false) => {
|
|
populateServerLogsWithNoise(server);
|
|
|
|
const serverState = getServerState(server.hostname);
|
|
if (peek) {
|
|
return serverState.serverLogs.slice(0, logLines);
|
|
}
|
|
const logs = serverState.serverLogs.slice(0, logLines);
|
|
serverState.serverLogs = serverState.serverLogs.slice(logLines);
|
|
return logs;
|
|
};
|