From e23cac512ea54ec2b5805ab58ef3725a5a33a781 Mon Sep 17 00:00:00 2001 From: Andrii Dokhniak Date: Tue, 5 Aug 2025 11:27:44 +0200 Subject: [PATCH] upd --- android/Dockerfile | 2 +- android/code/index.mjs | 9 +- android/conf/get_adid.sh | 2 +- android/conf/recent.sh | 1 + certgen/Dockerfile | 2 +- http_server/code/docker-entrypoint.sh | 4 +- http_server/code/index.html | 438 ++---------------- http_server/code/index.mjs | 16 +- http_server/code/package-lock.json | 174 +++++++ http_server/code/package.json | 6 +- http_server/code/src/main.ts | 232 ++++++++++ http_server/code/src/notifications.jsx | 67 --- http_server/code/src/notifications.ts | 41 ++ http_server/code/src/shared.ts | 15 + http_server/code/src/trafficLog.tsx | 253 ---------- http_server/code/src/traffic_log.ts | 224 +++++++++ http_server/code/styles.css | 167 +++++++ http_server/code/test.html | 15 + httptoolkit_ui/Dockerfile | 5 +- pre_android/preconf/AdIdreader/.idea/misc.xml | 1 - 20 files changed, 950 insertions(+), 724 deletions(-) create mode 100644 android/conf/recent.sh create mode 100644 http_server/code/src/main.ts delete mode 100644 http_server/code/src/notifications.jsx create mode 100644 http_server/code/src/notifications.ts create mode 100644 http_server/code/src/shared.ts delete mode 100644 http_server/code/src/trafficLog.tsx create mode 100644 http_server/code/src/traffic_log.ts create mode 100644 http_server/code/styles.css create mode 100644 http_server/code/test.html diff --git a/android/Dockerfile b/android/Dockerfile index 2813e82..0521c96 100644 --- a/android/Dockerfile +++ b/android/Dockerfile @@ -9,7 +9,7 @@ RUN node --version RUN git clone https://github.com/httptoolkit/httptoolkit-server /httptoolkit-server WORKDIR /httptoolkit-server -RUN git checkout 5c60a70b08d30126639484314f5b5619a388b026 \ +RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \ && npm i && npm run build:src # Set up proxy_cache_thing diff --git a/android/code/index.mjs b/android/code/index.mjs index 1c1fd34..b911125 100644 --- a/android/code/index.mjs +++ b/android/code/index.mjs @@ -73,12 +73,19 @@ io.on("connection", (socket) => { socket.on("back", async () => { if (gps_setting_in_progress) { send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); - send_notification(socket, false, "Back", ""); return ; } await spawnPromise("bash", ["/conf/back.sh"]); }); + socket.on("recent", async () => { + if (gps_setting_in_progress) { + send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); + return ; + } + await spawnPromise("bash", ["/conf/recent.sh"]); + }); + socket.on("home", async () => { if (gps_setting_in_progress) { send_notification(socket, false, "Interactions not allowed when setting gps coordinates", ""); diff --git a/android/conf/get_adid.sh b/android/conf/get_adid.sh index 896eceb..d4b1307 100644 --- a/android/conf/get_adid.sh +++ b/android/conf/get_adid.sh @@ -1 +1 @@ -adb shell su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - +adb shell su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - | tr -d $'\n' diff --git a/android/conf/recent.sh b/android/conf/recent.sh new file mode 100644 index 0000000..df3fb52 --- /dev/null +++ b/android/conf/recent.sh @@ -0,0 +1 @@ +/opt/android-sdk-linux/platform-tools/adb shell input keyevent 187 diff --git a/certgen/Dockerfile b/certgen/Dockerfile index 4089f2e..971ffad 100644 --- a/certgen/Dockerfile +++ b/certgen/Dockerfile @@ -3,7 +3,7 @@ FROM node:22.14.0 RUN git clone https://github.com/httptoolkit/httptoolkit-server WORKDIR /httptoolkit-server -RUN git checkout 5c60a70b08d30126639484314f5b5619a388b026 \ +RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \ && npm i && npm run build:src CMD /httptoolkit-server/bin/run start -c /certificates diff --git a/http_server/code/docker-entrypoint.sh b/http_server/code/docker-entrypoint.sh index 8c58d63..8933c6c 100644 --- a/http_server/code/docker-entrypoint.sh +++ b/http_server/code/docker-entrypoint.sh @@ -2,5 +2,5 @@ npm i npm run build -node --watch index.mjs -#tail -f /dev/null +node index.mjs & +tail -f /dev/null diff --git a/http_server/code/index.html b/http_server/code/index.html index a58cc3b..007a1d4 100644 --- a/http_server/code/index.html +++ b/http_server/code/index.html @@ -4,154 +4,8 @@ Rentgen android - + +
@@ -168,6 +22,7 @@ tabindex="0" />
+
@@ -196,23 +51,42 @@
- -
-
-

