Déverrouiller SpringBoard avant le lancement des applications

This commit is contained in:
Workbench IA committed 2026-09-30 15:08:21 +00:00
1 parent 72c64688f1
commit e28583407a
5 files changed
+151 -13

No files matched your search

+24
View File
@@ -171,3 +171,27 @@ Debug : `IPOD_QEMU_EXTRA="-d unimp,guest_errors -D ~/ipod-emu/run/int.log"` ; `I
- Vérifications : syntaxe Bash/Python, compilation du relais avec `-Wall`,
échec QEMU volontaire détecté immédiatement, port 7788 occupé détecté et QEMU
arrêté, `stop` après échec. Aucun changement des modèles matériels dans cette reprise.
## Correction du verrouillage — agent v11
- `SBGetScreenLockStatus` confirme `locked=1 passcode=0` après redémarrage.
`SBApplicationRequestedDeviceUnlock` réveille un clavier de code sans déverrouiller.
- Solution validée : `SBDimScreen(port, false)`, attendre deux secondes, glisser
`(60,450)` → `(319,450)`, attendre deux secondes, vérifier `locked=0`.
Le geste historique `(40,430)` → `(280,430)` n'atteint pas correctement le bord
de la surface tactile émulée. Home/Power et gestes lors de la reprise du pilote
peuvent encore provoquer un arrêt QEMU ; aucun correctif matériel n'est revendiqué.
- Agent v11 : commandes `screen` et `wake`. Client : `unlock`, vérification des
marqueurs de disponibilité, refus si un code est actif, trois essais maximum.
`start -W` / `wait` ne rendent la main avec succès qu'après déverrouillage.
- Validations sans débogueur sur la session copiée : démarrage en 36 s ; nouvelle
installation de HelloEmu avec `push-app`, redémarrage conservant la NAND en 47 s,
`launch` réussi et `front` = `com.allovoisins.helloemu`. Capture :
`work/reprise-20260930/hello-v11-apres-push.png`.
- L'agent v11 a été installé atomiquement dans la session de test puis synchronisé
avant redémarrage ; aucune relance à chaud de l'agent/PPP.
Nouvelle image `images/5H11a-agent-v11` construite avec l'overlay de l'agent,
sans modifier l'image 5H11a ni la NAND d'origine. L'image de base v10 doit être
mise à jour avant d'utiliser le déverrouillage automatique.
- La faute `diff_y` utilisant `x` dans `get_frame` reste à étudier séparément ;
elle n'a pas été modifiée pour cette validation.
+38 -6
View File
@@ -1,7 +1,7 @@
# Simulateur iPod Touch 2G (iPhone OS 2.2.1)
Émulateur [qemu-ios](https://git.mahtan-melwasul.com/Mahtan/qemu-ios) (branche iPod Touch 2G, N72AP), adapté pour
démarrer **iPhone OS 2.2.1 (5H11a)**. iPhone OS 2.1.1 (5F138) reste disponible. Les outils sont dans `/opt/ios-legacy/bin`.
démarrer **iPhone OS 2.2.1 (5H11a)**. iPhone OS 2.1.1 (5F138) reste disponible. Les outils sont dans `ipod-emu/bin` ; dans le workbench actuel, des wrappers les exposent via `/home/dev/.local/bin`.
## Environnement déplacé
@@ -27,7 +27,7 @@ redirection. `stop` nettoie le relais suivi par PID même si QEMU est déjà arr
## Utilisation
```bash
ipod-emu start -W # démarre 2.2.1 en conservant la NAND et attend SpringBoard (~25–50 s)
ipod-emu start -W # agent v11 : conserve la NAND, attend SpringBoard et déverrouille (~35–50 s)
# Ajouter -f uniquement pour remettre la NAND à zéro.
ipod-qmp screenshot ecran.png # capture 320x480
ipod-qmp tap 160 326 # touche (coordonnées écran), ipod-qmp tap X Y 1.5 = appui long
@@ -42,11 +42,41 @@ ipod-emu stop
- VNC sur `127.0.0.1:5900` pour interagir à la main (tunnel SSH : `ssh -L 5900:127.0.0.1:5900 …`).
- Le lancement d'une app prend 10-20 s.
## Déverrouillage après redémarrage
Avec l'agent **v11**, `start -W` et `wait` vérifient le verrouillage, réveillent
l'écran et effectuent le glissement normal avant de rendre la main.
`ipod-agent screen` retourne `locked=0 passcode=0` une fois déverrouillé ;
`ipod-agent unlock` permet de refaire ce parcours après démarrage.
Si un code est activé, le client s'arrête et demande un déverrouillage manuel.
Aucun geste n'est envoyé avant les marqueurs de démarrage du pilote tactile.
Le réveil utilise `SBDimScreen(port, false)`, puis un glissement de `(60,450)`
à `(319,450)` adapté à la surface tactile du modèle. Le client laisse deux
secondes au réveil et à la fin du geste, puis vérifie le verrouillage ; trois
essais maximum. `SBApplicationRequestedDeviceUnlock` affiche le clavier de code
sur 2.2.1 même sans code actif : ce n'est pas le parcours utilisé.
Une image contenant l'agent v10 doit être mise à jour avant d'utiliser ce workflow.
Créer une image distincte préserve l'image de base :
```bash
cd /srv/homes/iphoneos2/ipod-emu/agent
PATH=/opt/cctools/bin:$PATH make
ipod-mkimage -o 5H11a-agent-v11 overlay
```
Le firmware se choisit avec `start -w 5H11a-agent-v11 -W`. Attention : comme pour
tout changement de firmware, cette commande remplace la NAND du dossier `run` ;
conserver sa session précédente ou utiliser un environnement de test séparé.
Le dossier de diagnostic `work/reprise-20260930` conserve une session avec l'agent
v11 et HelloEmu, indépendante de la session d'origine.
## Tester nos apps
```bash
ipod-mkimage MonApp.app autre.ipa paquet.deb # -> images/5H11a-apps (base : -b 5H11a ou -b 5F138)
ipod-emu start -f -W -w 5H11a-apps
ipod-mkimage -b 5H11a-agent-v11 MonApp.app autre.ipa paquet.deb # -> images/5H11a-agent-v11-apps
ipod-emu start -f -W -w 5H11a-agent-v11-apps
ipod-agent launch com.exemple.monapp --log /private/var/mobile/monapp.log
ipod-agent get /private/var/mobile/monapp.log
@@ -72,8 +102,10 @@ la toolchain de l'environnement (`arm-apple-darwin9-gcc -isysroot /opt/theos/sdk
avant la fin du démarrage de SpringBoard.
L'image de base est en français, région France, fuseau Europe/Paris.
- **Relance de SpringBoard** : `respring` peut bloquer la reconstruction du catalogue d'apps. `push-app` redémarre
donc l'émulateur. Après un redémarrage d'une session conservée, le verrouillage iOS peut encore empêcher un
lancement (`device locked`) ; le lancement d'une app installée ainsi n'est pas encore validé.
donc l'émulateur sans reconstruire l'image ni réinitialiser la NAND. Avec l'agent v11, le déverrouillage puis
le lancement de HelloEmu au premier plan sont validés après cette installation. Les gestes Home/Power restent
susceptibles de déclencher un défaut du pilote lors de la reprise de l'écran ; le workflow automatique utilise
le réveil par l'API d'affichage et vérifie le verrouillage.
- **Veille** : l'agent v10 pose `SleepDisabled` sur `IOPMrootDomain`, le SoC ne sachant pas reprendre une veille profonde.
- Pas de son, pas d'accélération graphique (SpringBoard tourne avec `LK_ENABLE_MBX2D=0`).
+30 -2
View File
@@ -67,7 +67,7 @@ struct rt_msghdr {
#include <notify.h>
#include <unistd.h>
#define VERSION "10"
#define VERSION "11"
#define PORT 7788
#define PPP_TTY "/dev/tty.bluetooth"
#define GUEST_IP "10.0.2.15"
@@ -332,6 +332,9 @@ static void cmd_exec(int fd, char **args, int nargs)
static int (*p_SBSLaunchApplication)(CFStringRef, CFURLRef, CFArrayRef, CFDictionaryRef, CFStringRef, CFStringRef, unsigned);
static CFStringRef (*p_SBSApplicationLaunchingErrorString)(int);
static CFStringRef (*p_SBSCopyFrontmostApplicationDisplayIdentifier)(void);
static unsigned (*p_SBSSpringBoardServerPort)(void);
static int (*p_SBGetScreenLockStatus)(unsigned, unsigned char *, unsigned char *);
static int (*p_SBDimScreen)(unsigned, unsigned char);
static void load_sbs(void)
{
@@ -339,6 +342,9 @@ static void load_sbs(void)
if (h) return;
h = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_NOW);
if (!h) return;
p_SBSSpringBoardServerPort = dlsym(h, "SBSSpringBoardServerPort");
p_SBGetScreenLockStatus = dlsym(h, "SBGetScreenLockStatus");
p_SBDimScreen = dlsym(h, "SBDimScreen");
p_SBSLaunchApplication = dlsym(h, "SBSLaunchApplication");
p_SBSApplicationLaunchingErrorString = dlsym(h, "SBSApplicationLaunchingErrorString");
p_SBSCopyFrontmostApplicationDisplayIdentifier = dlsym(h, "SBSCopyFrontmostApplicationDisplayIdentifier");
@@ -350,6 +356,26 @@ static void cfstr(CFStringRef s, char *out, size_t len)
if (s) CFStringGetCString(s, out, len, kCFStringEncodingUTF8);
}
/* API MIG du firmware 2.2.1 : les deux états sont des octets, retour kern_return_t. */
static void cmd_screen(int fd, int wake)
{
load_sbs();
if (!p_SBSSpringBoardServerPort || !p_SBGetScreenLockStatus || !p_SBDimScreen) {
reply_str(fd, 0, "API d'écran indisponible"); return;
}
unsigned port = p_SBSSpringBoardServerPort();
unsigned char locked = 0, passcode = 0;
int rc = p_SBGetScreenLockStatus(port, &locked, &passcode);
if (rc) { reply_str(fd, 0, "SBGetScreenLockStatus : %d", rc); return; }
if (wake) {
/* SBApplicationRequestedDeviceUnlock affiche le clavier de code même
sans code activé. Réveiller seulement l'écran, puis glisser côté hôte. */
rc = p_SBDimScreen(port, 0);
if (rc) { reply_str(fd, 0, "SBDimScreen : %d", rc); return; }
}
reply_str(fd, 1, "locked=%u passcode=%u", locked, passcode);
}
static void cmd_launch(int fd, char **args, int nargs)
{
load_sbs();
@@ -710,6 +736,8 @@ static void handle(int fd, char *line)
else if (!strcmp(cmd, "exec")) cmd_exec(fd, a, na);
else if (!strcmp(cmd, "launch")) cmd_launch(fd, a, na);
else if (!strcmp(cmd, "front")) cmd_front(fd);
else if (!strcmp(cmd, "screen")) cmd_screen(fd, 0);
else if (!strcmp(cmd, "wake")) cmd_screen(fd, 1);
else if (!strcmp(cmd, "fetch")) cmd_fetch(fd, a, na);
else if (!strcmp(cmd, "sc")) cmd_sc(fd, a, na);
else if (!strcmp(cmd, "routes")) cmd_routes(fd);
@@ -733,7 +761,7 @@ static void handle(int fd, char *line)
else if (!strcmp(cmd, "net")) cmd_net(fd);
else if (!strcmp(cmd, "respring")) reply_str(fd, kill_by_name("SpringBoard", SIGTERM) > 0, "SpringBoard relancé");
else if (!strcmp(cmd, "sync")) { sync(); reply_str(fd, 1, "ok"); }
else reply_str(fd, 0, "commande inconnue : %s (ping exec launch fetch sc routes settime front apps put get ls rm mkdir ps kill net respring sync)", cmd);
else reply_str(fd, 0, "commande inconnue : %s (ping exec launch fetch sc routes settime screen wake front apps put get ls rm mkdir ps kill net respring sync)", cmd);
}
static void serve_client(int fd)
+56 -4
View File
@@ -7,6 +7,8 @@
lance une app (stdout/stderr, donc NSLog, dans F sur l'iPod ; URL à ouvrir)
ipod-agent fetch http://hôte/chemin requête HTTP depuis l'iPod (test réseau : DNS + TCP)
ipod-agent front app au premier plan
ipod-agent screen | wake état du verrouillage / réveil sans clavier de code (agent v11)
ipod-agent unlock réveille puis glisse et vérifie le déverrouillage (sans code)
ipod-agent exec PROG [ARGS...] exécute un programme (pas de shell : chemin absolu)
ipod-agent ps | net | respring | sync
ipod-agent kill PID|NOM [SIGNAL]
@@ -51,17 +53,63 @@ def out(body):
sys.stdout.buffer.write(b"\n")
def push_app(a, app):
def emu_home():
tools_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
default_home = tools_home if os.path.isdir(os.path.join(tools_home, "images")) else os.path.expanduser("~/ipod-emu")
emu_home = os.environ.get("IPOD_EMU_HOME", default_home)
run = os.path.join(emu_home, "run")
return os.environ.get("IPOD_EMU_HOME", default_home)
def unlock(a):
def screen():
ok, body, _ = a.call("screen")
if not ok:
sys.exit("État d'écran indisponible (agent v11 requis) : " + body.decode())
return dict(field.split("=", 1) for field in body.decode().split())
state = screen()
if state.get("locked") == "0":
print("Écran déverrouillé")
return
if state.get("passcode") != "0":
sys.exit("Un code de verrouillage est activé : déverrouiller manuellement.")
# Le pilote tactile doit avoir terminé son démarrage avant tout glissement.
try:
with open(os.path.join(emu_home(), "run", "serial.log"), "rb") as f:
serial = f.read()
except OSError as e:
sys.exit(f"Console de session inaccessible : {e}")
helper_ready = any(b"SCHelper" in line and b'"EXIT"' in line for line in serial.splitlines())
if serial.count(b"AppleMultitouchSPIUserClient::attach") < 2 or not helper_ready:
sys.exit("Contrôleur tactile pas encore prêt : attendre ipod-emu wait.")
qmp = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ipod-qmp")
sock = os.environ.get("IPOD_QMP", os.path.join(emu_home(), "run", "qmp.sock"))
for attempt in range(3):
ok, body, _ = a.call("wake")
if not ok:
sys.exit(body.decode())
time.sleep(2) # laisser le pilote réactiver l'écran et le tactile
# La surface tactile émulée est légèrement plus petite que l'écran :
# le geste doit atteindre le bord droit pour terminer le déverrouillage.
subprocess.run([qmp, "-s", sock, "swipe", "60", "450", "319", "450"], check=True)
time.sleep(2)
state = screen()
if state.get("locked") == "0":
print("Écran déverrouillé")
return
if state.get("passcode") != "0":
sys.exit("Un code de verrouillage est activé : déverrouiller manuellement.")
sys.exit("Écran toujours verrouillé après trois glissements.")
def push_app(a, app):
home = emu_home()
run = os.path.join(home, "run")
try:
with open(os.path.join(run, "nand", ".ipod-nand-format")) as f:
layout = f.read().strip()
with open(os.path.join(run, "fw")) as f:
firmware = f.read().strip()
with open(os.path.join(emu_home, "images", firmware, "machine-opts")) as f:
with open(os.path.join(home, "images", firmware, "machine-opts")) as f:
machine_opts = f.read()
except OSError:
sys.exit("push-app nécessite une image locale avec persistance NAND (ipod-mkimage / ipod-build-fw).")
@@ -122,6 +170,10 @@ def main():
except OSError as e:
sys.exit(f"agent injoignable sur {HOST}:{PORT} ({e}) — ipod-netd lancé ?")
if cmd == "unlock":
a.s.settimeout(15)
unlock(a)
return
if cmd == "get":
ok, body, _ = a.call("get", rest[0])
if not ok:
+3 -1
View File
@@ -44,7 +44,9 @@ wait_ready() {
[ $((SECONDS - s)) -lt "$t" ] || { echo "Timeout : SpringBoard pas prêt après ${t}s" >&2; return 1; }
sleep 2
done
sleep 3; echo "SpringBoard prêt ($((SECONDS - s)) s)"
sleep 3
"$(dirname "$0")/ipod-agent" unlock
echo "SpringBoard prêt et déverrouillé ($((SECONDS - s)) s)"
}
pid() { [ -f "$RUN/qemu.pid" ] && kill -0 "$(cat "$RUN/qemu.pid")" 2>/dev/null && cat "$RUN/qemu.pid"; }