1. 程式人生 > >mac中增強版進程查看類(RDProcess)

mac中增強版進程查看類(RDProcess)

cocoa

1.cocoa 自帶的進程查看信息太少 RDProcess增強版它可以檢查一個進程是否被沙箱化,搜索其包含路徑等
//apple_sandbox.h

/*
 * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the ‘License‘). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an ‘AS IS‘ basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */
#ifndef _SANDBOX_H_
#define _SANDBOX_H_

#include <sys/cdefs.h>
#include <stdint.h>
#include <unistd.h>

__BEGIN_DECLS
/*
 * @function sandbox_init
 * Places the current process in a sandbox with a profile as
 * specified.  If the process is already in a sandbox, the new profile
 * is ignored and sandbox_init() returns an error.
 *
 * @param profile (input)   The Sandbox profile to be used.  The format
 * and meaning of this parameter is modified by the `flags‘ parameter.
 *
 * @param flags (input)   Must be SANDBOX_NAMED.  All other
 * values are reserved.
 *
 * @param errorbuf (output)   In the event of an error, sandbox_init
 * will set `*errorbuf` to a pointer to a NUL-terminated string
 * describing the error. This string may contain embedded newlines.
 * This error information is suitable for developers and is not
 * intended for end users.
 *
 * If there are no errors, `*errorbuf` will be set to NULL.  The
 * buffer `*errorbuf` should be deallocated with `sandbox_free_error`.
 *
 * @result 0 on success, -1 otherwise.
 */
int sandbox_init(const char *profile, uint64_t flags, char **errorbuf);

/*
 * @define SANDBOX_NAMED  The `profile‘ argument specifies a Sandbox
 * profile named by one of the kSBXProfile* string constants.
 */
#define SANDBOX_NAMED        0x0001

#ifdef __APPLE_API_PRIVATE

/* The following flags are reserved for Mac OS X.  Developers should not
 * depend on their availability.
 */

/*
 * @define SANDBOX_NAMED_BUILTIN   The `profile‘ argument specifies the
 * name of a builtin profile that is statically compiled into the
 * system.
 */
#define SANDBOX_NAMED_BUILTIN    0x0002

/*
 * @define SANDBOX_NAMED_EXTERNAL   The `profile‘ argument specifies the
 * pathname of a Sandbox profile.  The pathname may be abbreviated: If
 * the name does not start with a `/‘ it is treated as relative to
 * /usr/share/sandbox and a `.sb‘ suffix is appended.
 */
#define SANDBOX_NAMED_EXTERNAL    0x0003

/*
 * @define SANDBOX_NAMED_MASK   Mask for name types: 4 bits, 15 possible
 * name types, 3 currently defined.
 */
#define SANDBOX_NAMED_MASK    0x000f

#endif /* __APPLE_API_PRIVATE */

/*
 * Available Sandbox profiles.
 */

/* TCP/IP networking is prohibited. */
extern const char kSBXProfileNoInternet[];

/* All sockets-based networking is prohibited. */
extern const char kSBXProfileNoNetwork[];

/* File system writes are prohibited. */
extern const char kSBXProfileNoWrite[];

/* File system writes are restricted to temporary folders /var/tmp and
 * confstr(_CS_DARWIN_USER_DIR, ...).
 */
extern const char kSBXProfileNoWriteExceptTemporary[];

/* All operating system services are prohibited. */
extern const char kSBXProfilePureComputation[];

/*
 * @function sandbox_free_error
 * Deallocates an error string previously allocated by sandbox_init.
 *
 * @param errorbuf (input)   The buffer to be freed.  Must be a pointer
 * previously returned by sandbox_init in the `errorbuf‘ argument, or NULL.
 *
 * @result void
 */
void sandbox_free_error(char *errorbuf);

#ifdef __APPLE_API_PRIVATE

/* The following definitions are reserved for Mac OS X.  Developers should not
 * depend on their availability.
 */

int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);

int sandbox_init_with_extensions(const char *profile, uint64_t flags, const char *const extensions[], char **errorbuf);

enum sandbox_filter_type {
    SANDBOX_FILTER_NONE,
    SANDBOX_FILTER_PATH,
    SANDBOX_FILTER_GLOBAL_NAME,
    SANDBOX_FILTER_LOCAL_NAME,
    SANDBOX_FILTER_APPLEEVENT_DESTINATION,
    SANDBOX_FILTER_RIGHT_NAME,
};

extern const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT __attribute__((weak_import));

enum sandbox_extension_flags {
    FS_EXT_DEFAULTS =              0,
    FS_EXT_FOR_PATH =       (1 << 0),
    FS_EXT_FOR_FILE =       (1 << 1),
    FS_EXT_READ =           (1 << 2),
    FS_EXT_WRITE =          (1 << 3),
    FS_EXT_PREFER_FILEID =  (1 << 4),
};

int sandbox_check(pid_t pid, const char *operation, enum sandbox_filter_type type, ...);

int sandbox_note(const char *note);

int sandbox_suspend(pid_t pid);
int sandbox_unsuspend(void);

int sandbox_issue_extension(const char *path, char **ext_token);
int sandbox_issue_fs_extension(const char *path, uint64_t flags, char **ext_token);
int sandbox_issue_fs_rw_extension(const char *path, char **ext_token);
int sandbox_issue_mach_extension(const char *name, char **ext_token);