-

-
+
+

+
+
+ + +
+

stats:

+

+ Request + responce pairs: 0 +

+

+ Waiting for the responce: 0 +

+
+
+
+
+
+
@@ -224,7 +98,7 @@ >
-
+
- - - - - - - - - - - - - - - - - -
ADID:UNKNOWN
Longitude:UNKNOWN
Latitude:UNKNOWN
+ + + + + + + + + + + + + + + + + +
ADID:UNKNOWN
Longitude:UNKNOWN
Latitude:UNKNOWN
- - - - diff --git a/http_server/code/index.mjs b/http_server/code/index.mjs index 222f219..9b18a9b 100644 --- a/http_server/code/index.mjs +++ b/http_server/code/index.mjs @@ -4,11 +4,14 @@ import { execSync } from "node:child_process"; import { Server } from "socket.io"; import { io } from "socket.io-client"; -import { build_html } from "./har-analyzer/build_html.js" +import { build_html } from "har-analyzer" import fileUpload from "express-fileupload"; const app = express(); +import multer from "multer" + +const upload = multer(); async function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); @@ -60,14 +63,15 @@ app.get("/", async function (req, res) { //POST app.use(express.text({limit: "100mb"})); -app.post("/inspect_har", function (req, res) { - let body = JSON.parse(req.body); - let har = JSON.stringify(body.har); +app.post("/inspect_har", upload.none(), function (req, res) { + let body = req.body; + let har = body.har; let private_data; if (body.private_data) - private_data = body.private_data; + private_data = JSON.parse(body.private_data); res.setHeader("Content-Type", "text/html"); - res.send(build_html(har, private_data, "/code/har-analyzer/")); + console.log(private_data); + res.send(build_html(har, private_data)); }); app.use(fileUpload()); diff --git a/http_server/code/package-lock.json b/http_server/code/package-lock.json index bd86331..11f9461 100644 --- a/http_server/code/package-lock.json +++ b/http_server/code/package-lock.json @@ -8,7 +8,9 @@ "@types/har-format": "^1.2.16", "express": "^4.18.2", "express-fileupload": "^1.5.1", + "har-analyzer": "git+ssh://git@hub.sealcode.org/diffusion/171/har-parser.git#master", "htmx.org": "^1.9.12", + "multer": "^2.0.2", "preact": "^10.18.1", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", @@ -370,6 +372,18 @@ "node": ">=12" } }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -412,11 +426,37 @@ "node": ">= 0.6" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -449,6 +489,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -480,6 +526,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -857,6 +918,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/har-analyzer": { + "name": "module-starter", + "version": "0.0.1", + "resolved": "git+ssh://git@hub.sealcode.org/diffusion/171/har-parser.git#167d6bce983a30ac9699474c26ec6b0222a7309e", + "license": "ISC", + "dependencies": { + "@smithy/util-hex-encoding": "^4.0.0", + "base64-js": "^1.5.1", + "pako": "^2.1.0", + "tabulator-tables": "^6.3.1" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -986,11 +1059,50 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1027,6 +1139,12 @@ "node": ">= 0.8" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1097,6 +1215,20 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1366,6 +1498,21 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tabulator-tables": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz", + "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==", + "license": "MIT" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1374,6 +1521,12 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1386,6 +1539,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -1400,6 +1559,12 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1443,6 +1608,15 @@ "engines": { "node": ">=0.4.0" } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } } } } diff --git a/http_server/code/package.json b/http_server/code/package.json index 4ea4dce..6529d3f 100644 --- a/http_server/code/package.json +++ b/http_server/code/package.json @@ -1,16 +1,18 @@ { "scripts": { - "build": "esbuild --sourcemap --bundle src/trafficLog.tsx src/notifications.jsx --outdir=dist/ --jsx-factory=h --jsx-fragment=Fragment" + "build": "esbuild --format=esm --sourcemap --bundle src/main.ts --outdir=dist/ --jsx-factory=h --jsx-fragment=Fragment" }, "dependencies": { "@types/har-format": "^1.2.16", "express": "^4.18.2", "express-fileupload": "^1.5.1", "htmx.org": "^1.9.12", + "multer": "^2.0.2", "preact": "^10.18.1", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", - "ws": "^8.18.0" + "ws": "^8.18.0", + "har-analyzer": "git+ssh://git@hub.sealcode.org/diffusion/171/har-parser.git#master" }, "devDependencies": { "esbuild": "^0.19.5" diff --git a/http_server/code/src/main.ts b/http_server/code/src/main.ts new file mode 100644 index 0000000..0941c8d --- /dev/null +++ b/http_server/code/src/main.ts @@ -0,0 +1,232 @@ +// make the export accessible from inline js +import * as main from "./main"; +// for some reason doing the same with the window object doesn't work +(globalThis as any).main = main; + +import { + backButton, + clicksLog, + homeButton, + socket, + screen, + adid_priv_info_table, + lat_priv_info_table, + lon_priv_info_table, + recentButton, +} from "./shared"; + +import { start_notifications } from "./notifications"; +import { start_traffic_log } from "./traffic_log"; + +export { download_har, inspect_har } from "./traffic_log"; + +export function reset_adid_handler(_: Event) { + socket.emit("reset_adid"); +} + +export function coords_handler(e: FormDataEvent) { + e.preventDefault(); + const form_data = new FormData(e.target as HTMLFormElement); + socket.emit("setcoord", { + lon: Number.parseFloat(form_data.get("lon") as string), + lat: Number.parseFloat(form_data.get("lat") as string), + }); +} + +export function open_tab(evt: Event, tab_name: string) { + let i, tabcontent, tablinks; + + // Get all elements with class="tabcontent" and hide them + tabcontent = document.getElementsByClassName("tabcontent"); + for (i = 0; i < tabcontent.length; i++) { + if (tabcontent[i].id != tab_name) { + tabcontent[i].classList.remove("active"); + } else { + tabcontent[i].classList.add("active"); + } + } + + // Get all elements with class="tablinks" and remove the class "active" + tablinks = document.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].classList.remove("active"); + } + + // Show the current tab, and add an "active" class to the button that opened the tab + (evt.currentTarget as HTMLElement).classList.add("active"); +} + +let lastTouch = new Date().getTime(); + +export const calculateElapsedTime = () => { + const currentTouch = new Date().getTime(); + const elapsedTime = currentTouch - lastTouch; + const elapsedSec = Math.round(elapsedTime / 1000); + lastTouch = currentTouch; + return elapsedSec; +}; + +export const waitToLog = (clickInfoText: string) => { + const clickInfo = document.createElement("span"); + const waitInfo = document.createElement("span"); + waitInfo.textContent = `await wait(${calculateElapsedTime()});`; + clicksLog.appendChild(waitInfo); + clickInfo.textContent = clickInfoText; + clicksLog.appendChild(clickInfo); +}; + +export const registerClick = ({ + path, + logText, + body, +}: { + path: string; + logText: string; + body?: any; +}) => { + waitToLog(logText); + socket.emit(path, body ? body : {}); +}; + +homeButton.addEventListener("click", () => + registerClick({ path: "home", logText: "await homeButton();" }) +); + +backButton.addEventListener("click", () => + registerClick({ path: "back", logText: "await backButton();" }) +); + +recentButton.addEventListener("click", () => + registerClick({ path: "recent", logText: "await recentButton();" }) +); + +socket.on("screenshot_data", (data) => { + try { + const blob = new Blob([data]); + screen.src = URL.createObjectURL(blob); + } catch (error) { + console.error("Error fetching image: ", error); + } +}); + +socket.on("private_info", (data) => { + console.log("private_info"); + adid_priv_info_table.textContent = data.adid; + lat_priv_info_table.textContent = data.latitude; + lon_priv_info_table.textContent = data.longitude; +}); + +socket.emit("private_info_req"); + +socket.onAny((ev, ...args) => { + console.log("ev: ", ev, args); +}); + +async function displayImage() { + socket.emit("screenshot"); +} + +let isDragging = false; +const screenSize = [320, 640]; + +export function calcMousePos(event: MouseEvent) { + let rect = screen.getBoundingClientRect(); + let x = ((event.clientX - rect.left) / rect.width) * screenSize[0]; + let y = ((event.clientY - rect.top) / rect.height) * screenSize[1]; + x = Math.min(Math.max(x, 0), screenSize[0]); + y = Math.min(Math.max(y, 0), screenSize[1]); + return { x, y }; +} + +screen.addEventListener( + "mousemove", + (event) => { + if (!isDragging) return; + let pos = calcMousePos(event); + + if (isDragging) { + registerClick({ + path: "motionevent", + logText: `await motionevent({motionType: "MOVE", x:${pos.x},y:${pos.y}}});`, + body: { + motionType: "MOVE", + x: pos.x, + y: pos.y, + }, + }); + } + }, + false +); + +export const handleDraggStart = (event: MouseEvent) => { + isDragging = true; + let pos = calcMousePos(event); + registerClick({ + path: "motionevent", + logText: `await motionevent({motionType: "DOWN", x:${pos.x},y:${pos.y}}});`, + body: { + motionType: "DOWN", + x: pos.x, + y: pos.y, + }, + }); +}; + +screen.addEventListener("mousedown", handleDraggStart); + +document.addEventListener("mouseup", (e) => { + if (!isDragging) return; + isDragging = false; + let pos = calcMousePos(e); + registerClick({ + path: "motionevent", + logText: `await motionevent({motionType: "MOVE", x:${pos.x},y:${pos.y}}});`, + body: { + motionType: "MOVE", + x: pos.x, + y: pos.y, + }, + }); + registerClick({ + path: "motionevent", + logText: `await motionevent({motionType: "UP", x:${pos.x},y:${pos.y}}});`, + body: { + motionType: "UP", + x: pos.x, + y: pos.y, + }, + }); +}); + +window.addEventListener("keydown", (event) => { + let key = event.key; + if (key === "Space") key = " "; + else if (key !== "Enter" && key !== "Backspace" && key.length !== 1) return; + console.log(event.key, key); + if (screen.matches(":hover")) { + registerClick({ + path: "key", + logText: `await key(${event.key});`, + body: { key }, + }); + } +}); + +export async function sleep(time: number) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +async function screenshot_loop() { + var before; + + while (true) { + before = performance.now(); + await displayImage(); + // TODO: Make this dynamic again + while (performance.now() - before < 100) await sleep(50); + } +} +screenshot_loop(); +start_notifications(); +start_traffic_log(); diff --git a/http_server/code/src/notifications.jsx b/http_server/code/src/notifications.jsx deleted file mode 100644 index edfe1b8..0000000 --- a/http_server/code/src/notifications.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import { render, Component } from "preact"; -import { io } from "socket.io-client"; - -function rand_num() { - return Math.floor(Math.random() * Number.MAX_VALUE); -} - -class Notifications extends Component { - constructor() { - super(); - this.state = { notifications: [] }; - } - - remove_notification(id) { - const newNotifications = this.state.notifications.filter( - (notification) => notification.id !== id - ); - this.setState({ notifications: newNotifications }); - } - - componentDidMount() { - // This should also be dynamic - - io().on("notification", (data) => { - let new_id = rand_num(); - this.setState({ - notifications: [ - { id: new_id, notification: data }, - ...this.state.notifications, - ], - }); - // a 10 sec timeout - setTimeout(() => { - this.remove_notification(new_id); - }, 10000); - }); - } - - render() { - return this.state.notifications.map(({ id, notification }) => ( -
this.remove_notification(id)} - style={` - background-color: ${notification.is_ok ? "#66ff99" : "#ff5c33"}; - border-radius: 5px; - border-width: 2px; - border-style: solid; - border-color: ${notification.is_ok ? "#369648" : "#a23915"}; - padding: 5px; - margin-top: 2px; - margin-bottom: 2px; - `} - > -
- {notification.context} -
-
- {notification.message.split("\n").map((line) => ( -

{line}

- ))} -
-
- )); - } -} - -render(, document.getElementById("notifications")); diff --git a/http_server/code/src/notifications.ts b/http_server/code/src/notifications.ts new file mode 100644 index 0000000..c84e955 --- /dev/null +++ b/http_server/code/src/notifications.ts @@ -0,0 +1,41 @@ +import * as shared from "./shared"; + +const notifications = document.getElementById("notifications")!; + +function create_notification(notification: { + is_ok: boolean; + message: string; + context: string; +}): HTMLElement { + const el = document.createElement("div"); + + el.addEventListener("click", (_) => { + el.remove(); + }); + + el.classList.add("notification"); + if (notification.is_ok) el.classList.add("notification_ok"); + else el.classList.add("notification_err"); + + // el.innerText = notification.message; + el.innerHTML = ` +
${notification.context}
+
+ ${notification.message + .split("\n") + .map((line) => `

${line}

`) + .join("")} +
`; + return el; +} + +export function start_notifications() { + shared.socket.on("notification", (data) => { + let el = create_notification(data); + notifications.appendChild(el); + // a 10 sec timeout + setTimeout(() => { + el.remove(); + }, 10000); + }); +} diff --git a/http_server/code/src/shared.ts b/http_server/code/src/shared.ts new file mode 100644 index 0000000..e9ee988 --- /dev/null +++ b/http_server/code/src/shared.ts @@ -0,0 +1,15 @@ +import { io } from "socket.io-client"; + +export const screen = document.getElementById("screen")! as HTMLImageElement; + +export const clicksLog = document.getElementById("clicks-log")!; + +export const recentButton = document.querySelector(".screen-buttons-recent-apps")!; +export const homeButton = document.querySelector(".screen-buttons-home")!; +export const backButton = document.querySelector(".screen-buttons-back")!; + +export const adid_priv_info_table = document.getElementById("adid_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 socket = io(); diff --git a/http_server/code/src/trafficLog.tsx b/http_server/code/src/trafficLog.tsx deleted file mode 100644 index 461648e..0000000 --- a/http_server/code/src/trafficLog.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import { Entry, Har, PostData, Request, Response } from "har-format"; -import { render, Component } from "preact"; - -type MyState = { - finished_entries: Entry[]; - unfinished_entries: Map; -}; - -class TrafficLog extends Component { - connection: WebSocket | undefined; - - state: MyState = { finished_entries: [], unfinished_entries: new Map() }; - - constructor() { - super(); - } - - componentDidMount() { - // This should also be dynamic - this.connection = new WebSocket("ws://localhost:10001"); - this.connection.onmessage = (msg) => { - this.process_msg(msg.data); - this.setState({ - finished_entries: this.state.finished_entries, - unfinished_entries: this.state.unfinished_entries, - }); - }; - this.connection.onclose = this.connection.onerror = () => { - window.location.reload(); - }; - } - - render() { - const download_har = () => { - var tempLink = document.createElement("a"); - var taBlob = new Blob([JSON.stringify(this.export_har())], { - type: "text/plain", - }); - - tempLink.setAttribute("href", URL.createObjectURL(taBlob)); - tempLink.setAttribute("download", `rentgendroid-capture.har`); - - tempLink.click(); - - URL.revokeObjectURL(tempLink.href); - }; - - const inspect_har = async () => { - const req_body = { - har: this.export_har(), - private_data: [ - [ - "adid", - document.getElementById("adid_priv_info_table")! - .textContent, - ], - [ - "latitude", - document.getElementById("lat_priv_info_table")! - .textContent, - ], - [ - "longitude", - document.getElementById("lon_priv_info_table")! - .textContent, - ], - ], - }; - - const resp = await fetch("/inspect_har", { - method: "POST", - body: JSON.stringify(req_body), - }); - const resp_text = await resp.text(); - const newWindow = window.open(); - newWindow?.document.write(resp_text); - newWindow?.document.close(); - }; - - const contentWithLineBreaks = this.state.finished_entries.map((req) => { - return ( - - {req.request.url} -
-
- ); - }); - - return ( -
- - -
-

stats:

-

- Request + responce pairs:{" "} - {this.state.finished_entries.length} -

-

- Waiting for the responce:{" "} - {this.state.unfinished_entries.size} -

-
-
{contentWithLineBreaks}
-
- ); - } - - 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) - this.process_req(obj.payload.data.requestReceived); - if ( - obj.payload && - obj.payload.data && - obj.payload.data.responseCompleted - ) - this.process_res(obj.payload.data.responseCompleted); - } - - process_res(res: any) { - let entry = this.state.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; - this.state.unfinished_entries.delete(res.id); - this.state.finished_entries.push(entry); - } - - 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, - }, - }; - this.state.unfinished_entries.set(req.id, entry); - } - - export_har(): Har { - let ret: Har = { - log: { - version: "1.2", - creator: { - name: "Rentgendroid", - version: "0.0.1", - }, - entries: [ - ...this.state.finished_entries, - ...this.state.unfinished_entries.values(), - ], - }, - }; - return ret; - } -} - -render(, document.getElementById("traffic-log")!); diff --git a/http_server/code/src/traffic_log.ts b/http_server/code/src/traffic_log.ts new file mode 100644 index 0000000..a770a3d --- /dev/null +++ b/http_server/code/src/traffic_log.ts @@ -0,0 +1,224 @@ +import { Entry, Har, PostData, Request, Response } from "har-format"; +import { + adid_priv_info_table, + lat_priv_info_table, + lon_priv_info_table, +} from "./shared"; + +// Request + responce pairs: 0 +const req_res_pairs = document.getElementById("traffic-log-req-res-pairs")!; + +// Waiting for the responce: 0 +const waiting = document.getElementById("traffic-log-waiting")!; + +const traffic_log_lines = document.getElementById("traffic-log-lines")!; + +const inspect_har_form: HTMLFormElement = document.getElementById( + "inspect-har-form" +)! as HTMLFormElement; + +const connection = new WebSocket("ws://localhost:10001"); + +const unfinished_entries: Map = new Map(); +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() { + var tempLink = document.createElement("a"); + var taBlob = new Blob([JSON.stringify(export_har())], { + type: "text/plain", + }); + + tempLink.setAttribute("href", URL.createObjectURL(taBlob)); + tempLink.setAttribute("download", `rentgendroid-capture.har`); + + tempLink.click(); + + URL.revokeObjectURL(tempLink.href); +} + +function launch_window_with_post(url: string, data: any) { + let form = document.createElement("form"); + form.target = "_blank"; + form.method = "POST"; + form.action = url; + form.enctype = "multipart/form-data" + form.style.display = "none"; + + for (var key in data) { + var input = document.createElement("input"); + input.type = "hidden"; + input.name = key; + input.value = data[key]; + form.appendChild(input); + } + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); +} + +export async function inspect_har() { + let data = { + har: JSON.stringify(export_har()), + + private_data: JSON.stringify([ + {desc: "adid", data: adid_priv_info_table.textContent}, + {desc: "latitude", data: lat_priv_info_table.textContent }, + {desc: "longitude", data: lon_priv_info_table.textContent }, + ]), + }; + launch_window_with_post("/inspect_har", data); +} + +function update_stats() { + req_res_pairs.innerText = `Request + responce pairs: ${finished_entries.length}`; + 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} +
`; + 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; +} diff --git a/http_server/code/styles.css b/http_server/code/styles.css new file mode 100644 index 0000000..39491a5 --- /dev/null +++ b/http_server/code/styles.css @@ -0,0 +1,167 @@ +main { + display: flex; +} + +.log-section { + height: auto; + width: 400px; + overflow: auto; + display: flex; + flex-direction: column; + margin-left: 20px; +} + +.screen { + display: inline-block; + cursor: pointer; +} + +.screen-buttons { + display: flex; + justify-content: space-around; + margin-top: 5px; + gap: 10px; +} + +.screen-buttons button { + font-size: 1.1rem; + padding: 10px 20px; + width: 100%; + cursor: pointer; + background-color: transparent; +} + +.screen-buttons button:hover { + background-color: aqua; +} + +#clicks-log { + font-family: + Menlo, + Consolas, + Monaco, + Liberation Mono, + Lucida Console, + monospace; +} + +.tab { + border: 1px solid #ccc; + background-color: #f1f1f1; +} + +/* Style the buttons that are used to open the tab content */ +.tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; +} + +/* Change background color of buttons on hover */ +.tab button:hover { + background-color: #ddd; +} + +/* Create an active/current tablink class */ +.tab button.active { + background-color: #ccc; +} +.tabcontent.active { + display: flex; + flex-direction: column; + flex-grow: 1; +} + +/* Style the tab content */ +.tabcontent { + display: none; + padding: 6px 12px; + border: 1px solid #ccc; + border-top: none; +} +html, +body, +main { + width: 100%; + height: 100%; + overflow: hidden; + margin: 0; +} +main { + display: flex; + flex-direction: row; + align-items: stretch; +} +#logs-tab { + overflow: auto; + text-wrap: wrap; +} +.screen-section { + display: flex; + flex-direction: column; +} +.screen-section #screen, +.screen-section.screen_buttons { + flex-grow: 0; +} +#screen { + user-select: none; +} +.tab-section { + display: flex; + flex-direction: column; + flex-grow: 1; +} +#resp { + display: none; +} +#upload_form { + display: flex; + flex-direction: column; +} +#upload_form button, +#upload_form label { + border: 2px solid #ccc; + background-color: #f1f1f1; + cursor: pointer; + padding: 3px 10px; + transition: 0.3s; +} +#upload_form button:hover, +#upload_form label:hover { + background-color: #ddd; +} +#notifications { + width: 40%; + margin-left: 60%; + position: absolute; +} + +.notification { + border-radius: 5px; + border-width: 2px; + border-style: solid; + padding: 5px; + margin-top: 2px; + margin-bottom: 2px; +} + +.notification_ok { + background-color: #66ff99; + border-color: #369648; +} + +.notification_err { + background-color: #ff5c33; + border-color: #a23915; +} + +#logs { + display: flex; + flex-direction: row; +} + diff --git a/http_server/code/test.html b/http_server/code/test.html new file mode 100644 index 0000000..499b4c0 --- /dev/null +++ b/http_server/code/test.html @@ -0,0 +1,15 @@ + +
+ +
diff --git a/httptoolkit_ui/Dockerfile b/httptoolkit_ui/Dockerfile index 5738a08..bf2f3eb 100644 --- a/httptoolkit_ui/Dockerfile +++ b/httptoolkit_ui/Dockerfile @@ -1,12 +1,15 @@ 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 a0dbb8e1cd38b346fe411b03de0c5191ff06c669 \ +RUN git checkout ac44f8e6e1f5f41be988a32eb89c98f57723d005 \ && npm i && npm run server:setup ARG DOCKER_HOST=localhost diff --git a/pre_android/preconf/AdIdreader/.idea/misc.xml b/pre_android/preconf/AdIdreader/.idea/misc.xml index 74dd639..b2c751a 100644 --- a/pre_android/preconf/AdIdreader/.idea/misc.xml +++ b/pre_android/preconf/AdIdreader/.idea/misc.xml @@ -1,4 +1,3 @@ -