Mac 进程管理器(基于OpenBSM)

介绍

使用OpenBSM库对macOS进行实时审核,使用名为auditpipe的特定设备,位于/dev/auditpipe中获取实时的审计事件,重点关注进程的创建和退出

审计管道操作与配置

AUDITPIPE(4) - 用于实时审计事件跟踪的伪设备 (gsp.com)

基于OpenBSM库从管道获取事件

基本安全模块库提供了一些功能来读取这些事件并自动解析它们。

 au_read_rec()
 int au_read_rec(FILE *fp, u_char **buf);

这个函数让我们从文件描述符中读取一个事件记录,并将内容放在缓冲区buf作为参数传递(必须在使用后被释放)。该函数返回读取的字节数。

au_fetch_tok()
int au_fetch_tok(tokenstr_t *tok, u_char *buf, int len)

 从au_read_rec获取的缓冲区包含令牌,每个令牌都是具有不同信息的结构,根据令牌id。缓冲区的第一个令牌始终是AUT_HEADER *令牌:它包含一个字段,指示缓冲区中的哪种事件。下一个令牌包含有关引发事件的进程的路径,事件感兴趣的文件的路径以及其他信息(如用户,时间戳)的信息…要使用记录读取缓冲区,我们必须获取每个令牌它依次使用au_fetch_tok

au_print_tok()
void au_print_tok(FILE *outfp, tokenstr_t *tok, char *del, char raw, char sfrm)

每个事件都是由一些令牌组成的。令牌只是一个C结构,它包含一些根据令牌ID的信息。例如,读取事件具有   3个主令牌:AUT_HEADER,AUT_SUBJECT和AUT_PATH。

AUT_HEADER包含有关事件的信息。在一个读取事件中,它显示事件实际上是一个文件读取(fr)。

AUT_SUBJECT定义哪些过程引发此事件

AUT_PATH指定路线由读AUT_SUBJECT。

代码

