mirror of
https://github.com/objective-see/TaskExplorer.git
synced 2026-03-22 07:02:39 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
//
|
||||
// TaskEnumerator.m
|
||||
//
|
||||
//
|
||||
// Created by Patrick Wardle on 5/2/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <libproc.h>
|
||||
#import <sys/proc_info.h>
|
||||
|
||||
#import "Task.h"
|
||||
#import "Consts.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "Utilities.h"
|
||||
#import "TaskEnumerator.h"
|
||||
#import "serviceInterface.h"
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation TaskEnumerator
|
||||
|
||||
|
||||
@synthesize files;
|
||||
@synthesize tasks;
|
||||
@synthesize dylibs;
|
||||
@synthesize binaryQueue;
|
||||
@synthesize executables;
|
||||
@synthesize xpcConnection;
|
||||
|
||||
//@synthesize firstScanComplete;
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
{
|
||||
//init super
|
||||
self = [super init];
|
||||
if(nil != self)
|
||||
{
|
||||
//init tasks dictionary
|
||||
tasks = [[OrderedDictionary alloc] init];
|
||||
|
||||
//alloc executables dictionary
|
||||
executables = [NSMutableDictionary dictionary];
|
||||
|
||||
//alloc dylibs dictionary
|
||||
dylibs = [NSMutableDictionary dictionary];
|
||||
|
||||
//alloc XPC connection
|
||||
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"];
|
||||
|
||||
//set remote object interface
|
||||
self.xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)];
|
||||
|
||||
//set classes
|
||||
// ->arrays & strings are what is ok to vend
|
||||
[self.xpcConnection.remoteObjectInterface
|
||||
setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil]
|
||||
forSelector: @selector(enumerateDylibs:withReply:)
|
||||
argumentIndex: 0 // the first parameter
|
||||
ofReply: YES // in the method itself.
|
||||
];
|
||||
|
||||
//set classes
|
||||
// ->arrays & strings are what is ok to vend
|
||||
[self.xpcConnection.remoteObjectInterface
|
||||
setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil]
|
||||
forSelector: @selector(enumerateFiles:withReply:)
|
||||
argumentIndex: 0 // the first parameter
|
||||
ofReply: YES // in the method itself.
|
||||
];
|
||||
|
||||
//set classes
|
||||
// ->arrays & strings are what is ok to vend
|
||||
[self.xpcConnection.remoteObjectInterface
|
||||
setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], [NSNumber class], nil]
|
||||
forSelector: @selector(enumerateNetwork:withReply:)
|
||||
argumentIndex: 0 // the first parameter
|
||||
ofReply: YES // in the method itself.
|
||||
];
|
||||
|
||||
//resume
|
||||
[self.xpcConnection resume];
|
||||
|
||||
//init binary processing queue
|
||||
binaryQueue = [[Queue alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//enumerate all tasks
|
||||
// ->call back into app delegate to update task (top) table
|
||||
// call every x # of seconds~
|
||||
|
||||
//TODO: re-enumerate dylibs for everytime!
|
||||
// only need to update task.dylibs list (not global one, unless its new!)
|
||||
|
||||
-(void)enumerateTasks
|
||||
{
|
||||
//(new) task item
|
||||
Task* newTask = nil;
|
||||
|
||||
//new tasks
|
||||
OrderedDictionary* newTasks = nil;
|
||||
|
||||
//get all tasks
|
||||
// ->pids and binary obj with just path/name
|
||||
newTasks = [self getAllTasks];
|
||||
|
||||
//build ancestries
|
||||
[self generateAncestries:newTasks];
|
||||
|
||||
//add all tasks that are really new to 'tasks' iVar
|
||||
// ->ensures existing task and their info are reused
|
||||
for(NSNumber* key in newTasks.allKeys)
|
||||
{
|
||||
//get task
|
||||
newTask = newTasks[key];
|
||||
|
||||
//remove any non-new (i.e. existing) tasks
|
||||
if(nil != self.tasks[newTask.pid])
|
||||
{
|
||||
//not new
|
||||
// ->remove
|
||||
[newTasks removeObjectForKey:key];
|
||||
|
||||
//next
|
||||
continue;
|
||||
}
|
||||
|
||||
//enumerate task's dylibs
|
||||
//[newTask enumerateDylibs:self.xpcConnection allDylibs:self.dylibs];
|
||||
|
||||
//add new task
|
||||
// TODO: ordering!?
|
||||
[self.tasks setObject:newTask forKey:newTask.pid];
|
||||
|
||||
}
|
||||
|
||||
//reload table
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable];
|
||||
|
||||
//now generate signing info
|
||||
// ->for (new) tasks & their dylibs
|
||||
for(NSNumber* key in newTasks)
|
||||
{
|
||||
//get task
|
||||
newTask = newTasks[key];
|
||||
|
||||
//generate signing info
|
||||
[newTask.binary generatedSigningInfo];
|
||||
|
||||
//reload task (row) in table
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:newTask item:newTask.binary pane:PANE_TOP];
|
||||
|
||||
//reload bottom pane
|
||||
// ->this will only reload if new task is the currently selected one, etc
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:newTask itemView:CURRENT_VIEW];
|
||||
|
||||
}//signing info for all tasks and dylibs
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
//determine if dylibs should be (re)enumerated
|
||||
// ->generally yes, unless the first enumeration (of all tasks) is not complete
|
||||
-(BOOL)shouldEnumDylibs
|
||||
{
|
||||
//flag
|
||||
BOOL shouldEnum = NO;
|
||||
|
||||
//task key
|
||||
NSNumber* taskKey = nil;
|
||||
|
||||
//Task
|
||||
Task* task = nil;
|
||||
|
||||
for(NSInteger i = self.tasks.count-1; i>=0; i--)
|
||||
{
|
||||
taskKey = [self.tasks keyAtIndex:i];
|
||||
|
||||
task = self.tasks[taskKey];
|
||||
|
||||
//skip dead procs
|
||||
if(YES != isAlive([task.pid unsignedIntValue]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//alive
|
||||
// ->does it have dylibs?
|
||||
if(0 != task.dylibs.count)
|
||||
{
|
||||
//a end task has dylibs
|
||||
// ->indicates all done?
|
||||
shouldEnum = YES;
|
||||
|
||||
//bail
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldEnum = NO;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shouldEnum;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
//TODO: only do this once!
|
||||
// ->otherwise done JIT (on click)
|
||||
//TODO: sync!!
|
||||
//iterate over all tasks
|
||||
// ->invoke task's emun method to get all files/network (via XPC)
|
||||
-(void)enumerateFiles
|
||||
{
|
||||
//Task
|
||||
Task* task = nil;
|
||||
|
||||
//iterate over all tasks
|
||||
// ->invoke method to enumerate files
|
||||
for(NSNumber* key in self.tasks)
|
||||
{
|
||||
//get task
|
||||
task = self.tasks[key];
|
||||
|
||||
//enumerate files
|
||||
[task enumerateFiles:self.xpcConnection];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//get list of all pids
|
||||
-(OrderedDictionary*)getAllTasks
|
||||
{
|
||||
//tasks
|
||||
// ->pid/name
|
||||
OrderedDictionary* allTasks = nil;
|
||||
|
||||
//task
|
||||
Task* task = nil;
|
||||
|
||||
//alloc/init list
|
||||
allTasks = [[OrderedDictionary alloc] init];
|
||||
|
||||
//# of procs
|
||||
int numberOfProcesses = 0;
|
||||
|
||||
//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);
|
||||
|
||||
//alloc buffer for pids
|
||||
pids = calloc(numberOfProcesses, sizeof(pid_t));
|
||||
|
||||
//get list of pids
|
||||
status = proc_listpids(PROC_ALL_PIDS, 0, pids, numberOfProcesses * sizeof(pid_t));
|
||||
if(status < 0)
|
||||
{
|
||||
//err
|
||||
NSLog(@"OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//iterate over all pids
|
||||
// ->get name for each
|
||||
for(int i = 0; i < numberOfProcesses; ++i)
|
||||
{
|
||||
//skip blank pids
|
||||
if(0 == pids[i])
|
||||
{
|
||||
//skip
|
||||
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)) )
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//init process name
|
||||
processName = [NSString stringWithUTF8String:pathBuffer];
|
||||
|
||||
//init task
|
||||
// ->pass in pid and name
|
||||
task = [[Task alloc] initWithPID:processID andPath:processName];
|
||||
|
||||
//add task to list
|
||||
// ->order by pid for now
|
||||
[allTasks setObject:task forKey:processID];
|
||||
}
|
||||
|
||||
//always add kernel's task
|
||||
// ->hardcoded pid (0) and path to kernel
|
||||
task = [[Task alloc] initWithPID:@0 andPath:path2Kernel()];
|
||||
|
||||
//add kernel task
|
||||
[allTasks setObject:task forKey:@0];
|
||||
|
||||
//reverse array
|
||||
// ->want order to go from 0 -> ...
|
||||
[allTasks reverse];
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//free buffer
|
||||
if(NULL != pids)
|
||||
{
|
||||
//free
|
||||
free(pids);
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
//NSLog(@"OBJECTIVE-SEE INFO: done scanning running processes");
|
||||
|
||||
return allTasks;
|
||||
}
|
||||
|
||||
//insert tasks into appropriate parent
|
||||
// ->ensures order of parent's (by pid), is preserved
|
||||
//TODO: what about parentless procs!? (e.g. malware?)
|
||||
-(void)generateAncestries:(OrderedDictionary*)newTasks
|
||||
{
|
||||
//task
|
||||
Task* task = nil;
|
||||
|
||||
//parent
|
||||
Task* parent = nil;
|
||||
|
||||
//comparator
|
||||
NSComparator comparator = nil;
|
||||
|
||||
//index
|
||||
// ->where task should be inserted into parent's child array
|
||||
NSUInteger childIndex = 0;
|
||||
|
||||
//init comparator
|
||||
comparator = ^(id obj1, id obj2) { return NSOrderedSame; };
|
||||
|
||||
//interate over all task
|
||||
// ->insert task into parent's *ordered* child array
|
||||
for(NSNumber* key in newTasks.allKeys)
|
||||
{
|
||||
//get task
|
||||
task = newTasks[key];
|
||||
|
||||
//get parent
|
||||
parent = newTasks[task.ppid];
|
||||
|
||||
//ignore tasks that are their own parent
|
||||
// ->i.e. kernel_task
|
||||
if(YES == [task.pid isEqualToNumber:task.ppid])
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//get index where child should be inserted
|
||||
childIndex = [parent.children indexOfObject:task.pid
|
||||
inSortedRange:(NSRange){0, [parent.children count]}
|
||||
options:NSBinarySearchingInsertionIndex usingComparator:comparator];
|
||||
|
||||
//insert child
|
||||
[parent.children insertObject:task.pid atIndex:childIndex];
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user