Objective C - How to check if Executable can be launched (eg. terminal)

此生再无相见时 提交于 2019-12-08 04:49:58

问题


I am currently building an Executable handling application in Objective C and I just wanna know a simple code that can determine if an executable file can be launched (without launching it) or if it is just a loadable one.

Thanks.


回答1:


Once you've taken care of permission bits and whether the file is a Mach-O, there are three things you need to consider:

  • File type
  • CPU compatibility
  • Fat binaries

File type

Whether your Mach-O is an executable, dylib, kext, etc., can be determined from a field in its header.
From <mach-o/loader.h>:

struct mach_header {
    uint32_t magic;
    cpu_type_t cputype;
    cpu_subtype_t cpusubtype;
    uint32_t filetype;  // <---
    uint32_t ncmds;
    uint32_t sizeofcmds;
    uint32_t flags;
};

Also from <mach-o/loader.h> you get all possible values for that field:

#define MH_OBJECT       0x1 /* relocatable object file */
#define MH_EXECUTE      0x2 /* demand paged executable file */
#define MH_FVMLIB       0x3 /* fixed VM shared library file */
#define MH_CORE         0x4 /* core file */
#define MH_PRELOAD      0x5 /* preloaded executable file */
#define MH_DYLIB        0x6 /* dynamically bound shared library */
#define MH_DYLINKER     0x7 /* dynamic link editor */
#define MH_BUNDLE       0x8 /* dynamically bound bundle file */
#define MH_DYLIB_STUB   0x9 /* shared library stub for static linking only, no section contents */
#define MH_DSYM         0xa /* companion file with only debug sections */
#define MH_KEXT_BUNDLE  0xb /* x86_64 kexts */

CPU compatibility

Just because it says "executable", doesn't mean it can be launched though. If you take an iOS app and try to execute it on your iMac, you'll get a "Bad CPU type in executable" error message.
The different CPU types are defined in <mach/machine.h>, but the only of comparing against the current CPU type is via defines:

#include <mach/machine.h>

bool is_cpu_compatible(cpu_type_t cputype)
{
    return
#ifdef __i386__
    cputype == CPU_TYPE_X86
#endif
#ifdef __x86_64__
    cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64
#endif
#ifdef __arm__
    cputype == CPU_TYPE_ARM
#endif
#if defined(__arm64__)
    cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64
#endif
    ;
}

(This will only work if your application has 64-bit slices, so that it always runs as 64-bit when it can. If you want to be able to run as a 32-bit binary and detect whether a 64-bit binary could be run, you'd have to use sysctl on "hw.cpu64bit_capable" together with defined, but then it gets even uglier.)

Fat binaries

Lastly, your binaries could be enclosed in fat headers. If so, you'll simply need to iterate over all slices, find the one corresponding to your current architecture, and check the two conditions above for that.

Implementation

There is no Objective-C API for this that I know of, so you'll have to fall back to C.
Given a pointer to the file's contents and the is_cpu_compatible function from above, you could do it like this:

#include <stdbool.h>
#include <stddef.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>

bool macho_is_executable(char *file)
{
    struct fat_header *fat = (struct fat_header*)file;
    // Fat file
    if(fat->magic == FAT_CIGAM) // big endian magic
    {
        struct fat_arch *arch = (struct fat_arch*)(fat + 1);
        for(size_t i = 0; i < fat->nfat_arch; ++i)
        {
            if(is_cpu_compatible(arch->cputype))
            {
                return macho_is_executable(&file[arch->offset]);
            }
        }

        // File is not for this architecture
        return false;
    }

    // Thin file
    struct mach_header *hdr32 = (struct mach_header*)file;
    struct mach_header_64 *hdr64 = (struct mach_header_64*)file;

    if(hdr32->magic == MH_MAGIC) // little endian magic
    {
        return hdr32->filetype == MH_EXECUTE && is_cpu_compatible(hdr32->cputype);
    }
    else if(hdr64->magic == MH_MAGIC_64)
    {
        return hdr64->filetype == MH_EXECUTE && is_cpu_compatible(hdr64->cputype);
    }

    // Not a Mach-O
    return false;
}

Note that these are still rather basic checks though, which will e.g. not detect corrupt Mach-O's, and which could easily be fooled by malicious files. If you wanted that, you would have to either emulate an operating system and launch the binary within, or get into the research field of theoretical IT and revolutionize the mathematics of provability.




回答2:


My understanding is you want to distinguish a Mach-O standalone executable from a Mach-O dyld library. A standalone executable will use either:

  • LC_MAIN load command to denote the entry point, supported since MacOS 10.7
  • LC_UNIXTHREAD load command , older non-dyld approach to do the same (still supported)

A dyld library will not have either of these Mach-O load commands, so if you detect one of them it means it's a runnable standalone executable. That of course does not imply the binary executable is valid and kernel won't kill it for other reasons.

If you want inspect some test files to verify it I recommend using a free tool called MachOView



来源:https://stackoverflow.com/questions/44037322/objective-c-how-to-check-if-executable-can-be-launched-eg-terminal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!