int sandbox_consume_extension(const char *path, const char *ext_token);
int sandbox_consume_fs_extension(const char *ext_token, char **path);
int sandbox_consume_mach_extension(const char *ext_token, char **name);

int sandbox_release_fs_extension(const char *ext_token);

int sandbox_container_path_for_pid(pid_t pid, char *buffer, size_t bufsize);

int sandbox_wakeup_daemon(char **errorbuf);

const char *_amkrtemp(const char *);

#endif /* __APPLE_API_PRIVATE */

__END_DECLS
#endif /* _SANDBOX_H_ */

//RDProcess.h

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

typedef void (^RDProcessEnumerator)(id process, NSString *bundleID, BOOL *stop);

@interface RDProcess : NSObject

#pragma mark Initialization with PID

- (instancetype)init __attribute__((unavailable("use -initWithPID: instead")));
/**
 * Returns a process for specified PID with following fields pre-initiated:
 * - PID value
 * - Process name (via either LaunchServices API or argv[0]-based value)
 * - Bundle ID
 * - Executable path (via either LaunchServices API or proc_pidpath() or, if nothing else, argv[0]-based value)
 *
 * @param  {(pid_t}       aPid the PID of a target process
 */
- (instancetype)initWithPID: (pid_t)aPid;

#pragma mark Creation with Bundle ID

+ (instancetype)oldestProcessWithBundleID: (NSString *)bundleID;
+ (instancetype)youngestProcessWithBundleID: (NSString *)bundleID;
+ (void)enumerateProcessesWithBundleID: (NSString *)bundleID usingBlock: (RDProcessEnumerator)block;
+ (NSArray *)allProcessesWithBundleID: (NSString *)bundleID;

- (pid_t)pid;
/**
 * A name of the process (using either LaunchServices API or argv[0]-based value)
 *
 * @return this method should not return `nil` value, but the value may be invalid any other way,
 * so it‘s up to you to verify it.
 */
- (NSString *)processName;
/**
 * Sets a new title for the process.
 *
 * @brief
 *     This method sets a new value for LaunchServices‘ "Display Name" key of the process;
 *     Please, note that some utils like `ps` or `top` rather depend on an argv[0] value than
 *     on the "Display Name".
 * @param
 *     {NSString *} new title for the process
 * @return
 *     {BOOL}       Always NO (0)
 */
- (BOOL)setProcessName: (NSString *)new_proc_name;
/**
 * These methods will return (obviously) `nil` for non-bundled applications.
 */
- (NSString *)bundleID;
- (NSURL    *)bundleURL;
- (NSString *)bundlePath;

- (NSURL    *)executableURL;
- (NSString *)executablePath;
/**
 * UID, name and full name for a user who owns this process.
 */
- (uid_t)ownerUserID;
- (NSString *)ownerUserName;
- (NSString *)ownerFullUserName;
/**
 * List of groups of which the user is member of.
 *
 * @format: Keys are groupd ids, values are groups names;
 */
- (NSDictionary *)ownerGroups;

/**
 * Check if the process is sanboxed by OS X.
 *
 * @note
 *     this method returns YES for any process with invalid PID, so you should also check if
 *     [proc sandboxContainerPath] is not nil.
 *
 * @return {BOOL} YES or NO, or neither.
 */
- (BOOL)isSandboxedByOSX;
/**
 * Sandbox container path for the process (if it has one).
 * @return
 *     {NSString *} containter path or `nil` if the process is not sandboxed.
 */
- (NSString *)sandboxContainerPath;
- (NSURL    *)sandboxContainerURL;
- (BOOL)canWriteToFileAtPath: (NSString *)file_path;
- (BOOL)canWriteToFileAtURL:  (NSURL *)file_url;
- (BOOL)canReadFileAtPath: (NSString *)file_path;
- (BOOL)canReadFileAtURL:  (NSURL *)file_url;

/**
 * ARGV and ENV values of a process
 *
 * @brief
 *     Until the current user is not a member of `procmod` group, these method will work only for
 *     processes owned by this user (for other‘s processes they return `nil`).
 */
- (NSArray *)launchArguments;
/* @note variable values are percent escaped */
- (NSDictionary *)environmentVariables;

/* ------------------------{ NOT IMPLEMENTED YET }------------------------ */

/**
 * More sandbox stuff
 */
- (int)_enableSandbox __attribute__((unavailable("not implemented yet")));
- (BOOL)_isSandboxedByUser __attribute__((unavailable("not implemented yet")));
// gonna crash it down
- (int)_disableSandbox __attribute__((unavailable("not implemented yet")));

// Intel
- (NSString *)architectureString __attribute__((unavailable("not implemented yet")));
// smth like "Intel (64 bit)"
- (NSString *)kindString __attribute__((unavailable("not implemented yet")));
- (BOOL)is64bit __attribute__((unavailable("not implemented yet")));

// 0-100%
- (NSUInteger)CPUUsagePercentages __attribute__((unavailable("not implemented yet")));
// msec
- (NSUInteger)CPUTimeMsec __attribute__((unavailable("not implemented yet")));

- (NSUInteger)threadsCount __attribute__((unavailable("not implemented yet")));
- (NSUInteger)activeThreadsCount __attribute__((unavailable("not implemented yet")));
- (NSUInteger)inactiveThreadsCount __attribute__((unavailable("not implemented yet")));
- (NSUInteger)openPortsCount __attribute__((unavailable("not implemented yet")));

- (NSUInteger)memoryUsageRealBytes __attribute__((unavailable("not implemented yet")));
- (NSUInteger)memoryUsageRealPrivateBytes __attribute__((unavailable("not implemented yet")));
- (NSUInteger)memoryUsageRealSharedBytes __attribute__((unavailable("not implemented yet")));
- (NSUInteger)memoryUsageVirtualPrivateBytes __attribute__((unavailable("not implemented yet")));

- (NSUInteger)messagesSent __attribute__((unavailable("not implemented yet")));
- (NSUInteger)messagesReceived __attribute__((unavailable("not implemented yet")));

@end

//RDProcess.m

#import <grp.h>
#import <pwd.h>
#import <unistd.h>
#import <libproc.h>
#import <sys/sysctl.h>
#import <mach-o/dyld.h>
#import <sys/proc_info.h>

#import "RDProcess.h"
#import "apple_sandbox.h"

#define RDSymbolNameStr(symbol) (CFSTR("_"#symbol))
#define kPasswdBufferSize (128)
#define kSandboxContainerPathBufferSize (2024)
#define kLaunchServicesMagicConstant (-2) // or (-1), the difference is unknown

static CFTypeRef (*LSCopyApplicationInformation)(int, const void*, CFArrayRef) = NULL;
static CFTypeRef (*LSSetApplicationInformation)(int, CFTypeRef, CFDictionaryRef, void *) = NULL;
static CFTypeRef (*LSASNCreateWithPid)(CFAllocatorRef, pid_t) = NULL;

static CFStringRef (kLSDisplayNameKey) = NULL;
static CFStringRef (kLSPIDKey) = NULL;
static CFStringRef (kLSBundlePathKey) = NULL;
static CFStringRef (kLSExecutablePathKey) = NULL;

typedef NS_ENUM(NSUInteger, RDProcessForBundleIDEnumerationOption) {
    kRDProcessForBundleIDYoungest = 0,
    kRDProcessForBundleIDOldest,
    kRDProcessForBundleIDAll
};

static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");

@interface RDProcess()
{
    /* General *dynamic* info */
    pid_t _pid;
    NSString *_process_name;
    NSString *_bundle_id, *_bundle_path;
    NSString *_executable_path;
    uid_t _uid;
    NSString *_owner_user_name, *_owner_full_user_name;

    /* Sanboxing */
    BOOL _sandboxed; // sandboxed by OS X
    BOOL _sandboxed_by_user;
    NSString *_sandbox_container_path;

    /* stuff */
    NSArray *_launch_args;
    NSDictionary *_env_variables;

    /* Not implemented yet */
    NSString *kind_string;
    NSUInteger cpu_usage, cpu_time_msec;
    NSUInteger threads_count, open_ports_count;
    NSUInteger memory_real_bytes, memory_real_private_bytes,
    memory_real_shared_bytes, memory_virtual_private_bytes;
    NSUInteger messages_sent, messages_received;

    NSLock *lock;
}
+ (BOOL)_checkIfWeCanAccessPIDAtTheMoment: (pid_t)a_pid;
+ (NSArray *)_lookupForProcessesWithBundleID: (NSString *)bundleID options: (RDProcessForBundleIDEnumerationOption)option;

- (void)_requestOwnerNames;
- (BOOL)_requestProcessArgumentsAndEnvironment;
- (BOOL)_checkSandboxOperation: (const char *)operation forItemAtPath: (NSString *)item_path;

- (BOOL)_findLSPrivateSymbols;
- (void)_fetchNewDataFromLaunchServicesWithAtLeastOneKey: (CFStringRef)key;
- (void)_updateWithLSDictionary: (CFDictionaryRef)dictionary;
@end

@implementation RDProcess

- (instancetype)initWithPID: (pid_t)a_pid
{
    BOOL pid_is_available = [[self class] _checkIfWeCanAccessPIDAtTheMoment: a_pid];
    if (NO == pid_is_available) {
        return (nil);
    }
    if ((self = [super init])) {
        _pid = a_pid;
        _uid = -1;
        _owner_user_name = nil;
        _owner_full_user_name = nil;

        [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: NULL];
    }

    return (self);
}

+ (instancetype)oldestProcessWithBundleID: (NSString *)bundleID
{
    return [[self _lookupForProcessesWithBundleID: bundleID option: kRDProcessForBundleIDOldest] lastObject];
}

+ (instancetype)youngestProcessWithBundleID: (NSString *)bundleID
{
    return [[self _lookupForProcessesWithBundleID: bundleID option: kRDProcessForBundleIDYoungest] lastObject];
}

+ (void)enumerateProcessesWithBundleID: (NSString *)bundleID usingBlock: (RDProcessEnumerator)block
{
    if (!block) {
        return;
    }
    NSArray *procs = [self allProcessesWithBundleID: bundleID];
    if (!procs) {
        return;
    }
    [procs enumerateObjectsUsingBlock: ^(id process, NSUInteger idx, BOOL *stop){
        block(process, bundleID, stop);
    }];
}

+ (NSArray *)allProcessesWithBundleID: (NSString *)bundleID
{
    return [self _lookupForProcessesWithBundleID: bundleID option: kRDProcessForBundleIDAll];
}

+ (BOOL)_checkIfWeCanAccessPIDAtTheMoment: (pid_t)a_pid
{
    if (a_pid < 0) return NO;
    /**
     * kill(0) here is an indicator that we have a process with
     * such PID and can access it.
     */
    int err = kill(a_pid, 0);
    switch (err) {
        case (-1): {
            NSLog(@"Could not access pid (%d)", a_pid);
            return (errno != ESRCH);
        }
        case (0): {
            return YES;
        }
        default: {
            NSLog(@"Pid %d doesn‘t exist", a_pid);
            return NO;
        }
    }
}

+ (NSArray *)_lookupForProcessesWithBundleID: (NSString *)bundleID option: (RDProcessForBundleIDEnumerationOption)option
{
    if (bundleID.length == 0) {
        return (nil);
    }

    ProcessSerialNumber psn = {0, kNoProcess};
    UInt32 oldest_proc_launch_date   = UINT32_MAX,
    youngest_proc_launch_date = 0;

    NSMutableArray *procs = nil;
    BOOL find_youngest = (option == kRDProcessForBundleIDYoungest);
    BOOL find_oldest   = (option == kRDProcessForBundleIDOldest);
    pid_t target_pid   = (-1);
    BOOL find_all      = !(find_youngest || find_oldest);
    if (find_all) {
        procs = [[NSMutableArray alloc] init];
    }
    /**
     * Seems like the only *public* way to iterate over all running processes is GetNextProcess()
     * which is a simple wrapper for LSCopyRunningApplicationArray().
     *
     * So, @todo: use LSCopyRunningApplicationArray() directly.
     */
    while (KERN_SUCCESS == GetNextProcess(&psn)) {
        struct ProcessInfoRec info = {0};
        info.processInfoLength = sizeof(&info);
        int err = GetProcessInformation(&psn, &info);
        if (err != KERN_SUCCESS) {
            NSLog(@"GetProcessInformation returned %d: %@",
                  err, [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo: nil]);
            continue;
        }

        pid_t pid = 0;
        GetProcessPID(&psn, &pid);
        CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);

        CFDictionaryRef proc_info = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, NULL);
        CFRelease(asn);
        if (!proc_info) {
            continue;
        }

        CFStringRef found_bundle_id = CFDictionaryGetValue(proc_info, kCFBundleIdentifierKey);
        if (!found_bundle_id) {
            CFRelease(proc_info);
            continue;
        }
        BOOL (^checkIfBundleIDMatches)(CFStringRef, CFStringRef) = ^BOOL(CFStringRef a, CFStringRef b) {
            return (CFStringCompare(a, b, 0) == kCFCompareEqualTo);
        };

        if (find_oldest && info.processLaunchDate < oldest_proc_launch_date) {
            if (checkIfBundleIDMatches(found_bundle_id, (__bridge CFStringRef)bundleID)) {
                oldest_proc_launch_date = info.processLaunchDate;
                target_pid = pid;
            }
        }
        if (find_youngest && info.processLaunchDate > youngest_proc_launch_date) {
            if (checkIfBundleIDMatches(found_bundle_id, (__bridge CFStringRef)bundleID)) {
                youngest_proc_launch_date = info.processLaunchDate;
                target_pid = pid;
            }
        }
        if (find_all) {
            if (checkIfBundleIDMatches(found_bundle_id, (__bridge CFStringRef)bundleID)) {
                [procs addObject: [[RDProcess alloc] initWithPID: pid]];
            }
        }

        CFRelease(proc_info);
    }

    if (find_all) {
        NSArray *result = [NSArray arrayWithArray: procs];
       // [procs release];
        return (result);

    } else {
        if (target_pid == (-1)) {
            return nil;
        } else {
            return @[[[RDProcess alloc] initWithPID: target_pid]];
        }
    }
}

- (NSString *)description
{
    return [NSString stringWithFormat: @"<%@: %@ (%@/%d) owned by %@ (%d)>",
            NSStringFromClass([self class]), self.processName, self.bundleID, self.pid,
            self.ownerUserName, self.ownerUserID];
}

- (NSString *)processName
{
    if (!_process_name) {
        [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kLSDisplayNameKey];

        if (!_process_name) {
            //_process_name = [[self.executablePath lastPathComponent] retain];
        }
    }

    return _process_name;
}

- (BOOL)setProcessName: (NSString *)new_proc_name
{
    if (self.processName.length == 0 || new_proc_name.length == 0) {
        return NO;
    }
    const char *pConstChar = [new_proc_name UTF8String];
//    CFDictionaryRef tmp_dict = CFDictionaryCreate(kCFAllocatorDefault,
//                                                  (const void **)&kLSDisplayNameKey, (const void **)&new_proc_name,
//                                                  1,
//                                                  &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionaryRef tmp_dict = CFDictionaryCreate(kCFAllocatorDefault,
                                                  (const void **)&kLSDisplayNameKey, (const void **)&pConstChar,
                                                  1,
                                                  &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    LSSetApplicationInformation(kLaunchServicesMagicConstant,
                                LSASNCreateWithPid(kCFAllocatorDefault, self.pid),
                                tmp_dict,
                                NULL);
    CFRelease(tmp_dict);
    /* Force updating the process name value via LaunchServices */
    [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kLSDisplayNameKey];

    return NO;
}

- (pid_t)pid
{
    /**
     * I‘m not sure
     * 1) if PID is likely to change during a process lifetime and
     * 2) if we should ask LS for a PID every time this method gets called;
     *
     * @todo: make sure about both points above.
     */
    [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kLSPIDKey];

    return _pid;
}

- (NSString *)bundleID
{
    if (!_bundle_id) {
        [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kCFBundleIdentifierKey];
        if (!_bundle_id && _executable_path) {
            /* Maybe we have a bundle even if Launch Services said no? */
            NSString *path = [_executable_path stringByDeletingLastPathComponent]; // remove executable name
            path = [path stringByReplacingOccurrencesOfString: @"/Contents/MacOS" withString: @""];
            NSBundle *lasthope = [NSBundle bundleWithPath: path];
            _bundle_id = [lasthope bundleIdentifier];

            /* Let‘s also fix the bundle path */
            if (!_bundle_path) {
               // _bundle_path = [path retain];
            }
        }
    }

    return _bundle_id;
}

- (NSURL *)bundleURL
{
    if (!self.bundlePath) {
        return (nil);
    }

    return [NSURL fileURLWithPath: self.bundlePath];
}

- (NSString *)bundlePath
{
    if (!_bundle_path) {
        [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kLSBundlePathKey];
    }

    return _bundle_path;
}

- (NSURL *)executableURL
{
    if (!self.executablePath) {
        return (nil);
    }
    return [NSURL fileURLWithPath: self.executablePath];
}

- (NSString *)executablePath
{
    if (!_executable_path) {
        /* First we ask LaunchServies API */
        [self _fetchNewDataFromLaunchServicesWithAtLeastOneKey: kLSExecutablePathKey];
        /* If it fails, ask for argv[0] */
        if (!_executable_path) {
           // _executable_path = [[self.launchArguments objectAtIndex: 0] retain];
        }
        /* If argv[0] doesn‘t exist (which is unlikely to happen, but anyway), use `proc_pidpath()`*/
        if (!_executable_path) {
            char *buf = malloc(sizeof(*buf) * kSandboxContainerPathBufferSize);
            int err = proc_pidpath(self.pid, buf, kSandboxContainerPathBufferSize);
            if (err) {
                _executable_path = [NSString stringWithUTF8String: buf];
            }
            free(buf);
        }
    }

    return _executable_path;
}

- (uid_t)ownerUserID
{
    if (_uid == -1) {
        pid_t current_pid = self.pid;
        struct kinfo_proc process_info;
        int ctl_args[4] = {
            CTL_KERN, KERN_PROC, KERN_PROC_PID, current_pid
        };
        size_t info_size = sizeof(process_info);
        int err = sysctl(ctl_args, 4, &process_info, &info_size, NULL, 0);
        if (err == KERN_SUCCESS && info_size > 0) {
            _uid = process_info.kp_eproc.e_ucred.cr_uid;
        }
    }
    return _uid;
}

- (void)_requestOwnerNames
{
    if (_owner_user_name && _owner_full_user_name) {
        return;
    }

    struct passwd user_data, *tmp = NULL;
    uid_t user_id = [self ownerUserID];
    if (user_id == -1) {
        return;
    }
    char* buffer = malloc(sizeof(*buffer) * kPasswdBufferSize);
    int err = getpwuid_r(user_id, &user_data, buffer, kPasswdBufferSize, &tmp);
    if (err != KERN_SUCCESS) {
        free(buffer);
        return;
    }

    _owner_full_user_name = [[NSString stringWithUTF8String: user_data.pw_gecos] copy];
    _owner_user_name      = [[NSString stringWithUTF8String: user_data.pw_name] copy];

    free(buffer);
}

- (NSString *)ownerUserName
{
    [self _requestOwnerNames];
    return (_owner_user_name);
}

- (NSString *)ownerFullUserName
{
    [self _requestOwnerNames];
    return (_owner_full_user_name);
}

- (NSDictionary *)ownerGroups
{
    NSDictionary *result = nil;

    int ngroups = NGROUPS_MAX;
    int *gr_bytes = malloc(sizeof(*gr_bytes) * ngroups);
    const char *user_name = [self.ownerUserName UTF8String];
    if (!user_name) {
        return result;
    }
    getgrouplist(user_name, 12, gr_bytes, &ngroups);
    if (ngroups == 0) {
        /* will it ever happen? */
        return result;
    }

    NSMutableDictionary *tmp_dict = [[NSMutableDictionary alloc] initWithCapacity: ngroups];
    for (int i = 0; i < ngroups; i++) {
        struct group *some_group = getgrgid(gr_bytes[i]);
        if (!some_group) { continue; }
        [tmp_dict setObject: [NSString stringWithUTF8String: some_group->gr_name]
                     forKey: [NSNumber numberWithUnsignedInt: gr_bytes[i]]];
    }

    result = [NSDictionary dictionaryWithDictionary: tmp_dict];
    free(gr_bytes);
   // [tmp_dict release];
    return result;
}

#pragma mark
#pragma mark Inspecting process
#pragma mark

- (BOOL)_requestProcessArgumentsAndEnvironment
{
    /* Max size of arguments (KERN_ARGMAX) */
    int request_argmax[2] = {
        CTL_KERN, KERN_ARGMAX
    };

    int argmax = 0;
    size_t size = sizeof(argmax);
    int err = sysctl(request_argmax, 2, &argmax, &size, NULL, 0);
    if (err != KERN_SUCCESS) {
        NSLog(@"[%d] sysctl failed in method %s", __LINE__, __PRETTY_FUNCTION__);
        return (NO);
    }

    /* Request arguments pointer */
    uint8_t *arguments = malloc(argmax);
    if (!arguments) {
        return (NO);
    }

    pid_t current_pid = self.pid;
    int request_args[3] = {
        CTL_KERN, KERN_PROCARGS2, current_pid
    };
    size = argmax;
    err = sysctl(request_args, 3, arguments, &size, NULL, 0);
    if (err != KERN_SUCCESS) {
        free(arguments);
        NSLog(@"[%d] sysctl failed in method %s", __LINE__, __PRETTY_FUNCTION__);
        return (NO);
    }

    int argc = *arguments;
    int counter = 0;
    uint8_t *arguments_ptr = arguments;
    // skip `argc`
    arguments += sizeof(argc);
    // skip `exec_path` which is a duplicate of argv[0]
    arguments += strlen((const char *)arguments);

    if (argc <= 0) {
        free(arguments_ptr);
        NSLog(@"argc <= 0; weird :(");
        return (NO);
    }

    NSMutableArray *tmp_argv = [[NSMutableArray alloc] initWithCapacity: argc];
    NSMutableDictionary *tmp_env = [[NSMutableDictionary alloc] init];
    for (int i = 0; i < size;) {
        if ((*(arguments+i)) == ‘\0‘) {
            i++;
        }
        const char *arg = (const char *)(arguments+i);
        if (strlen(arg) > 0) {
            if (counter < argc) {
                [tmp_argv addObject: [NSString stringWithUTF8String: arg]];

            } else {
                /* Parse env vars */
                NSArray *parts = [[NSString stringWithUTF8String: arg]
                                  componentsSeparatedByString: @"="];
                /**
                 * Sometimes environment variable pair contains only the key, so
                 * let‘s handle it correctly.
                 */
                NSString *value = (parts.count > 1) ? parts[1] : @"";
                [tmp_env setObject: [value stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]
                            forKey: parts[0]];
            }
            ++counter;
            i += strlen(arg);
        } else {
            i++;
        }
    }
    if (_launch_args) {
       // [_launch_args release];
    }
    _launch_args = [[NSArray alloc] initWithArray: tmp_argv copyItems: NO];
   // [tmp_argv release];

    if (_env_variables) {
       // [_env_variables release];
    }
    _env_variables = [[NSDictionary alloc] initWithDictionary: tmp_env];
   // [tmp_env release];

    free(arguments_ptr);

    return (YES);
}

- (NSArray *)launchArguments
{
    if (!_launch_args) {
        [self _requestProcessArgumentsAndEnvironment];
    }

    return (_launch_args);
}

- (NSDictionary *)environmentVariables
{
    if (!_env_variables) {
        [self _requestProcessArgumentsAndEnvironment];
    }

    return (_env_variables);
}

#pragma mark
#pragma mark Sandbox
#pragma mark

/**
 * Returns YES if a proccess is living in Sandbox environment.
 *
 * NOTE: this may return wrong result if the process was sandboxed by user, not by OS X.
 * Use `_isSandboxedByUser` to make sure you get corrent results;
 *
 * NOTE: this method also returns YES for any process with *invalid* PID, so it may be
 * better to check if `-sandboxContainerPath` is not equal to `nil` to find out that
 * the process is actually sandboxed.
 */
- (BOOL)isSandboxedByOSX
{
    static pid_t old_pid = -1;
    pid_t new_pid = self.pid;
    if (old_pid != new_pid) {
        old_pid = new_pid;
        _sandboxed = sandbox_check(self.pid, NULL, SANDBOX_FILTER_NONE);
    }

    return (_sandboxed);
}

- (NSString *)sandboxContainerPath
{
    if (!_sandbox_container_path) {
        char *buf = malloc(sizeof(*buf) * kSandboxContainerPathBufferSize);
        int err = sandbox_container_path_for_pid(_pid, buf, kSandboxContainerPathBufferSize);
        if (err == KERN_SUCCESS && strlen(buf) > 0) {
          //  _sandbox_container_path = [[NSString stringWithUTF8String: buf] retain];
        }

        free(buf);
    }

    return (_sandbox_container_path);
}

- (NSURL *)sandboxContainerURL
{
    return [NSURL fileURLWithPath: self.sandboxContainerPath];
}

- (BOOL)_checkSandboxOperation: (const char *)operation forItemAtPath: (NSString *)item_path
{
    BOOL result = NO;
    if (strlen(operation) == 0 || item_path.length == 0) {
        return result;
    }

    result = (KERN_SUCCESS == sandbox_check(self.pid, operation,
                                            (SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT), [item_path UTF8String]));

    return (result);
}

