Déverrouiller SpringBoard avant le lancement des applications
This commit is contained in:
1 parent
72c64688f1
commit
e28583407a
5 files changed
+151
-13
No files matched your search
@@ -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.
|
||||
@@ -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`).
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
Reference in new issue
Block a user