Compare commits

..

10 Commits

Author SHA1 Message Date
Andrii Dokhniak
77a459d64f symlink 2025-10-05 14:30:24 +02:00
Andrii Dokhniak
2ebb20f1af symlink 2025-10-05 14:26:45 +02:00
Andrii Dokhniak
095ff9f680 WIP: stable version 2025-09-29 20:17:30 +02:00
Andrii Dokhniak
22d07c8bdc WIP: stable version 2025-09-29 20:13:32 +02:00
Andrii Dokhniak
7a2af14a33 WIP: stable version 2025-09-29 17:18:59 +02:00
Andrii Dokhniak
ccd9ccecbd WIP: stable version 2025-09-29 16:32:27 +02:00
Andrii Dokhniak
cab43f63ce mitmproxy 2025-08-30 18:27:49 +02:00
Andrii Dokhniak
e3f97f5488 WIP: Back to mitmproxy 2025-08-17 23:36:06 +02:00
Andrii Dokhniak
2a836ea6cb Manuall frida 2025-08-14 18:04:12 +02:00
Andrii Dokhniak
178c65be34 adding open ports 2025-08-07 16:46:36 +02:00
39 changed files with 1453 additions and 3558 deletions

View File

@ -12,11 +12,6 @@ WORKDIR /httptoolkit-server
RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \ RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \
&& npm i && npm run build:src && npm i && npm run build:src
# Set up proxy_cache_thing
ADD proxy_cache_thing /proxy_cache_thing
WORKDIR /proxy_cache_thing
RUN npm i && npm run build
ADD entrypoint.sh /entrypoint.sh ADD entrypoint.sh /entrypoint.sh
ARG PROXY_PORT ARG PROXY_PORT

View File