/* @todo: "job-creation", anyone? */

- (BOOL)canReadFileAtPath: (NSString *)file_path
{
    return [self _checkSandboxOperation: "file-read-data" forItemAtPath: file_path];
}

- (BOOL)canReadFileAtURL: (NSURL *)file_url
{
    return [self canReadFileAtPath: [file_url path]];
}

- (BOOL)canWriteToFileAtPath: (NSString *)file_path
{
    return [self _checkSandboxOperation: "file-write-data" forItemAtPath: file_path];
}

- (BOOL)canWriteToFileAtURL: (NSURL *)file_url
{
    return [self canWriteToFileAtPath: [file_url path]];
}

/**
 * Enable or disable(?) custom sanbox for the process.
 */
#pragma mark Custom Sandboxing

/**
 * to be implemented
 * @return {int} [description]
 */
- (int)_enableSandbox
{
    if ([self isSandboxedByOSX]) {
        return KERN_FAILURE;
    }
    if ([self _isSandboxedByUser]) {
        return KERN_SUCCESS;
    }
    return KERN_FAILURE;
}

/**
 * to be implemented
 * @return {int} [description]
 */
- (BOOL)_isSandboxedByUser
{
    return NO;
}
/**
 * to be implemented
 * @return {int} [description]
 */
- (int)_disableSandbox
{
    return KERN_FAILURE;
}

#pragma mark
#pragma mark LaunchServices Magic
#pragma mark

- (void)_fetchNewDataFromLaunchServicesWithAtLeastOneKey: (CFStringRef)key
{
    [lock lock];
    if (!LSCopyApplicationInformation) {
        if ( ! [self _findLSPrivateSymbols]) {
            goto done;
        }
    }

    CFArrayRef request_array = NULL;
    if (key) {
        request_array = CFArrayCreate(NULL, (const void **)key, 1, NULL);
    }

    CFDictionaryRef ls_update = LSCopyApplicationInformation(kLaunchServicesMagicConstant, LSASNCreateWithPid(NULL, _pid), request_array);
    if (!ls_update) {
        goto done;
    }

    [self _updateWithLSDictionary: ls_update];
    CFRelease(ls_update);

done: {
    [lock unlock];
    return;
}
}

- (void)_updateWithLSDictionary: (CFDictionaryRef)dictionary
{
    CFTypeRef tmp = NULL;
    if (CFDictionaryGetValueIfPresent(dictionary, kLSPIDKey, &tmp)) {
        CFNumberGetValue(tmp, kCFNumberIntType, &_pid);
    }
    tmp = NULL;
    if (CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, &tmp)) {
       // if (_process_name) [_process_name release];
       // _process_name = [[NSString stringWithString: tmp] retain];
    }
    tmp = NULL;
    if (CFDictionaryGetValueIfPresent(dictionary, kCFBundleIdentifierKey, &tmp)) {
      //  if (_bundle_id) [_bundle_id release];
      //  _bundle_id = [[NSString stringWithString: tmp] retain];
    }
    tmp = NULL;
    if (CFDictionaryGetValueIfPresent(dictionary, kLSBundlePathKey, &tmp)) {
       // if (_bundle_path) [_bundle_path release];
       // _bundle_path = [[NSString stringWithString: tmp] retain];
    }
    tmp = NULL;
    if (CFDictionaryGetValueIfPresent(dictionary, kLSExecutablePathKey, &tmp)) {
      //  if (_executable_path) [_executable_path release];
      //  _executable_path = [[NSString stringWithString: tmp] retain];
    }
}

- (BOOL)_findLSPrivateSymbols
{

    CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
    if (!launch_services_bundle) { return NO; }

    LSCopyApplicationInformation = CFBundleGetFunctionPointerForName(launch_services_bundle, RDSymbolNameStr(LSCopyApplicationInformation));
    if (!LSCopyApplicationInformation) { return NO; }
    NSLog(@"LSCopyApplicationInformation = %p", LSCopyApplicationInformation);

    LSASNCreateWithPid = CFBundleGetFunctionPointerForName(launch_services_bundle, RDSymbolNameStr(LSASNCreateWithPid));
    if (!LSASNCreateWithPid) { return NO; }
    NSLog(@"LSASNCreateWithPid = %p", LSASNCreateWithPid);

    LSSetApplicationInformation = CFBundleGetFunctionPointerForName(launch_services_bundle, RDSymbolNameStr(LSSetApplicationInformation));
    if (!LSSetApplicationInformation) { return NO; }

    kLSDisplayNameKey = *(CFStringRef *)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSDisplayNameKey));
    if (!kLSDisplayNameKey) { return NO; }
    NSLog(@"kLSDisplayNameKey = %p (%@)", kLSDisplayNameKey, (__bridge id)kLSDisplayNameKey);

    kLSPIDKey = *(CFStringRef *)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSPIDKey));
    if (!kLSPIDKey) { return NO; }
    NSLog(@"kLSPIDKey = %p (%@)", kLSPIDKey, (__bridge id)kLSPIDKey);

    kLSBundlePathKey = *(CFStringRef *)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSBundlePathKey));
    if (!kLSBundlePathKey) { return NO; }
    NSLog(@"kLSBundlePathKey = %p (%@)", kLSBundlePathKey, (__bridge id)kLSBundlePathKey);

    kLSExecutablePathKey = *(CFStringRef *)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSExecutablePathKey));
    if (!kLSExecutablePathKey) { return NO; }
    NSLog(@"kLSExecutablePathKey = %p (%@)", kLSExecutablePathKey, (__bridge id)kLSExecutablePathKey);

    /******************************************************/
    return YES;
}
@end

