├── .gitignore ├── Makefile ├── idevicels ├── escapify.c ├── LICENSE.md ├── idb.conf ├── iproxy-quiet.patch ├── README.md └── idb /.gitignore: -------------------------------------------------------------------------------- 1 | escapify 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=cc 2 | CFLAGS= #-g -Wall 3 | LDFLAGS= 4 | 5 | .PHONY: all 6 | 7 | all: escapify 8 | 9 | escapify: escapify.c 10 | $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 11 | 12 | .PHONY: clean 13 | 14 | clean: 15 | rm -f escapify *.o 16 | -------------------------------------------------------------------------------- /idevicels: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # List all iOS devices attached to the computer via USB. 3 | # Uses `idevicename`, `idevice_id`, and `ideviceinfo` from libimobiledevice. 4 | # On Debian, they are all in the package `libimobiledevice-utils`. 5 | 6 | # This script should be 100% POSIX-compliant. Replacing the `expr` call with 7 | # $(( )) and replacing all the [ ]'s with [[ ]]'s would probably speed it up 8 | # some in bash and ksh, but sacrifices 100% POSIX compliance. 9 | 10 | SPACING=18 11 | fetch(){ 12 | # ensure even spacing for each column 13 | printf ' '"$2"':' 14 | LEN=$( expr "$SPACING" '-' "$( printf "$2" | wc -c )" ) 15 | I=0 16 | while [ "$I" -lt "$LEN" ]; do 17 | printf ' ' 18 | I=`expr $I '+' 1` 19 | done 20 | ideviceinfo -u "$1" -k "$2" 21 | } 22 | 23 | idevice_id -l | while read UUID; do 24 | idevicename -u "$UUID" | tr '\n' ':' | sed 's/:$/:\n/' 25 | printf " UUID: ""$UUID"'\n' 26 | fetch "$UUID" "ProductType" 27 | fetch "$UUID" "ProductVersion" 28 | done 29 | -------------------------------------------------------------------------------- /escapify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | int main(int argc, char **argv) 5 | { 6 | char c='\0'; 7 | int i=0; 8 | int j=1; 9 | /* if(argc >= 2)*/ 10 | /* if(argc < 2) 11 | { 12 | fprintf(stderr,"Error: Exactly one argument should be given: a string to shell-escape.\n"); 13 | exit(1); 14 | }*/ 15 | /* don't care if no args passed, should just return emptiness. */ 16 | if(argc < 2) 17 | { 18 | return 0; 19 | } 20 | while(j': 43 | case '[': 44 | case ']': 45 | case '\\': 46 | case '^': 47 | case '`': 48 | case '{': 49 | case '}': 50 | putchar('\\'); 51 | default: 52 | putchar(c); 53 | } 54 | i++; 55 | } 56 | j++; 57 | if(jfd); 11 | + */ 12 | 13 | while (!cdata->stop_stoc && cdata->fd > 0 && cdata->sfd > 0) { 14 | recv_len = socket_receive_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000); 15 | @@ -71,7 +73,9 @@ static void *run_stoc_loop(void *arg) 16 | // try again 17 | continue; 18 | } else { 19 | + /* wyatt - quiet 20 | fprintf(stderr, "recv failed: %s\n", strerror(-recv_len)); 21 | + */ 22 | break; 23 | } 24 | } else { 25 | @@ -107,9 +111,9 @@ static void *run_ctos_loop(void *arg) 26 | #else 27 | pthread_t stoc; 28 | #endif 29 | - 30 | + /* wyatt - quiet 31 | printf("%s: fd = %d\n", __func__, cdata->fd); 32 | - 33 | + */ 34 | cdata->stop_stoc = 0; 35 | #ifdef WIN32 36 | stoc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run_stoc_loop, cdata, 0, NULL); 37 | @@ -124,7 +128,9 @@ static void *run_ctos_loop(void *arg) 38 | // try again 39 | continue; 40 | } else { 41 | + /* wyatt - quiet 42 | fprintf(stderr, "recv failed: %s\n", strerror(-recv_len)); 43 | + */ 44 | break; 45 | } 46 | } else { 47 | @@ -132,10 +138,14 @@ static void *run_ctos_loop(void *arg) 48 | sent = socket_send(cdata->sfd, buffer, recv_len); 49 | if (sent < recv_len) { 50 | if (sent <= 0) { 51 | + /* wyatt - quiet 52 | fprintf(stderr, "send failed: %s\n", strerror(errno)); 53 | + */ 54 | break; 55 | } else { 56 | + /* wyatt - quiet 57 | fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); 58 | + */ 59 | } 60 | } 61 | } 62 | @@ -167,14 +177,18 @@ static void *acceptor_thread(void *arg) 63 | int count; 64 | 65 | if (!arg) { 66 | + /* wyatt - quiet 67 | fprintf(stderr, "invalid client_data provided!\n"); 68 | + */ 69 | return NULL; 70 | } 71 | 72 | cdata = (struct client_data*)arg; 73 | 74 | if ((count = usbmuxd_get_device_list(&dev_list)) < 0) { 75 | + /* wyatt - quiet 76 | printf("Connecting to usbmuxd failed, terminating.\n"); 77 | + */ 78 | free(dev_list); 79 | if (cdata->fd > 0) { 80 | socket_close(cdata->fd); 81 | @@ -183,10 +197,14 @@ static void *acceptor_thread(void *arg) 82 | return NULL; 83 | } 84 | 85 | + /* wyatt - quiet 86 | fprintf(stdout, "Number of available devices == %d\n", count); 87 | + */ 88 | 89 | if (dev_list == NULL || dev_list[0].handle == 0) { 90 | + /* wyatt - quiet 91 | printf("No connected device found, terminating.\n"); 92 | + */ 93 | free(dev_list); 94 | if (cdata->fd > 0) { 95 | socket_close(cdata->fd); 96 | @@ -209,7 +227,9 @@ static void *acceptor_thread(void *arg) 97 | } 98 | 99 | if (dev == NULL || dev->handle == 0) { 100 | + /* wyatt - quiet 101 | printf("No connected/matching device found, disconnecting client.\n"); 102 | + */ 103 | free(dev_list); 104 | if (cdata->fd > 0) { 105 | socket_close(cdata->fd); 106 | @@ -218,12 +238,16 @@ static void *acceptor_thread(void *arg) 107 | return NULL; 108 | } 109 | 110 | + /* wyatt - quiet 111 | fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev->handle, dev->udid, device_port); 112 | + */ 113 | 114 | cdata->sfd = usbmuxd_connect(dev->handle, device_port); 115 | free(dev_list); 116 | if (cdata->sfd < 0) { 117 | + /* wyatt - quiet 118 | fprintf(stderr, "Error connecting to device!\n"); 119 | + */ 120 | } else { 121 | cdata->stop_ctos = 0; 122 | 123 | @@ -276,7 +300,9 @@ int main(int argc, char **argv) 124 | // first create the listening socket endpoint waiting for connections. 125 | mysock = socket_create(listen_port); 126 | if (mysock < 0) { 127 | + /* wyatt - quiet 128 | fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); 129 | + */ 130 | return -errno; 131 | } else { 132 | #ifdef WIN32 133 | @@ -287,10 +313,14 @@ int main(int argc, char **argv) 134 | struct client_data *cdata; 135 | int c_sock; 136 | while (1) { 137 | + /* wyatt - quiet 138 | printf("waiting for connection\n"); 139 | + */ 140 | c_sock = socket_accept(mysock, listen_port); 141 | if (c_sock) { 142 | + /* wyatt - quiet 143 | printf("accepted connection, fd = %d\n", c_sock); 144 | + */ 145 | cdata = (struct client_data*)malloc(sizeof(struct client_data)); 146 | if (!cdata) { 147 | socket_close(c_sock); 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # idb - "iOS Debug Bridge" 2 | An emulation of a handful of useful adb commands I use for Android devices, 3 | adapted for jailbroken iOS devices connected via USB. 4 | 5 | It currently has been tested on iDevices ranging from iOS 4 to iOS 10, but it 6 | should work on 2.x up through whatever the most recent that can run an SSH 7 | daemon is. 8 | 9 | Now (as of early 2020) supports multiple iOS devices connected simultaneously! 10 | Use `idb -u UDID [command]` to try it! 11 | 12 | #### History 13 | 14 | As an Android developer with an old jailbroken iPhone 4S (and others) that I 15 | toy with occasionally, I was getting annoyed with typing in `ssh`/`scp` 16 | commands constantly to make my device do things, so I decided to write a 17 | script for it. 18 | 19 | By the way, I had absolutely no idea Facebook had an identically named project. 20 | 21 | I wholly expect theirs is far more capable, at least on Mac OS machines, but I 22 | like mine and I will continue using it. 23 | 24 | Due to being a POSIX shell script, my version is quite flexible, and (I hope) 25 | rather easy to pick up and hack on. I have commented the script heavily, in 26 | the hopes that it should even be approachable for someone with next to no shell 27 | scripting experience for tweaking. 28 | 29 | ## Usage: 30 | 31 | Note that this is currently slightly out of date; there are more options 32 | available than are documented. For the moment, you can search the shell script 33 | for it. I hope to have time to clean this document up in the future and add the 34 | functions to the help text, but some of them need a little polishing before 35 | I'll feel like they're airworthy. 36 | 37 | ```` 38 | idb [-u ] command [options] 39 | -u can optionally be placed before most commands to make them operate 40 | on the device with the given UDID (check with "idb devices" or "idb list"). 41 | 42 | Detailed command usage: 43 | idb push [target] [destination] 44 | Copies a targeted file on the computer to a destination on the iDevice. 45 | idb pull [target] [destination] 46 | Copies a targeted file from the iDevice to a destination on the computer. 47 | idb shell 48 | Starts a remote shell on the iDevice. 49 | idb shell [command] 50 | Starts a remote shell on the iDevice, runs the given command, and exits. 51 | idb forward [local] [remote] 52 | Forwards socket connections (currently only TCP ports are tested). 53 | Unlike ADB, [local] and [remote] here should be integer values. That is, 54 | 'tcp:25565', the ADB syntax, would be just '25565' here. 55 | idb install [target] 56 | Installs the indicated target IPA on the iDevice using `ipainstaller`. 57 | Will need modification to work with other CLI IPA installer programs 58 | (which need to be installed on the iDevice itself via Cydia or similar). 59 | idb uninstall [appid] 60 | Try to remove the app with the given ID from the iDevice. May fail on 61 | system apps. 62 | idb remove [appid] 63 | Synonym for `idb uninstall`. 64 | idb list-packages [-a|-u|-s|-x] [appid] 65 | Lists packages installed on the iDevice. Has several optional flags: 66 | -a: List all packages on the iDevice. 67 | -u: List user packages on the iDevice (default). 68 | -s: List system packages on the iDevice. 69 | -x: List all packages on the iDevice in XML format. 70 | If 'appid' is specified, only that package's information is displayed. 71 | This is incompatible with -x (for now at least). 72 | idb devices 73 | Lists the UDID's of all connected devices. Part of preliminary 74 | (incomplete but planned) support for multi-device capability. 75 | idb list 76 | Synonym for `idb devices`. 77 | idb kill-server 78 | Kills all instances of 'iproxy,' the TCP-over-usbmuxd forwarding program. 79 | idb help 80 | Show this usage information. 81 | idb -h 82 | Synonym for `idb help`. 83 | idb --help 84 | Synonym for `idb help`. 85 | ```` 86 | ### System Requirements: 87 | #### On the computer: 88 | * `usbmuxd` ([https://github.com/libimobiledevice/usbmuxd.git](https://github.com/libimobiledevice/usbmuxd.git)) needs to be 89 | running for this to work. Tested on Debian Linux, but probably works in 90 | Mac OS (or Windows with MSYS/Cygwin), based on past experience. On those 91 | platforms, installing iTunes from Apple will get you usbmuxd, but you'll 92 | still need to get or compile the "iproxy" tool from the open-source clone, 93 | libusbmuxd (see the next item in this list.) 94 | 95 | * Because of the new multi-device support, libimobiledevice's "tools" are 96 | also required (specifically, "idevice_id"). These tools are in the 'tools' 97 | directory of https://github.com/libimobiledevice/libimobiledevice.git . 98 | 99 | * `iproxy`, found in the 'tools' subdirectory of [https://github.com/libimobiledevice/libusbmuxd.git](https://github.com/libimobiledevice/libusbmuxd.git). 100 | It is also obtainable in some linux distros - in Debian (and presumably 101 | Ubuntu), it is in the package `libusbmuxd-tools`. 102 | 103 | Whatever the case, if you compile `iproxy`, put the binary in a directory in 104 | your `$PATH` variable. I use `iproxy-quiet`, which is a custom version of it 105 | that I made that doesn't print information except on certain errors (i.e. it 106 | follows the Unix philosophy better). **You will have to edit the script to use 107 | `iproxy-quiet`** (just change the variable `IPROXY_PROG` near the top of the 108 | file). You will also have to apply the patch to the libusbmuxd repository and 109 | build your own version of the tool if you take this route; I'd suggest you just 110 | use the one that they provide you and accept the extra prints. I only provide 111 | the patch because I personally use it. 112 | 113 | * `ideviceinstaller` for some functionality. From [this repository](https://github.com/libimobiledevice/ideviceinstaller) of libimobiledevice. 114 | 115 | * Included in this repository is a patch which should allow you to build 116 | your own `iproxy-quiet`, if you choose. Apply the patch with either 117 | `git apply iproxy-quiet.patch` or `patch -p1 < iproxy-quiet.patch` from the 118 | libusbmuxd source root. 119 | 120 | * There's also a soft (optional) dependency on 'escapify,' a tiny C program 121 | I wrote which I have included in this repository. It's something I might be 122 | able to replace with native shell stuff some day, but for me it was easier to 123 | write in C. Basically, it just makes escaping strings with things like spaces 124 | more intuitive and consistent by formatting them so programs like `scp` will 125 | accept them. If you don't use it, you may have to add some backslashes to your 126 | strings with spaces in them for things like `push` and `pull` to work as 127 | intended. Compile it with the makefile, or run a command similar to 128 | `cc -o escapify escapify.c`, and then put the binary in a directory that's in 129 | your PATH variable. If available, the script will automatically utilize it. 130 | 131 | ### On the iDevice: 132 | * The device has to be connected via USB and have sshd listening on the port 133 | defined in the script as REMOTEPORT. Due to the sshd requirement, the device 134 | must be jailbroken. In Cydia, sshd is installed through the package "OpenSSH" 135 | (called "openssh", note the casing, if you install it via apt on the 136 | command-line). Newer iOS versions may need to install Dropbear as their SSH 137 | daemon instead; I have not done so on my newer iDevices yet (newest is a 5S). 138 | A couple of commands may work without SSH, such as `install`. 139 | 140 | * Note also that you can get openssh either from Saurik's repository 141 | (available by default on basically any jailbroken iDevice), or from other 142 | sources like ios-webstack ~~(see http://ios-webstack.tk )~~ (repo is dead; try 143 | https://cydia.akemi.ai/webstack/ instead). ios-webstack has a newer version of 144 | OpenSSH than Saurik's repository does as of January 2018. 145 | 146 | * This script can probably be easily adapted for wireless transfers by 147 | commenting out the iproxy stuff and changing the IP address/LOCALPORT to 148 | the device address and the port that it's sshd is listening on. 149 | 150 | ### Password-less Authentication 151 | To avoid having to type a password every time, set up key authentication 152 | between the computer and the iDevice. I DO NOT RECOMMEND disabling password 153 | login once key authentication is established. 154 | 155 | On the computer, run: 156 | ```` 157 | ssh-keygen 158 | (leave default filenames for the keys) 159 | ssh-keygen -p 160 | (leave default filenames for the keys) 161 | ```` 162 | 163 | On the iDevice (probably over ssh), run: 164 | ```` 165 | mkdir /var/mobile/.ssh 166 | echo authstr >> /var/mobile/.ssh/authorized_keys 167 | ```` 168 | 169 | (where authstr is the output of `cat ~/.ssh/id_rsa.pub` on the computer) 170 | 171 | Alternatively if you want to log in as root, you'd change the "DEVICE_USER" field 172 | above in this script and write to /var/root/.ssh/authorized_keys instead. 173 | 174 | ### Troubleshooting 175 | 176 | Errors I've encountered: 177 | * `bind(): Permission denied` when forwarding 178 | 179 | Make sure your selected local and remote ports are not already bound by other 180 | applications. If you have multiple devices, try setting up their LOCALPORT 181 | values separately in the new `idb.conf` file. See the example idb.conf file for 182 | an example of how it works. Put it in `~/.config/idb.conf`. 183 | 184 | * `ssh_exchange_identification: read: Connection reset by peer` 185 | 186 | Check that the variable REMOTEPORT is set to the port that the idevice is 187 | listening for SSH connections on it. Also make sure `usbmuxd` is running and 188 | has permissions to access your device (might require a udev rule or similar.) 189 | 190 | * `ERROR: Unable to retrieve device list!` (and similar problems finding a device): 191 | 192 | This means that `usbmuxd` is probably messing up on your computer. Try killing 193 | it and re-loading `usbmuxd`. I have to use `kill -9` or `pkill -9` to make it stop, 194 | typically. You can also run `usbmuxd -f` to get messages in the foreground for 195 | diagnostics. 196 | 197 | `idb list` runs the libimobiledevice `idevicels` command, if available, to 198 | enumerate devices. You could also try running `idevicels` manually to see what 199 | it has to say. Typically `usbmuxd -f` offers more useful information, though. 200 | 201 | ### Notes/Miscellania 202 | 203 | This script currently has several features that aren't advertised above in this 204 | readme, with the justification that they are sort of hard to use, or hacks 205 | that work well enough for me but which are sort of silly to expect others to 206 | pick up easily. I'll briefly list a few of them here. 207 | 208 | If you want to learn more about one of these features, read the shell script - 209 | and, if you have questions, contact me. You can either file an issue or send 210 | an email; both are fine. I'll probably respond more quickly to an issue than 211 | a plain email, though. 212 | 213 | * `idb.conf`: a file containing a colon-separated list of values. You can use 214 | this if you have multiple iDevices attached at once, to differentiate them when 215 | using SSH with `iproxy`. Each device must use iproxy listening on a separate 216 | port on the host, so this file lets you assign device UUID's to addresses. 217 | 218 | * This could also be used to allow wireless device access - at least for SSH 219 | based functionality. The example idb.conf file in this repository tries to 220 | illustrate the syntax. But it's quite hacky. 221 | 222 | * Screenshots: this functionality requires mounting a developer disk image on 223 | the iDevice in question. Since different iOS versions have different 224 | developer disk images, there's no easy way I could think of to make a generic 225 | function for it in the script. So you can provide a path to an appropriate 226 | developer disk image in `idb.conf` for your given UUID. 227 | 228 | * debug: wrapper for `idevicedebug` 229 | I just haven't gotten around to documenting this properly yet. Check the 230 | comments in the script for more. 231 | 232 | I'd like to implement iFUSE-based file transfers, too - that way, idb could be 233 | used to push files into application directories just like iTunes can. This is 234 | currently not implemented. 235 | -------------------------------------------------------------------------------- /idb: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Definitions at top of file for easy editing. 3 | 4 | # Local machine's TCP relay port 5 | LOCALPORT=2222 6 | # Port on the computer to use for iproxy's TCP forwarding. Low port numbers 7 | # may require root on some systems. 8 | 9 | # Remote port 10 | REMOTEPORT=22 11 | # The port sshd runs on on the device. The default sshd listens on is usually 12 | # port 22, so use that unless you've changed it on the iDevice in /etc/sshd_config 13 | 14 | # "Overrides" configuration file 15 | CONFFILE="$HOME"'/.config/idb.conf' 16 | # Location of a file which contains UDID-specific override settings 17 | # (port numbers and such) for specific iDevices. See the sample idb.conf 18 | # for more information on how to use it. Only really handy if you have 19 | # more than one device wired in to your computer at a time. 20 | 21 | # User name on iDevice 22 | DEVICE_USER="mobile" 23 | # The user to log in as, for operations performed through SSH-over-USB. 24 | 25 | # ideviceinstaller name/path goes here. 26 | IDEVICEINSTALLER="ideviceinstaller" 27 | # You should change this only if you have ideviceinstaller outside of your 28 | # $PATH or you have renamed the program. 29 | 30 | # idevicedebug name/path goes here. 31 | IDEVICEDEBUG="idevicedebug" 32 | # You should change this only if you have idevicedebug outside of your 33 | # $PATH or you have renamed the program. 34 | 35 | # idevicesyslog name/path goes here. 36 | IDEVICESYSLOG="idevicesyslog" 37 | 38 | IDEVICEIMAGEMOUNTER="ideviceimagemounter" 39 | IDEVICESCREENSHOT="idevicescreenshot" 40 | 41 | 42 | # developer disk image can optionally go here. /dev/null = no image. 43 | # Can be overridden by idb.conf. 44 | DEVDISKIMG=/dev/null 45 | 46 | # Should IPA's be installed via SSH commands or via libimobiledevice? 47 | # In future: should we use SSH or libimobiledevice functions to administer 48 | # your device, if both methods are available? 49 | SSH_PREFERRED=0 50 | # 0 to use ideviceinstaller, anything else to run a command on the iDevice 51 | # over an SSH connection via USB TCP relaying. 52 | # In the future, this will be used to say that SSH is preferred over libusb 53 | # for all tasks where possible. This is useful if one of your devices is 54 | # connected via network instead of a USB cord, but requires SSH on your 55 | # iDevice. There are some things I have not figured out how to do without SSH, 56 | # so it is likely that for full functionality SSH will always be a requirement. 57 | # The ideviceinstaller method is nicer for idevices that don't have sshd 58 | # started at boot, like modern tethered iOS jailbreaks (the poor things). 59 | # It also will work for non-jailbroken devices if the app is signed off on by 60 | # Apple, and is a lot less fickle to set up than sshd and iproxy since the 61 | # program comes with the unofficial FLOSS libimobiledevice suite. It can also 62 | # be compiled and installed to operate alongside the official library on OSX 63 | # and Windows. 64 | # The SSH method is my original behavior and requires an IPA installer 65 | # program to be installed on the iDevice, which must be jailbroken and also 66 | # running an SSH daemon. 67 | 68 | REMOTE_IPA_INSTALL_PROG="ipainstaller" # Program on the iDevice to install IPA's with. 69 | 70 | IPROXY_PROG="iproxy" 71 | #IPROXY_PROG="iproxy-quiet" # Change to 'iproxy' if you don't want to use my 72 | # patched version or haven't renamed it. 73 | IPADDR="127.0.0.1" # Don't change unless you plan to use this for wireless ssh. 74 | 75 | UDID='' # If empty, the script chooses the first UDID enumerated later on. 76 | 77 | ### SCRIPT BODY STARTS HERE ### 78 | # Check if my 'escapify' tool is built (lets one not have to double-escape 79 | # things with spaces and such for them to work in ssh commands) 80 | type escapify 81 | HAS_ESCAPIFY=$? 82 | export HAS_ESCAPIFY 83 | 84 | # remove grep aliases as to avoid color highlighting potential issues 85 | # don't show any error messages 86 | # (used for kill-server) 87 | 2>/dev/null 1>&2 unalias grep 88 | 2>/dev/null 1>&2 unalias tail 89 | 2>/dev/null 1>&2 unalias awk 90 | 91 | IPROXYPID="" # placeholder global variable, set by some commands 92 | 93 | getCol() { 94 | echo "$2" | awk '{ print $'"$1"' }' 95 | } 96 | confLoad() { 97 | if [ -e "$HOME"'/.config/idb.conf' ]; then # if idb.conf exists 98 | # search for given UDID. 99 | # last instance supersedes previous ones (but you should not have multiple) 100 | # !!Spaces might cause issues with this implementation!! 101 | # (they are okay in comments only.) 102 | CONFLINE="$(grep '^'"$1"':' "$CONFFILE"|tail -n 1|sed 's/:/\t/g')" 103 | if [ -n "$CONFLINE" ]; then # if conf line is not empty (was found) 104 | REMOTEPORT="$(getCol 2 "$CONFLINE")" 105 | LOCALPORT="$(getCol 3 "$CONFLINE")" 106 | IPADDR="$(getCol 4 "$CONFLINE")" 107 | SSH_PREFERRED="$(getCol 5 "$CONFLINE")" 108 | DEVDISKIMG="$(getCol 6 "$CONFLINE")" 109 | fi # /if conf line was found 110 | fi # /if idb.conf exists 111 | # do nothing if no match was found or file doesn't exist (continue on) 112 | } 113 | 114 | 115 | # check if UDID is specified. If not, the tools I call should default to the 116 | # first device that is enumerated by 'idevice_id -l'. 117 | if [ "$1" = '-u' ]; then 118 | shift 119 | UDID="$1" 120 | shift 121 | else #choose first device that appears otherwise 122 | UDID=`idevice_id -l | head -n 1` 123 | fi 124 | # attempt to load UDID-specific config overrides from ~/.config/idb.conf, if 125 | # said file exists 126 | confLoad "$UDID" 127 | 128 | 129 | startRelay() { 130 | # hide iproxy output. Look here if you can't make it work and remove the 131 | # pipe to see the diagnostics. 132 | set -x 133 | "$IPROXY_PROG" "$LOCALPORT" "$REMOTEPORT" "$UDID" > /dev/null 2>&1 & 134 | set +x 135 | IPROXYPID="$!" 136 | } 137 | 138 | listDevices() { 139 | # preliminary work to support multiple iDevices, untested because I only own 140 | # one iDevice. Uses "idevice_id" from libimobiledevice tools at 141 | # https://github.com/libimobiledevice/libimobiledevice.git 142 | echo "Listing all iDevice UDID's." 143 | SPACING=18 144 | fetch(){ 145 | # Ensure even spacing for each column. 146 | # This can likely be optimized massively. 147 | # One simple thing would be replacing [ ] with [[ ]], but that's not 148 | # in POSIX so you'd have to change this script to be using bash/ksh or 149 | # something. If using ksh, $(( )) for math instead of 'expr' would also 150 | # help a lot. 151 | printf ' '"$2"':' 152 | LEN=$( expr "$SPACING" '-' "$( printf "$2" | wc -c )" ) 153 | I=0 154 | while [ "$I" -lt "$LEN" ]; do 155 | printf ' ' 156 | I=`expr $I '+' 1` 157 | done 158 | ideviceinfo -u "$1" -k "$2" 159 | } 160 | 161 | idevice_id -l | while read UUID; do # get info about each UUID's properties. 162 | # Slow because it ends up having to call ideviceinfo once for each field. 163 | # Saving the output of a single run to a temporary file and reading that 164 | # would likely be much more performant. 165 | # Unfortunately, mktemp isn't a POSIX command though and I haven't felt 166 | # up to rolling my own just yet. 167 | idevicename -u "$UUID" | tr '\n' ':' | sed 's/:$/:\n/' 168 | printf " UUID: ""$UUID"'\n' 169 | fetch "$UUID" "ProductType" 170 | fetch "$UUID" "ProductVersion" 171 | done 172 | # idevice_id -l 173 | } 174 | 175 | push() { 176 | if [ $HAS_ESCAPIFY -eq 0 ] && [ -z $NO_ESCAPIFY ]; then 177 | scp -P"$LOCALPORT" "$1" "$DEVICE_USER""@""$IPADDR"":""$(escapify "$2")" 178 | else 179 | scp -P"$LOCALPORT" "$1" "$DEVICE_USER""@""$IPADDR"":""$2" 180 | fi 181 | } 182 | 183 | pull() { 184 | # if only one argument is supplied, the file's destination is in the current 185 | # directory. 186 | if [ -z "$2" ]; then 187 | DEST="." 188 | else 189 | DEST="$2" 190 | fi 191 | # check if we have my 'escapify' program. If so, idb push/pull won't require 192 | # additional escaping like scp usually does (it will work more as expected). 193 | # NO_ESCAPIFY can be set to non-empty to force idb to not use it. 194 | if [ $HAS_ESCAPIFY -eq 0 ] && [ -z $NO_ESCAPIFY ]; then # has escapify 195 | scp -P"$LOCALPORT" "$DEVICE_USER""@""$IPADDR"":""$(escapify "$1")" "$DEST" 196 | else 197 | # will neex extra escaping for spaces and such. 198 | scp -P"$LOCALPORT" "$DEVICE_USER""@""$IPADDR"":""$1" "$DEST" 199 | fi 200 | } 201 | 202 | # uses forced pseudoterminal allocation by default (-t) 203 | shell() { 204 | if [ $HAS_ESCAPIFY -eq 0 ] && [ -z $NO_ESCAPIFY ]; then # has escapify 205 | set -x 206 | ssh -t -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$(escapify "$@")" 207 | set +x 208 | else 209 | set -x 210 | ssh -t -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$@" 211 | set +x 212 | fi 213 | } 214 | 215 | # to enable later: x forwarded shell 216 | shellx() { 217 | if [ $HAS_ESCAPIFY -eq 0 ] && [ -z $NO_ESCAPIFY ]; then # has escapify 218 | set -x 219 | ssh -Y -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$(escapify "$@")" 220 | set +x 221 | else 222 | set -x 223 | ssh -Y -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$@" 224 | set +x 225 | fi 226 | } 227 | 228 | # to enable later: shell w/o forced pseudoterminal allocation 229 | shellnt() { 230 | if [ $HAS_ESCAPIFY -eq 0 ] && [ -z $NO_ESCAPIFY ]; then # has escapify 231 | set -x 232 | ssh -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$(escapify "$@")" 233 | set +x 234 | else 235 | set -x 236 | ssh -p"$LOCALPORT" "$DEVICE_USER""@""$IPADDR" "$@" 237 | set +x 238 | fi 239 | } 240 | 241 | killServer() { 242 | # Only POSIX way to get PID's of processes by name that I can find. 243 | # pidof is not POSIX, nor is pkill, nor is killall. 244 | # this makes a space-separated list of PID's to kill, and then pumps them 245 | # through xargs to kill. Since POSIX kill does not explicitly say that 246 | # killing multiple PID's with one command is supported, I run it once for 247 | # each PID in the list. 248 | # I am 99.9% sure this sed usage is POSIX, but I'm not very good at 249 | # regexes. 250 | echo "Killing all instances of ""$IPROXY_PROG""..." 251 | PIDS=`ps -o pid,comm -u "$USER" | grep "$IPROXY_PROG" | sed 's/\s.*$//' | tr '\n' ' '` 252 | echo "$PIDS" | xargs -n 1 kill 253 | echo "Done." 254 | } 255 | 256 | install() { 257 | if [ "$SSH_PREFERRED" -ne 0 ]; then 258 | FILENAME=`basename "$1"` 259 | echo 'Pushing '"$1"' to the iDevice...' 260 | push "$1" '/private/var/'"$DEVICE_USER"'/'"$FILENAME" 261 | echo 'Installing '"$FILENAME"' using '"$REMOTE_IPA_INSTALL_PROG"'... Please wait.' 262 | shell "$REMOTE_IPA_INSTALL_PROG" "/private/var/""$DEVICE_USER""/""$FILENAME" 263 | echo "Install program finished, deleting the IPA from the iDevice..." 264 | shell rm '/private/var/'"$DEVICE_USER"'/'"$FILENAME" 265 | else 266 | # use ideviceinstaller and obviate the need for ssh availability. 267 | # nice for idevices that don't have sshd started at boot, like modern 268 | # tethered ones (poor things). 269 | "$IDEVICEINSTALLER" -u "$UDID" -i "$1" 270 | fi 271 | echo "Done." 272 | } 273 | 274 | remove() { 275 | # todo: try to implement an ssh version of this command? 276 | "$IDEVICEINSTALLER" -u "$UDID" -U "$1" 277 | } 278 | 279 | forward() { 280 | if [ ! "$#" -eq 2 ]; then 281 | echo "forward: error: Need two args, but was given ""$#""." 1>&2 282 | echo "Syntax:" 1>&2 283 | echo " forward [local port] [remote port]" 1>&2 284 | else 285 | echo "Attempting to forward between local port ""$1"" & remote port ""$2""." 286 | echo "Use ^C to close the connection (it runs in the foreground.)" 287 | "$IPROXY_PROG" "$@" 288 | fi 289 | 290 | } 291 | 292 | listPackages() { 293 | # last arg flag takes precedence, ignore all others 294 | # default is to list user apps only 295 | MODE='list_user' 296 | # confusing syntax, but i'm saying "while $1 starts with a dash" 297 | while case $1 in -*) true;; *) false;; esac; do 298 | case $1 in 299 | -a) #list all packages 300 | MODE='list_all' 301 | ;; 302 | -u) 303 | MODE='list_user' 304 | ;; 305 | -s) 306 | MODE='list_system' 307 | ;; 308 | -x) 309 | MODE='xml' 310 | ;; 311 | *) 312 | echo "Warning: Unknown parameter passed to listPackages: ""$1" 1>&2 313 | ;; 314 | esac 315 | shift 316 | done 317 | if [ -n "$1" ]; then 318 | "$IDEVICEINSTALLER" -u "$UDID" -l -o "$MODE" | grep '^'"$1"',' 319 | else 320 | "$IDEVICEINSTALLER" -u "$UDID" -l -o "$MODE" 321 | fi 322 | } 323 | 324 | mountimg() { 325 | # if not already mounted, try to mount 326 | "$IDEVICEIMAGEMOUNTER" -u "$UDID" -l | grep ImagePresent | grep -q false 327 | if [ "$?" -eq 0 ]; then 328 | if [ -n "$DEVDISKIMG" ]; then 329 | "$IDEVICEIMAGEMOUNTER" -u "$UDID" "$DEVDISKIMG" "$DEVDISKIMG"'.signature' 330 | fi 331 | fi 332 | } 333 | 334 | debug() { # uses idevicedebug 335 | # idevicedebug [OPTIONS] COMMAND 336 | # command: 337 | # run BUNDLEID [ARGS...] run app BUNDLEID with optional ARGS on the device. 338 | # options: 339 | # -e, --env NAME=VALUE set environment variable NAME to VALUE 340 | # -d, --debug enable communication debugging 341 | mountimg 342 | "$IDEVICEDEBUG" -u "$UDID" run "$@" 343 | } 344 | 345 | screenshot() { 346 | mountimg 347 | # outputs a tiff image 348 | "$IDEVICESCREENSHOT" -u "$UDID" "$@" 349 | } 350 | 351 | syslog() { 352 | "$IDEVICESYSLOG" -u "$UDID" "$@" 353 | } 354 | 355 | logcat() { # alias for syslog (not 'debug') 356 | syslog "$@" 357 | } 358 | 359 | experimentalFeatureMsg() { 360 | echo " - Experimental, on account of requiring pre-configuration in the sparsely" 361 | echo " documented and highly volatile 'idb.conf' file for mounting an iOS" 362 | echo " developer disk image. Some brief explanation is in the README as well as" 363 | echo " an example file." 364 | } 365 | 366 | # Printed when no command is given as a memory jogger. 367 | # 'usageHelp' contains the full descriptions. 368 | usageHelpBrief() { 369 | echo "Abridged usage:" 370 | echo " ""`basename "$0"`"" push [target] [destination]" 371 | echo " ""`basename "$0"`"" pull [target] [destination]" 372 | echo " ""`basename "$0"`"" shell" 373 | echo " ""`basename "$0"`"" shell [command]" 374 | echo " ""`basename "$0"`"" forward [local] [remote]" 375 | echo " ""`basename "$0"`"" install [target]" 376 | echo " ""`basename "$0"`"" uninstall [appid]" 377 | echo " ""`basename "$0"`"" list-packages [-a|-u|-s|-x] [appid]" 378 | echo " ""`basename "$0"`"" devices" 379 | echo " ""`basename "$0"`"" list" 380 | echo " ""`basename "$0"`"" kill-server" 381 | echo " ""`basename "$0"`"" help" 382 | echo "To see more detailed descriptions of these commands, as well as more options," 383 | echo "run '""`basename "$0"`"" help'." 384 | } 385 | 386 | usageHelp() { 387 | echo "Usage:" 388 | echo "`basename "$0"`"" [-u ] command [options]" 389 | echo " -u can optionally be placed before most commands to make them operate" 390 | echo ' on the device with the given UDID (check with "'"`basename "$0"`"' devices" or "'"`basename "$0"`"' list").' 391 | echo '' 392 | echo "Detailed descriptions:" 393 | echo "`basename "$0"`"" push [target] [destination]" 394 | echo " Copies a targeted file on the computer to a destination on the iDevice." 395 | echo "`basename "$0"`"" pull [target] [destination]" 396 | echo " Copies a targeted file from the iDevice to a destination on the computer." 397 | echo "`basename "$0"`"" shell" 398 | echo " Starts a remote shell on the iDevice." 399 | echo "`basename "$0"`"" shell [command]" 400 | echo " Starts a remote shell on the iDevice, runs the given command, and exits." 401 | echo "`basename "$0"`"" forward [local] [remote]" 402 | echo " Forwards socket connections (currently only TCP ports are tested)." 403 | echo " Unlike ADB, [local] and [remote] here should be integer values. That is," 404 | echo " 'tcp:25565', the ADB syntax, would be just '25565' here." 405 | echo "`basename "$0"`"" syslog [options]" 406 | echo " Display the iDevice's system message log. See the documentation for" 407 | echo " libimobiledevice's idevicesyslog for a full description of options." 408 | echo "`basename "$0"`"" debug [application bundle identifier]" 409 | echo " [EXPERIMENTAL] View the diagnostics log of the specified application." 410 | experimentalFeatureMsg 411 | echo "`basename "$0"`"" screenshot [options] [output_filename]" 412 | echo " [EXPERIMENTAL] Take a screenshot of the display on a connected" 413 | echo " iDevice. 'output_filename' is optional; if not given a screenshot will be" 414 | echo " written to a file in 'screenshot-DATE' format." 415 | echo " Options are also, of course, optional. See the 'idevicescreenshot'" 416 | echo " documentation for a list of options." 417 | experimentalFeatureMsg 418 | echo "`basename "$0"`"" install [target]" 419 | if [ "$SSH_PREFERRED" -ne 0 ]; then 420 | echo " Installs the indicated target IPA on the iDevice using ""$REMOTE_IPA_INSTALL_PROG""." 421 | echo " Will need modification to work with other CLI IPA installer programs" 422 | echo " (which need to be installed on the iDevice itself via Cydia or similar)." 423 | else 424 | echo ' Installs the indicated target IPA on the iDevice using '"$IDEVICEINSTALLER" 425 | echo ' from libimobiledevice. '"$IDEVICEINSTALLER"' must of course be installed' 426 | echo ' on this computer for this to work.' 427 | fi 428 | echo "`basename "$0"`"" uninstall [appid]" 429 | echo " Try to remove the app with the given ID from the iDevice. May fail on" 430 | echo " system apps." 431 | echo "`basename "$0"`"" remove [appid]" 432 | echo " Synonym for "'`uninstall`'"." 433 | echo "`basename "$0"`"" list-packages [-a|-u|-s|-x] [appid]" 434 | echo " Lists packages installed on the iDevice. Has several optional flags:" 435 | echo " -a: List all packages on the iDevice." 436 | echo " -u: List user packages on the iDevice (default)." 437 | echo " -s: List system packages on the iDevice." 438 | echo " -x: List all packages on the iDevice in XML format." 439 | echo " If 'appid' is specified, only that package's information is displayed." 440 | echo " This is incompatible with -x (for now at least)." 441 | echo "`basename "$0"`"" devices" 442 | echo " Lists the UDID's of all connected devices. Part of preliminary" 443 | echo " (incomplete but in progress) support for multi-device capability." 444 | echo "`basename "$0"`"" list" 445 | echo " Synonym for '""`basename "$0"`"" devices'." 446 | echo "`basename "$0"`"" kill-server" 447 | echo " Kills all instances of ""$IPROXY_PROG"", the TCP-over-usbmuxd program." 448 | echo "`basename "$0"`"" help" 449 | echo " Show this usage information." 450 | echo "`basename "$0"`"" -h" 451 | echo " Synonym for '""`basename "$0"`"" help'." 452 | echo "`basename "$0"`"" --help" 453 | echo " Synonym for '""`basename "$0"`"" help'." 454 | echo 455 | echo "Some configuration inside this script's source may be required." 456 | echo "Open this program in a text editor for more documentation." 457 | } 458 | 459 | case "$1" in 460 | # push 461 | "push") 462 | startRelay 463 | shift 1 # strip first argument (i.e., "push"), leaving only remaining args 464 | push "$1" "$2" 465 | ;; 466 | # pull 467 | "pull") 468 | startRelay 469 | shift 1 470 | pull "$1" "$2" 471 | ;; 472 | # shell with forced pseudoterminal allocation (works for ncurses programs) 473 | "shell") 474 | startRelay 475 | shift 1 476 | shell "$@" 477 | ;; 478 | # shell without pseudoterminal allocation 479 | "shellnt") 480 | startRelay 481 | shift 1 482 | shellnt "$@" 483 | ;; 484 | "forward") 485 | shift 1 486 | forward "$@" 487 | ;; 488 | "install") 489 | if [ "$SSH_PREFERRED" -ne 0 ]; then 490 | startRelay 491 | fi 492 | shift 1 493 | install "$1" 494 | ;; 495 | # uninstall (remove) 496 | "uninstall"|"remove") 497 | shift 1 498 | remove "$1" 499 | ;; 500 | # list-packages 501 | "list-packages") 502 | shift 1 503 | listPackages "$@" 504 | ;; 505 | # list (devices) 506 | "list"|"devices") 507 | listDevices 508 | ;; 509 | # debug (logcat) 510 | "debug"|"logcat") 511 | shift 512 | debug "$@" 513 | ;; 514 | # screenshot 515 | "screenshot") 516 | shift 517 | screenshot "$@" 518 | ;; 519 | "syslog") 520 | shift 521 | syslog "$@" 522 | ;; 523 | # kill-server 524 | "kill-server") 525 | killServer 526 | ;; 527 | # help 528 | "help"|"-help"|"--help"|"-h") 529 | usageHelp 530 | ;; 531 | *) 532 | if [ "$#" -gt 0 ]; then # If we were given a command, but it was incorrect 533 | # The fallback case should dump the help text to stderr rather than stdout. 534 | echo "`basename "$0"`"": Error: '""$1""' is not a valid command." 1>&2 535 | echo "Please note that not all 'adb'commands have exact analogues in this" 1>&2 536 | echo "script." 1>&2 537 | else 538 | echo "`basename "$0"`"": Error: No command given." 1>&2 539 | usageHelpBrief 1>&2 540 | fi 541 | ;; 542 | esac 543 | 544 | # clean-up 545 | kill "$IPROXYPID" 2>/dev/null # not all commands need to use iproxy, but for those that do we need to make sure it's closed. 546 | true #return 0 547 | --------------------------------------------------------------------------------