Any better way to determine if file is executable?

  • I've searched the list archives and no one seems to have a good way to
    do this.
    As discussed before, NSFileManager's isExecutableAtPath: returns YES
    for directories and any other file with the execute bit set.
    This isn't what I want so I've had to fudge together a solution.

    Basically, I'm using NSTask and executing /usr/bin/file on all
    suspected executable files and parsing the output for Mach-O.
    BTW, can anyone tell me what the program "file" will output for CFM
    binaries?  I don't know of any that I can test it on to see.

    The reason I'm doing this:
    This is my first real Cocoa application and it's for a university
    assignment.  We can use any language we want
    so I figured, why not use Cocoa and learn something, which I have
    learned quite a lot of already.

    The assignment is, take any input directories, parse them recursively
    for executables, either applications, dylibs, frameworks, and
    do a hash on them.  Store this all in an encrypted file.

    Change a binary somehow and recompute all the hashes.  Determine if
    any binary has changed.

    My method of using NSTask works just fine, but I'm wondering if
    there's a faster method, because launching hundreds
    of processes can't be very fast.
    It is "fast enough" but takes a few minutes when run against a few
    Adobe folders because they have a zillion folders and files
    with literally hundreds of executables like frameworks and plugins in
    the application bundles.

    Here's my code, which I'm running in an NSOperation.  I'm using GC, so
    forgive if my code doesn't use good non-GC memory
    management, however some things I put in just to get the hang of best
    practice memory management, even though
    I know it won't do anything with GC turned on.

    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSMutableArray* executables = [[NSMutableArray alloc] init];
    NSDirectoryEnumerator* enumerator = [fileManager
    enumeratorAtPath:rootPath];
    BOOL isEx = NO;
    BOOL isDir = NO;
    BOOL isSymbolicLink = NO;

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    for(NSString* file in enumerator)
    {
      NSString* fullPath = [NSString stringWithFormat:@"%@/%@", rootPath,
    file];
      isEx = [fileManager isExecutableFileAtPath:fullPath];
      NSDictionary* attributes = [fileManager
    fileAttributesAtPath:fullPath traverseLink:NO];
      NSString* fileType = [attributes valueForKey:NSFileType];
      isDir = [fileType isEqualToString:NSFileTypeDirectory];
      isSymbolicLink = [fileType isEqualToString:NSFileTypeSymbolicLink];

      //preliminary checks to rule out some files
      //directories have the execute bit set so
      //some files may be executable but not actually an executable binary
      if(isEx && !isDir && !isSymbolicLink)
      {

      //this will execute the "file" command which will output
      //to the command line what the file type is
      //can use this to get information about if it's an executable or not
      NSTask* theProcess = [[NSTask alloc] init];
      [theProcess setLaunchPath:@"/usr/bin/file"];
      [theProcess setArguments:[NSArray arrayWithObject:fullPath]];
      [theProcess setStandardOutput:[NSPipe pipe]];
      [theProcess setStandardError:[theProcess standardOutput]];
      [theProcess launch];
      [theProcess waitUntilExit];

      if([theProcess terminationStatus] == 0)
      {
        NSData* result = [[[theProcess standardOutput]
    fileHandleForReading] readDataToEndOfFile];
        NSMutableString* resultText = [[NSMutableString alloc]
    initWithData:result encoding:NSUTF8StringEncoding];
        NSRange loc = [resultText rangeOfString:@"Mach-O"];
        if(loc.length > 0)
        {
        [executables addObject:fullPath];
        //NSLog(fullPath);
        NSDictionary* info = [NSDictionary
    dictionaryWithObjectsAndKeys:fullPath, @"executable", nil];


        if(![self isCancelled])
        {
          [[NSNotificationCenter defaultCenter]
    postNotificationName:FileFound object:nil userInfo:info];
        }
        }
      }
      else
        NSLog(@"failed to execute shell command");

      [theProcess release];
      }
    }
    [pool release];

    Hopefully someone has read this far, and knows any better way.

    Thanks,
    Devon.
  • On 10/30/07, Devon Ferns <dferns...> wrote:
    > I've searched the list archives and no one seems to have a good way to
    > do this.
    > As discussed before, NSFileManager's isExecutableAtPath: returns YES
    > for directories and any other file with the execute bit set.
    > This isn't what I want so I've had to fudge together a solution.
    >
    > Basically, I'm using NSTask and executing /usr/bin/file on all
    > suspected executable files and parsing the output for Mach-O.
    > BTW, can anyone tell me what the program "file" will output for CFM
    > binaries?  I don't know of any that I can test it on to see.

    You could look up the on disk format for Mach-O and parse a small bit
    of the files yourself and avoid calling out to file.

    I bet you can find the source for file in the darwin source code
    (<http://www.opensource.apple.com/darwinsource/>) which would make
    your job a little easier.

    -Shawn
  • On Oct 30, 2007, at 5:04 AM, Devon Ferns wrote:

    > I've searched the list archives and no one seems to have a good way
    > to do this.
    > As discussed before, NSFileManager's isExecutableAtPath: returns YES
    > for directories and any other file with the execute bit set.
    > This isn't what I want so I've had to fudge together a solution.
    >
    > Basically, I'm using NSTask and executing /usr/bin/file on all
    > suspected executable files and parsing the output for Mach-O.
    > BTW, can anyone tell me what the program "file" will output for CFM
    > binaries?  I don't know of any that I can test it on to see.

    /usr/bin/file may not be the best match for your needs.  It's designed
    to be highly flexible, to handle files of many different types, and to
    produce detailed human-readable output; it's not really intended to be
    highly efficient or to produce output that can be readily parsed.  As
    it happens, executable files generally can be identified (for sniffing
    purposes) simply by reading their first four bytes.  For Mach-o files,
    the possibilities are FAT_MAGIC, FAT_CIGAM, MH_MAGIC, MH_CIGAM,
    MH_MAGIC_64, and MH_CIGAM_64.  Of course, this doesn't rule out the
    possibility of encountering a file that starts with one of these bytes
    but isn't actually formatted as a Mach-o executable, but then neither
    does using /usr/bin/file.

    You might want to think about what you consider to be an executable.
    Does an executable shell script count?  How about a Java jar-file?  Do
    you care about executables from other platforms?  (By the way, file
    reports PowerPC PEF/CFM executables as "header for PowerPC PEF
    executable", and their first four bytes are 'Joy!', but be sure to
    take endianness into account.)

    Douglas Davidson
  • Thanks Doug and Shawn.  I had considered reading the file but was
    hoping for some framework function might already do something like
    what I need.
    For this assignment I suppose it's "easy" enough to use file because
    it's not really a big deal and I have many many other things to do at
    the moment.
    For the assignment it doesn't matter the language or how I accomplish
    the end goal so for now, I think I might stick with /usr/bin/file.

    Although it doesn't seem like too much work to read 4 bytes from a
    file, I'll probably look at it anyways.  It doesn't matter to me the
    endianness at the moment because I'm running this on my MBP and I just
    have to demonstrate that it works.

    Now that I know what to look for(FAT_MAGIC, FAT_CIGAM, etc.) I came
    across this example from Apple.  I'll post if for the sake of any
    people in the future searching the list archives.  Really, it's almost
    exactly what I was looking to do.
    http://developer.apple.com/samplecode/CheckExecutableArchitecture/listing1.
    html


    Although, it's C, not Objective-C, I'm sure it's fairly easy to do
    something like that in Objective-C.

    Devon.

    On 30-Oct-07, at 3:10 PM, Douglas Davidson wrote:

    >
    > On Oct 30, 2007, at 5:04 AM, Devon Ferns wrote:
    >
    >> I've searched the list archives and no one seems to have a good way
    >> to do this.
    >> As discussed before, NSFileManager's isExecutableAtPath: returns
    >> YES for directories and any other file with the execute bit set.
    >> This isn't what I want so I've had to fudge together a solution.
    >>
    >> Basically, I'm using NSTask and executing /usr/bin/file on all
    >> suspected executable files and parsing the output for Mach-O.
    >> BTW, can anyone tell me what the program "file" will output for CFM
    >> binaries?  I don't know of any that I can test it on to see.
    >
    > /usr/bin/file may not be the best match for your needs.  It's
    > designed to be highly flexible, to handle files of many different
    > types, and to produce detailed human-readable output; it's not
    > really intended to be highly efficient or to produce output that can
    > be readily parsed.  As it happens, executable files generally can be
    > identified (for sniffing purposes) simply by reading their first
    > four bytes.  For Mach-o files, the possibilities are FAT_MAGIC,
    > FAT_CIGAM, MH_MAGIC, MH_CIGAM, MH_MAGIC_64, and MH_CIGAM_64.  Of
    > course, this doesn't rule out the possibility of encountering a file
    > that starts with one of these bytes but isn't actually formatted as
    > a Mach-o executable, but then neither does using /usr/bin/file.
    >
    > You might want to think about what you consider to be an
    > executable.  Does an executable shell script count?  How about a
    > Java jar-file?  Do you care about executables from other platforms?
    > (By the way, file reports PowerPC PEF/CFM executables as "header for
    > PowerPC PEF executable", and their first four bytes are 'Joy!', but
    > be sure to take endianness into account.)
    >
    > Douglas Davidson
  • Since Objective-C is a true superset of C, any valid C code is also valid Objective-C. So the code you found will work exactly the same.

    Cheers,
    Chuck

    ----- Original Message ----
    From: Devon Ferns <dferns...>
    To: Douglas Davidson <ddavidso...>
    Cc: Cocoa-Dev Mail <cocoa-dev...>
    Sent: Tuesday, October 30, 2007 12:32:32 PM
    Subject: Re: Any better way to determine if file is executable?

    Thanks Doug and Shawn.  I had considered reading the file but was
    hoping for some framework function might already do something like
    what I need.
    For this assignment I suppose it's "easy" enough to use file because
    it's not really a big deal and I have many many other things to do at
    the moment.
    For the assignment it doesn't matter the language or how I accomplish
    the end goal so for now, I think I might stick with /usr/bin/file.

    Although it doesn't seem like too much work to read 4 bytes from a
    file, I'll probably look at it anyways.  It doesn't matter to me the
    endianness at the moment because I'm running this on my MBP and I just

    have to demonstrate that it works.

    Now that I know what to look for(FAT_MAGIC, FAT_CIGAM, etc.) I came
    across this example from Apple.  I'll post if for the sake of any
    people in the future searching the list archives.  Really, it's almost

    exactly what I was looking to do.
    http://developer.apple.com/samplecode/CheckExecutableArchitecture/listing1.
    html


    Although, it's C, not Objective-C, I'm sure it's fairly easy to do
    something like that in Objective-C.

    Devon.

    On 30-Oct-07, at 3:10 PM, Douglas Davidson wrote:

    >
    > On Oct 30, 2007, at 5:04 AM, Devon Ferns wrote:
    >
    >> I've searched the list archives and no one seems to have a good way

    >> to do this.
    >> As discussed before, NSFileManager's isExecutableAtPath: returns
    >> YES for directories and any other file with the execute bit set.
    >> This isn't what I want so I've had to fudge together a solution.
    >>
    >> Basically, I'm using NSTask and executing /usr/bin/file on all
    >> suspected executable files and parsing the output for Mach-O.
    >> BTW, can anyone tell me what the program "file" will output for CFM

    >> binaries?  I don't know of any that I can test it on to see.
    >
    > /usr/bin/file may not be the best match for your needs.  It's
    > designed to be highly flexible, to handle files of many different
    > types, and to produce detailed human-readable output; it's not
    > really intended to be highly efficient or to produce output that can

    > be readily parsed.  As it happens, executable files generally can be

    > identified (for sniffing purposes) simply by reading their first
    > four bytes.  For Mach-o files, the possibilities are FAT_MAGIC,
    > FAT_CIGAM, MH_MAGIC, MH_CIGAM, MH_MAGIC_64, and MH_CIGAM_64.  Of
    > course, this doesn't rule out the possibility of encountering a file

    > that starts with one of these bytes but isn't actually formatted as
    > a Mach-o executable, but then neither does using /usr/bin/file.
    >
    > You might want to think about what you consider to be an
    > executable.  Does an executable shell script count?  How about a
    > Java jar-file?  Do you care about executables from other platforms?

    > (By the way, file reports PowerPC PEF/CFM executables as "header for

    > PowerPC PEF executable", and their first four bytes are 'Joy!', but
    > be sure to take endianness into account.)
    >
    > Douglas Davidson


    __________________________________________________
    Do You Yahoo!?
    Tired of spam?  Yahoo! Mail has the best spam protection around
    http://mail.yahoo.com
  • Oh I know but who wants the ugly C code in their nice looking
    Objective-C? ;)

    Anyways, I pretty much copied some code from the link I gave before
    and it works very fast and finds
    some other executables that my previous method did not.  Thanks to
    whoever at Apple that wrote it.

    Don't worry I give full credit to the source for my school assignment.

    Devon.

    On 30-Oct-07, at 5:33 PM, Charles Steinman wrote:

    > Since Objective-C is a true superset of C, any valid C code is also
    > valid Objective-C. So the code you found will work exactly the same.
    >
    >
    > Cheers,
    > Chuck
    >
  • On Oct 31, 2007, at 8:40 AM, Devon Ferns wrote:

    > Anyways, I pretty much copied some code from the link I gave before
    > and it works very fast and finds
    > some other executables that my previous method did not.  Thanks to
    > whoever at Apple that wrote it.

    You're welcome. :)  For future reference, most of the
    CheckExecutableArchitecture functionality is available in Leopard via
    CFBundle/NSBundle APIs, CFBundleCopyExecutableArchitectures(),
    CFBundleCopyExecutableArchitecturesForURL(), and -[NSBundle
    executableArchitectures].  See the Leopard release notes for more
    details.  For the purposes of this assignment,
    CFBundleCopyExecutableArchitecturesForURL() would suffice to detect
    Mach-o executables, but it does not distinguish PEF/CFM or other types
    of executables.

    Douglas Davidson
previous month october 2007 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today