diff --git a/AppDelegate.m b/AppDelegate.m index 9cdd891..a73dc98 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -20,7 +20,9 @@ //TODO: autolayout vertically //TODO: show 'from where' via quarantine attrz or database!! (simon email) //TODO: detect as procs die via GCD (simon blog post) - +//TODO: missing icon (128) - new icon? +//TODO: exception handling for mutated array! +//TODO: check for "Apple Mac OS Application Signing" for Apple Apps - and add to 'OBJ-See' TODO doc @implementation AppDelegate diff --git a/Consts.h b/Consts.h index 64d874c..7e68dfe 100644 --- a/Consts.h +++ b/Consts.h @@ -332,4 +332,7 @@ //hotkey 'i' #define KEYCODE_I 0x22 +//unknown task +#define TASK_PATH_UNKNOWN @"" + #endif diff --git a/InfoWindowController.m b/InfoWindowController.m index b4c9046..b711938 100644 --- a/InfoWindowController.m +++ b/InfoWindowController.m @@ -95,6 +95,9 @@ // ->when showing info about a task Task* task = nil; + //task arguments + NSMutableString* taskArguments = nil; + //binary // ->when showing info about a dylib Binary* dylib = nil; @@ -136,9 +139,32 @@ [((Task*)self.itemObj) getArguments]; } - //set args - [self.arguments setStringValue:[self valueForStringItem:[task.arguments componentsJoinedByString:@""] default:@"no arguments/unknown"]]; - + //set default value for args + self.arguments.stringValue = @"no arguments/unknown"; + + //set any args + // ->task path and name, make up the first to 'args', so skip/ignore those + if(task.arguments.count > 2) + { + //alloc string to build up args + taskArguments = [NSMutableString string]; + + //build up args string + // ->start at index 2, to skip path/name + for(NSUInteger index = 2; index +#import +#import #import +//socket states +// ->note, index correspondes to numberic value +static const char* socketStates[] = +{ + "closed", + "listening", + "syn sent", + "syn received", + "established", + "close/wait", + "fin wait 1", + "closing", + "last act", + "fin wait 2", + "time wait", +}; + +static const char *socketFamilies[] = +{ + "AF_UNSPEC", + "AF_UNIX", + "AF_INET", + "AF_IMPLINK", + "AF_PUP", + "AF_CHAOS", + "AF_NS", + "AF_ISO", + "AF_ECMA", + "AF_DATAKIT", + "AF_CCITT", + "AF_SNA", + "AF_DECnet", + "AF_DLI", + "AF_LAT", + "AF_HYLINK", + "AF_APPLETALK", + "AF_ROUTE", + "AF_LINK", + "#define", + "AF_COIP", + "AF_CNT", + "pseudo_AF_RTIP", + "AF_IPX", + "AF_SIP", + "pseudo_AF_PIP", + "pseudo_AF_BLUE", + "AF_NDRV", + "AF_ISDN", + "pseudo_AF_KEY", + "AF_INET6", + "AF_NATM", + "AF_SYSTEM", + "AF_NETBIOS", + "AF_PPP", + "pseudo_AF_HDRCMPLT", + "AF_RESERVED_36", +}; +#define SOCKET_FAMILY_MAX (int)(sizeof(socketFamilies)/sizeof(char *)) + + + @interface Connection : ItemBase { diff --git a/Items/Connection.m b/Items/Connection.m index 19a2127..ae3c957 100644 --- a/Items/Connection.m +++ b/Items/Connection.m @@ -39,16 +39,16 @@ self.remotePort = params[KEY_REMOTE_PORT]; //extract/save type - self.type = params[KEY_SOCKET_TYPE]; + self.type = [self socketType2String:params[KEY_SOCKET_TYPE]]; //extract/save family - self.family = params[KEY_SOCKET_FAMILY]; + self.family = [self socketFamily2String:params[KEY_SOCKET_FAMILY]]; //extract/save proto - self.proto = params[KEY_SOCKET_PROTO]; + self.proto = [self socketProto2String:params[KEY_SOCKET_PROTO]]; //extract/save state - self.state = params[KEY_SOCKET_STATE]; + self.state = [self socketState2String:params[KEY_SOCKET_STATE]]; //set icon [self setConnectionIcon]; @@ -104,13 +104,10 @@ //set self.icon = [NSImage imageNamed:@"closedIcon"]; } - - //by design, other connection states won't have an icon - // TODO: maybe add other icons? } //set icon for UDP sockets - // ->can't listen, so just show em as streaming + // ->can't listen, so just show 'em as streaming else if(YES == [self.type isEqualToString:@"SOCK_DGRAM"]) { //set @@ -137,6 +134,122 @@ return; } +//convert a socket type into string +-(NSString*) socketType2String:(NSNumber*)type +{ + //socket type + NSString* socketType = nil; + + //convert + switch(type.intValue) + { + //stream + case SOCK_STREAM: + socketType = @"SOCK_STREAM"; + break; + + //dgram + case SOCK_DGRAM: + socketType = @"SOCK_DGRAM"; + break; + + //raw + case SOCK_RAW: + socketType = @"SOCK_RAW"; + break; + + //rdm + case SOCK_RDM: + socketType = @"SOCK_RDM"; + break; + + //seq packet + case SOCK_SEQPACKET: + socketType = @"SOCK_SEQPACKET"; + break; + + default: + break; + } + + return socketType; +} + + +//convert a socket family into string +-(NSString*) socketFamily2String:(NSNumber*)family +{ + //socket family + NSString* socketFamily = nil; + + //sanity check + if( (family.intValue < 0) || + (family.intValue >= SOCKET_FAMILY_MAX) ) + { + //bail + goto bail; + } + + //init socket family string + socketFamily = [NSString stringWithUTF8String:socketFamilies[family.intValue]]; + +//bail +bail: + + return socketFamily; +} + +//convert a socket protocol into string +-(NSString*) socketProto2String:(NSNumber*)proto +{ + //socket proto + NSString* socketProto = nil; + + //proto struct + struct protoent *protoInfo = NULL; + + //get proto info + protoInfo = getprotobynumber(proto.intValue); + + //sanity check + if(NULL == protoInfo) + { + //bail + goto bail; + } + + //init proto string + // ->name comes from struct + socketProto = [NSString stringWithUTF8String:protoInfo->p_name]; + +//bail +bail: + + return socketProto; +} + + +//convert a socket state into string +-(NSString*) socketState2String:(NSNumber*)state +{ + //socket proto + NSString* socketState = nil; + + //set state + if(state.intValue < TCP_NSTATES) + { + //set state + socketState = [NSString stringWithUTF8String:socketStates[state.intValue]]; + } + //invalid/unknown socket state + else + { + socketState = [NSString stringWithFormat:@"unknown state (%d)", state.intValue]; + } + + return socketState; +} + //build printable connection string -(void)setConnectionString { diff --git a/Task.h b/Task.h index 9cc8df7..7878e75 100644 --- a/Task.h +++ b/Task.h @@ -57,13 +57,12 @@ struct dyld_image_info_32 { //connections @property(nonatomic, retain)NSMutableArray* connections; +//uid @property uid_t uid; + +//parent's pid @property (nonatomic, retain)NSNumber* ppid; - -//signing info -//@property(nonatomic, retain)NSDictionary* signingInfo; - //children @property (nonatomic, retain)NSMutableArray* children; @@ -72,7 +71,11 @@ struct dyld_image_info_32 { //init w/ a pid + path // note: icons are dynamically determined only when process is shown in alert --(id)initWithPID:(NSNumber*)taskPID andPath:(NSString*)taskPath; +-(id)initWithPID:(NSNumber*)taskPID; + +//get task's path +// ->via 'proc_pidpath()' or via task's args ('KERN_PROCARGS2') +-(NSString*)getPath; //get command-line args -(void)getArguments; diff --git a/Task.m b/Task.m index b58671c..790aa6b 100644 --- a/Task.m +++ b/Task.m @@ -14,20 +14,10 @@ #import "AppDelegate.h" #import "remoteTaskService.h" -#import -#import -#import -#import -#import -#import -#import +#import +#import #import #import -#import -#import -#import -#import - @implementation Task @@ -41,10 +31,14 @@ @synthesize arguments; @synthesize connections; -//init w/ a pid + path +//init w/ a pid // note: time consuming init's are done in other methods --(id)initWithPID:(NSNumber*)taskPID andPath:(NSString*)taskPath +-(id)initWithPID:(NSNumber*)taskPID { + //task's path + // ->not iVar, as assigned into task's binary obj + NSString* taskPath = nil; + //existing binaries NSMutableDictionary* existingBinaries = nil; @@ -81,9 +75,16 @@ //get parent id self.ppid = [NSNumber numberWithInteger:getParentID([taskPID intValue])]; + //get task's path + taskPath = [self getPath]; + //try extract existing binary - // ->will succeed for multiple instances of the same task (process) - existingBinary = existingBinaries[taskPath]; + // ->but only if task's path is known + if(YES != [taskPath isEqualToString:TASK_PATH_UNKNOWN]) + { + //lookup + existingBinary = existingBinaries[taskPath]; + } //re-use existing binaries if(nil != existingBinary) @@ -128,179 +129,122 @@ bail: return self; } -//get command-line args --(void)getArguments +//get task's path +// ->via 'proc_pidpath()' or via task's args (via XPC) if that fails... +-(NSString*)getPath { - //'management info base' array - int mib[3] = {0}; + //task path + NSString* taskPath = nil; - //system's size for max args - int systemMaxArgs = 0; + //buffer for process path + char pathBuffer[PROC_PIDPATHINFO_MAXSIZE] = {0}; - //process's args - char* processArgs = NULL; + //status + int status = -1; - //# of args - int numberOfArgs = 0; + //reset buffer + bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE); - //start of (each) arg - char* argStart = NULL; - - //size of buffers, etc - size_t size = 0; - - //parser pointer - char *parser; - - //init mib - // ->want system's size for max args - mib[0] = CTL_KERN; - mib[1] = KERN_ARGMAX; - - //first time - // ->alloc array for args - if(nil == self.arguments) + //kernel 'task' is special + if(0 == self.pid.intValue) { - //alloc - arguments = [NSMutableArray array]; - } - - //set size - size = sizeof(systemMaxArgs); - - //get system's size for max args - if(-1 == sysctl(mib, 2, &systemMaxArgs, &size, NULL, 0)) - { - //bail - goto bail; - } - - //alloc space for args - processArgs = malloc(systemMaxArgs); - if(NULL == processArgs) - { - //bail - goto bail; - } - - //init mib - // ->want process args - mib[0] = CTL_KERN; - mib[1] = KERN_PROCARGS2; - mib[2] = [self.pid intValue]; - - //set size - size = (size_t)systemMaxArgs; - - //get process's args - if(-1 == sysctl(mib, 3, processArgs, &size, NULL, 0)) - { - //bail - goto bail; - } - - //extract number of args - // ->at start of buffer - memcpy(&numberOfArgs, processArgs, sizeof(numberOfArgs)); - - //skip procs w/ no args - // ->note: don't care about arg[0] - if(numberOfArgs < 2) - { - //no args - goto bail; - } - - //init point to start of args - // ->they start right after # of args - parser = processArgs + sizeof(numberOfArgs); - - //skip over exe name - // ->always at front, yes, even before arg[0] (which is also exe name) - while(parser < &processArgs[size]) - { - //scan till NULL-terminator - if(0x0 == *parser) - { - //end of exe name - break; - } + //set + taskPath = path2Kernel(); - //next char - parser++; - } - - //sanity check - // ->make sure end-of-buffer wasn't reached - if(parser == &processArgs[size]) - { - //bail + //all set goto bail; } - //skip all trailing NULLs - // ->scan will non-NULL is found - while(parser < &processArgs[size]) + //get task's path via 'proc_pidpath()' + // ->this might fail, so will then attempt via task's args ('KERN_PROCARGS2') + status = proc_pidpath(self.pid.intValue, pathBuffer, sizeof(pathBuffer)); + if(0 != status) { - //scan till NULL-terminator - if(0x0 != *parser) - { - //ok, got to argv[0] - break; - } + //init task's name + taskPath = [NSString stringWithUTF8String:pathBuffer]; + } + //try via task's args (via XPC) + else + { + //grab args + // ->set's 'arguments' iVar + [self getArguments]; - //next char - parser++; - } - - //sanity check - // ->(again), make sure end-of-buffer wasn't reached - if(parser == &processArgs[size]) - { - //bail - goto bail; - } - - //keep scanning until all args are found - // ->each is NULL-terminated - while(parser < &processArgs[size]) - { - //bail if we've hit arg cnt - // ->note: don't save arg[0], so add 1 - if(self.arguments.count + 1 == numberOfArgs) + //sanity check + if( (nil == self.arguments) || + (0 == self.arguments.count) ) { //bail - break; + goto bail; } - //each arg is NULL-terminated - if(*parser == '\0') - { - //save arg - // ->'argStart' is purposely NULL for argv[0] - if(NULL != argStart) - { - [self.arguments addObject:[NSString stringWithUTF8String:argStart]]; - } - - //init string pointer to (possibly) next arg - argStart = ++parser; - } - - //next char - parser++; + //arg[0] should be full path + taskPath = self.arguments.firstObject; } //bail bail: - //free process args - if(NULL != processArgs) + //when task path is still nil + // ->set to const for unknown path... + if(nil == taskPath) { - //free - free(processArgs); + //set + taskPath = TASK_PATH_UNKNOWN; } + return taskPath; +} + +//get command-line args via XPC request to remote service +// ->waits for XPC, then sets 'arguments' iVar +-(void)getArguments +{ + //xpc connection + __block NSXPCConnection* xpcConnection = nil; + + //wait semaphore + dispatch_semaphore_t waitSema = nil; + + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; + + //set classes + // ->arrays & strings are what is ok to vend + [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSString class], nil] + forSelector: @selector(getTaskArgs:withReply:) argumentIndex: 0 ofReply: YES]; + + //resume + [xpcConnection resume]; + + //init wait semaphore + waitSema = dispatch_semaphore_create(0); + + //invoke XPC service (running as r00t) + // ->will enumerate files, then invoke reply block so can save into iVar + [[xpcConnection remoteObjectProxy] getTaskArgs:self.pid withReply:^(NSMutableArray* taskArguments) + { + //close connection + [xpcConnection invalidate]; + + //nil out + xpcConnection = nil; + + //grab array + self.arguments = taskArguments; + + //signal sema + dispatch_semaphore_signal(waitSema); + + }]; + + //wait until XPC is done + // ->XPC reply block will signal semaphore + dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); + return; } @@ -328,7 +272,7 @@ bail: xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; //set classes - // ->arrays & strings are what is ok to vend + // ->arrays, dictionaries, & strings are what is ok to vend [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] forSelector: @selector(enumerateDylibs:withReply:) @@ -359,7 +303,8 @@ bail: //add all dylibs for(NSString* dylibPath in dylibPaths) { - //skip main image + //skip main executable image + //TODO: also check realpath() or obj-c equiv! if(YES == [dylibPath isEqualToString:self.binary.path]) { //skip @@ -412,7 +357,7 @@ bail: //add to task's dylibs [self.dylibs addObject:dylib]; - } //all dylibs + }//all dylibs //sort by name self.dylibs = [[self.dylibs sortedArrayUsingComparator:^NSComparisonResult(id a, id b) @@ -488,7 +433,7 @@ bail: xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; //set classes - // ->arrays & strings are what is ok to vend + // ->arrays, dictionaries, & strings are what is ok to vend [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] forSelector: @selector(enumerateFiles:withReply:) diff --git a/TaskEnumerator.m b/TaskEnumerator.m index 2db2c8a..d06b2e9 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -250,6 +250,9 @@ //task Task* task = nil; + //status + int status = -1; + //alloc/init list allTasks = [[OrderedDictionary alloc] init]; @@ -259,18 +262,9 @@ //array of pids pid_t* pids = NULL; - //buffer for process path - char pathBuffer[PROC_PIDPATHINFO_MAXSIZE] = {0}; - - //status - int status = -1; - //process ID NSNumber* processID = nil; - //process name - NSString* processName = nil; - //get # of procs numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); @@ -299,30 +293,26 @@ continue; } - //reset buffer - bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE); - //init process ID processID = [NSNumber numberWithInt:pids[i]]; - //get path - status = proc_pidpath(pids[i], pathBuffer, sizeof(pathBuffer)); - - //sanity check - // ->this generally just fails if process has exited.... - if( (status < 0) || - (0 == strlen(pathBuffer)) ) + //ignore procs that have exited + if(YES != isAlive(pids[i])) { //skip continue; } - - //init process name - processName = [NSString stringWithUTF8String:pathBuffer]; //init task - // ->pass in pid and name - task = [[Task alloc] initWithPID:processID andPath:processName]; + // ->pass in pid + task = [[Task alloc] initWithPID:processID]; + + //again, ignore procs that have exited + if(YES != isAlive(pids[i])) + { + //skip + continue; + } //add task to list // ->order by pid for now @@ -331,7 +321,7 @@ //always add kernel's task // ->hardcoded pid (0) and path to kernel - task = [[Task alloc] initWithPID:@0 andPath:path2Kernel()]; + task = [[Task alloc] initWithPID:@0]; //add kernel task [allTasks setObject:task forKey:@0]; diff --git a/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xcscmblueprint b/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xcscmblueprint new file mode 100644 index 0000000..b0bb6fb --- /dev/null +++ b/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "61F07AFB33748EF0C810BEEF6126283DAC63A899", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "61F07AFB33748EF0C810BEEF6126283DAC63A899" : 0, + "7564E3FECD3AE625755C217749E31B3EE2B69E20" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "FE4103FE-6F26-4639-8C9F-D8D32C76D6A9", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "61F07AFB33748EF0C810BEEF6126283DAC63A899" : "TaskExplorer", + "7564E3FECD3AE625755C217749E31B3EE2B69E20" : "machO\/" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "TaskExplorer", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "TaskExplorer.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/bitbucket.org\/objective-see\/taskexplorer.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "61F07AFB33748EF0C810BEEF6126283DAC63A899" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/bitbucket.org\/objective-see\/macho.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "7564E3FECD3AE625755C217749E31B3EE2B69E20" + } + ] +} \ No newline at end of file diff --git a/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate b/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..cf288de Binary files /dev/null and b/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Utilities.m b/Utilities.m index f8144e7..4391890 100644 --- a/Utilities.m +++ b/Utilities.m @@ -84,7 +84,7 @@ bail: return version; } - +//TODO: calling 'isApple' does this all over again!? //get the signing info of a file NSDictionary* extractSigningInfo(NSString* path) { @@ -115,9 +115,6 @@ NSDictionary* extractSigningInfo(NSString* path) //init signing status signingStatus = [NSMutableDictionary dictionary]; - //signingStatus[KEY_SIGNATURE_STATUS] = @0; - //return signingStatus; - //create static code status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); @@ -624,21 +621,18 @@ pid_t getParentID(int pid) //size size_t procBufferSize = sizeof(processStruct); - //mib - const u_int mibLength = 4; - //syscall result int sysctlResult = -1; //init mib - int mib[mibLength] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; //make syscall - sysctlResult = sysctl(mib, mibLength, &processStruct, &procBufferSize, NULL, 0); + sysctlResult = sysctl(mib, sizeof(mib)/sizeof(*mib), &processStruct, &procBufferSize, NULL, 0); //check if got ppid if( (STATUS_SUCCESS == sysctlResult) && - (0 != procBufferSize) ) + (0 != procBufferSize) ) { //save ppid parentID = processStruct.kp_eproc.e_ppid; @@ -695,15 +689,61 @@ BOOL isAlive(pid_t targetPID) //flag BOOL isAlive = YES; + //reset errno + errno = 0; + + //'management info base' array + int mib[4] = {0}; + + //kinfo proc + struct kinfo_proc procInfo = {0}; + //try 'kill' with 0 - // ->no harm done, but will fail with 'ESRCH' if process is dead! - if( (0 != kill(targetPID, 0)) && - (ESRCH == errno) ) + // ->no harm done, but will fail with 'ESRCH' if process is dead + kill(targetPID, 0); + + //dead proc -> 'ESRCH' + // ->'No such process' + if(ESRCH == errno) { - //alive + //dead isAlive = NO; + + //bail + goto bail; } + //size + size_t size = 0; + + //init mib + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = targetPID; + + //init size + size = sizeof(procInfo); + + //get task's flags + // ->allows to check for zombies + if(0 == sysctl(mib, sizeof(mib)/sizeof(*mib), &procInfo, &size, NULL, 0)) + { + //check for zombies + if(((procInfo.kp_proc.p_stat) & SZOMB) == SZOMB) + { + //dead + isAlive = NO; + + //bail + goto bail; + + } + } + +//bail +bail: + return isAlive; } @@ -903,8 +943,7 @@ BOOL Is32Bit(pid_t targetPID) //bail bail: - return isI386; - + return isI386; } - + diff --git a/images/closeWait.png b/images/closeWait.png new file mode 100644 index 0000000..b540844 Binary files /dev/null and b/images/closeWait.png differ diff --git a/images/closedIcon.png b/images/closedIcon.png new file mode 100644 index 0000000..ede7f45 Binary files /dev/null and b/images/closedIcon.png differ diff --git a/remoteTaskService/remoteTaskService.h b/remoteTaskService/remoteTaskService.h index 4589f82..cb37292 100644 --- a/remoteTaskService/remoteTaskService.h +++ b/remoteTaskService/remoteTaskService.h @@ -6,27 +6,16 @@ // Copyright (c) 2015 Objective-See, LLC. All rights reserved. // -#import #import "serviceInterface.h" +#import + + // This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. @interface remoteTaskService : NSObject - -// +//default service +(remoteTaskService *)defaultService; @end - -//convert a socket type in to string -NSString* socketType2String(int type); - -//convert a socket family into string -NSString* socketFamily2String(int family); - -//convert a socket protocol into string -NSString* socketProto2String(int proto); - -//convert a socket state into string -NSString* socketState2String(int state); diff --git a/remoteTaskService/remoteTaskService.m b/remoteTaskService/remoteTaskService.m index edfe719..348bdfd 100644 --- a/remoteTaskService/remoteTaskService.m +++ b/remoteTaskService/remoteTaskService.m @@ -10,81 +10,12 @@ #import "Utilities.h" #import "remoteTaskService.h" - -#import -#import -#import -#import -#import -#import -#import -#import -#import +#import #import #import -#import -#import -#import - -//socket states -// ->note, index correspondes to numberic value -static const char* socketStates[] = -{ - "closed", - "listening", - "syn sent", - "syn received", - "established", - "close/wait", - "fin wait 1", - "closing", - "last act", - "fin wait 2", - "time wait", -}; - -static const char *socketFamilies[] = -{ - "AF_UNSPEC", - "AF_UNIX", - "AF_INET", - "AF_IMPLINK", - "AF_PUP", - "AF_CHAOS", - "AF_NS", - "AF_ISO", - "AF_ECMA", - "AF_DATAKIT", - "AF_CCITT", - "AF_SNA", - "AF_DECnet", - "AF_DLI", - "AF_LAT", - "AF_HYLINK", - "AF_APPLETALK", - "AF_ROUTE", - "AF_LINK", - "#define", - "AF_COIP", - "AF_CNT", - "pseudo_AF_RTIP", - "AF_IPX", - "AF_SIP", - "pseudo_AF_PIP", - "pseudo_AF_BLUE", - "AF_NDRV", - "AF_ISDN", - "pseudo_AF_KEY", - "AF_INET6", - "AF_NATM", - "AF_SYSTEM", - "AF_NETBIOS", - "AF_PPP", - "pseudo_AF_HDRCMPLT", - "AF_RESERVED_36", -}; -#define SOCKET_FAMILY_MAX (int)(sizeof(socketFamilies)/sizeof(char *)) - +#import +#import +#import //32bit struct dyld_image_info_32 { @@ -105,9 +36,198 @@ struct dyld_image_info_32 { return shared; } +//get task's commandline args +// ->args returned in array arg +-(void)getTaskArgs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply +{ + //task's args + NSMutableArray* arguments = nil; + + //'management info base' array + int mib[3] = {0}; + + //system's size for max args + int systemMaxArgs = 0; + + //process's args + char* taskArgs = NULL; + + //# of args + int numberOfArgs = 0; + + //start of (each) arg + char* argStart = NULL; + + //size of buffers, etc + size_t size = 0; + + //parser pointer + char* parser = NULL; + + //init mib + // ->want system's size for max args + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + //alloc array for args + arguments = [NSMutableArray array]; + + //set size + size = sizeof(systemMaxArgs); + + //get system's size for max args + if(-1 == sysctl(mib, 2, &systemMaxArgs, &size, NULL, 0)) + { + //bail + goto bail; + } + + //alloc space for args + taskArgs = malloc(systemMaxArgs); + if(NULL == taskArgs) + { + //bail + goto bail; + } + + //init mib + // ->want process args + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = taskPID.intValue; + + //set size + size = (size_t)systemMaxArgs; + + //get process's args + if(-1 == sysctl(mib, 3, taskArgs, &size, NULL, 0)) + { + //bail + goto bail; + } + + //sanity check + // ->ensure buffer is somewhat sane + if(size <= sizeof(int)) + { + //bail + goto bail; + } + + //extract number of args + // ->at start of buffer + memcpy(&numberOfArgs, taskArgs, sizeof(numberOfArgs)); + + //extract task's name + // ->follows # of args (int) and is NULL-terminated + [arguments addObject:[NSString stringWithUTF8String:taskArgs + sizeof(int)]]; + + //init point to start of args + // ->they start right after # of args + parser = taskArgs + sizeof(numberOfArgs); + + //scan until end of task's NULL-terminated path + while(parser < &taskArgs[size]) + { + //scan till NULL-terminator + if(0x0 == *parser) + { + //end of exe name + break; + } + + //next char + parser++; + } + + //sanity check + // ->make sure end-of-buffer wasn't reached + if(parser == &taskArgs[size]) + { + //bail + goto bail; + } + + //skip all trailing NULLs + // ->scan will non-NULL is found + while(parser < &taskArgs[size]) + { + //scan till NULL-terminator + if(0x0 != *parser) + { + //ok, got to argv[0] + break; + } + + //next char + parser++; + } + + //sanity check + // ->(again), make sure end-of-buffer wasn't reached + if(parser == &taskArgs[size]) + { + //bail + goto bail; + } + + //parser should now point to argv[0], task name + // ->init arg start + argStart = parser; + + //keep scanning until all args are found + // ->each is NULL-terminated + while(parser < &taskArgs[size]) + { + //each arg is NULL-terminated + // ->so scan till NULL, then save into array + if(*parser == '\0') + { + //save arg + if(NULL != argStart) + { + //save + [arguments addObject:[NSString stringWithUTF8String:argStart]]; + } + + //init string pointer to (possibly) next arg + argStart = ++parser; + + //bail if we've hit arg cnt + // ->note: added full process path as faux arg[0], so add 1 + if(arguments.count == numberOfArgs + 1) + { + //bail + break; + } + } + + //next char + parser++; + } + +//bail +bail: + + //free process args + if(NULL != taskArgs) + { + //free + free(taskArgs); + + //reset + taskArgs = NULL; + } + + //invoke reply block + reply(arguments); + + return; +} + //enumerate dylibs for a specified task // ->dylibs returned in array arg --(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; +-(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply { //dylibs NSMutableArray* dylibPaths = nil; @@ -368,7 +488,7 @@ bail: // ->assume vmmap is 'fat', and exec 32bit version (pre El Capitan) else { - //exec 'file' to get file type + //exec vmmap, but it's i386 version results = [[NSString alloc] initWithData:execTask(ARCH, @[@"-i386", VMMAP, [pid stringValue]]) encoding:NSUTF8StringEncoding]; } } @@ -428,6 +548,12 @@ bail: //remove dups [dylibs setArray:[[[NSSet setWithArray:dylibs] allObjects] mutableCopy]]; + + //TODO: remove + if(pid.intValue == 13084) + { + syslog(LOG_ERR, "TASK-EXPLORER: %s\n", dylibs.description.UTF8String); + } //bail bail: @@ -438,7 +564,7 @@ bail: //enumerate open files // ->accomplish this via lsof, since proc_pidinfo() misses some files... --(void)enumerateFiles:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; +-(void)enumerateFiles:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply { //results NSData* results = nil; @@ -551,7 +677,7 @@ bail: //TODO: soi_rcv/soi_snd to get packets!? //enumerate network connections --(void)enumerateNetwork:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; +-(void)enumerateNetwork:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply { //task's sockets NSMutableArray* sockets = nil; @@ -702,21 +828,21 @@ bail: } //set type - socket[KEY_SOCKET_TYPE] = socketType2String(socketInfo.psi.soi_type); + socket[KEY_SOCKET_TYPE] = [NSNumber numberWithInt:socketInfo.psi.soi_type]; //set family // ->for now this will only be 'AF_INET' or 'AF_INET6' - socket[KEY_SOCKET_FAMILY] = socketFamily2String(socketInfo.psi.soi_family); + socket[KEY_SOCKET_FAMILY] = [NSNumber numberWithInt:socketInfo.psi.soi_family]; //set protocol - socket[KEY_SOCKET_PROTO] = socketProto2String(socketInfo.psi.soi_protocol); + socket[KEY_SOCKET_PROTO] = [NSNumber numberWithInt:socketInfo.psi.soi_protocol]; //get state // ->only for stream stockets though if(SOCK_STREAM == socketInfo.psi.soi_type) { //set state - socket[KEY_SOCKET_STATE] = socketState2String(socketInfo.psi.soi_proto.pri_tcp.tcpsi_state); + socket[KEY_SOCKET_STATE] = [NSNumber numberWithInt:socketInfo.psi.soi_proto.pri_tcp.tcpsi_state]; } //add @@ -724,7 +850,6 @@ bail: }//all FDs - //bail bail: @@ -744,122 +869,3 @@ bail: @end -//convert a socket type into string -NSString* socketType2String(int type) -{ - //socket type - NSString* socketType = nil; - - //convert - switch(type) - { - //stream - case SOCK_STREAM: - socketType = @"SOCK_STREAM"; - break; - - //dgram - case SOCK_DGRAM: - socketType = @"SOCK_DGRAM"; - break; - - //raw - case SOCK_RAW: - socketType = @"SOCK_RAW"; - break; - - //rdm - case SOCK_RDM: - socketType = @"SOCK_RDM"; - break; - - //seq packet - case SOCK_SEQPACKET: - socketType = @"SOCK_SEQPACKET"; - break; - - default: - break; - } - - return socketType; -} - - -//convert a socket family into string -NSString* socketFamily2String(int family) -{ - //socket family - NSString* socketFamily = nil; - - //sanity check - if( (family < 0) || - (family >= SOCKET_FAMILY_MAX) ) - { - //bail - goto bail; - } - - //init socket family string - socketFamily = [NSString stringWithUTF8String:socketFamilies[family]]; - -//bail -bail: - - return socketFamily; -} - -//convert a socket protocol into string -NSString* socketProto2String(int proto) -{ - //socket proto - NSString* socketProto = nil; - - //proto struct - struct protoent *protoInfo = NULL; - - //get proto info - protoInfo = getprotobynumber(proto); - - //sanity check - if(NULL == protoInfo) - { - //bail - goto bail; - } - - //init proto string - // ->name comes from struct - socketProto = [NSString stringWithUTF8String:protoInfo->p_name]; - -//bail -bail: - - return socketProto; -} - - -//convert a socket state into string -NSString* socketState2String(int state) -{ - //socket proto - NSString* socketState = nil; - - //set state - if(state < TCP_NSTATES) - { - //set state - socketState = [NSString stringWithUTF8String:socketStates[state]]; - } - //invalid/unknown socket state - else - { - socketState = [NSString stringWithFormat:@"unknown state (%d)", state]; - } - - return socketState; -} - - - - diff --git a/serviceInterface.h b/serviceInterface.h index e84b9b8..bbad03d 100644 --- a/serviceInterface.h +++ b/serviceInterface.h @@ -12,7 +12,10 @@ #import "Task.h" @protocol remoteTaskProto -//- (void)compressFile:(NSFileHandle *)inFile toFile:(NSFileHandle *)outFile withReply:(void (^)(NSError *error))reply; + +//get task's commandline args +// ->args returned in array arg +-(void)getTaskArgs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; //enumerate loaded dylibs in a task -(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply;