如何在运行时获取构建 UUID 和镜像基地址

Posted

技术标签:

【中文标题】如何在运行时获取构建 UUID 和镜像基地址【英文标题】:How to get the build UUID in runtime and the image base address 【发布时间】:2013-11-07 23:07:49 【问题描述】:

是否可以获取构建 UUID,您可以在 dSYM 生成的文件和 ios 中的图像基地址中检查该 UUID。

低级的东西不太好,谁能指教?

【问题讨论】:

【参考方案1】:

这是一个类似于 Kerni 的答案的解决方案,但适用于任何平台(iOS 设备 + 模拟器和 OS X)和任何架构(32 位 + 64 位)。它还返回 NSUUID 而不是 NSString

#import <mach-o/dyld.h>
#import <mach-o/loader.h>

static NSUUID *ExecutableUUID(void)

    const struct mach_header *executableHeader = NULL;
    for (uint32_t i = 0; i < _dyld_image_count(); i++)
    
        const struct mach_header *header = _dyld_get_image_header(i);
        if (header->filetype == MH_EXECUTE)
        
            executableHeader = header;
            break;
        
    

    if (!executableHeader)
        return nil;

    BOOL is64bit = executableHeader->magic == MH_MAGIC_64 || executableHeader->magic == MH_CIGAM_64;
    uintptr_t cursor = (uintptr_t)executableHeader + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
    const struct segment_command *segmentCommand = NULL;
    for (uint32_t i = 0; i < executableHeader->ncmds; i++, cursor += segmentCommand->cmdsize)
    
        segmentCommand = (struct segment_command *)cursor;
        if (segmentCommand->cmd == LC_UUID)
        
            const struct uuid_command *uuidCommand = (const struct uuid_command *)segmentCommand;
            return [[NSUUID alloc] initWithUUIDBytes:uuidCommand->uuid];
        
    

    return nil;

【讨论】:

【参考方案2】:

你自己的建议太复杂了。

检查 HockeySDK 中的这段代码,只需几行代码即可:https://github.com/bitstadium/HockeySDK-iOS/blob/develop/Classes/BITHockeyBaseManager.m#L136

- (NSString *)executableUUID 
  // This now requires the testing of this feature to be done on an actual device, since it returns always empty strings on the simulator
#if !TARGET_IPHONE_SIMULATOR
  const uint8_t *command = (const uint8_t *)(&_mh_execute_header + 1);
  for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx) 
    const struct load_command *load_command = (const struct load_command *)command;
    if (load_command->cmd == LC_UUID) 
      const struct uuid_command *uuid_command = (const struct uuid_command *)command;
      const uint8_t *uuid = uuid_command->uuid;
      return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
               uuid[0], uuid[1], uuid[2], uuid[3],
               uuid[4], uuid[5], uuid[6], uuid[7],
               uuid[8], uuid[9], uuid[10], uuid[11],
               uuid[12], uuid[13], uuid[14], uuid[15]]
              lowercaseString];
     else 
      command += load_command->cmdsize;
    
  
#endif
  return @"";

【讨论】:

谢谢,试试看。【参考方案3】:

这可以通过我从一些同事那里找到的课程来实现,他们在合理的实验室中找到并根据他们的需要进行了重构。

这里可以获取构建UUID、镜像基地址和应用名称。

    /**
 * @internal
 *
 * Async-safe binary image list element.
 */
typedef struct bin_image 
    /** The binary image's header address. */
    uintptr_t header;

    /** The binary image's name/path. */
    char *name;

    /** The previous image in the list, or NULL */
    struct bin_image *prev;

    /** The next image in the list, or NULL. */
    struct bin_image *next;
 bin_image_t;

/**
 * @internal
 *
 * Async-safe binary image list. May be used to iterate over the binary images currently
 * available in-process.
 */
typedef struct bs_image_list 
    /** The lock used by writers. No lock is required for readers. */
    OSSpinLock write_lock;

    /** The head of the list, or NULL if the list is empty. Must only be used to iterate or delete entries. */
    bin_image_t *head;

    /** The tail of the list, or NULL if the list is empty. Must only be used to append new entries. */
    bin_image_t *tail;

    /** The list reference count. No nodes will be deallocated while the count is greater than 0. If the count
     * reaches 0, all nodes in the free list will be deallocated. */
    int32_t refcount;

    /** The node free list. */
    bin_image_t *free;
 bs_image_list_t;

/**
 * @internal
 *
 * Shared dyld image list.
 */
static bs_image_list_t shared_image_list =  0 ;

/**
 * @internal
 *
 * Maintains a linked list of binary images with support for async-safe iteration. Writing may occur concurrently with
 * async-safe reading, but is not async-safe.
 *
 * Atomic compare and swap is used to ensure a consistent view of the list for readers. To simplify implementation, a
 * write mutex is held for all updates; the implementation is not designed for efficiency in the face of contention
 * between readers and writers, and it's assumed that no contention should realistically occur.
 * @
 */

/**
 * Initialize a new binary image list and issue a memory barrier
 *
 * @param list The list structure to be initialized.
 *
 * @warning This method is not async safe.
 */
static void image_list_init (bs_image_list_t *list) 
    memset(list, 0, sizeof(*list));

    list->write_lock = OS_SPINLOCK_INIT;


/**
 * Free any binary image list resources.
 *
 * @warning This method is not async safe.
 */
static void image_list_free (bs_image_list_t *list) 
    bin_image_t *next = list->head;
    while (next != NULL) 
        /* Save the current pointer and fetch the next pointer. */
        bin_image_t *cur = next;
        next = cur->next;

        /* Deallocate the current item. */
        if (cur->name != NULL)
            free(cur->name);
        free(cur);
    


/**
 * Append a new binary image record to @a list.
 *
 * @param list The list to which the image record should be appended.
 * @param header The image's header address.
 * @param name The image's name.
 *
 * @warning This method is not async safe.
 */
static void image_list_append (bs_image_list_t *list, uintptr_t header, const char *name) 
    /* Initialize the new entry. */
    bin_image_t *new = calloc(1, sizeof(bin_image_t));
    new->header = header;
    new->name = strdup(name);

    /* Update the image record and issue a memory barrier to ensure a consistent view. */
    OSMemoryBarrier();

    /* Lock the list from other writers. */
    OSSpinLockLock(&list->write_lock); 

        /* If this is the first entry, initialize the list. */
        if (list->tail == NULL) 

            /* Update the list tail. This need not be done atomically, as tail is never accessed by a lockless reader. */
            list->tail = new;

            /* Atomically update the list head; this will be iterated upon by lockless readers. */
            if (!OSAtomicCompareAndSwapPtrBarrier(NULL, new, (void **) (&list->head))) 
                /* Should never occur */
                NSLog(@"An async image head was set with tail == NULL despite holding lock.");
            
        

        /* Otherwise, append to the end of the list */
        else 
            /* Atomically slot the new record into place; this may be iterated on by a lockless reader. */
            if (!OSAtomicCompareAndSwapPtrBarrier(NULL, new, (void **) (&list->tail->next))) 
                NSLog(@"Failed to append to image list despite holding lock");
            

            /* Update the prev and tail pointers. This is never accessed without a lock, so no additional barrier
             * is required here. */
            new->prev = list->tail;
            list->tail = new;
        
     OSSpinLockUnlock(&list->write_lock);


/**
 * Remove a binary image record from @a list.
 *
 * @param header The header address of the record to be removed. The first record matching this address will be removed.
 *
 * @warning This method is not async safe.
 */
static void image_list_remove (bs_image_list_t *list, uintptr_t header) 
    /* Lock the list from other writers. */
    OSSpinLockLock(&list->write_lock); 
        /* Find the record. */
        bin_image_t *item = list->head;
        while (item != NULL) 
            if (item->header == header)
                break;

            item = item->next;
        

        /* If not found, nothing to do */
        if (item == NULL) 
            OSSpinLockUnlock(&list->write_lock);
            return;
        

        /*
         * Atomically make the item unreachable by readers.
         *
         * This serves as a synchronization point -- after the CAS, the item is no longer reachable via the list.
         */
        if (item == list->head) 
            if (!OSAtomicCompareAndSwapPtrBarrier(item, item->next, (void **) &list->head)) 
                NSLog(@"Failed to remove image list head despite holding lock");
            
         else 
            /* There MUST be a non-NULL prev pointer, as this is not HEAD. */
            if (!OSAtomicCompareAndSwapPtrBarrier(item, item->next, (void **) &item->prev->next)) 
                NSLog(@"Failed to remove image list item despite holding lock");
            
        

        /* Now that the item is unreachable, update the prev/tail pointers. These are never accessed without a lock,
         * and need not be updated atomically. */
        if (item->next != NULL) 
            /* Item is not the tail (otherwise next would be NULL), so simply update the next item's prev pointer. */
            item->next->prev = item->prev;
         else 
            /* Item is the tail (next is NULL). Simply update the tail record. */
            list->tail = item->prev;
        

        /* If a reader is active, simply spin until inactive. */
        while (list->refcount > 0) 
        

        if (item->name != NULL)
            free(item->name);
        free(item);
     OSSpinLockUnlock(&list->write_lock);


/**
 * Retain or release the list for reading. This method is async-safe.
 *
 * This must be issued prior to attempting to iterate the list, and must called again once reads have completed.
 *
 * @param list The list to be be retained or released for reading.
 * @param enable If true, the list will be retained. If false, released.
 */
