initial commit

This commit is contained in:
Patrick Wardle
2015-06-13 16:51:34 -10:00
commit e8ce5006bb
144 changed files with 21324 additions and 0 deletions
+744
View File
@@ -0,0 +1,744 @@
//
// remoteTaskService.m
// remoteTaskService
//
// Created by Patrick Wardle on 5/27/15.
// Copyright (c) 2015 Lucas Derraugh. All rights reserved.
//
//TODO: make sure task is still active (maybe in client, b4 calling this?!
#import "remoteTaskService.h"
#import "Consts.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 <libproc.h>
#import <arpa/inet.h>
#import <netinet/tcp_fsm.h>
#import <netdb.h>
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 *))
//32bit
struct dyld_image_info_32 {
int imageLoadAddress;
int imageFilePath;
int imageFileModDate;
};
@implementation remoteTaskService
+ (remoteTaskService *)defaultService {
static dispatch_once_t onceToken;
static remoteTaskService *shared;
dispatch_once(&onceToken, ^{
shared = [remoteTaskService new];
});
return shared;
}
//enumerate dylibs for a specified task
// ->dylibs returned in array arg
-(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply;
{
//status
kern_return_t status = !KERN_SUCCESS;
//task for remote process
task_t remoteTask = 0;
//remote read addr
vm_address_t remoteReadAddr = 0;
//number of remote bytes to read
mach_msg_type_number_t bytesToRead = 0;
//dyld info structure
// ->contains remote addr/size of dyld_all_image_infos
struct task_dyld_info dyldInfo = {0};
//set size of task info
mach_msg_type_number_t taskInfoSize = 0;
//pointer to structure for...
// ->populated by mach_vm_read()
struct dyld_all_image_infos* allImageInfo = NULL;
//number of bytes read for dyld_all_image_infos
mach_msg_type_number_t aifBytesRead = 0;
//array of structs w/ dylib info
void* dyldImageInfos = NULL;
//number of bytes read for list of dyld_image_infos
mach_msg_type_number_t diiBytesRead = 0;
//pointer to dylib info struct
// ->depending on remote (target) process either dyld_image_info or dyld_image_info_32
void* imageInfo = NULL;
//buffer for dylib path
char* dylibPath = NULL;
//number of bytes read for dyld_all_image_infos
mach_msg_type_number_t dpBytesRead = 0;
//dylibs
NSMutableArray* dylibPaths = nil;
//alloc array for dylibs
dylibPaths = [NSMutableArray array];
//get task for pid
// ->allows access to read remote process memory
status = task_for_pid(mach_task_self(), [taskPID intValue], &remoteTask);
if(KERN_SUCCESS != status)
{
//err msg
NSLog(@"ERROR: task_for_pid() failed w/ %d", status);
//bail
goto bail;
}
//set task info size
taskInfoSize = TASK_DYLD_INFO_COUNT;
//get TASK_DYLD_INFO
// ->populates task_dyld_info structure
status = task_info(remoteTask, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &taskInfoSize);
if(KERN_SUCCESS != status)
{
//bail
goto bail;
}
//dbg msg
//NSLog(@"got dyld_info: %#llx : %llx - %d", dyldInfo.all_image_info_addr, dyldInfo.all_image_info_size, dyldInfo.all_image_info_format);
//remotely read dyld_all_image_infos
status = mach_vm_read(remoteTask, (vm_address_t)dyldInfo.all_image_info_addr, dyldInfo.all_image_info_size, (vm_offset_t*)&allImageInfo, &aifBytesRead);
if(KERN_SUCCESS != status)
{
//err msg
NSLog(@"ERROR: mach_vm_read() failed w/ %d", status);
//bail
goto bail;
}
//set remote read addr & size
// ->64bit mode
if(TASK_DYLD_ALL_IMAGE_INFO_64 == dyldInfo.all_image_info_format)
{
//straight assign
remoteReadAddr = (vm_address_t)allImageInfo->infoArray;
//init output size
bytesToRead = allImageInfo->infoArrayCount * sizeof(struct dyld_image_info);
}
//set remote read addr & size
// ->32bit mode
else
{
//hack, can use 64bit version of struct (since 'infoArray' is first pointer
// ->but zero out top bits
remoteReadAddr = (vm_address_t)allImageInfo->infoArray & 0xFFFFFFFF;
//init output size
bytesToRead = allImageInfo->infoArrayCount * sizeof(struct dyld_image_info_32);
}
//read remote array of dyld_image_info/32 structs
status = mach_vm_read(remoteTask, (vm_address_t)remoteReadAddr, bytesToRead, (vm_offset_t*)&dyldImageInfos, &diiBytesRead);
//check
if(KERN_SUCCESS != status)
{
//err msg
NSLog(@"ERROR: mach_vm_read() failed w/ %d", status);
//bail
goto bail;
}
//iterate over all dyld_image_info_32/dyld_image_info structs
// ->extract and remotely read address to dylib path
for(NSUInteger i = 0; i<allImageInfo->infoArrayCount; i++)
{
//reset
dpBytesRead = 0;
//advance to next image_info struct and set remote read addr
// ->64bit mode
if(TASK_DYLD_ALL_IMAGE_INFO_64 == dyldInfo.all_image_info_format)
{
//next dyld_image_info struct
imageInfo = (unsigned char*)dyldImageInfos + i * sizeof(struct dyld_image_info);
//remote read addr
remoteReadAddr = (vm_address_t)((struct dyld_image_info*)imageInfo)->imageFilePath;
}
//advance to next image_info struct and set remote read addr
// ->64bit mode
else
{
//next dyld_image_info_32 struct
imageInfo = (unsigned char*)dyldImageInfos + i * sizeof(struct dyld_image_info_32);
//remote read addr
remoteReadAddr = (vm_address_t)((struct dyld_image_info_32*)imageInfo)->imageFilePath & 0xFFFFFFFF;
}
//remotely read into dylib's path!
// ->seems to always fail for first image, which is base executable...
status = mach_vm_read(remoteTask, (vm_address_t)remoteReadAddr, PATH_MAX, (vm_offset_t*)&dylibPath, &dpBytesRead);
if( (KERN_SUCCESS != status) ||
(NULL == dylibPath) )
{
//err msg
//NSLog(@"ERROR: mach_vm_read() failed w/ %d", status);
//try next
continue;
}
//dbg msg
//NSLog(@"path (%d): %s", dpBytesRead, dylibPath);
//save it
[dylibPaths addObject:[NSString stringWithUTF8String:dylibPath]];
//dealloc
mach_vm_deallocate(mach_task_self(), (vm_offset_t)dylibPath, dpBytesRead);
}//for all dyld_image_info_32/dyld_image_info structs
//bail
bail:
//dealloc list of dylib info structs
if(NULL != dyldImageInfos)
{
//dealloc
mach_vm_deallocate(mach_task_self(), (vm_offset_t)dyldImageInfos, diiBytesRead);
}
//dealloc dyld_all_image_infos struct
if(NULL != allImageInfo)
{
//dealloc
mach_vm_deallocate(mach_task_self(), (vm_offset_t)allImageInfo, aifBytesRead);
}
//invoke reply block
reply(dylibPaths);
return;
}
//enumerate open files
// ->accomplish this via lsof, since proc_pidinfo() misses some files...
-(void)enumerateFiles:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply;
{
//task
NSTask* task = nil;
//pipe
NSPipe* pipe = nil;
//results
NSData* results = nil;
//results split on '\n'
NSArray* splitResults = nil;
//file path
NSString* filePath = nil;
//file info dictionary
NSMutableDictionary* fileInfo = nil;
//files
NSMutableArray* files = nil;
//init task
task = [[NSTask alloc] init];
//init pipe
pipe = [NSPipe pipe];
//init array for files
files = [NSMutableArray array];
//set task's stdout to pipe
[task setStandardOutput:pipe];
//set task's stderr to pipe
[task setStandardError:pipe];
//set launch path
[task setLaunchPath:LSOF];
//set tasks's args
// -> -Fn -p <taskPid>
[task setArguments:@[@"-Fn", @"-p", taskPID.stringValue]];
//launch
[task launch];
//get results
results = [[[task standardOutput] fileHandleForReading] readDataToEndOfFile];
//sanity check(s)
if( (nil == results) ||
(0 == results.length) )
{
//bail
goto bail;
}
//split results into array
splitResults = [[[NSString alloc] initWithData:results encoding:NSUTF8StringEncoding] componentsSeparatedByString:@"\n"];
//sanity check(s)
if( (nil == splitResults) ||
(0 == splitResults.count) )
{
//bail
goto bail;
}
//iterate over all results
// ->make file info dictionary for files (not sockets, etc)
for(NSString* result in splitResults)
{
//skip any odd/weird/short lines
// lsof outpupt will be in format: 'n<filePath'>
if( (YES != [result hasPrefix:@"n"]) ||
(result.length < 0x2) )
{
//skip
continue;
}
//init file path
// ->result, minus first (lsof-added) char
filePath = [result substringFromIndex:0x1];
//skip 'non files'
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
//skip
continue;
}
//also skip files such as '/', /dev/null, etc
if( (YES == [filePath isEqualToString:@"/"]) ||
(YES == [filePath isEqualToString:@"/dev/null"]) )
{
//skip
continue;
}
//alloc info dictionary
fileInfo = [NSMutableDictionary dictionary];
//add path
fileInfo[KEY_FILE_PATH] = filePath;
//save
[files addObject:fileInfo];
}
//bail
bail:
//invoke reply
reply(files);
return;
}
//TODO: soi_rcv/soi_snd to get packets!?
//enumerate network connections
-(void)enumerateNetwork:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply;
{
//task's sockets
NSMutableArray* sockets = nil;
//socket info dictionary
NSMutableDictionary* socket = nil;
//size
int bufferSize = -1;
//proc handles
struct proc_fdinfo *procFDInfo = NULL;
//number of handle
int numberOfProcFDs = 0;
//socket handle struct
struct socket_fdinfo socketInfo = {0};
//socket local addr
// ->big enough for both IPv4 and IPv6
char localIPAddr[INET6_ADDRSTRLEN] = {0};
//socket remote addr
// ->big enough for both IPv4 and IPv6
char remoteIPAddr[INET6_ADDRSTRLEN] = {0};
//socket remote port
short remotePort = 0;
//alloc array for sockets
sockets = [NSMutableArray array];
//invoke proc_pidinfo w/ NULL
// ->get's required buffer size
bufferSize = proc_pidinfo([taskPID intValue], PROC_PIDLISTFDS, 0, 0, 0);
if(bufferSize <= 0)
{
//bail
goto bail;
}
//alloc buffer for handles
procFDInfo = (struct proc_fdinfo *)malloc(bufferSize);
//sanity check
if(NULL == procFDInfo)
{
//bail
goto bail;
}
//get proc's handles
bufferSize = proc_pidinfo([taskPID intValue], PROC_PIDLISTFDS, 0, procFDInfo, bufferSize);
if(bufferSize <= 0)
{
//bail
goto bail;
}
//calc number of handles
numberOfProcFDs = bufferSize / PROC_PIDLISTFD_SIZE;
//iterate over all file descriptors
// ->only care about sockets though...
for(NSUInteger i = 0; i < numberOfProcFDs; i++)
{
//skip all non-sockets
if(PROX_FDTYPE_SOCKET != procFDInfo[i].proc_fdtype)
{
//skip
continue;
}
//get (detailed) info about socket
// ->should return # of bytes that matches size of socket struct
if(sizeof(struct socket_fdinfo) != proc_pidfdinfo([taskPID intValue], procFDInfo[i].proc_fd, PROC_PIDFDSOCKETINFO, &socketInfo, sizeof(struct socket_fdinfo)))
{
//skip
continue;
}
//skip any non-internet sockets
if( (socketInfo.psi.soi_family != AF_INET) &&
(socketInfo.psi.soi_family != AF_INET6) )
{
//skip
continue;
}
//alloc dictionary for socket
socket = [NSMutableDictionary dictionary];
//add local port
socket[KEY_LOCAL_PORT] = [NSNumber numberWithShort:ntohs(socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport)];
//get remote port
remotePort = ntohs(socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
//IPv4 sockets
if(socketInfo.psi.soi_family == AF_INET)
{
//get local ip addr
inet_ntop(AF_INET, &socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4, localIPAddr, sizeof(localIPAddr));
//add local ip addr
socket[KEY_LOCAL_ADDR] = [NSString stringWithUTF8String:localIPAddr];
//for connected sessions
// ->get/save remote ip addr
if(0 != remotePort)
{
//add remote port
socket[KEY_REMOTE_PORT] = [NSNumber numberWithShort:remotePort];
//get remote ip addr
inet_ntop(AF_INET, &socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4, remoteIPAddr, sizeof(remoteIPAddr));
//add remote ip addr
socket[KEY_REMOTE_ADDR] = [NSString stringWithUTF8String:remoteIPAddr];
}
}
//IPv6 sockets
else if(socketInfo.psi.soi_family == AF_INET6)
{
//get local ip addr
inet_ntop(AF_INET6, &socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6, localIPAddr, sizeof(localIPAddr));
//add local ip addr
socket[KEY_LOCAL_ADDR] = [NSString stringWithUTF8String:localIPAddr];
//for connected sessions
// ->get remote ip addr
if(0 != remotePort)
{
//add remote port
socket[KEY_REMOTE_PORT] = [NSNumber numberWithShort:remotePort];
//get remote ip addr
inet_ntop(AF_INET6, &socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6, remoteIPAddr, sizeof(remoteIPAddr));
//add remote ip addr
socket[KEY_REMOTE_ADDR] = [NSString stringWithUTF8String:remoteIPAddr];
}
}
//set type
socket[KEY_SOCKET_TYPE] = socketType2String(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);
//set protocol
socket[KEY_SOCKET_PROTO] = socketProto2String(socketInfo.psi.soi_protocol);
//get state
// ->only for stream stockets though
if(SOCK_STREAM == socketInfo.psi.soi_type)
{
//set state
// ->for now, this will only be 'listening' or 'established'
socket[KEY_SOCKET_STATE] = socketState2String(socketInfo.psi.soi_proto.pri_tcp.tcpsi_state);
}
//add
[sockets addObject:socket];
}//all FDs
//bail
bail:
//free buffer
if(nil != procFDInfo)
{
//free
free(procFDInfo);
}
//invoke reply
reply(sockets);
return;
}
@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;
//convert to string
switch(state)
{
//listening
case TCPS_LISTEN:
//set
socketState = SOCKET_LISTENING;
break;
//established
case TCPS_ESTABLISHED:
//set
socketState = SOCKET_ESTABLISHED;
break;
default:
//TODO: what states?
NSLog(@"unknown state: %d", state);
//can't return nil
//TODO: hrmm
socketState = @"unknown";
break;
}
return socketState;
}