Load command 67 cmdsize not a multiple of 8

调试一款APP的时候遇到这个错误。于是用machoview打开,如图

就只看到这几个cmdsize是40,没看到size是67的呢?是怎么回事呢?另外67size是怎么造成的呢,不是size在arm64都是8的倍数,在32位是4的倍数,不知道这个是怎么造成的。。另外大佬们谁有machoview包,我的一打开十几秒就会闪退。。。

Load command 67意思是第67条lc

手动改成8的倍数

怎么修改?有工具还是写代码?

1, 010 手动改
2, 写代码改

对不起我比较新手,,。我可不可以认为我在手动注入几个动态库,就能凑到8的倍数?

不行的 他是注入的dylib的LC command size 不是8的倍数 你插入dylib 也影响不到之前插入的值

自己写一段代码去读取 Mach Header 和 Load Commands,遍历一下 load_commands 列表,找一下异常的 cmd,然后修改掉 cmdsize 再写回去。

给你一段代码参考,我把 cmdsize 向上圆整了,可能会有问题,向下圆整同样有可能会出问题,如果都不行就得魔改 Mach-O 文件了。

#import <Foundation/Foundation.h>
#import <mach-o/loader.h>
#import <sys/stat.h>

void readMachO() {
    NSString *filePath = @"<path to mach-o file>";
    int fd = open(filePath.UTF8String, O_RDWR);
    if (fd < 0) {
        printf("open file error");
        exit(1);
    }
    struct stat fileStatus;
    fstat(fd, &fileStatus);
    uint8_t *mappedData = mmap(NULL, fileStatus.st_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
    struct mach_header_64 *header = (struct mach_header_64 *)mappedData;
    uint32_t ncmds = header->ncmds;
    uint64_t cur = sizeof(struct mach_header_64);
    uint8_t *loadCommandsData = mappedData + cur;
    for (uint32_t i = 0; i < ncmds; i++) {
        struct load_command *loadCommand = (struct load_command *)loadCommandsData;
        if (loadCommand->cmdsize % 8 != 0) {
            printf("bad load_command with size %d\n", loadCommand->cmdsize);
            // fix up
            uint32_t fixup = 8 * (1 + loadCommand->cmdsize / 8);
            printf("fix it to %d\n", fixup);
            loadCommand->cmdsize = fixup;
        }
        loadCommandsData += loadCommand->cmdsize;
        cur += loadCommand->cmdsize;
    }
    
    // sync to disk
    msync(mappedData, cur, MS_SYNC);
}
2 Likes

太牛逼了,哥

哈哈,张总见笑了。

太牛逼了,哥

嗯,我也参考yololib写了代码,和你的有一些差别。要是是fat的,你这样能改得到吗?另外修改了lc的cmdsize,header里面总的size是不是也要修改啊?

肯定的 总数也要改,很简单,目标lc cmdsize 向上补齐到8的倍数,lc末尾补若干0, 总的cmdsize也加,最后在lc cmd后面和第一个段之间有一大片0的位置减去几个0 这样文件后面偏移都不会变 fat 也可以

你这个代码好像是没有修改总的size呢?另外lc不是有dylib_command、segment_command_64等等很多种,他们的内存结构这些都不一样,你都是按照load_command来处理的,虽说不同的lc都有load_command里面的东西,但是都按照load_command这个来处理不会出错吗?比如dylib_command里面还有path这些要影响size的,union lc_str name 里面的offset是不是也要被修改才对?比如我发的图rpath_command他的size也是不对的,也能按照load_command来处理吗? 我的意思是是不是要区分不同的lc来做处理?另外有没有可视化的mach-o编辑器啊,没有的话撸一个怎么样?:smiley::smiley:

他们有公共的头部字段 cmd 和 cmdsize,读取的时候是先按照 lc 处理的,根据 cmd 类型再转换。MachOView 就可以编辑,你如果闪退用源码 debug 下看看原因。

总大小要在 header 里修改,上面漏掉了,另外这个是以 ARM64 为示例的,Fat 的话需要额外处理 Fat Header。

嗯,我用macho找到位置用hexfriend修改的,你刚才说的的每个lc后面有很多空白的零,意思是比如我修改lcsize加了几个size,我就把后面空白去掉几个size,这样后面的偏移不会变,而且总的lcsize也不用修改是吗?

哈哈,我们是俩人,这个是那个大佬跟你说的,他应该是指虽然你把 cmdsize 改大了,但是由于 lc 最后和第一个段之间有 padding,可以从修改的 lc 开始 padding 到 8 的整数倍,余下的整体向下移。

哈哈哈哈,解决了,还是直接修改二进制快,感觉好简单,谢谢你们指导!:smiley::smiley:
顺带问下这是防注入的一个手段吗?

不是,是一个注入dylib 代码的bug

修改lc之后签名又不行了,报这个错:invalid or unsupported format for signature。难道是我修改lc没修改对?