static void image_list_set_reading (bs_image_list_t *list, bool enable) 
    if (enable) 
        /* Increment and issue a barrier. Once issued, no items will be deallocated while a reference is held. */
        OSAtomicIncrement32Barrier(&list->refcount);
     else 
        /* Increment and issue a barrier. Once issued, items may again be deallocated. */
        OSAtomicDecrement32Barrier(&list->refcount);
    


/**
 * Return the next image record. This method is async-safe. If no additional images are available, will return NULL;
 *
 * @param list The list to be iterated.
 * @param current The current image record, or NULL to start iteration.
 */
static bin_image_t *image_list_next (bs_image_list_t *list, bin_image_t *current) 
    if (current != NULL)
        return current->next;

    return list->head;


/**
 * @internal
 * dyld image add notification callback.
 */
static void image_add_callback (const struct mach_header *mh, intptr_t vmaddr_slide) 
    Dl_info info;

    /* Look up the image info */
    if (dladdr(mh, &info) == 0) 
        NSLog(@"%s: dladdr(%p, ...) failed", __FUNCTION__, mh);
        return;
    

    /* Register the image */
    image_list_append(&shared_image_list, (uintptr_t) mh, info.dli_fname);


/**
 * @internal
 *
 * Write a binary image frame
 *
 * @param file Output file
 * @param name binary image path (or name).
 * @param image_base Mach-O image base.
 */
static void process_binary_image (const char *name, const void *header,
                                    struct uuid_command *out_uuid, uintptr_t *out_baseaddr) 
    uint32_t ncmds;
    const struct mach_header *header32 = (const struct mach_header *) header;
    const struct mach_header_64 *header64 = (const struct mach_header_64 *) header;

    struct load_command *cmd;

    /* Check for 32-bit/64-bit header and extract required values */
    switch (header32->magic) 
            /* 32-bit */
        case MH_MAGIC:
        case MH_CIGAM:
            ncmds = header32->ncmds;
            cmd = (struct load_command *) (header32 + 1);
            break;

            /* 64-bit */
        case MH_MAGIC_64:
        case MH_CIGAM_64:
            ncmds = header64->ncmds;
            cmd = (struct load_command *) (header64 + 1);
            break;

        default:
            NSLog(@"Invalid Mach-O header magic value: %x", header32->magic);
            return;
    

    /* Compute the image size and search for a UUID */
    struct uuid_command *uuid = NULL;

    for (uint32_t i = 0; cmd != NULL && i < ncmds; i++) 
        /* DWARF dSYM UUID */
        if (cmd->cmd == LC_UUID && cmd->cmdsize == sizeof(struct uuid_command))
            uuid = (struct uuid_command *) cmd;

        cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
    

    /* Base address */
    uintptr_t base_addr;
    base_addr = (uintptr_t) header;

    *out_baseaddr = base_addr;
    if(out_uuid && uuid)
        memcpy(out_uuid, uuid, sizeof(struct uuid_command));


+ (void)registerCallback 
    _dyld_register_func_for_add_image(image_add_callback);


+ (NSArray *)loadedImages 
    NSMutableArray *array = [NSMutableArray array];
    int i;
    struct uuid_command uuid =  0 ;
    uintptr_t baseaddr;
    char uuidstr[64] =  0 ;

    image_list_set_reading(&shared_image_list, true);

    bin_image_t *image = NULL;
    while ((image = image_list_next(&shared_image_list, image)) != NULL) 

        process_binary_image(image->name, (const void *) (image->header), &uuid, &baseaddr);

        for(i=0; i<16; i++) 
            sprintf(&uuidstr[2*i], "%02x", uuid.uuid[i]);
        

        [array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithCString:image->name encoding:NSASCIIStringEncoding], [NSString stringWithCString:uuidstr encoding:NSASCIIStringEncoding], [NSNumber numberWithUnsignedLong:(long unsigned) baseaddr], nil]
                                                     forKeys:[NSArray arrayWithObjects:@"name", @"uuid", @"baseaddr", nil]]];
    

    return [NSArray arrayWithArray:array];

你必须在某个地方调用registerCallbacks,这样它才能正确返回结果。

问候。

【讨论】:

以上是关于如何在运行时获取构建 UUID 和镜像基地址的主要内容,如果未能解决你的问题,请参考以下文章

获取应用程序运行时归档时 dSYM 具有的 UUID

在 React 中提交表单时如何获取要显示的名称和 uuid

JS怎么能实现获取设备的UUID,比如手机访问这个地址可以获取它的UUID。

如何在运行时获取 SPRING Boot HOST 和 PORT 地址?

如何获取iphone的UUID

uuid是啥意思