美文网首页iOS知多少
代码实现dylib注入iPA

代码实现dylib注入iPA

作者: ZhongXi | 来源:发表于2017-05-08 17:15 被阅读139次

    yololib实现了使用终端dylib注入到ipa,当我们编写Mac端工具的时候也可以调用NSTask执行终端,然而为了简单化,这里使用了纯代码的方式.
    MachOView打开xxx.app/exec文件的时候可以看到所有库的加载路径,注入dylib文件其实也就是在exec文件中添加一条dylib加载路径
    otool命令行同样可以查看到xxx.app/exec文件库的加载路径

    otool -L xxx.app/exec
    

    NEInjector
    .h

    +(void)injectMachoPath:(NSString *)machoPath dylibPath:(NSString *)dylibPath;
    
    #include <mach-o/loader.h>
    #include <mach-o/fat.h>
    

    .m

    +(void)injectMachoPath:(NSString *)machoPath dylibPath:(NSString *)dylibPath{
        int fd = open(machoPath.UTF8String, O_RDWR, 0777);
        if (fd < 0)
        {
            NSLog(@"Inject failed: failed to open %@",machoPath);
            return;
        }
        else
        {
            uint32_t magic;
            read(fd, &magic, sizeof(magic));
            if (magic == MH_MAGIC || magic == MH_MAGIC_64)
            {
                lseek(fd, 0, SEEK_SET);
                [self injectArchitecture:fd dylibPath:dylibPath exePath:machoPath];
            }
            else if (magic == FAT_MAGIC || magic == FAT_CIGAM)
            {
                struct fat_header header;
                lseek(fd, 0, SEEK_SET);
                read(fd, &header, sizeof(fat_header));
                int nArch = header.nfat_arch;
                if (magic == FAT_CIGAM) nArch = [self bigEndianToSmallEndian:header.nfat_arch];
                
                struct fat_arch arch;
                NSMutableArray *offsetArray = [NSMutableArray array];
                for (int i = 0; i < nArch; i++)
                {
                    memset(&arch, 0, sizeof(fat_arch));
                    read(fd, &arch, sizeof(fat_arch));
                    int offset = arch.offset;
                    if (magic == FAT_CIGAM) offset = [self bigEndianToSmallEndian:arch.offset];
                    [offsetArray addObject:[NSNumber numberWithUnsignedInt:offset]];
                }
                
                for (NSNumber *offsetNum in offsetArray)
                {
                    lseek(fd, [offsetNum unsignedIntValue], SEEK_SET);
                    [self injectArchitecture:fd dylibPath:dylibPath exePath:machoPath];
                }
            }
            close(fd);
        }
    }
    + (void)injectArchitecture:(int)fd dylibPath:(NSString *)dylibPath exePath:(NSString *)exePathForInfoOnly
    {
        off_t archPoint = lseek(fd, 0, SEEK_CUR);
        struct mach_header header;
        read(fd, &header, sizeof(header));
        if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64)
        {
            NSLog(@"Inject failed: Invalid executable %@",exePathForInfoOnly);
        }
        else
        {
            if (header.magic == MH_MAGIC_64)
            {
                int delta = sizeof(mach_header_64) - sizeof(mach_header);
                lseek(fd, delta, SEEK_CUR);
            }
            
            char *buffer = (char *)malloc(header.sizeofcmds + 2048);
            read(fd, buffer, header.sizeofcmds);
            
            if ([[NSFileManager defaultManager] fileExistsAtPath:dylibPath])
            {
                dylibPath = [@"@executable_path" stringByAppendingPathComponent:[dylibPath lastPathComponent]];
            }
            const char *dylib = dylibPath.UTF8String;
            struct dylib_command *p = (struct dylib_command *)buffer;
            struct dylib_command *last = NULL;
            for (uint32_t i = 0; i < header.ncmds; i++, p = (struct dylib_command *)((char *)p + p->cmdsize))
            {
                if (p->cmd == LC_LOAD_DYLIB || p->cmd == LC_LOAD_WEAK_DYLIB)
                {
                    char *name = (char *)p + p->dylib.name.offset;
                    if (strcmp(dylib, name) == 0)
                    {
                        NSLog(@"Already Injected: %@ with %s", exePathForInfoOnly, dylib);
                        close(fd);
                        return;
                    }
                    last = p;
                }
            }
            
            if ((char *)p - buffer != header.sizeofcmds)
            {
                NSLog(@"LC payload not mismatch: %@", exePathForInfoOnly);
            }
            
            if (last)
            {
                struct dylib_command *inject = (struct dylib_command *)((char *)last + last->cmdsize);
                char *movefrom = (char *)inject;
                uint32_t cmdsize = sizeof(*inject) + (uint32_t)strlen(dylib) + 1;
                cmdsize = (cmdsize + 0x10) & 0xFFFFFFF0;
                char *moveout = (char *)inject + cmdsize;
                for (int i = (int)(header.sizeofcmds - (movefrom - buffer) - 1); i >= 0; i--)
                {
                    moveout[i] = movefrom[i];
                }
                memset(inject, 0, cmdsize);
                inject->cmd = LC_LOAD_DYLIB;
                inject->cmdsize = cmdsize;
                inject->dylib.name.offset = sizeof(dylib_command);
                inject->dylib.timestamp = 2;
                inject->dylib.current_version = 0x00010000;
                inject->dylib.compatibility_version = 0x00010000;
                strcpy((char *)inject + inject->dylib.name.offset, dylib);
                
                header.ncmds++;
                header.sizeofcmds += inject->cmdsize;
                lseek(fd, archPoint, SEEK_SET);
                write(fd, &header, sizeof(header));
                
                lseek(fd, archPoint + ((header.magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header)), SEEK_SET);
                write(fd, buffer, header.sizeofcmds);
            }
            else
            {
                NSLog(@"nject failed: No valid LC_LOAD_DYLIB %@",exePathForInfoOnly);
            }
            
            free(buffer);
        }
    }
    
    + (uint32_t)bigEndianToSmallEndian:(uint32_t)bigEndian
    {
        uint32_t smallEndian = 0;
        unsigned char *small = (unsigned char *)&smallEndian;
        unsigned char *big = (unsigned char *)&bigEndian;
        for (int i=0; i<4; i++)
        {
            small[i] = big[3-i];
        }
        return smallEndian;
    }
    

    使用

    [NEInjector injectMachoPath:@"~.xxx/Payload/xxx.app/machoFileName" dylibPath:@"~.xxx/Payload/xxx.app/xxxx.dylib"];
    

    后续

    当我们从xxxx助手下载的无限金币游戏ipa的时候,必定会存在以这种方式存在的启动广告.
    使用install_name_tool 终端命令行进行替换,例如ipa原有dylib文件名称为xx.dylib,需要注入的为NE.dylib

    $install_name_tool -change @executable_path/xx.dylib @executable_path/NE.dylib ~/xxxx.app/exec
    

    执行成功后使用文中开头的方法把NE.dylib注入
    当然,注入了dylib,ipa需要进行重签名

    相关文章

      网友评论

        本文标题:代码实现dylib注入iPA

        本文链接:https://www.haomeiwen.com/subject/yzomtxtx.html