//main.m

//
//  main.m
//  testmac
//
//  Created by Allenboy on 2018/5/13.
//  Copyright ? 2018年 Allenboy. All rights reserved.
//
#import "RDProcess.h"
#include <mach-o/dyld.h>
#import <Cocoa/Cocoa.h>
static void print_usage(const char *prog_name)
{
    printf("Usage: %s [pid]\nIf no pid specified, getpid() is used\n\n", prog_name);
}
void pid( pid_t pid);
int main(int argc, const char * argv[]) {

    NSArray *runningApps = [[NSWorkspace sharedWorkspace] runningApplications];
    for(int i=0;i<runningApps.count;i++){
        NSRunningApplication *app = [runningApps objectAtIndex:i];
        //進程 pid
        NSLog(@"----------------------------------------進程 pid:%d--------------------------------------", app.processIdentifier);
        pid(app.processIdentifier);
//        //進程的url
//        NSLog(@"進程 bundleURL:%@", app.bundleURL);
//        NSLog(@"進程 bundleIdentifier:%@", app.bundleIdentifier);
//        // 可執行文件 url
//        NSLog(@"進程 executableURL:%@", app.executableURL);
//        NSLog(@"進程 executableArchitecture:%ld", (long)app.executableArchitecture);
//        //進程名稱
//        NSLog(@"進程 name:%@", app.localizedName);
    }

//    pid_t pid = (-1);
//    if (argc < 2) {
//        print_usage(argv[0]);
//        pid = getpid();
//    } else {
//       // pid = strtol(argv[1], NULL, 10);  //字符串轉 類型為 long int 型  最後一個為幾進制
//    }
//

   // [proc release];

    //return NSApplicationMain(argc, argv);
}
void pid( pid_t pid){
    RDProcess *proc = [[RDProcess alloc] initWithPID: 75712];
    if (!proc) {
        NSLog(@"Could not create RDProcess with invalid PID (%d)", pid);
        return;
    }
    NSLog(@"Proc general: %@", proc);

    NSLog(@"PID: %d", proc.pid);
    NSLog(@"Name: %@", proc.processName);
    NSLog(@"Bundle ID: %@", proc.bundleID);
    NSLog(@"Bundle URL: %@", proc.bundleURL);
    NSLog(@"Executable URL: %@", proc.executableURL);
    NSLog(@"Owner: %@, %@ (%d)", proc.ownerUserName, proc.ownerFullUserName, proc.ownerUserID);

    NSDictionary *tmp = proc.ownerGroups;
    if (tmp.count > 0) {
        NSMutableString *owner_groups = [[NSMutableString alloc] init];
        [tmp enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [owner_groups appendFormat:@"%@(%@), ", obj, key];
        }];

        NSLog(@"Owner groups (%lu): %@",
              [tmp allKeys].count, [owner_groups substringToIndex: owner_groups.length-2]);
        // [owner_groups release];
    }

    NSLog(@"Sandboxed by OS X (unreliable): %@", proc.isSandboxedByOSX ? @"YES" : @"NO");
    NSLog(@"Sandbox container: %@", proc.sandboxContainerPath);

    NSArray *paths = @[
                       @"/usr/bin",
                       @"~/Library/Fonts",
                       @"~/Library/Colors",
                       @"~/Desktop",
                       @"/",
                       @"~/Library/Container/com.apple.Preview/Data/Library",
                       proc.executablePath
                       ];
    if (proc.sandboxContainerPath) {
        paths = [paths arrayByAddingObject: proc.sandboxContainerPath];
    }
    [paths enumerateObjectsUsingBlock: ^(id path, NSUInteger idx, BOOL *stop){
        NSLog(@"Sandbox file permissions {%@%@} for [%@]:\t",
              [proc canReadFileAtPath: [path stringByExpandingTildeInPath]] ? @"R" : @"-",
              [proc canWriteToFileAtPath: [path stringByExpandingTildeInPath]] ? @"W" : @"-",
              path);
    }];

    NSLog(@"Arguments: %@", proc.launchArguments);
    NSLog(@"Environment: %@", proc.environmentVariables);

    // proc.processName = [proc.processName stringByAppendingString: @" (RDProcess)"];

    NSLog(@"All processes with the same Bundle ID:");
    [RDProcess enumerateProcessesWithBundleID: proc.bundleID
                                   usingBlock:^(id process, NSString *bundleID, BOOL *stop){
                                       NSLog(@"\t* %@", process);
                                   }];
    NSLog(@"And again, here they are:");
    NSLog(@"%@", [RDProcess allProcessesWithBundleID: proc.bundleID]);
    NSLog(@"The youngest process: %@", [RDProcess youngestProcessWithBundleID: proc.bundleID]);
    NSLog(@"The oldest process: %@", [RDProcess oldestProcessWithBundleID: proc.bundleID]);
}

mac中增強版進程查看類(RDProcess)