Page MenuHomeSealhub

No OneTemporary

diff --git a/android/Dockerfile b/android/Dockerfile
index adc494c..2813e82 100644
--- a/android/Dockerfile
+++ b/android/Dockerfile
@@ -1,30 +1,32 @@
FROM pre_android/ready
# Set up node
RUN npm install -g n && n install 22.14.0 && n use 22.14.0 && hash -r
+RUN node --version
+
# Set up httptoolkit-server
RUN git clone https://github.com/httptoolkit/httptoolkit-server /httptoolkit-server
WORKDIR /httptoolkit-server
RUN git checkout 5c60a70b08d30126639484314f5b5619a388b026 \
&& 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
ARG PROXY_PORT
ARG SERVER_PORT
ARG MONITORING_API_PORT
ARG LOOPBACK_PORT
ENV PROXY_PORT=${PROXY_PORT}
ENV SERVER_PORT=${SERVER_PORT}
ENV MONITORING_API_PORT=${MONITORING_API_PORT}
ENV LOOPBACK_PORT=${LOOPBACK_PORT}
ENTRYPOINT /entrypoint.sh
diff --git a/android/code/index.mjs b/android/code/index.mjs
index 3fc1ba3..bb5c961 100644
--- a/android/code/index.mjs
+++ b/android/code/index.mjs
@@ -1,64 +1,76 @@
import { WebSocketServer } from "ws";
import child_process from "child_process";
import fs from "fs";
import { send_notification } from "./notifications.mjs";
async function spawnPromise(program, args) {
return new Promise((resolve, reject) => {
let output = "";
const process = child_process.spawn(program, args);
process.stdout.on("data", (data) => {
output += data;
});
process.stderr.on("data", (data) => {
output += data;
});
process.on("close", (code) => {
resolve({ output, code });
});
});
}
const wss = new WebSocketServer({ port: 3000 });
//maybe check output of child processes and send errors in some way
wss.on("connection", (ws) => {
ws.on("message", async (dataBuf) => {
let data = dataBuf.toString();
if (data === "screenshot") {
await spawnPromise("bash", ["/conf/screenshot.sh"]);
ws.send(fs.readFileSync("/screenshot.png"));
} else if (data.includes("touch")) {
const dataSplit = data.split(" ");
await spawnPromise("bash", [
"/conf/touch.sh",
dataSplit[1],
dataSplit[2],
]);
} else if (data === "back") {
await spawnPromise("bash", ["/conf/back.sh"]);
} else if (data === "home") {
await spawnPromise("bash", ["/conf/home.sh"]);
} else if (data === "install") {
const res = await spawnPromise("bash", ["/conf/install.sh"]);
send_notification(
res.code === 0,
"Installing the application",
res.output
);
} else if (data.includes("drag")) {
const dataSplit = data.split(" ");
await spawnPromise("bash", [
"/conf/drag.sh",
dataSplit[1],
dataSplit[2],
dataSplit[3],
dataSplit[4],
]);
+ } else if (data.startsWith("setcoord")) {
+ const dataSplit = data.split(" ");
+ const res = await spawnPromise("bash", [
+ "/conf/set_geo.sh",
+ dataSplit[1],
+ dataSplit[2],
+ ]);
+ send_notification(
+ res.code === 0,
+ "Setting the moch location",
+ res.output
+ );
}
});
ws.on("close", (_) => {
ws.close();
});
});
diff --git a/android/conf/set_geo.sh b/android/conf/set_geo.sh
new file mode 100644
index 0000000..5de2ea8
--- /dev/null
+++ b/android/conf/set_geo.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+ko=$((
+ echo "auth $(cat ~/.emulator_console_auth_token)"
+ echo "geo fix $1 $2 0.0"
+ sleep 0.2
+ ) | telnet localhost 5554 | grep KO: );
+
+if [ -z "${ko}" ]; then
+ exit 0
+else
+ echo "$ko"
+ exit 1;
+fi
diff --git a/android/conf/start_culebra.sh b/android/conf/start_culebra.sh
index 6ceeda8..d638ea7 100644
--- a/android/conf/start_culebra.sh
+++ b/android/conf/start_culebra.sh
@@ -1,15 +1,22 @@
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 &
-adb wait-for-device
-adb emu avd snapshot load configured
+cat > /simple.gpx << EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.1" creator="ChatGPT" xmlns="http://www.topografix.com/GPX/1/1">
+ <wpt lat="37.422" lon="-122.084">
+ <name>Simple Waypoint</name>
+ </wpt>
+</gpx>
+EOF
+
+emulator -grpc 5556 -avd virtual_dev -snapshot configured -no-window -no-audio -debug all,-adb,-gles1emu,-gles,-mtport,-metrics,-memory,-car,-tvremote &
adb wait-for-device
export PATH=$PATH:/root/culebraDependencies
cd /root/culebra
./culebratester2 start-server &
#wait for the server to start
while ! curl http://localhost:9987/v2/uiDevice/screenshot > /dev/null 2> /dev/null; do
sleep 0.1
-done
\ No newline at end of file
+done
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 1505ce7..0b693af 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,58 +1,59 @@
services:
android:
build:
context: ./android/
args:
PROXY_PORT: "45459"
SERVER_PORT: "45456"
LOOPBACK_PORT: "10000"
MONITORING_API_PORT: "10001"
container_name: httptoolkit_server
container_name: android
sysctls:
- net.ipv6.conf.all.disable_ipv6=1
cap_add:
- NET_ADMIN
devices:
- /dev/kvm
networks:
- rent_gen_android
ports:
- 45456:45459 # This cannot change
- 45457:45459 # This cannot change
- 10001:10001 # api port
- 3000:3000 # android server port
- 3001:3001 # Notifications server
+ - 5556:5556 # emulator grpc port
volumes:
- $PWD/shared_buffer:/shared_buffer
- $PWD/android/conf:/conf
- $PWD/certificates:/certificates
- $PWD/android/code:/code
httptoolkit_ui:
build:
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:
- rent_gen_android
ports:
- 9080:9080
http_server:
build: ./http_server/
container_name: http_server
networks:
- rent_gen_android
volumes:
- $PWD/http_server/code:/code
- $PWD/shared_buffer:/shared_buffer
- $PWD/log:/log
ports:
- 8080:8080
networks:
rent_gen_android:
driver: bridge
diff --git a/http_server/code/index.html b/http_server/code/index.html
index 83ab631..ef48e0f 100644
--- a/http_server/code/index.html
+++ b/http_server/code/index.html
@@ -1,327 +1,332 @@
<!DOCTYPE html>
<html lang="en">
<head style="height: 100vh">
<meta charset="UTF-8" />
<title>Rentgen android</title>
<script src="/htmx.js"></script>
<style>
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-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;
}
.tab-section {
display: flex; flex-direction: column; flex-grow: 1;
}
<!-- TODO: A notification system -->
#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;
}
</style>
</head>
<body>
<div id="notifications"></div>
<div id="resp" style="display: none;"></div>
<main>
<section class="screen-section" >
<img
id="screen"
alt="android screen"
src=""
draggable="false"
class="screen"
style="flex-grow: 0"
/>
<div class="screen-buttons" style="flex-grow: 0">
<button class="screen-buttons-home">home</button>
<button class="screen-buttons-back">back</button>
</div>
<form id="upload_form" hx-post="/upload_apk" enctype="multipart/form-data" hx-target="#resp">
<label id="upload_input" for="app">Select file
<input type="file" id="app" name="app" accept=".apk" required multiple/>
</label>
<button type="submit">Install the app</button>
</form>
</section>
<div class="tab-section" >
+ <form id="set_coords" hx-post="/setcoord" hx-target="#resp">
+ <input type="text" name="lon"/>
+ <input type="text" name="lat"/>
+ <button type="submit">Submit coords</button>
+ </form>
<div class="tab">
<button class="tablinks active" onclick="open_tab(event, 'httptoolkit-tab')">HttpToolkit UI</button>
<button class="tablinks" onclick="open_tab(event, 'logs-tab')">Logs</button>
</div>
<div class="tabcontent" id="logs-tab">
<p id="clicks-log" class="log-section" ></p>
<p id="traffic-log" class="log-section"></p>
</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>
</main>
<script>
function open_tab(evt, tab_name) {
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.classList.add("active");
}
</script>
<script>
var screen = document.getElementById("screen");
var clicksLog = document.getElementById("clicks-log");
const homeButton = document.querySelector(".screen-buttons-home");
const backButton = document.querySelector(".screen-buttons-back");
let lastTouch = new Date().getTime();
const calculateElapsedTime = (last) => {
const currentTouch = new Date().getTime();
const elapsedTime = currentTouch - lastTouch;
const elapsedSec = Math.round(elapsedTime / 1000);
lastTouch = currentTouch;
return elapsedSec;
};
const waitToLog = (clickInfoText) => {
const clickInfo = document.createElement("span");
const waitInfo = document.createElement("span");
waitInfo.textContent = `await wait(${calculateElapsedTime(
lastTouch
)});`;
clicksLog.appendChild(waitInfo);
clickInfo.textContent = clickInfoText;
clicksLog.appendChild(clickInfo);
};
const registerClick = ({ path, logText, body }) => {
const clicksLog = document.getElementById("clicks-log");
const span = document.createElement("span");
waitToLog(logText);
fetch(path, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
...(body ? { body } : {}),
});
};
homeButton.addEventListener("click", () =>
registerClick({ path: "home", logText: "await homeButton();" })
);
backButton.addEventListener("click", () =>
registerClick({ path: "back", logText: "await backButton();" })
);
async function displayImage() {
try {
const response = await fetch("screen");
const blob = await response.blob();
screen.src = URL.createObjectURL(blob);
} catch (error) {
console.error("Error fetching image: ", error);
}
}
let isDragging = false;
let startDraggingPosX = 0;
let endDraggingPosX = 0;
let startDraggingPosY = 0;
let endDraggingPosY = 0;
const screenSize = [320, 640];
const handleDraggStart = (e) => {
e.preventDefault();
isDragging = true;
startDraggingPosX = e.offsetX;
startDraggingPosY = e.offsetY;
};
screen.addEventListener("mousedown", handleDraggStart);
document.addEventListener("mouseup", (e) => {
endDraggingPosX = e.offsetX;
endDraggingPosY = e.offsetY;
if (
isDragging &&
(Math.abs(endDraggingPosY - startDraggingPosY) > 10 ||
Math.abs(endDraggingPosX - startDraggingPosX) > 10)
) {
registerClick({
path: "drag",
logText: `await drag({x:${startDraggingPosX},y:${startDraggingPosY}},{x:${e.offsetX},y:${e.offsetY}});`,
body: `startX=${startDraggingPosX}&startY=${startDraggingPosY}&endX=${e.offsetX}&endY=${e.offsetY}`,
});
} else if (e.target === screen) {
const phoneX = event.offsetX;
const phoneY = event.offsetY;
if (phoneX <= screenSize[0] && phoneY <= screenSize[1])
registerClick({
path: "touch",
logText: `await click(${phoneX}, ${phoneY});`,
body: `x=${phoneX}&y=${phoneY}`,
});
}
isDragging = false;
});
async function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
async function screenshot_loop() {
var before;
while (true) {
before = performance.now();
await displayImage();
while (performance.now() - before < ___screenshotDelayMs___)
await sleep(50);
}
}
screenshot_loop();
</script>
<script src="/trafficLog.js"></script>
<script src="/notifications.js"></script>
</body>
</html>
diff --git a/http_server/code/index.mjs b/http_server/code/index.mjs
index 26c6d4a..386054e 100644
--- a/http_server/code/index.mjs
+++ b/http_server/code/index.mjs
@@ -1,112 +1,123 @@
import express from "express";
import { readFile } from "node:fs/promises";
import {
guardedScreenshot,
android_websocket,
waitFullBoot,
} from "./screenshot.mjs";
import { execSync } from "node:child_process";
import fileUpload from "express-fileupload";
const device_size_x = 320;
const device_size_y = 640;
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.static("/code/dist"));
console.log("Waiting for full boot...");
await waitFullBoot();
console.log("Boot detected! activating endpoints");
//GET
app.get("/favicon.ico", function (req, res) {
res.sendFile("/code/favicon.ico");
});
app.get("/htmx.js", function (req, res) {
res.sendFile("/code/node_modules/htmx.org/dist/htmx.min.js");
});
app.get("/trafficLog", async function (req, res) {
res.sendFile("/log/trafficLog");
});
app.get("/screen", async function (req, res) {
await guardedScreenshot();
res.sendFile("/code/screenshot.png");
});
app.get("/", async function (req, res) {
let fileData = (await readFile("/code/index.html")).toString();
fileData = fileData.replace(
"___screenshotDelayMs___",
process.env.screenshotDelayMs
);
res.setHeader("Content-Type", "text/html");
res.setHeader("Content-Disposition", "inline");
res.send(fileData);
});
//POST
app.post("/back", function (req, res) {
android_websocket.send(`back`);
res.sendStatus(200);
});
// default options
app.use(fileUpload());
app.post("/upload_apk", async function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send("No files were uploaded.");
}
execSync("rm -rf /shared_buffer/*");
if (Array.isArray(req.files.app)) {
for (const [idx, file] of req.files.app.entries()) {
let uploadPath = "/shared_buffer/app" + idx + ".apk";
await file.mv(uploadPath);
}
} else {
let uploadPath = "/shared_buffer/app" + 0 + ".apk";
await req.files.app.mv(uploadPath);
}
android_websocket.send(`install`);
res.send("Files uploaded!");
});
app.post("/home", function (req, res) {
android_websocket.send(`home`);
res.sendStatus(200);
});
app.post("/touch", function (req, res) {
const x = parseInt(req.body.x);
const y = parseInt(req.body.y);
if (isNaN(x) || isNaN(y) || x > device_size_x || y > device_size_y) {
res.send(
`the query params must be x <= ${device_size_x}, y <= ${device_size_y}\n`
);
} else {
android_websocket.send(`touch ${x} ${y}`);
res.sendStatus(200);
}
});
app.post("/drag", function (req, res) {
const body = req.body;
const startX = Number(body.startX);
const startY = Number(body.startY);
const endX = Number(body.endX);
const endY = Number(body.endY);
android_websocket.send(`drag ${startX} ${startY} ${endX} ${endY}`);
res.sendStatus(200);
});
+app.post("/setcoord", function (req, res) {
+ console.log(req.body);
+ console.log(req.body.lat);
+ console.log(req.body.lon);
+ const lat = Number(req.body.lat);
+ const lon = Number(req.body.lon);
+ console.log(lat, lon);
+ android_websocket.send(`setcoord ${lat} ${lon}`);
+ res.sendStatus(200);
+});
+
app.listen(8080, () => console.log("Listening in port 8080"));
diff --git a/pre_android/Dockerfile b/pre_android/Dockerfile
index 95efdde..a74afaf 100644
--- a/pre_android/Dockerfile
+++ b/pre_android/Dockerfile
@@ -1,9 +1,9 @@
FROM runmymind/docker-android-sdk:ubuntu-standalone-20230511
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/android-sdk-linux/cmdline-tools/latest/bin:/opt/android-sdk-linux/cmdline-tools/tools/bin:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/build-tools/32.0.0:/opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/emulator:/opt/android-sdk-linux/bin
RUN sdkmanager --channel=2 "system-images;android-30;google_apis;x86_64" \
&& echo no | avdmanager create avd -n virtual_dev -b google_apis/x86_64 -k "system-images;android-30;google_apis;x86_64" \
- && apt-get update && apt-get install -y iproute2 iputils-ping npm git
+ && apt-get update && apt-get install -y iproute2 iputils-ping npm git libxml2-utils telnet
CMD bash /preconf/docker-entrypoint.sh
diff --git a/pre_android/preconf/docker-entrypoint.sh b/pre_android/preconf/docker-entrypoint.sh
index 149ae85..a56a68d 100644
--- a/pre_android/preconf/docker-entrypoint.sh
+++ b/pre_android/preconf/docker-entrypoint.sh
@@ -1,11 +1,14 @@
adb start-server
emulator -avd virtual_dev -writable-system -no-window -no-audio &
bash /preconf/install_culebra.sh
+# Save the ad id while the emulator is still up
+su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - > /adid
+
adb emu avd snapshot save configured
adb emu kill
#to let the host know it finished installing
install -m 777 /dev/null /preconf/finished
tail -f /dev/null

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 8, 06:42 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1032032
Default Alt Text
(20 KB)

Event Timeline