@ -20,13 +20,46 @@ async function spawnPromise(program, args) {
let io = new Server(); let io = new Server();
import { get_screenshot } from "./screenshot.mjs";
async function send_private_data() { async function send_private_data() {
let adid = await spawnPromise("bash", ["/conf/get_adid.sh"]) let adid = await spawnPromise("bash", ["/conf/get_adid.sh"]);
adid = adid.output; adid = adid.output;
let gps_coords = await spawnPromise("bash", ["/conf/get_location.sh"]) let gps_coords = await spawnPromise("bash", ["/conf/get_location.sh"]);
gps_coords = gps_coords.output; gps_coords = gps_coords.output;
gps_coords = gps_coords.trim().split(','); gps_coords = gps_coords.trim().split(",");
io.emit("private_info", {adid, latitude: gps_coords[0], longitude: gps_coords[1]}) io.emit("private_info", {
adid,
latitude: gps_coords[0],
longitude: gps_coords[1],
});
}
async function send_open_ports() {
let ports = new Set(
(await spawnPromise("bash", ["/conf/get_ports.sh"])).output
.trim()
.split(" ")
);
let start_ports = fs.readFileSync("/ports").toString().trim().split(" ");
for (let port of start_ports) {
ports.delete(port);
}
io.emit("open_ports", [...ports]);
}
async function send_installed_apps() {
let out = await spawnPromise("bash", ["/conf/get_installed_apps.sh"]);
if (out.code != 0) {
send_notification(
io,
false,
"Listing installed apps with frida",
out.output
);
}
io.emit("installed_apps", JSON.parse(out.output));
} }
function send_notification(socket, is_ok, context, message) { function send_notification(socket, is_ok, context, message) {
@ -37,59 +70,106 @@ function send_notification(socket, is_ok, context, message) {
}); });
} }
let screenshot_in_flight = false;
let gps_setting_in_progress = false; let gps_setting_in_progress = false;
//maybe check output of child processes and send errors in some way //maybe check output of child processes and send errors in some way
io.on("connection", (socket) => { io.on("connection", (socket) => {
socket.onAny((ev, ...args) => { socket.onAny((ev, ...args) => {
console.log("server got: ", ev, ...args); console.log("server got: ", ev, ...args);
}); });
let screenshots_in_flight = 0;
socket.on("screenshot", async () => { socket.on("screenshot", async () => {
if (screenshot_in_flight) if (screenshots_in_flight > 1) return;
return; screenshots_in_flight++;
screenshot_in_flight = true;
let screen; let screen;
try { try {
screen = await fetch("http://localhost:9987/v2/uiDevice/screenshot"); screen = await get_screenshot();
} catch(err) { socket.emit("screenshot_data", screen);
console.error("Failed to get the screenshot from culebra, the emulator probably died", err); } catch (err) {
screenshot_in_flight = false; console.error(
"Failed to get the screenshot. The emulator probably died",
err
);
return; return;
} }
let body = await screen.bytes(); screenshots_in_flight--;
socket.emit("screenshot_data", body);
screenshot_in_flight = false;
}); });
let priv_data_requests_in_flight = 0;
socket.on("private_info_req", async () => { socket.on("private_info_req", async () => {
if (priv_data_requests_in_flight > 3) return;
priv_data_requests_in_flight++;
await send_private_data(); await send_private_data();
}) priv_data_requests_in_flight--;
});
let port_requests_in_flight = 0;
socket.on("open_ports_req", async () => {
if (port_requests_in_flight > 3) return;
port_requests_in_flight++;
await send_open_ports();
port_requests_in_flight--;
});
let installed_apps_requests_in_flight = 0;
socket.on("installed_apps_req", async () => {
if (installed_apps_requests_in_flight > 3) return;
installed_apps_requests_in_flight++;
await send_installed_apps();
installed_apps_requests_in_flight--;
});
socket.on("start_frida_app", async (app_id) => {
await spawnPromise("bash", ["/conf/start_frida_app.sh", app_id]);
send_notification(
socket,
true,
"Frida intercepting stopped",
"the traffic of " + app_id + " is no longer being intercepted"
);
});
socket.on("reset_adid", async () => { socket.on("reset_adid", async () => {
await spawnPromise("bash", ["/conf/reset_adid.sh"]); await spawnPromise("bash", ["/conf/reset_adid.sh"]);
await send_private_data(); await send_private_data();
}) });
socket.on("back", async () => { socket.on("back", async () => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", ["/conf/back.sh"]); await spawnPromise("bash", ["/conf/back.sh"]);
}); });
socket.on("recent", async () => { socket.on("recent", async () => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", ["/conf/recent.sh"]); await spawnPromise("bash", ["/conf/recent.sh"]);
}); });
socket.on("home", async () => { socket.on("home", async () => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", ["/conf/home.sh"]); await spawnPromise("bash", ["/conf/home.sh"]);
}); });
@ -107,22 +187,32 @@ io.on("connection", (socket) => {
// drag handles both drag and click // drag handles both drag and click
socket.on("motionevent", async (data) => { socket.on("motionevent", async (data) => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", [ await spawnPromise("bash", [
"/conf/motionevent.sh", "/conf/motionevent.sh",
data.motionType, data.motionType,
data.x + "", data.x + "",
data.y + "" data.y + "",
]); ]);
}); });
// drag handles both drag and click // drag handles both drag and click
socket.on("drag", async (data) => { socket.on("drag", async (data) => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", [ await spawnPromise("bash", [
"/conf/drag.sh", "/conf/drag.sh",
@ -136,19 +226,26 @@ io.on("connection", (socket) => {
socket.on("key", async (data) => { socket.on("key", async (data) => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
await spawnPromise("bash", [ await spawnPromise("bash", ["/conf/press_key.sh", data.key]);
"/conf/press_key.sh",
data.key
]);
}); });
socket.on("setcoord", async (data) => { socket.on("setcoord", async (data) => {
if (gps_setting_in_progress) { if (gps_setting_in_progress) {
send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); send_notification(
return ; socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
} }
gps_setting_in_progress = true; gps_setting_in_progress = true;
const res = await spawnPromise("bash", [ const res = await spawnPromise("bash", [

View File

@ -5,10 +5,116 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.14.0",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"ws": "^8.18.0" "ws": "^8.18.0"
} }
}, },
"node_modules/@grpc/grpc-js": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz",
"integrity": "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==",
"license": "Apache-2.0",
"dependencies": {
"@grpc/proto-loader": "^0.8.0",
"@js-sdsl/ordered-map": "^4.4.2"
},
"engines": {
"node": ">=12.10.0"
}
},
"node_modules/@grpc/proto-loader": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
"integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
"license": "Apache-2.0",
"dependencies": {
"lodash.camelcase": "^4.3.0",
"long": "^5.0.0",
"protobufjs": "^7.5.3",
"yargs": "^17.7.2"
},
"bin": {
"proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@js-sdsl/ordered-map": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"node_modules/@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
@ -46,6 +152,30 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/base64id": { "node_modules/base64id": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
@ -55,6 +185,38 @@
"node": "^4.5.0 || >= 5.9" "node": "^4.5.0 || >= 5.9"
} }
}, },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.7.2", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@ -94,6 +256,12 @@
} }
} }
}, },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/engine.io": { "node_modules/engine.io": {
"version": "6.6.4", "version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
@ -144,6 +312,45 @@
} }
} }
}, },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"license": "MIT"
},
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/mime-db": { "node_modules/mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@ -189,6 +396,39 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/protobufjs": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/socket.io": { "node_modules/socket.io": {
"version": "4.8.1", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
@ -251,6 +491,32 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
@ -266,6 +532,23 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/ws": { "node_modules/ws": {
"version": "8.18.0", "version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
@ -285,9 +568,124 @@
"optional": true "optional": true
} }
} }
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
} }
}, },
"dependencies": { "dependencies": {
"@grpc/grpc-js": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz",
"integrity": "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==",
"requires": {
"@grpc/proto-loader": "^0.8.0",
"@js-sdsl/ordered-map": "^4.4.2"
}
},
"@grpc/proto-loader": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
"integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
"requires": {
"lodash.camelcase": "^4.3.0",
"long": "^5.0.0",
"protobufjs": "^7.5.3",
"yargs": "^17.7.2"
}
},
"@js-sdsl/ordered-map": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="
},
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
},
"@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
},
"@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
},
"@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
},
"@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"requires": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
},
"@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
},
"@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
},
"@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
},
"@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"@socket.io/component-emitter": { "@socket.io/component-emitter": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
@ -318,11 +716,47 @@
"negotiator": "0.6.3" "negotiator": "0.6.3"
} }
}, },
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"base64id": { "base64id": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
}, },
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"cookie": { "cookie": {
"version": "0.7.2", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@ -345,6 +779,11 @@
"ms": "^2.1.3" "ms": "^2.1.3"
} }
}, },
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"engine.io": { "engine.io": {
"version": "6.6.4", "version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
@ -374,6 +813,31 @@
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==" "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="
}, },
"escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
},
"long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
},
"mime-db": { "mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@ -402,6 +866,30 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
}, },
"protobufjs": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
"requires": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"socket.io": { "socket.io": {
"version": "4.8.1", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
@ -442,6 +930,24 @@
"debug": "~4.3.1" "debug": "~4.3.1"
} }
}, },
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"undici-types": { "undici-types": {
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
@ -452,11 +958,45 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
}, },
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"ws": { "ws": {
"version": "8.18.0", "version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"requires": {} "requires": {}
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
},
"yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"requires": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
}
},
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
} }
} }
} }

View File

@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.14.0",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"ws": "^8.18.0" "ws": "^8.18.0"
} }

View File

@ -0,0 +1,30 @@
import { loadPackageDefinition, credentials } from '@grpc/grpc-js';
import { loadSync } from '@grpc/proto-loader';
let PROTO_PATH = '/opt/android-sdk-linux/emulator/lib/emulator_controller.proto';
let packageDefinition = loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
let emu_control = loadPackageDefinition(packageDefinition).android.emulation.control;
export async function get_screenshot() {
let target = 'localhost:8888';
let emu_controller = new emu_control.EmulatorController(target,
credentials.createInsecure());
let promise = new Promise((resolve, reject) => {
emu_controller.getScreenshot({}, function(err, response) {
if (err === null) {
resolve (response.image);
} else {
reject (err);
}
});
});
return promise;
}

