moabile 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moabile-1.1.1/LICENSE +21 -0
- moabile-1.1.1/PKG-INFO +238 -0
- moabile-1.1.1/README.md +209 -0
- moabile-1.1.1/moabile.egg-info/PKG-INFO +238 -0
- moabile-1.1.1/moabile.egg-info/SOURCES.txt +10 -0
- moabile-1.1.1/moabile.egg-info/dependency_links.txt +1 -0
- moabile-1.1.1/moabile.egg-info/entry_points.txt +2 -0
- moabile-1.1.1/moabile.egg-info/requires.txt +2 -0
- moabile-1.1.1/moabile.egg-info/top_level.txt +1 -0
- moabile-1.1.1/moabile.py +5432 -0
- moabile-1.1.1/pyproject.toml +53 -0
- moabile-1.1.1/setup.cfg +4 -0
moabile-1.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Francesco Berti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
moabile-1.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moabile
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: Mother of All Mobile — TUI for Android and iOS mobile app security assessment
|
|
5
|
+
Author-email: Francesco Berti <berti.francesco@proton.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bertifrancesco/MOABile
|
|
8
|
+
Project-URL: Documentation, https://github.com/bertifrancesco/MOABile#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/bertifrancesco/MOABile
|
|
10
|
+
Project-URL: Issues, https://github.com/bertifrancesco/MOABile/issues
|
|
11
|
+
Keywords: frida,android,ios,mobile-security,pentesting,tui,terminal,textual
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: textual>=6.2
|
|
27
|
+
Requires-Dist: pyte
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# MOABile — mother of all mobile
|
|
31
|
+
|
|
32
|
+
[](https://pypi.org/project/moabile/)
|
|
33
|
+
[](LICENSE)
|
|
34
|
+
[](https://www.python.org/downloads/)
|
|
35
|
+
[](#)
|
|
36
|
+
[](#)
|
|
37
|
+
[](#)
|
|
38
|
+
|
|
39
|
+
A multi-device terminal UI for mobile app testing.
|
|
40
|
+
Android over `adb`, jailbroken iOS over usbmux and ssh, both at the same time,
|
|
41
|
+
each attached device in its own panel.
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<img src="assets/screenshot_splash.svg" width="49%" alt="MOABile Splashscreen" />
|
|
47
|
+
<img src="assets/screenshot_filemanager.svg" width="49%" alt="Dual-Pane File Manager" />
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
It does not reimplement the toolchain: it drives `adb`, `scrcpy`,
|
|
51
|
+
libimobiledevice, `iproxy`, `ioscpy`, `frida`, `objection`, `curl` and `xz`,
|
|
52
|
+
which have to be on `PATH` already. The startup screen reports what is missing
|
|
53
|
+
and which of the two device families that leaves usable — an Android-only
|
|
54
|
+
machine never needs libimobiledevice installed to get past it.
|
|
55
|
+
|
|
56
|
+
An iPhone is reached with libimobiledevice for everything that needs no
|
|
57
|
+
cooperation from the phone (device info, syslog, installing an ipa) and with ssh
|
|
58
|
+
down an `iproxy` tunnel for a shell, the filesystem and frida-server. The ssh
|
|
59
|
+
password is asked for once and kept in memory. No key is ever installed on the
|
|
60
|
+
phone: that would be a file of ours left behind on someone else's device.
|
|
61
|
+
|
|
62
|
+
Nothing is assumed to be installed on the phone either. Jailbreaks ship
|
|
63
|
+
different halves of a userland, so where a tool the panel asks for is not there
|
|
64
|
+
it says so once and falls back — the address row reads `usb` for a phone on the
|
|
65
|
+
end of a cable with nothing on the network, because that is what there is to
|
|
66
|
+
say: usbmux carries no address, and lockdownd hands out the wifi MAC and never
|
|
67
|
+
the lease. What the phone *does* have is found, though: every command carries
|
|
68
|
+
the `PATH` a non-interactive ssh leaves out — `/usr/sbin`, and everything a
|
|
69
|
+
rootless jailbreak keeps under `/var/jb` — inside `sudo` as well as outside it,
|
|
70
|
+
so `grep` and `open` are not reported missing on a phone that has them.
|
|
71
|
+
|
|
72
|
+
## Installation & Run
|
|
73
|
+
|
|
74
|
+
### Recommended: `pipx` (Isolated CLI)
|
|
75
|
+
|
|
76
|
+
The easiest and cleanest way to run MOABile without dependency conflicts or `externally-managed-environment` errors:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Install globally in an isolated environment
|
|
80
|
+
pipx install moabile
|
|
81
|
+
|
|
82
|
+
# Run
|
|
83
|
+
moabile
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or run it directly on the fly without permanent installation:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pipx run moabile
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Via `pip`
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install moabile
|
|
96
|
+
moabile
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### From Source
|
|
100
|
+
|
|
101
|
+
Clone the repository and install requirements:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install -r requirements.txt
|
|
105
|
+
python3 moabile.py
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
On distributions that manage their own Python (`error: externally-managed-environment`), use a virtualenv:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
112
|
+
pip install -r requirements.txt
|
|
113
|
+
python3 moabile.py
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Python 3.10 or newer, and Textual 6.2 or newer — the floor `requirements.txt`
|
|
117
|
+
names, and the oldest release the suite passes on. Linux and macOS: it needs a
|
|
118
|
+
real pty, so it does not run on Windows outside WSL.
|
|
119
|
+
|
|
120
|
+
Tested and fully verified on **Kali Linux** (`x86_64`).
|
|
121
|
+
|
|
122
|
+
No arguments and no options — `--help` says as much as there is to say, and
|
|
123
|
+
everything else is a key inside.
|
|
124
|
+
|
|
125
|
+
## Tools it drives
|
|
126
|
+
|
|
127
|
+
The startup screen checks for these and reports each one's version. Nothing here
|
|
128
|
+
is installed for you — which package manager your machine has, and what it calls
|
|
129
|
+
a package, is your business.
|
|
130
|
+
|
|
131
|
+
| Tool | For | Family | From |
|
|
132
|
+
|---|---|---|---|
|
|
133
|
+
| `frida` | instrumentation, and its version | both | frida-tools |
|
|
134
|
+
| `objection` | exploration REPL | both | |
|
|
135
|
+
| `curl` | fetch frida-server and codeshare | both | |
|
|
136
|
+
| `xz` | unpack frida-server | both | XZ Utils |
|
|
137
|
+
| `adb` | device control | android | android platform-tools |
|
|
138
|
+
| `scrcpy` | screen mirroring | android | |
|
|
139
|
+
| `idevice_id` | device discovery, info and syslog | ios | libimobiledevice |
|
|
140
|
+
| `ideviceinstaller` | ipa install and app list | ios | |
|
|
141
|
+
| `iproxy` | ssh tunnel over usb | ios | libusbmuxd |
|
|
142
|
+
| `ssh` | shell, filesystem and frida-server | ios | OpenSSH |
|
|
143
|
+
| `ioscpy` | screen mirroring | ios | [lautarovculic/ioscpy](https://github.com/lautarovculic/ioscpy) |
|
|
144
|
+
|
|
145
|
+
Either family works on its own. Both missing, or a core tool missing, and the
|
|
146
|
+
gate does not let you through — there would be nothing past it to do.
|
|
147
|
+
|
|
148
|
+
## Keys
|
|
149
|
+
|
|
150
|
+
| | | | |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| `r` | **r**escan for devices | `i` | device **i**nfo dump |
|
|
153
|
+
| `b` | the side**b**ar, on and off | `u` | ssh **u**ser for this device |
|
|
154
|
+
| `f` | **f**rida-server, on and off | `p` | **p**urge frida-server off the device |
|
|
155
|
+
| `s` | **s**pawn the app under frida, or attach | `k` | clear this panel's log |
|
|
156
|
+
| `o` | explore the app with **o**bjection | `/` | filter log stream by keyword |
|
|
157
|
+
| `w` | mirror the screen in a **w**indow | `c` | **c**opy log / text viewer modal |
|
|
158
|
+
| `t` | **t**erminal on the device | `v` | save an s**v**g of the interface |
|
|
159
|
+
| `l` | stream the device **l**og | `m` | dark/light **m**ode |
|
|
160
|
+
| `d` | files: browse host ↔ device | `h` | **h**elp: keys and widgets |
|
|
161
|
+
| `a` | **a**dd an app: install an apk or ipa | `alt+c` | **c**opy terminal session / viewer |
|
|
162
|
+
| `e` | **e**xport the app's apk/ipa | `f8` | return focus from tool pane |
|
|
163
|
+
| `q`, `ctrl+q` | **q**uit | | |
|
|
164
|
+
|
|
165
|
+
`s` spawns the app under frida, which is what a script that has to be in
|
|
166
|
+
place before the app starts needs. Where the app is already running it offers
|
|
167
|
+
to attach to it instead — the app keeps whatever state it is in, and a script
|
|
168
|
+
on the running process sees what the device log does not carry: the unified
|
|
169
|
+
log's debug and info entries never reach `idevicesyslog`. Enter and escape keep
|
|
170
|
+
the spawn. `f` toggles frida-server, offering to match the host client, keep
|
|
171
|
+
what is installed, or install a specific custom version.
|
|
172
|
+
|
|
173
|
+
An app that is off screen is suspended on iOS, and attaching to a suspended
|
|
174
|
+
process is a prompt that never arrives — so `s` and `o` bring the app to the
|
|
175
|
+
front first, with `open` on the phone. A jailbreak that has no `open` is asked
|
|
176
|
+
to do it by hand rather than left looking stuck, and `o` says so at once
|
|
177
|
+
instead of watching a process table that is not going to change. Android needs
|
|
178
|
+
none of this: a process there runs whether it is on screen or not.
|
|
179
|
+
|
|
180
|
+
The device log, `l`, is pinned to the app's pid rather than its name: `logcat`
|
|
181
|
+
is asked for `--pid`, and on iOS `idevicesyslog` has no pid filter at all — its
|
|
182
|
+
`-p` matches process *names*, and a process merely named something similar
|
|
183
|
+
comes with it — so the pid is applied here, on the bracket every syslog line
|
|
184
|
+
carries after the process name. Which is why the whole-device-or-one-app
|
|
185
|
+
question comes up only while the app is running: with no pid there is no
|
|
186
|
+
filter to be had, so the stream is the whole device and the panel says why.
|
|
187
|
+
`/` filters the active stream in real time by keyword, and `c` opens the
|
|
188
|
+
accumulated log in a selectable viewer modal with native clipboard copy.
|
|
189
|
+
|
|
190
|
+
Inside the file browser (`d`): `p` push host → device, `l` pull device → host, `a`
|
|
191
|
+
jumps to the app's own data directory and `h` back to where the device side
|
|
192
|
+
opened (`/sdcard` or `/var/mobile`), `n` rename, `d` delete, `←`/`→` switch
|
|
193
|
+
side, `backspace` up, `r` reload, `esc` close, and `enter` opens a directory or
|
|
194
|
+
transfers the file under the cursor.
|
|
195
|
+
|
|
196
|
+
Every panel keeps its own selected app and frida arguments, so multiple devices
|
|
197
|
+
can be worked in parallel without crossing over. The sidebar highlights the active
|
|
198
|
+
device at the top, and lists its installed packages at the bottom.
|
|
199
|
+
|
|
200
|
+
## Nothing is left behind
|
|
201
|
+
|
|
202
|
+
Not a design goal that happened to fall out — the point. Nothing is written to
|
|
203
|
+
disk between runs: no config, no history, no selected app, no ssh account.
|
|
204
|
+
What you were looking at is a record of the work, and this is a tool for leaving
|
|
205
|
+
none of that. The single exception is the frida-server download, cached one
|
|
206
|
+
version deep where caches go.
|
|
207
|
+
|
|
208
|
+
While an iOS panel is open, ssh's multiplexing socket lives in the temporary
|
|
209
|
+
directory — an empty file holding no data of yours, unlinked when the panel
|
|
210
|
+
closes. A crash leaves it there, and the next run clears the one it finds.
|
|
211
|
+
|
|
212
|
+
## Tests
|
|
213
|
+
|
|
214
|
+
Headless, driven by fake tools on `PATH`, no device required:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
python3 test_moabile.py
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
It prints a `PASS` line per check and `all good` at the end. Run it as a script,
|
|
221
|
+
not under pytest: the module executes the suite on import. One copy at a time —
|
|
222
|
+
it opens real local ports for the usb tunnel, so two runs at once collide.
|
|
223
|
+
|
|
224
|
+
Lint:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
ruff check .
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Scope
|
|
231
|
+
|
|
232
|
+
A testing tool for devices you own or are authorised to test. It talks to
|
|
233
|
+
whatever is plugged in over the phone's own debug interfaces — that is the job,
|
|
234
|
+
and it is yours to have permission for.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT — see [LICENSE](LICENSE).
|
moabile-1.1.1/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# MOABile — mother of all mobile
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/moabile/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](#)
|
|
7
|
+
[](#)
|
|
8
|
+
[](#)
|
|
9
|
+
|
|
10
|
+
A multi-device terminal UI for mobile app testing.
|
|
11
|
+
Android over `adb`, jailbroken iOS over usbmux and ssh, both at the same time,
|
|
12
|
+
each attached device in its own panel.
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="assets/screenshot_splash.svg" width="49%" alt="MOABile Splashscreen" />
|
|
18
|
+
<img src="assets/screenshot_filemanager.svg" width="49%" alt="Dual-Pane File Manager" />
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
It does not reimplement the toolchain: it drives `adb`, `scrcpy`,
|
|
22
|
+
libimobiledevice, `iproxy`, `ioscpy`, `frida`, `objection`, `curl` and `xz`,
|
|
23
|
+
which have to be on `PATH` already. The startup screen reports what is missing
|
|
24
|
+
and which of the two device families that leaves usable — an Android-only
|
|
25
|
+
machine never needs libimobiledevice installed to get past it.
|
|
26
|
+
|
|
27
|
+
An iPhone is reached with libimobiledevice for everything that needs no
|
|
28
|
+
cooperation from the phone (device info, syslog, installing an ipa) and with ssh
|
|
29
|
+
down an `iproxy` tunnel for a shell, the filesystem and frida-server. The ssh
|
|
30
|
+
password is asked for once and kept in memory. No key is ever installed on the
|
|
31
|
+
phone: that would be a file of ours left behind on someone else's device.
|
|
32
|
+
|
|
33
|
+
Nothing is assumed to be installed on the phone either. Jailbreaks ship
|
|
34
|
+
different halves of a userland, so where a tool the panel asks for is not there
|
|
35
|
+
it says so once and falls back — the address row reads `usb` for a phone on the
|
|
36
|
+
end of a cable with nothing on the network, because that is what there is to
|
|
37
|
+
say: usbmux carries no address, and lockdownd hands out the wifi MAC and never
|
|
38
|
+
the lease. What the phone *does* have is found, though: every command carries
|
|
39
|
+
the `PATH` a non-interactive ssh leaves out — `/usr/sbin`, and everything a
|
|
40
|
+
rootless jailbreak keeps under `/var/jb` — inside `sudo` as well as outside it,
|
|
41
|
+
so `grep` and `open` are not reported missing on a phone that has them.
|
|
42
|
+
|
|
43
|
+
## Installation & Run
|
|
44
|
+
|
|
45
|
+
### Recommended: `pipx` (Isolated CLI)
|
|
46
|
+
|
|
47
|
+
The easiest and cleanest way to run MOABile without dependency conflicts or `externally-managed-environment` errors:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Install globally in an isolated environment
|
|
51
|
+
pipx install moabile
|
|
52
|
+
|
|
53
|
+
# Run
|
|
54
|
+
moabile
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or run it directly on the fly without permanent installation:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pipx run moabile
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Via `pip`
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install moabile
|
|
67
|
+
moabile
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### From Source
|
|
71
|
+
|
|
72
|
+
Clone the repository and install requirements:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install -r requirements.txt
|
|
76
|
+
python3 moabile.py
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
On distributions that manage their own Python (`error: externally-managed-environment`), use a virtualenv:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
83
|
+
pip install -r requirements.txt
|
|
84
|
+
python3 moabile.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Python 3.10 or newer, and Textual 6.2 or newer — the floor `requirements.txt`
|
|
88
|
+
names, and the oldest release the suite passes on. Linux and macOS: it needs a
|
|
89
|
+
real pty, so it does not run on Windows outside WSL.
|
|
90
|
+
|
|
91
|
+
Tested and fully verified on **Kali Linux** (`x86_64`).
|
|
92
|
+
|
|
93
|
+
No arguments and no options — `--help` says as much as there is to say, and
|
|
94
|
+
everything else is a key inside.
|
|
95
|
+
|
|
96
|
+
## Tools it drives
|
|
97
|
+
|
|
98
|
+
The startup screen checks for these and reports each one's version. Nothing here
|
|
99
|
+
is installed for you — which package manager your machine has, and what it calls
|
|
100
|
+
a package, is your business.
|
|
101
|
+
|
|
102
|
+
| Tool | For | Family | From |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `frida` | instrumentation, and its version | both | frida-tools |
|
|
105
|
+
| `objection` | exploration REPL | both | |
|
|
106
|
+
| `curl` | fetch frida-server and codeshare | both | |
|
|
107
|
+
| `xz` | unpack frida-server | both | XZ Utils |
|
|
108
|
+
| `adb` | device control | android | android platform-tools |
|
|
109
|
+
| `scrcpy` | screen mirroring | android | |
|
|
110
|
+
| `idevice_id` | device discovery, info and syslog | ios | libimobiledevice |
|
|
111
|
+
| `ideviceinstaller` | ipa install and app list | ios | |
|
|
112
|
+
| `iproxy` | ssh tunnel over usb | ios | libusbmuxd |
|
|
113
|
+
| `ssh` | shell, filesystem and frida-server | ios | OpenSSH |
|
|
114
|
+
| `ioscpy` | screen mirroring | ios | [lautarovculic/ioscpy](https://github.com/lautarovculic/ioscpy) |
|
|
115
|
+
|
|
116
|
+
Either family works on its own. Both missing, or a core tool missing, and the
|
|
117
|
+
gate does not let you through — there would be nothing past it to do.
|
|
118
|
+
|
|
119
|
+
## Keys
|
|
120
|
+
|
|
121
|
+
| | | | |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| `r` | **r**escan for devices | `i` | device **i**nfo dump |
|
|
124
|
+
| `b` | the side**b**ar, on and off | `u` | ssh **u**ser for this device |
|
|
125
|
+
| `f` | **f**rida-server, on and off | `p` | **p**urge frida-server off the device |
|
|
126
|
+
| `s` | **s**pawn the app under frida, or attach | `k` | clear this panel's log |
|
|
127
|
+
| `o` | explore the app with **o**bjection | `/` | filter log stream by keyword |
|
|
128
|
+
| `w` | mirror the screen in a **w**indow | `c` | **c**opy log / text viewer modal |
|
|
129
|
+
| `t` | **t**erminal on the device | `v` | save an s**v**g of the interface |
|
|
130
|
+
| `l` | stream the device **l**og | `m` | dark/light **m**ode |
|
|
131
|
+
| `d` | files: browse host ↔ device | `h` | **h**elp: keys and widgets |
|
|
132
|
+
| `a` | **a**dd an app: install an apk or ipa | `alt+c` | **c**opy terminal session / viewer |
|
|
133
|
+
| `e` | **e**xport the app's apk/ipa | `f8` | return focus from tool pane |
|
|
134
|
+
| `q`, `ctrl+q` | **q**uit | | |
|
|
135
|
+
|
|
136
|
+
`s` spawns the app under frida, which is what a script that has to be in
|
|
137
|
+
place before the app starts needs. Where the app is already running it offers
|
|
138
|
+
to attach to it instead — the app keeps whatever state it is in, and a script
|
|
139
|
+
on the running process sees what the device log does not carry: the unified
|
|
140
|
+
log's debug and info entries never reach `idevicesyslog`. Enter and escape keep
|
|
141
|
+
the spawn. `f` toggles frida-server, offering to match the host client, keep
|
|
142
|
+
what is installed, or install a specific custom version.
|
|
143
|
+
|
|
144
|
+
An app that is off screen is suspended on iOS, and attaching to a suspended
|
|
145
|
+
process is a prompt that never arrives — so `s` and `o` bring the app to the
|
|
146
|
+
front first, with `open` on the phone. A jailbreak that has no `open` is asked
|
|
147
|
+
to do it by hand rather than left looking stuck, and `o` says so at once
|
|
148
|
+
instead of watching a process table that is not going to change. Android needs
|
|
149
|
+
none of this: a process there runs whether it is on screen or not.
|
|
150
|
+
|
|
151
|
+
The device log, `l`, is pinned to the app's pid rather than its name: `logcat`
|
|
152
|
+
is asked for `--pid`, and on iOS `idevicesyslog` has no pid filter at all — its
|
|
153
|
+
`-p` matches process *names*, and a process merely named something similar
|
|
154
|
+
comes with it — so the pid is applied here, on the bracket every syslog line
|
|
155
|
+
carries after the process name. Which is why the whole-device-or-one-app
|
|
156
|
+
question comes up only while the app is running: with no pid there is no
|
|
157
|
+
filter to be had, so the stream is the whole device and the panel says why.
|
|
158
|
+
`/` filters the active stream in real time by keyword, and `c` opens the
|
|
159
|
+
accumulated log in a selectable viewer modal with native clipboard copy.
|
|
160
|
+
|
|
161
|
+
Inside the file browser (`d`): `p` push host → device, `l` pull device → host, `a`
|
|
162
|
+
jumps to the app's own data directory and `h` back to where the device side
|
|
163
|
+
opened (`/sdcard` or `/var/mobile`), `n` rename, `d` delete, `←`/`→` switch
|
|
164
|
+
side, `backspace` up, `r` reload, `esc` close, and `enter` opens a directory or
|
|
165
|
+
transfers the file under the cursor.
|
|
166
|
+
|
|
167
|
+
Every panel keeps its own selected app and frida arguments, so multiple devices
|
|
168
|
+
can be worked in parallel without crossing over. The sidebar highlights the active
|
|
169
|
+
device at the top, and lists its installed packages at the bottom.
|
|
170
|
+
|
|
171
|
+
## Nothing is left behind
|
|
172
|
+
|
|
173
|
+
Not a design goal that happened to fall out — the point. Nothing is written to
|
|
174
|
+
disk between runs: no config, no history, no selected app, no ssh account.
|
|
175
|
+
What you were looking at is a record of the work, and this is a tool for leaving
|
|
176
|
+
none of that. The single exception is the frida-server download, cached one
|
|
177
|
+
version deep where caches go.
|
|
178
|
+
|
|
179
|
+
While an iOS panel is open, ssh's multiplexing socket lives in the temporary
|
|
180
|
+
directory — an empty file holding no data of yours, unlinked when the panel
|
|
181
|
+
closes. A crash leaves it there, and the next run clears the one it finds.
|
|
182
|
+
|
|
183
|
+
## Tests
|
|
184
|
+
|
|
185
|
+
Headless, driven by fake tools on `PATH`, no device required:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
python3 test_moabile.py
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
It prints a `PASS` line per check and `all good` at the end. Run it as a script,
|
|
192
|
+
not under pytest: the module executes the suite on import. One copy at a time —
|
|
193
|
+
it opens real local ports for the usb tunnel, so two runs at once collide.
|
|
194
|
+
|
|
195
|
+
Lint:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
ruff check .
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Scope
|
|
202
|
+
|
|
203
|
+
A testing tool for devices you own or are authorised to test. It talks to
|
|
204
|
+
whatever is plugged in over the phone's own debug interfaces — that is the job,
|
|
205
|
+
and it is yours to have permission for.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT — see [LICENSE](LICENSE).
|