├── pgcheck.gif ├── postinst ├── control ├── Library └── LaunchDaemons │ └── sh.load.pgcheck.plist ├── README.md └── usr └── bin └── pgcheck /pgcheck.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z448/pgcheck/HEAD/pgcheck.gif -------------------------------------------------------------------------------- /postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | launchctl unload -w /Library/LaunchDaemons/sh.load.pgcheck.plist 4 | launchctl load -w /Library/LaunchDaemons/sh.load.pgcheck.plist 5 | pgcheck i 6 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Name: pgcheck 2 | Version: 2.7-1 3 | Author: zed448 4 | Architecture: iphoneos-arm 5 | Package: pgcheck 6 | Section: Utility 7 | Maintainer: zed448 8 | Homepage: http://load.sh 9 | Description: check if device contains Pegasus Spyware files 10 | Depends: perl, libactivator, com.ericasadun.utilities, mobilesubstrate 11 | 12 | -------------------------------------------------------------------------------- /Library/LaunchDaemons/sh.load.pgcheck.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Label 7 | sh.load.pgcheck 8 | 9 | Program 10 | /usr/bin/pgcheck 11 | 12 | RunAtLoad 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Article](https://info.lookout.com/rs/051-ESQ-475/images/lookout-pegasus-technical-analysis.pdf) didn't reveal full contents of spyware files so only way to check for presence of these files was to check/compare filenames. But it's unlikely that now after such news coverage they would use same filenames. So instead pgcheck searches for one main Pegasus file which can find without knowing it's name as it's copy of signed binary in iOS (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc). Seems 'jsc' has been removed from 9.3.5. 2 | 3 | *update v2.8-7* 4 | - pgcheck is now comparing files to 'jsc' binary which Pegasus copies into `/usr/libexec/` directory. This copy is used to start Pegasus daemons when user restarts device 5 | - removed deleting of files as modified spyware could use different names 6 | - log will be written into `/var/mobile/Documents/pgcheck.log` after triggering notification + airplane mode 7 | 8 | *update v2.7-1* - ~~`pgcheck` will now also remove Pegasus files~~, then trigger alert + airplane mode 9 | 10 | # pgcheck 11 | check if device contains Pegasus Spyware files reported in https://info.lookout.com/rs/051-ESQ-475/images/lookout-pegasus-technical-analysis.pdf 12 | 13 | # INSTALLATION 14 | add Cydia repository http://load.sh/cydia/ 15 | search for pgcheck 16 | 17 | # or 18 | 19 | clone this repository and use `pgcheck` as root 20 | 21 | ```bash 22 | git clone https://github.com/z448/pgcheck 23 | cd pgcheck/usr/bin 24 | pgcheck i 25 | # respring device 26 | pgcheck 27 | ``` 28 | 29 | To check if `pgcheck` is running 30 | ```bash 31 | #switch to root 32 | su root 33 | ps -ef | grep pgcheck 34 | ``` 35 | 36 | 37 | 38 | 39 | # INFO 40 | Perl process 'pgcheck' will run in background, if any of reported files apears, it'll delete them, trigger activator notification and turn on airplane mode. 41 | 42 | After rebooting iDevice, LaunchDaemon (/Library/LaunchDaemons/sh.load.pgcheck.plist) will start pgcheck automatically. 43 | 44 | # GIF 45 | 46 | ![pgcheck](https://raw.githubusercontent.com/z448/pgcheck/master/pgcheck.gif) 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /usr/bin/pgcheck: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/perl 2 | 3 | # detect Pegasus spyware in iOS 4 | # https://info.lookout.com/rs/051-ESQ-475/images/lookout-pegasus-technical-analysis.pdf 5 | 6 | use warnings; 7 | use strict; 8 | 9 | use POSIX qw(setsid); 10 | use File::Find; 11 | use File::Compare; 12 | 13 | my $log = "/var/mobile/Documents/pgcheck.log"; 14 | my $jsc = "/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc"; 15 | my $activator_cache = "/var/mobile/Library/Caches/libactivator.plist"; 16 | 17 | if($ARGV[0]){ init(); exit; } 18 | 19 | sub init_activator { 20 | unless(`activator get LAMessageListeners`){ 21 | system("plutil -key LAMessageListeners -dict $activator_cache"); 22 | } 23 | system("plutil -key LAMessageListeners -key libactivator.message.show.PGCHECK -dict $activator_cache"); 24 | system("plutil -key LAMessageListeners -key libactivator.message.show.PGCHECK -key title -value pgcheck $activator_cache"); 25 | system("plutil -key LAMessageListeners -key libactivator.message.show.PGCHECK -key message -value \"Your device might contain Pegasus Spyware\" $activator_cache"); 26 | } 27 | 28 | sub init { 29 | my $init_pgcheck = `activator get LAMessageListeners | grep pgcheck`; 30 | unless($init_pgcheck){ 31 | init_activator(); 32 | } 33 | } 34 | 35 | sub daemonize { 36 | no strict 'subs'; 37 | chdir '/' or die "Can't chdir to /: $!"; 38 | open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; 39 | open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; 40 | defined(my $pid = fork) or die "Can't fork: $!"; 41 | exit if $pid; 42 | setsid or die "Can't start a new session: $!"; 43 | open STDERR, '>&STDOUT' or die "Can't dup stdout: $!"; 44 | } 45 | 46 | daemonize(); 47 | 48 | sub start { 49 | find( sub{ if( my $compare = compare("$File::Find::dir/$_", $jsc) == 0 ){ 50 | open(my $fh,">>", $log) || die "cant open $log"; 51 | print $fh '[pgcheck] ' . localtime() . ': ' . "$File::Find::dir/$_ is a copy of $jsc\n"; 52 | close $fh; 53 | system("activator send libactivator.message.show.PGCHECK"); 54 | system("activator send switch-on.com.a3tweaks.switch.airplane-mode"); 55 | sleep(300); 56 | }}, qw< /usr/sbin /usr/libexec >); 57 | } 58 | 59 | while( 1 ){ 60 | sleep 1; 61 | start(); 62 | } 63 | --------------------------------------------------------------------------------