View File

@ -1,6 +1,22 @@
bash /conf/start_culebra.sh & bash /conf/startup.sh
npm i -C /code
bash /conf/wait_for_sd.sh
node /code/index.mjs
#tail -f /dev/null bash /conf/wait_for_sd.sh
adb shell su root /tmp/frida-server &
apt-get install iptables -y
# configuring the pinning / unpinning scripts
perl -i -0777p -e 's|CERT_PEM = .*?;|CERT_PEM = `'"$(cat /certificates/mitmproxy-ca-cert.pem | sed -z 's/\n/\\n/g')"'`;|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const PROXY_SUPPORTS_SOCKS5 = false|const PROXY_SUPPORTS_SOCKS5 = false|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const BLOCK_HTTP3 = true|const BLOCK_HTTP3 = true|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const PROXY_PORT = 8000|const PROXY_PORT = 8000|gsm' /frida-scripts/config.js
# configuring forwarding the proxy info to mitmproxy
adb reverse tcp:8000 tcp:8000
iptables -t nat -A OUTPUT -m addrtype --src-type LOCAL --dst-type LOCAL -p tcp --dport 8000 -j DNAT --to-destination $(getent hosts mitmproxy | awk '{ print $1 }'):1080
iptables -t nat -A POSTROUTING -m addrtype --src-type LOCAL --dst-type UNICAST -j MASQUERADE
npm i -C /code
node /code/index.mjs

View File

@ -0,0 +1,2 @@
set -e
frida-ps -Uai --json

View File

@ -0,0 +1,6 @@
out=$(adb shell su root "ss -tunlp | tail -n+2 | grep -v users:\(\(\\\"frida-server | awk -F \" \" '{print \$5}' | awk -F ':' '{print \$NF}' | sort -n | uniq")
if [ ! -f /ports ]; then
echo -n $out > /ports
fi
echo -n $out

View File

@ -1,18 +0,0 @@
set -x
function culebra_loop() {
export PATH=$PATH:/root/culebraDependencies
cd /root/culebra
while true; do
./culebratester2 start-server
done
}
rm -f /opt/android-sdk-linux/.android/avd/virtual_dev.avd/*.lock
adb start-server
emulator -avd virtual_dev -writable-system -no-window -no-audio -memory 4096 &
adb wait-for-device
adb emu avd snapshot load configured
culebra_loop

View File

@ -0,0 +1,17 @@
set -e
cd /frida-scripts
frida -U \
-l ./config.js \
-l ./native-connect-hook.js \
-l ./android/android-proxy-override.js \
-l ./android/android-system-certificate-injection.js \
-l ./android/android-certificate-unpinning.js \
-l ./android/android-certificate-unpinning-fallback.js \
-l ./android/android-disable-root-detection.js \
-l ./android/android-disable-flutter-certificate-pinning.js \
-f $1
# This is broken with mitmproxy
#-l ./native-tls-hook.js \

13
android/conf/startup.sh Normal file
View File

@ -0,0 +1,13 @@
set -x
rm -f /opt/android-sdk-linux/.android/avd/virtual_dev.avd/*.lock
adb start-server
emulator -avd virtual_dev -writable-system -no-window -no-audio -memory 4096 -grpc 8888 &
adb wait-for-device
adb emu avd snapshot load configured
adb wait-for-device
# set the date on the emulator for the ssl to work properly
adb shell su root "date $(date +%m%d%H%M%G.%S)"
adb shell su root "am broadcast -a android.intent.action.TIME_SET"

View File

@ -1,23 +1,10 @@
#!/bin/bash #!/bin/bash
set -e set -e
node /proxy_cache_thing/dist/index.js &
CACHE_PID=$!
/httptoolkit-server/bin/run start -c /certificates &
HTTPTOOLKIT_SERVER_PID=$!
bash /conf/docker-entrypoint.sh & bash /conf/docker-entrypoint.sh &
ANDROID_PID=$! ANDROID_PID=$!
function check_dead() { function check_dead() {
if ! ps -p $CACHE_PID > /dev/null; then
echo "[ERROR] The proxy cache died, exiting...";
exit 1;
fi
if ! ps -p $HTTPTOOLKIT_SERVER_PID > /dev/null; then
echo "[ERROR] The httptoolkit_server died, exiting...";
exit 1;
fi
if ! ps -p $ANDROID_PID > /dev/null; then if ! ps -p $ANDROID_PID > /dev/null; then
echo "[ERROR] The android emulator died, exiting..."; echo "[ERROR] The android emulator died, exiting...";
exit 1; exit 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
{
"name": "proxy_thing",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "commonjs",
"main": "./lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"start": "tsc && node dist/index.js",
"lint": "tslint -c tslint.json src/**/*.ts",
"prepublish": "npm run build"
},
"dependencies": {
"express": "^4.21.2",
"mockttp": "^3.16.0",
"ts-node": "^10.9.2",
"tslint": "^5.20.1",
"ws": "^8.18.1"
},
"files": [
"./bin/*",
"./lib/*"
],
"typings": "./lib/index.d.ts",
"directories": {
"lib": "lib"
},
"devDependencies": {
"typescript": "^5.8.2"
},
"keywords": []
}

View File

@ -1,188 +0,0 @@
import { getLocal } from "mockttp";
import * as WebSock from "ws";
const mockServer = getLocal();
const wait_for_open = (socket: WebSock) => {
if (socket.readyState === socket.OPEN) return;
return new Promise((resolve, reject) => {
const maxNumberOfAttempts = 20;
const intervalTime = 100; //ms
let currentAttempt = 0;
const interval = setInterval(() => {
if (currentAttempt > maxNumberOfAttempts - 1) {
clearInterval(interval);
reject(new Error("Maximum number of attempts exceeded"));
} else if (socket.readyState === socket.OPEN) {
clearInterval(interval);
resolve(undefined);
} else if (socket.readyState == socket.CLOSED) {
console.log(socket.url);
reject(new Error("The server socket closed"));
}
currentAttempt++;
}, intervalTime);
});
};
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
function waitforhost(
url: string,
expected_status = 200,
interval = 1000,
attempts = 10
): Promise<void> {
let count = 1;
return new Promise(async (resolve, reject) => {
while (count < attempts) {
await sleep(interval);
try {
const response = await fetch(url);
if (response.status === expected_status) {
resolve();
break;
} else {
count++;
}
} catch {
count++;
console.log(`Still down, trying ${count} of ${attempts}`);
}
}
reject(new Error(`Server is down: ${count} attempts tried`));
});
}
(async () => {
const proxy_port = process.env.PROXY_PORT;
const server_port = process.env.SERVER_PORT;
const loopback_port = process.env.LOOPBACK_PORT; // internal only
const monitoring_api_port = process.env.MONITORING_API_PORT;
if (!proxy_port || !server_port || !monitoring_api_port || !loopback_port) {
throw Error("Configuration env variables not set");
}
// Expect 403 forbidden
await waitforhost("http://127.0.0.1:" + server_port, 403);
mockServer.start(+proxy_port);
let cached_resp: undefined | string = undefined;
const cors_headers = {
"access-control-allow-origin": "*",
"access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
"access-control-allow-headers": "Content-Type, Authorization",
"access-control-max-age": "86400",
"access-control-allow-credentials": "true",
Origin: "http://127.0.0.1:8080", // Trick the server into thinking that it is accessed through localhost
};
await mockServer.forOptions().thenReply(200, "", cors_headers);
await mockServer.forPost("/start").thenPassThrough({
beforeRequest: async (req) => {
if (cached_resp) {
return {
response: { body: cached_resp, headers: cors_headers },
};
}
// forward the request to the real server
return {
url: req.url,
headers: req.headers,
body: await req.body.getDecodedBuffer(),
};
},
beforeResponse: async (resp, _req) => {
// cache the response
cached_resp = (await resp.body.getText()) as string;
// set the cors headers, overwriting the prohibitive ones
return { headers: { ...resp.headers, ...cors_headers } };
},
});
await mockServer.forPost("/stop").thenReply(200);
// forward everything to the real server
await mockServer.forUnmatchedRequest().thenPassThrough({
beforeRequest: async (req) => {
return { url: req.url };
},
beforeResponse: async (resp, _req) => {
return { headers: { ...resp.headers, ...cors_headers } };
},
});
await mockServer
.forAnyWebSocket()
.thenForwardTo("ws://127.0.0.1:" + loopback_port);
let wss = new WebSock.Server({ port: +loopback_port });
// this is append only
let httptoolkit_server_messages: String[] = [];
let api_server = new WebSock.Server({ port: +monitoring_api_port });
api_server.on("connection", (ws) => {
let curr_idx_to_send = 0;
const interval = setInterval(() => {
while (httptoolkit_server_messages[curr_idx_to_send] != undefined) {
if (ws.readyState != ws.OPEN) {
clearInterval(interval);
ws.close();
break;
}
ws.send(httptoolkit_server_messages[curr_idx_to_send]);
curr_idx_to_send++;
}
}, 100);
});
wss.on("connection", (ws, req) => {
let server_session_url = "ws://127.0.0.1:" + server_port + req.url;
let httptoolkit_server_session: WebSock;
console.log("ws connection:", ws.protocol, typeof ws.protocol);
if (ws.protocol) {
httptoolkit_server_session = new WebSock(
server_session_url,
ws.protocol,
{ joinDuplicateHeaders: true, headers: req.headers }
);
} else {
httptoolkit_server_session = new WebSock(server_session_url, {
joinDuplicateHeaders: true,
headers: req.headers,
});
}
httptoolkit_server_session.onerror = (_) => {
ws.close();
httptoolkit_server_session.close();
return;
};
httptoolkit_server_session.onopen = () => {
console.log("ws connected to the server");
};
httptoolkit_server_session.onmessage = (msg) => {
httptoolkit_server_messages.push(msg.data.toString());
ws.send(msg.data);
};
// Listening for messages from the client
ws.on("message", async (message) => {
await wait_for_open(httptoolkit_server_session);
httptoolkit_server_session.send(message.toString());
});
// Handling client disconnection
ws.on("close", () => {
httptoolkit_server_session.close();
});
});
console.log("WS Proxy started");
console.log(mockServer.proxyEnv);
})();

View File

@ -1,38 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": [
"es2015"
],
"rootDir": "src",
"outDir": "dist",
"strict": true,
"alwaysStrict": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"downlevelIteration": true,
"declaration": true,
"pretty": true
},
"include": [
"typings/**/*",
"src/**/*"
]
}

View File

@ -1,3 +0,0 @@
{
"extends": "tslint:latest"
}

View File

@ -1,9 +1,8 @@
FROM node:22.14.0 # docker run --entrypoint sh --rm -it -v ./certificates:/certs alpine/openssl /certs/gen.sh
RUN git clone https://github.com/httptoolkit/httptoolkit-server FROM alpine/openssl
WORKDIR /httptoolkit-server COPY gen_certs.sh /gen_certs.sh
RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \
&& npm i && npm run build:src
CMD /httptoolkit-server/bin/run start -c /certificates ENTRYPOINT sh /gen_certs.sh
# CMD sh

