Files
2019-02-10 17:36:36 -10:00

568 lines
12 KiB
Objective-C

//
// File: Process.m
// Project: Proc Info
//
// Created by: Patrick Wardle
// Copyright: 2017 Objective-See
// License: Creative Commons Attribution-NonCommercial 4.0 International License
//
#import "Signing.h"
#import "procInfo.h"
#import "Utilities.h"
@implementation Process
@synthesize pid;
@synthesize exit;
@synthesize path;
@synthesize ppid;
@synthesize ancestors;
@synthesize arguments;
@synthesize timestamp;
@synthesize signingInfo;
//init
-(id)init
{
//init super
self = [super init];
if(nil != self)
{
//alloc array for args
arguments = [NSMutableArray array];
//alloc array for parents
ancestors = [NSMutableArray array];
//alloc array for args
arguments = [NSMutableArray array];
//set start time
timestamp = [NSDate date];
//init pid
self.pid = -1;
//init ppid
self.ppid = -1;
//init user
self.uid = -1;
//init exit
self.exit = -1;
}
return self;
}
//init with a pid
// method will then (try) fill out rest of object
-(id)init:(pid_t)processID
{
//init self/super
self = [self init];
if(self)
{
//save pid
self.pid = processID;
//set parent
self.ppid = [[self class] getParentID:self.pid];
//get path
[self pathFromPid];
if(0 == self.path.length)
{
#ifdef DEBUG
//err msg
NSLog(@"ERROR: failed to find path for process %d\n", self.pid);
#endif
//unset
self = nil;
//bail
goto bail;
}
//set args
[self getArgs];
//set user
[self getUser];
//enum ancestors
[self enumerateAncestors];
//init binary
self.binary = [[Binary alloc] init:self.path];
}
bail:
return self;
}
//get uid
// sets 'user' instance var
-(void)getUser
{
//kinfo_proc struct
struct kinfo_proc processStruct = {0};
//size
size_t procBufferSize = 0;
//mib
const u_int mibLength = 4;
//syscall result
int sysctlResult = -1;
//init buffer length
procBufferSize = sizeof(processStruct);
//init mib
int mib[mibLength] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, self.pid};
//make syscall
sysctlResult = sysctl(mib, mibLength, &processStruct, &procBufferSize, NULL, 0);
//check if got ppid
if( (noErr == sysctlResult) &&
(0 != procBufferSize) )
{
//save uid
self.uid = processStruct.kp_eproc.e_ucred.cr_uid;
}
return;
}
//generate list of ancestors
-(void)enumerateAncestors
{
//current process id
pid_t currentPID = -1;
//parent pid
pid_t parentPID = -1;
//add parent
if(-1 != self.ppid)
{
//add
[self.ancestors addObject:[NSNumber numberWithInt:self.ppid]];
//set current to parent
currentPID = self.ppid;
}
//don't know parent
// just start with self
else
{
//start w/ self
currentPID = self.pid;
}
//add until we get to to end (pid 0)
// or error out during the traversal
while(YES)
{
//get parent pid
parentPID = [Process getParentID:currentPID];
if( (0 == parentPID) ||
(-1 == parentPID) ||
(currentPID == parentPID) )
{
//bail
break;
}
//update
currentPID = parentPID;
//add
[self.ancestors addObject:[NSNumber numberWithInt:parentPID]];
}
return;
}
//set process's path
-(void)pathFromPid
{
//buffer for process path
char pathBuffer[PROC_PIDPATHINFO_MAXSIZE] = {0};
//status
int status = -1;
//'management info base' array
int mib[3] = {0};
//system's size for max args
int systemMaxArgs = 0;
//process's args
char* processArgs = NULL;
//size of buffers, etc
size_t size = 0;
//clear out buffer
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
//1st:
// attempt to get path via 'proc_pidpath()'
status = proc_pidpath(self.pid, pathBuffer, sizeof(pathBuffer));
if( (0 != status) &&
(0 != strlen(pathBuffer)) )
{
//init path
self.path = [NSString stringWithUTF8String:pathBuffer];
//all set
goto bail;
}
//2nd:
// try via process's args ('KERN_PROCARGS2')
//init mib
// want system's size for max args
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
//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 = calloc(systemMaxArgs, sizeof(char));
if(NULL == processArgs)
{
//bail
goto bail;
}
//init mib
// want process args
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
//set size
size = (size_t)systemMaxArgs;
//get process's args
if(-1 == sysctl(mib, 3, processArgs, &size, NULL, 0))
{
//bail
goto bail;
}
//sanity check
// ensure buffer is somewhat sane
if(size <= sizeof(int))
{
//bail
goto bail;
}
//extract process name
// follows # of args (int) and is NULL-terminated
self.path = [NSString stringWithUTF8String:processArgs + sizeof(int)];
bail:
//free process args
if(NULL != processArgs)
{
//free
free(processArgs);
//unset
processArgs = NULL;
}
return;
}
//check if process is alive
-(BOOL)isProcessAlive
{
//ret var
BOOL bIsAlive = NO;
//signal status
int signalStatus = -1;
//send kill with 0 to determine if alive
// see: http://stackoverflow.com/questions/9152979/check-if-process-exists-given-its-pid
signalStatus = kill(self.pid, 0);
//is alive?
if( (0 == signalStatus) ||
( (0 != signalStatus) && (errno != ESRCH) ) )
{
//alive!
bIsAlive = YES;
}
return bIsAlive;
}
//extract commandline args
// saves into 'arguments' ivar
-(void)getArgs
{
//'management info base' array
int mib[3] = {0};
//system's size for max args
int systemMaxArgs = 0;
//process's args
char* processArgs = 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;
//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] = pid;
//set size
size = (size_t)systemMaxArgs;
//get process's args
if(-1 == sysctl(mib, 3, processArgs, &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, processArgs, sizeof(numberOfArgs));
//init pointer to start of args
// they start right after # of args
parser = processArgs + sizeof(numberOfArgs);
//scan until end of process's NULL-terminated path
while(parser < &processArgs[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 == &processArgs[size])
{
//bail
goto bail;
}
//skip all trailing NULLs
// scan will end when non-NULL is found
while(parser < &processArgs[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 == &processArgs[size])
{
//bail
goto bail;
}
//parser should now point to argv[0], process name
// init arg start
argStart = parser;
//keep scanning until all args are found
// each is NULL-terminated
while(parser < &processArgs[size])
{
//each arg is NULL-terminated
// so scan till NULL, then save into array
if(*parser == '\0')
{
//save arg
if(NULL != argStart)
{
//save
[self.arguments addObject:[NSString stringWithUTF8String:argStart]];
}
//init string pointer to (possibly) next arg
argStart = ++parser;
//bail if we've hit arg cnt
if(self.arguments.count == numberOfArgs)
{
//bail
break;
}
}
//next char
parser++;
}
bail:
//free process args
if(NULL != processArgs)
{
//free
free(processArgs);
//unset
processArgs = NULL;
}
return;
}
//generate signing info
// also classifies if Apple/from App Store/etc.
-(void)generateSigningInfo:(SecCSFlags)flags
{
//extract signing info dynamically
self.signingInfo = extractSigningInfo(self.pid, nil, flags);
//failed?
// try extract signing info statically
if(nil == self.signingInfo)
{
//add 'all archs' / 'nested'
// cuz don't know which is/was running
flags |= kSecCSCheckAllArchitectures | kSecCSCheckNestedCode | kSecCSDoNotValidateResources;
//extract signing info statically
self.signingInfo = extractSigningInfo(0, self.binary.path, flags);
}
return;
}
//for pretty printing
-(NSString *)description
{
//pretty print
return [NSString stringWithFormat: @"pid: %d\npath: %@\nuser: %d\nargs: %@\nancestors: %@\n signing info: %@\n binary:\n%@", self.pid, self.path, self.uid, self.arguments, self.ancestors, self.signingInfo, self.binary];
}
//class method to get parent of arbitrary process
+(pid_t)getParentID:(pid_t)child
{
//parent id
pid_t parentID = -1;
//kinfo_proc struct
struct kinfo_proc processStruct = {0};
//size
size_t procBufferSize = 0;
//mib
const u_int mibLength = 4;
//syscall result
int sysctlResult = -1;
//init buffer length
procBufferSize = sizeof(processStruct);
//init mib
int mib[mibLength] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, child};
//make syscall
sysctlResult = sysctl(mib, mibLength, &processStruct, &procBufferSize, NULL, 0);
//check if got ppid
if( (noErr == sysctlResult) &&
(0 != procBufferSize) )
{
//save ppid
parentID = processStruct.kp_eproc.e_ppid;
}
return parentID;
}
@end