Skip to content

Commit

Permalink
Fixes #75: Replace libimobiledevice with pymobiledevice3
Browse files Browse the repository at this point in the history
  • Loading branch information
zner0L committed Jun 5, 2023
1 parent 8341876 commit 752d11d
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ If you want to work with physical devices, [some setup may be necessary dependin

### Host dependencies for iOS

For iOS, you need [`libimobiledevice`](https://libimobiledevice.org/) and `ideviceinstaller`. The distribution packages are fine, if available. On Windows, you will additionally need the Apple Device Driver and the Apple Application Support service. You can get those by installing iTunes.
On Windows, you will need the Apple Device Driver and the Apple Application Support service. You can get those by installing iTunes.

## Supported targets

Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ An ID of a known permission on iOS.

#### Defined in

[ios.ts:393](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L393)
[ios.ts:385](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L385)

___

Expand Down Expand Up @@ -332,7 +332,7 @@ The IDs of known permissions on iOS.

#### Defined in

[ios.ts:376](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L376)
[ios.ts:368](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L368)

## Functions

Expand Down
2 changes: 2 additions & 0 deletions scripts/common/python.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export const venvOptions = {
name: 'appstraction',
pythonVersion: '~3.11',
requirements: [
{ name: 'pip', version: '~=23.1' },
{ name: 'frida-tools', version: '~=12.1' },
{ name: 'objection', version: '~=1.11' },
{ name: 'pymobiledevice3', version: '~=1.42' },
],
};
46 changes: 19 additions & 27 deletions src/ios.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getVenv } from 'autopy';
import { createHash } from 'crypto';
import { execa } from 'execa';
import frida from 'frida';
import { NodeSSH } from 'node-ssh';
import type { PlatformApi, PlatformApiOptions, Proxy, SupportedCapability, SupportedRunTarget } from '.';
Expand Down Expand Up @@ -151,15 +150,24 @@ export const iosApi = <RunTarget extends SupportedRunTarget<'ios'>>(
async waitForDevice(tries = 100) {
if (
!(await retryCondition(
async () => (await execa('ideviceinfo', ['-k', 'DeviceName'], { reject: false })).exitCode === 0,
// Actually wait until the SpringBoard has been started and users could interact with the device.
async () =>
!(
await python('pymobiledevice3', ['springboard', 'state', 'get'], { reject: false })
).stderr.includes('ERROR'),
tries
))
)
throw new Error('Failed to wait for device: No booted device found after timeout.');
},
async ensureDevice() {
if ((await execa('ideviceinfo', ['-k', 'DeviceName'], { reject: false })).exitCode !== 0)
throw new Error('You need to connect your device and trust this computer.');
const { exitCode, stdout: devices } = await python('pymobiledevice3', ['usbmux', 'list', '--no-color'], {
reject: false,
});
if (exitCode !== 0 && JSON.parse(devices).length !== 1)
throw new Error('You need to connect exactly one device. Multiple devices are not supported.');
if ((await python('pymobiledevice3', ['lockdown', 'info'], { reject: false })).exitCode !== 0)
throw new Error('You need to trust this computer on your device.');

if (options.capabilities.includes('frida')) {
const session = await frida
Expand All @@ -182,29 +190,13 @@ export const iosApi = <RunTarget extends SupportedRunTarget<'ios'>>(
},
clearStuckModals: asyncUnimplemented('clearStuckModals') as never,

isAppInstalled: async (appId) => {
const { stdout } =
process.platform === 'win32'
? await execa('ideviceinstaller', ['-l', '-o', 'list_all'])
: await execa('ideviceinstaller', ['list', '-o', 'list_all']);
return (
stdout
.split('\n')
// The first line is the header.
.slice(1)
.some((l) => l.startsWith(`${appId},`))
);
},
// We're using `libimobiledevice` instead of `cfgutil` because the latter doesn't wait for the app to be fully
// installed before exiting.
installApp: async (ipaPath) => {
if (process.platform === 'win32') await execa('ideviceinstaller', ['install', ipaPath]);
else await execa('ideviceinstaller', ['--install', ipaPath]);
},
uninstallApp: async (appId) => {
if (process.platform === 'win32') await execa('ideviceinstaller', ['uninstall', appId]);
else await execa('ideviceinstaller', ['--uninstall', appId]);
},
isAppInstalled: (appId) =>
python('pymobiledevice3', ['apps', 'list', '--user', '--no-color']).then(({ stdout }) =>
Object.keys(JSON.parse(stdout)).includes(appId)
),
installApp: (ipaPath) => python('pymobiledevice3', ['apps', 'install', ipaPath]).then(),
uninstallApp: (appId) => python('pymobiledevice3', ['apps', 'uninstall', appId]).then(),

async setAppPermissions(appId, _permissions) {
if (!options.capabilities.includes('ssh') || !options.capabilities.includes('frida'))
throw new Error('SSH and Frida are required for setting app permissions.');
Expand Down

0 comments on commit 752d11d

Please sign in to comment.