-(void)monitor
{
    //event mask
    // what event classes to watch for
    u_int eventClasses = AUDIT_CLASS_EXEC | AUDIT_CLASS_PROCESS;
    
    //file pointer to audit pipe
    FILE* auditFile = NULL;
    
    //file descriptor for audit pipe
    int auditFileDescriptor = -1;
    
    //status var
    int status = -1;
    
    //preselect mode
    int mode = -1;
    
    //queue length
    int maxQueueLength = -1;
    
    //record buffer
    u_char* recordBuffer = NULL;
    
    //token struct
    tokenstr_t tokenStruct = {0};
    
    //total length of record
    int recordLength = -1;
    
    //amount of record left to process
    int recordBalance = -1;
    
    //amount currently processed
    int processedLength = -1;
    
    //process record obj
    Process* process = nil;
    
    //last fork
    Process* lastFork = nil;
    
    //argument
    NSString* argument = nil;
    
    //open audit pipe for reading
    auditFile = fopen(AUDIT_PIPE, "r");
    if(auditFile == NULL)
    {
        #ifdef DEBUG
        
        //err msg
        NSLog(@"ERROR: failed to open audit pipe %s", AUDIT_PIPE);
        
        #endif
        
        //bail
        goto bail;
    }
    
    //grab file descriptor
    auditFileDescriptor = fileno(auditFile);
    
    //init mode
    mode = AUDITPIPE_PRESELECT_MODE_LOCAL;
    
    //set preselect mode
    status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_MODE, &mode);
    if(-1 == status)
    {
        //bail
        goto bail;
    }
    
    //grab max queue length
    status = ioctl(auditFileDescriptor, AUDITPIPE_GET_QLIMIT_MAX, &maxQueueLength);
    if(-1 == status)
    {
        //bail
        goto bail;
    }
    
    //set queue length to max
    status = ioctl(auditFileDescriptor, AUDITPIPE_SET_QLIMIT, &maxQueueLength);
    if(-1 == status)
    {
        //bail
        goto bail;
        
    }
    
    //set preselect flags
    // event classes we're interested in
    status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_FLAGS, &eventClasses);
    if(-1 == status)
    {
        //bail
        goto bail;
    }
    
    //set non-attributable flags
    // event classes we're interested in
    status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_NAFLAGS, &eventClasses);
    if(-1 == status)
    {
        //bail
        goto bail;
    }
    
    //forever
    // read/parse/process audit records
    while(YES)
    {
        @autoreleasepool
        {
            
        //first check termination flag/condition
        if(YES == self.shouldStop)
        {
            //bail
            goto bail;
        }
        
        //reset process record object
        process = nil;
        
        //free prev buffer
        if(NULL != recordBuffer)
        {
            //free
            free(recordBuffer);
            
            //unset
            recordBuffer = NULL;
        }
        
        //read a single audit record
        // note: buffer is allocated by function, so must be freed when done
        recordLength = au_read_rec(auditFile, &recordBuffer);
        
        //sanity check
        if(-1 == recordLength)
        {
            //continue
            continue;
        }
        
        //init (remaining) balance to record's total length
        recordBalance = recordLength;
        
        //init processed length to start (zer0)
        processedLength = 0;
        
        //parse record
        // read all tokens/process
        while(0 != recordBalance)
        {
            //extract token
            // and sanity check
            if(-1  == au_fetch_tok(&tokenStruct, recordBuffer + processedLength, recordBalance))
            {
                //error
                // skip record
                break;
            }
            
            //ignore records that are not related to process exec'ing/spawning
            // gotta wait till we hit/capture a AUT_HEADER* though, as this has the event type
            if( (nil != process) &&
                (YES != [self shouldProcessRecord:process.type]) )
            {
                //bail
                // skips rest of record
                break;
            }
            
            //process token(s)
            // create Process object, etc
            switch(tokenStruct.id)
            {
                //handle start of record
                // grab event type, which allows us to ignore events not of interest
                case AUT_HEADER32:
                case AUT_HEADER32_EX:
                case AUT_HEADER64:
                case AUT_HEADER64_EX:
                {
                    //create a new process
                    process = [[Process alloc] init];
                    
                    //save type
                    process.type = tokenStruct.tt.hdr32.e_type;
                    
                    break;
                }
                    
                //path
                // note: this might be updated/replaced later (if it's '/dev/null', etc)
                case AUT_PATH:
                {
                    //save path
                    process.path = [NSString stringWithUTF8String:tokenStruct.tt.path.path];
                    
                    break;
                }
                    
                //subject
                //  extract/save pid || ppid
                //  all these cases can be treated as subj32 cuz only accessing initial members
                case AUT_SUBJECT32:
                case AUT_SUBJECT32_EX:
                case AUT_SUBJECT64:
                case AUT_SUBJECT64_EX:
                {
                    //SPAWN (pid/ppid)
                    // if there was an AUT_ARG32 (which always come first), that's the pid! so this will be the ppid
                    if(AUE_POSIX_SPAWN == process.type)
                    {
                        //no AUT_ARG32?
                        // set as pid, and try manually to get ppid
                        if(-1 == process.pid)
                        {
                            //set pid
                            process.pid = tokenStruct.tt.subj32.pid;
                            
                            //manually get parent
                            process.ppid = [Process getParentID:process.pid];
                        }
                        //pid already set (via AUT_ARG32)
                        // this then, is the ppid
                        else
                        {
                            //set ppid
                            process.ppid = tokenStruct.tt.subj32.pid;
                        }
                    }
                    
                    //FORK
                    // ppid (pid is in AUT_ARG32)
                    else if(AUE_FORK == process.type)
                    {
                        //set ppid
                        process.ppid = tokenStruct.tt.subj32.pid;
                    }
                    
                    //AUE_EXEC/VE & AUE_EXIT
                    // this is the pid
                    else
                    {
                        //save pid
                        process.pid = tokenStruct.tt.subj32.pid;
                        
                        //manually get parent
                        process.ppid = [Process getParentID:process.pid];
                    }
                    
                    //get effective user id
                    process.uid = tokenStruct.tt.subj32.euid;
                    
                    break;
                }
                    
                //args
                // SPAWN/FORK this is pid
                case AUT_ARG32:
                case AUT_ARG64:
                {
                    //save pid
                    if( (AUE_POSIX_SPAWN == process.type) ||
                        (AUE_FORK == process.type) )
                    {
                        //32bit
                        if(AUT_ARG32 == tokenStruct.id)
                        {
                            //save
                            process.pid = tokenStruct.tt.arg32.val;
                        }
                        //64bit
                        else
                        {
                            //save
                            process.pid = (pid_t)tokenStruct.tt.arg64.val;
                        }
                    }
                    
                    //FORK
                    // doesn't have token for path, so try manually find it now
                    if(AUE_FORK == process.type)
                    {
                        //set path
                        [process pathFromPid];
                    }
                    
                    break;
                }
                    
                //exec args
                // just save into args
                case AUT_EXEC_ARGS:
                {
                    //save args
                    for(int i = 0; i<tokenStruct.tt.execarg.count; i++)
                    {
                        //try create arg
                        // this sometimes fails, not sure why?
                        argument = [NSString stringWithUTF8String:tokenStruct.tt.execarg.text[i]];
                        if(nil == argument)
                        {
                            //next
                            continue;
                        }
                        
                        //add argument
                        [process.arguments addObject:argument];
                    }
                    
                    break;
                }
                    
                //exit
                // save status
                case AUT_EXIT:
                {
                    //save
                    process.exit = tokenStruct.tt.exit.status;
                    
                    break;
                }
                    
                //record trailer
                // end/save, etc
                case AUT_TRAILER:
                {
                    //end
                    if( (nil != process) &&
                        (YES == [self shouldProcessRecord:process.type]) )
                    {
                        //handle process exits
                        if(AUE_EXIT == process.type)
                        {
                            //handle
                            [self handleProcessExit:process];
                        }
                        
                        //handle process starts
                        else
                        {
                            //also try get process path
                            // this is the most 'trusted way' (since exec_args can change)
                            [process pathFromPid];
                            
                            //failed to get path at runtime
                            // if 'AUT_PATH' was something like '/dev/null' or '/dev/console' use arg[0]...yes this can be spoofed :/
                            if( ((0 == process.path.length) || (YES == [process.path hasPrefix:@"/dev/"])) &&
                                 (0 != process.arguments.count) )
                            {
                                //use arg[0]
                                process.path = process.arguments.firstObject;
                            }
                            
                            //save fork events
                            // this will have ppid that can be used for child events (exec/spawn, etc)
                            if(AUE_FORK == process.type)
                            {
                                //save
                                lastFork = process;
                            }
                            
                            //when we don't have a ppid
                            // see if there was a 'matching' fork() that has it (only for non AUE_FORK events)
                            else if( (-1 == process.ppid)  &&
                                     (lastFork.pid == process.pid) )
                            {
                                //update
                                process.ppid = lastFork.ppid;
                            }
                            
                            //handle new process
                            [self handleProcessStart:process];
                        }
                    }
                    
                    //unset
                    process = nil;
                    
                    break;
                }
                    
                    
                default:
                    ;
                    
            }//process token
            
            
            //add length of current token
            processedLength += tokenStruct.len;
            
            //subtract lenght of current token
            recordBalance -= tokenStruct.len;
        }
            
        }//autorelease
    
    } //while(YES)
    
bail:
    
    //free buffer
    if(NULL != recordBuffer)
    {
        //free
        free(recordBuffer);
        
        //unset
        recordBuffer = NULL;
    }
    
    //close audit pipe
    if(NULL != auditFile)
    {
        //close
        fclose(auditFile);
        
        //unset
        auditFile = NULL;
    }
    
    return;
}

项目地址:

https://github.com/objective-see/ProcInfo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值