20
certgen/gen_certs.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
set -xe
cd /certificates
rm -rf *
cat > myconfig.cnf << EOF
[mysection]
keyUsage = critical, digitalSignature, nonRepudiation, keyCertSign, cRLSign
basicConstraints = critical, CA:TRUE
EOF
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -sha256 -days 365 -not_before $(date --date="@$(( $(date +%s) - 86400 * 7 ))" +%y%m%d%H%M%SZ) -nodes -subj "/C=XX/O=Sealcode/OU=CompanySectionName/CN=Do not trust, rentgendroid certificate" -config myconfig.cnf -extensions mysection
cat cert.pem key.pem > mitmproxy-ca.pem
mv cert.pem mitmproxy-ca-cert.pem
rm myconfig.cnf

View File

@ -11,12 +11,14 @@ services:
container_name: android container_name: android
sysctls: sysctls:
- net.ipv6.conf.all.disable_ipv6=1 - net.ipv6.conf.all.disable_ipv6=1
- net.ipv4.conf.all.route_localnet=1
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
devices: devices:
- /dev/kvm - /dev/kvm
networks: networks:
- rent_gen_android - rent_gen_android
ports: ports:
- 45456:45459 # This cannot change - 45456:45459 # This cannot change
- 45457:45459 # This cannot change - 45457:45459 # This cannot change
@ -24,24 +26,20 @@ services:
- 3000:3000 # android server port - 3000:3000 # android server port
- 3001:3001 # Notifications server - 3001:3001 # Notifications server
- 5556:5556 # emulator grpc port - 5556:5556 # emulator grpc port
volumes: volumes:
- $PWD/shared_buffer:/shared_buffer - $PWD/shared_buffer:/shared_buffer
- $PWD/android/conf:/conf - $PWD/android/conf:/conf
- $PWD/certificates:/certificates - $PWD/certificates:/certificates
- $PWD/android/code:/code - $PWD/android/code:/code
httptoolkit_ui: mitmproxy:
build: build: ./mitmproxy
context: ./httptoolkit_ui/
args:
# The ip / hostname using which,
# the browser can reach this docker session
DOCKER_HOST: "127.0.0.1"
container_name: httptoolkit_ui
networks: networks:
- rent_gen_android - rent_gen_android
ports: volumes:
- 9080:9080 - $PWD/certificates:/root/.mitmproxy
container_name: mitmproxy
http_server: http_server:
build: ./http_server/ build: ./http_server/
container_name: http_server container_name: http_server

View File

@ -1,6 +1,6 @@
FROM alpine:3.18.2 FROM alpine:3.18.2
RUN apk add npm RUN apk add npm git openssh
WORKDIR /code WORKDIR /code

1
http_server/code/dist/styles.css vendored Symbolic link
View File

@ -0,0 +1 @@
../styles.css

View File

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -xe
npm i npm i
npm run build npm run build

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Rentgen android</title> <title>Rentgen android</title>
<script src="/htmx.js"></script> <script src="/htmx.js"></script>
<link rel="stylesheet" href="styles.css" /> <link rel="stylesheet" href="/styles.css" />
<script src="/main.js" type="module"></script> <script src="/main.js" type="module"></script>
</head> </head>
<body> <body>
@ -49,12 +49,6 @@
<div class="tab-section"> <div class="tab-section">
<div class="tab"> <div class="tab">
<button
class="tablinks active"
onclick="main.open_tab(event, 'httptoolkit-tab')"
>
HttpToolkit UI
</button>
<button class="tablinks" onclick="main.open_tab(event, 'logs-tab')"> <button class="tablinks" onclick="main.open_tab(event, 'logs-tab')">
Logs Logs
</button> </button>
@ -66,13 +60,13 @@
</button> </button>
</div> </div>
<div class="tabcontent" id="logs-tab"> <div class="tabcontent active" id="logs-tab">
<div id="logs"> <div id="logs">
<p id="clicks-log" class="log-section"></p> <p id="clicks-log" class="log-section"></p>
<div id="traffic-log" class="log-section"> <div id="traffic-log" class="log-section">
<div> <div>
<button onClick="main.download_har()">Download HAR</button> <button onclick="main.download_har()">Download HAR</button>
<button onClick="main.inspect_har()">Inspect HAR</button> <button onclick="main.inspect_har()">Inspect HAR</button>
<div> <div>
<h2>stats:</h2> <h2>stats:</h2>
<p id="traffic-log-req-res-pairs"> <p id="traffic-log-req-res-pairs">
@ -89,14 +83,6 @@
</div> </div>
</div> </div>
<div class="tabcontent active" id="httptoolkit-tab">
<iframe
id="httptoolkit-frame"
style="flex-grow: 1"
src="http://localhost:9080/"
title="httptoolkit"
></iframe>
</div>
<div class="tabcontent" id="controls-tab"> <div class="tabcontent" id="controls-tab">
<form id="set_coords" onsubmit="main.coords_handler(event)"> <form id="set_coords" onsubmit="main.coords_handler(event)">
<label> <label>
@ -129,6 +115,16 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h2>Lanuch app</h2>
<form id="launch_app_form">
<select name="appid" id="app_id_select">
</select>
<button type="submit">Launch app</button>
</form>
<h2>Open ports</h2>
<p id="open-ports"></p>
</div> </div>
</div> </div>
</main> </main>

View File

