pushed logic for getting task's args into XPC service (since needs to be done as root)

removed socket/string logic from root XPC service
improved 'isAlive' logic to detect dead process
improved process enumeration (to not ignore process that we can't get path for)
This commit is contained in:
Patrick Wardle
2016-03-24 17:02:11 -10:00
parent c1539d76be
commit b99b05888c
16 changed files with 659 additions and 445 deletions
+3 -1
View File
@@ -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
+3
View File
@@ -332,4 +332,7 @@
//hotkey 'i'
#define KEYCODE_I 0x22
//unknown task
#define TASK_PATH_UNKNOWN @"<unknown>"
#endif
+29 -3
View File
@@ -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<task.arguments.count; index++)
{
//add arg
[taskArguments appendFormat:@"%@ ", task.arguments[index]];
}
//set args into text field
if(0 != taskArguments.length)
{
//add
self.arguments.stringValue = taskArguments;
}
}
//set path
[self.path setStringValue:[self valueForStringItem:task.binary.path default:@"unknown"]];
+65
View File
@@ -7,8 +7,73 @@
//
#import "ItemBase.h"
#import <netdb.h>
#import <arpa/inet.h>
#import <netinet/tcp_fsm.h>
#import <Foundation/Foundation.h>
//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
{
+121 -8
View File
@@ -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
{
+8 -5
View File
@@ -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;
+114 -169
View File
@@ -14,20 +14,10 @@
#import "AppDelegate.h"
#import "remoteTaskService.h"
#import <mach-o/dyld_images.h>
#import <mach/mach_init.h>
#import <mach/mach_vm.h>
#import <sys/types.h>
#import <mach/mach.h>
#import <sys/ptrace.h>
#import <sys/wait.h>
#import <syslog.h>
#import <libproc.h>
#import <sys/sysctl.h>
#import <sys/proc_info.h>
#import <libproc.h>
#import <arpa/inet.h>
#import <netinet/tcp_fsm.h>
#import <syslog.h>
@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:)
+15 -25
View File
@@ -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];
@@ -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"
}
]
}
+56 -17
View File
@@ -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;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

+4 -15
View File
@@ -6,27 +6,16 @@
// Copyright (c) 2015 Objective-See, LLC. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "serviceInterface.h"
#import <Foundation/Foundation.h>
// 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 <remoteTaskProto, NSXPCListenerDelegate>
//
//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);
+207 -201
View File
@@ -10,81 +10,12 @@
#import "Utilities.h"
#import "remoteTaskService.h"
#import <mach-o/dyld_images.h>
#import <mach/mach_init.h>
#import <mach/mach_vm.h>
#import <sys/types.h>
#import <mach/mach.h>
#import <sys/ptrace.h>
#import <sys/wait.h>
#import <sys/sysctl.h>
#import <sys/proc_info.h>
#import <syslog.h>
#import <libproc.h>
#import <arpa/inet.h>
#import <netinet/tcp_fsm.h>
#import <netdb.h>
#import <syslog.h>
//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 <sys/sysctl.h>
#import <mach/mach_vm.h>
#import <mach-o/dyld_images.h>
//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;
}
+4 -1
View File
@@ -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;