@ -63,9 +63,11 @@ app.get("/", async function (req, res) {
//POST //POST
app.use(express.text({limit: "100mb"})); app.use(express.text({limit: "100mb"}));
app.post("/inspect_har", upload.none(), function (req, res) { app.post("/inspect_har", upload.none(), async function (req, res) {
let body = req.body; let body = req.body;
let har = body.har; let har_req = await fetch("http://mitmproxy:9111/get_har")
let har = await har_req.text();
let private_data; let private_data;
if (body.private_data) if (body.private_data)
private_data = JSON.parse(body.private_data); private_data = JSON.parse(body.private_data);
@ -74,6 +76,20 @@ app.post("/inspect_har", upload.none(), function (req, res) {
res.send(build_html(har, private_data)); res.send(build_html(har, private_data));
}); });
app.get("/download_har", upload.none(), async function (req, res) {
let har_req = await fetch("http://mitmproxy:9111/get_har")
let har = await har_req.text();
res.status(200).send(har);
});
app.get("/get_intercept_stats", upload.none(), async function (_req, res) {
let req = await fetch("http://mitmproxy:9111/get_stats")
let stats = await req.text();
res.status(200).send(stats);
});
app.use(fileUpload()); app.use(fileUpload());
app.post("/upload_apk", async function (req, res) { app.post("/upload_apk", async function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) { if (!req.files || Object.keys(req.files).length === 0) {

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,11 @@ import {
adid_priv_info_table, adid_priv_info_table,
lat_priv_info_table, lat_priv_info_table,
lon_priv_info_table, lon_priv_info_table,
recentButton, recentButton,
open_ports,
app_id_select,
launch_app_form,
sleep,
} from "./shared"; } from "./shared";
import { start_notifications } from "./notifications"; import { start_notifications } from "./notifications";
@ -116,6 +120,55 @@ socket.on("private_info", (data) => {
lon_priv_info_table.textContent = data.longitude; lon_priv_info_table.textContent = data.longitude;
}); });
socket.on("open_ports", (data: string[]) => {
console.log("open_ports");
open_ports.textContent = data.toString();
});
async function installed_apps_loop() {
var before;
while (true) {
before = performance.now();
socket.emit("installed_apps_req");
while (performance.now() - before < 2000) await sleep(100);
}
}
socket.on("installed_apps", (data) => {
let all_ids = new Set();
let all_inserted_ids = new Set();
for (let app of data) {
all_ids.add(app.identifier);
}
// remove all the elements that are no longer in the set
for (const el of app_id_select.children) {
const opt = el as HTMLOptionElement;
if (!all_ids.has(opt.value)) opt.remove();
else all_inserted_ids.add(opt.value);
}
for (let app of data) {
if (all_inserted_ids.has(app.identifier))
continue;
let app_opt = document.createElement("option");
app_opt.value = app.identifier;
app_opt.innerText = app.name;
app_id_select.appendChild(app_opt);
}
});
(launch_app_form as HTMLFormElement).addEventListener("submit", (event) => {
event.preventDefault();
event.stopImmediatePropagation();
const form_data = new FormData(event.target as HTMLFormElement);
socket.emit("start_frida_app", form_data.get("appid"));
})
socket.emit("private_info_req"); socket.emit("private_info_req");
socket.onAny((ev, ...args) => { socket.onAny((ev, ...args) => {
@ -213,10 +266,6 @@ window.addEventListener("keydown", (event) => {
} }
}); });
export async function sleep(time: number) {
return new Promise((resolve) => setTimeout(resolve, time));
}
async function screenshot_loop() { async function screenshot_loop() {
var before; var before;
@ -227,6 +276,19 @@ async function screenshot_loop() {
while (performance.now() - before < 100) await sleep(50); while (performance.now() - before < 100) await sleep(50);
} }
} }
async function open_ports_loop() {
var before;
while (true) {
before = performance.now();
socket.emit("open_ports_req");
while (performance.now() - before < 2000) await sleep(100);
}
}
installed_apps_loop();
open_ports_loop();
screenshot_loop(); screenshot_loop();
start_notifications(); start_notifications();
start_traffic_log(); start_traffic_log();

View File

@ -12,4 +12,14 @@ export const adid_priv_info_table = document.getElementById("adid_priv_info_tabl
export const lat_priv_info_table = document.getElementById("lat_priv_info_table")!; export const lat_priv_info_table = document.getElementById("lat_priv_info_table")!;
export const lon_priv_info_table = document.getElementById("lon_priv_info_table")!; export const lon_priv_info_table = document.getElementById("lon_priv_info_table")!;
export const open_ports = document.getElementById("open-ports")!;
export const app_id_select = document.getElementById("app_id_select")!;
export const launch_app_form = document.getElementById("launch_app_form")!;
export const socket = io(); export const socket = io();
export async function sleep(time: number) {
return new Promise((resolve) => setTimeout(resolve, time));
}

View File

@ -1,41 +1,38 @@
import { Entry, Har, PostData, Request, Response } from "har-format";
import { import {
adid_priv_info_table, adid_priv_info_table,
lat_priv_info_table, lat_priv_info_table,
lon_priv_info_table, lon_priv_info_table,
sleep,
} from "./shared"; } from "./shared";
// Request + responce pairs: 0 // Request + responce pairs: 0
const req_res_pairs = document.getElementById("traffic-log-req-res-pairs")!; const req_res_pairs = document.getElementById("traffic-log-req-res-pairs")!;
// Waiting for the responce: 0 export async function start_traffic_log() {
const waiting = document.getElementById("traffic-log-waiting")!;
const traffic_log_lines = document.getElementById("traffic-log-lines")!; var before;
const inspect_har_form: HTMLFormElement = document.getElementById( while (true) {
"inspect-har-form" before = performance.now();
)! as HTMLFormElement; let connections = "UNKNOWN";
try {
let req = await fetch("/get_intercept_stats");
const connection = new WebSocket("ws://localhost:10001"); let req_text = await req.text();
let json = JSON.parse(req_text);
connections = json.connections.toString();
} catch(_) {}
req_res_pairs.innerText = `Request + responce pairs: ${connections}`;
const unfinished_entries: Map<string, Entry> = new Map(); while (performance.now() - before < 1000) await sleep(500);
const finished_entries: Entry[] = []; }
export function start_traffic_log() {
update_stats();
connection.onmessage = (msg) => {
process_msg(msg.data);
update_stats();
};
connection.onclose = connection.onerror = () => {
window.location.reload();
};
} }
export function download_har() { export async function download_har() {
var tempLink = document.createElement("a"); var tempLink = document.createElement("a");
var taBlob = new Blob([JSON.stringify(export_har())], { let res = await fetch("/download_har");
let res_text = await res.text()
var taBlob = new Blob([res_text], {
type: "text/plain", type: "text/plain",
}); });
@ -69,8 +66,6 @@ function launch_window_with_post(url: string, data: any) {
export async function inspect_har() { export async function inspect_har() {
let data = { let data = {
har: JSON.stringify(export_har()),
private_data: JSON.stringify([ private_data: JSON.stringify([
{desc: "adid", data: adid_priv_info_table.textContent}, {desc: "adid", data: adid_priv_info_table.textContent},
{desc: "latitude", data: lat_priv_info_table.textContent }, {desc: "latitude", data: lat_priv_info_table.textContent },
@ -81,144 +76,5 @@ export async function inspect_har() {
} }
function update_stats() { function update_stats() {
req_res_pairs.innerText = `Request + responce pairs: ${finished_entries.length}`; // waiting.innerText = `Waiting for the responce: ${unfinished_entries.size}`;
waiting.innerText = `Waiting for the responce: ${unfinished_entries.size}`;
}
function process_msg(s: string) {
let obj = JSON.parse(s);
console.log(obj);
if (obj.type !== "data") return;
if (obj.payload && obj.payload.data && obj.payload.data.requestReceived)
process_req(obj.payload.data.requestReceived);
if (obj.payload && obj.payload.data && obj.payload.data.responseCompleted)
process_res(obj.payload.data.responseCompleted);
}
function process_res(res: any) {
let entry = unfinished_entries.get(res.id)!;
let content_type = "application/text";
let headers = JSON.parse(res.rawHeaders).map((header: [string, string]) => {
if (header[0].toLowerCase() === "content-type")
content_type = header[1];
return { name: header[0], value: header[1], comment: "" };
});
//'{"startTime":1751745139334,
// "startTimestamp":347666.762487,
// "bodyReceivedTimestamp":347667.529477,
// "headersSentTimestamp":347906.038202,
// "responseSentTimestamp":347906.616067}'
let timing_events = JSON.parse(res.timingEvents);
let start_ts = timing_events.startTimestamp;
let got_headers_ts = timing_events.headersSentTimestamp;
let end_ts = timing_events.responseSentTimestamp;
let wait_time = got_headers_ts - start_ts;
let recieve_time = end_ts - got_headers_ts;
let response: Response = {
status: res.statusCode,
statusText: res.statusMessage,
httpVersion: entry.request.httpVersion,
cookies: [],
headers,
content: {
size: 0,
mimeType: content_type,
text: res.body,
encoding: "base64",
},
redirectURL: "",
headersSize: -1,
bodySize: -1,
};
entry.response = response;
entry.timings.wait = wait_time;
entry.timings.receive = recieve_time;
unfinished_entries.delete(res.id);
finished_entries.push(entry);
let el = document.createElement("span");
el.innerHTML = `
${entry.request.url}
<br />`;
traffic_log_lines.appendChild(el);
}
function process_req(req: any) {
let content_type = "application/text";
let headers = JSON.parse(req.rawHeaders).map((header: [string, string]) => {
if (header[0].toLowerCase() === "Content-Type")
content_type = header[1];
return { name: header[0], value: header[1], comment: "" };
});
let timing_events = JSON.parse(req.timingEvents);
let start_time: number = timing_events.startTime!;
let start_datetime = new Date(start_time).toISOString();
let request: Request = {
method: req.method,
url: req.url,
httpVersion: req.httpVersion,
cookies: [],
headers,
queryString: [],
postData: req.body
? ({ text: req.body, mimeType: content_type } as PostData)
: undefined,
headersSize: -1,
bodySize: -1,
comment: "",
};
//'{"startTime":1751745139334,"startTimestamp":347666.762487,"bodyReceivedTimestamp":347667.529477,"headersSentTimestamp":347906.038202,"responseSentTimestamp":347906.616067}'
let entry: Entry = {
startedDateTime: start_datetime,
time: 0,
request: request,
response: {
status: 0,
statusText: "",
httpVersion: "",
cookies: [],
headers: [],
content: {
size: 0,
mimeType: "",
},
redirectURL: "",
headersSize: 0,
bodySize: 0,
},
cache: {},
timings: {
wait: 0,
receive: 0,
},
};
unfinished_entries.set(req.id, entry);
}
function export_har(): Har {
let ret: Har = {
log: {
version: "1.2",
creator: {
name: "Rentgendroid",
version: "0.0.1",
},
entries: [...finished_entries, ...unfinished_entries.values()],
},
};
return ret;
} }

View File

@ -1,15 +0,0 @@
<script type="text/javascript">
function randomlinks(){
var myrandom=Math.round(Math.random()*3)
var links=new Array()
links[0]="https://www.DemiCardGame.com"
links[1]="https://www.games.xyphienllc.com"
links[2]="https://www.htcg.news"
links[3]="https://www.eTableCon.com"
window.open(links[myrandom], '_blank');
}
</script>
<form>
<input type="button" value="random link!" onClick="randomlinks()">
</form>

View File

@ -1,23 +0,0 @@
FROM node:20.18.1
# Set up node
RUN npm install -g n && n install 22.14.0 && n use 22.14.0 && hash -r
# If iproute2 is not installed,
# node can't figure out what address to bind 0.0.0.0 to
RUN apt-get update && apt-get install iproute2 python3 --yes \
&& git clone https://github.com/httptoolkit/httptoolkit-ui
WORKDIR httptoolkit-ui
RUN git checkout ac44f8e6e1f5f41be988a32eb89c98f57723d005 \
&& npm i && npm run server:setup
ARG DOCKER_HOST=localhost
# This gets sent to the browser as a server URL,
# so it is important that it is set to the domain / ip of the server.
RUN sed -i "s/127.0.0.1/${DOCKER_HOST}/" src/model/proxy-store.ts \
&& npm run build:default # Can take a long time
CMD npm exec static-server ./dist -o

12
mitmproxy/Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM mitmproxy/mitmproxy
USER root
WORKDIR /root
COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY stats.py /stats.py
# CMD bash -c 'mitmdump -w ~/.mitmproxy/dump --set mode="socks5@0.0.0.0:1080"'
ENTRYPOINT /docker-entrypoint.sh
# cmd sleep 1000000000
#--set mode="regular@0.0.0.0:8000"

6
mitmproxy/docker-entrypoint.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -xe
cd /root
script -c 'mitmdump -s /stats.py --set mode=regular@0.0.0.0:1080 -v'

81
mitmproxy/stats.py Normal file
View File

@ -0,0 +1,81 @@
from typing import Optional
import mitmproxy.addons.savehar
from mitmproxy import http
from mitmproxy.types import Path
from mitmproxy.addonmanager import Loader
import threading
import time
import json
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer
class SaveHar2:
def __init__(self) -> None:
self.save_har = mitmproxy.addons.savehar.SaveHar()
self.counter = 0
server_address = ('', 9111)
parr = self
class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/get_har':
print(parr.save_har.flows);
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/json")
self.end_headers()
har = parr.get_har_json()
self.wfile.write(har)
if self.path == '/get_stats':
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/json")
self.end_headers()
har = parr.get_har_json()
self.wfile.write(("{\"connections\": " + str(len(parr.save_har.flows)) +" }\n").encode("utf8"))
self.httpd = HTTPServer(server_address, HTTPRequestHandler)
print("Server started ...")
self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
self.httpd_thread.start()
def save_har_to_file(self):
self.save_har.export_har(self.save_har.flows, Path("output.har"))
def get_har_json(self) -> bytes:
return json.dumps(self.save_har.make_har(self.save_har.flows), indent=4).encode()
def configure(self, updated):
self.save_har.configure(updated);
def _save_flow(self, flow: http.HTTPFlow) -> None:
self.save_har.flows.append(flow)
def response(self, flow: http.HTTPFlow) -> None:
# websocket flows will receive a websocket_end,
# we don't want to persist them here already
if flow.websocket is None:
self._save_flow(flow)
if flow.websocket is None:
self._save_flow(flow)
self.counter += 1
if (self.counter == 5):
print("Outputing har")
print(self.save_har.flows)
self.save_har_to_file()
self.counter = 0
def error(self, flow: http.HTTPFlow) -> None:
self.response(flow)
def websocket_end(self, flow: http.HTTPFlow) -> None:
self._save_flow(flow)
addons = [SaveHar2()]

View File

@ -4,6 +4,7 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/andro
RUN sdkmanager --channel=2 "system-images;android-35;google_apis;x86_64" \ RUN sdkmanager --channel=2 "system-images;android-35;google_apis;x86_64" \
&& echo no | avdmanager create avd -n virtual_dev -b google_apis/x86_64 -k "system-images;android-35;google_apis;x86_64" \ && echo no | avdmanager create avd -n virtual_dev -b google_apis/x86_64 -k "system-images;android-35;google_apis;x86_64" \
&& apt-get update && apt-get install -y iproute2 iputils-ping npm git libxml2-utils telnet bc && apt-get update && apt-get install -y iptables iproute2 iputils-ping npm git libxml2-utils telnet bc aapt python3 python3-pip \
&& pip install frida-tools && git clone https://github.com/httptoolkit/frida-interception-and-unpinning /frida-scripts
CMD bash /preconf/docker-entrypoint.sh CMD bash /preconf/docker-entrypoint.sh

View File

@ -2,8 +2,9 @@ adb start-server
emulator -avd virtual_dev -writable-system -no-window -no-audio -memory 4096 & emulator -avd virtual_dev -writable-system -no-window -no-audio -memory 4096 &
adb wait-for-device
bash /preconf/install_frida.sh
bash /preconf/install_adidreader.sh bash /preconf/install_adidreader.sh
bash /preconf/install_culebra.sh
# Open google maps once, skip all the screens, and give it gps permissions # Open google maps once, skip all the screens, and give it gps permissions
adb shell am start-activity com.google.android.apps.maps adb shell am start-activity com.google.android.apps.maps

View File

@ -1,11 +0,0 @@
adb wait-for-device
cd /root
git clone https://github.com/dtmilano/CulebraTester2-public culebra
git clone https://gist.github.com/dtmilano/4537110 culebraDependencies
export PATH=$PATH:/root/culebraDependencies
cd culebra
git checkout 4ce1987e7ec6ae627d8f33a1a3b59f684aff90c0
echo "/opt/android-sdk-linux" >> local.properties
./culebratester2 install

View File

@ -0,0 +1,11 @@
#!/bin/bash
set -xe
cd /tmp
wget https://github.com/frida/frida/releases/download/17.2.15/frida-server-17.2.15-android-x86_64.xz
xz -d frida-server-17.2.15-android-x86_64.xz
adb push frida-server-17.2.15-android-x86_64 /tmp/frida-server
adb shell su root 'chmod +x /tmp/frida-server'

View File

@ -5,10 +5,8 @@ async function sleep(time) {
} }
async function checkCertExistance() { async function checkCertExistance() {
return await Promise.all([ await fs.access("./certificates/mitmproxy-ca-cert.pem");
fs.access("./certificates/ca.key"), await fs.access("./certificates/mitmproxy-ca.pem", fs.constants.R_OK);
fs.access("./certificates/ca.pem", fs.constants.R_OK),
]);
} }
async function generateCert() { async function generateCert() {
@ -22,23 +20,12 @@ async function generateCert() {
); );
} }
await $`docker build --tag certgen $PWD/certgen` await $`docker build --tag certgen $PWD/certgen`;
// iniciate docker which will create certs // generate certs
$`docker run --rm -v $PWD/certificates:/certificates --name certGenerator certgen &`; await $`docker run --rm -v $PWD/certificates:/certificates --name certGenerator certgen`;
//wait for certs to generate // clean up
let generated = false; await $`docker rmi certgen`;
while (!generated) {
try {
await checkCertExistance();
await $`sleep 1`;
generated = true;
} catch {}
}
//kill docker container
await $`docker stop certGenerator`;
await $`docker rmi certgen`
} }
async function generatePreAndroid() { async function generatePreAndroid() {