iOS静态方式绕过svc反动态调试

在iOS反动态调试中,常用到”svc #0x80”,通过svc汇编实现对ptrace、syscall的调用,实现反动态调试,使得lldb无法附加到app进程,不易定位到代码位置,增加反调试绕过难度。

如何绕过这种反调试手段呢?

本文通过搜索app的可执行文件,查找svc相关汇编指令的位置,并修改”svc #0x80” 为 “nop” 从而绕过反动态调试。

本文通过搜索汇编代码块的方式,连续匹配多条汇编指令,从而确定最后一条指令的地址。

一、svc反动态调试代码

举例说明,新创建一个测试APP,在main函数中增加svc汇编,调用svc实现ptrace:

iOS静态方式绕过svc反动态调试_第1张图片

这个测试app编译生成可执行文件,找到svc所在位置:

iOS静态方式绕过svc反动态调试_第2张图片

此时,APP在手机运行,尝试附加app就会提示失败:

debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-900.3.106
 for arm64.
Attaching to process TestSpace...
Segmentation fault: 11

二、查找svc汇编指令流程

大体思路是读取APP的二进制文件,解析macho格式,找到对应的代码段,把代码段内的二进制转换成汇编指令,遍历找到svc指定对应的地址:

iOS静态方式绕过svc反动态调试_第3张图片

三、主要代码解析

(1)使用capstone库做二进制转汇编。

intmain(int argc, constchar * argv[]){
    //原文件路径
    string sFilePath = "/path/to/TestSpace";
    //存储路径
    string sFilePath_save = "/path/to/TestSpace_2";
    //将要搜索的汇编指令
    vector svc_asm_vec = {"movz x0,#0x1f", "movz x1,#0", "movz x2,#0", "movz x3,#0", "movz w16,#0x1a", "svc #0x80"};

    uint64_t file_size = FileGetSize((char*)sFilePath.c_str());//计算文件大小void *file_buf = gain_fileBuf(sFilePath.c_str());//加载文件到内存//搜索到的"svc #0x80"的地址
    vector addr_arry = search_svc_from_asm(file_buf, svc_asm_vec);

    if(addr_arry.size()>0){
        void * file_buf_save = alter_svc_to_nop(file_buf, addr_arry[0]);//修改第一个svc为nopsave_buf_to_file(file_buf_save, file_size, sFilePath_save);//存储修改后的二进制到本地文件
    }

    return0;
}

(2)gain_text_sections函数是从二进制文件中找到对应的代码段:

//获取二进制文件中的代码段vector gain_text_sections(void * file_buf){
    // 判断是否为胖文件
    mach_header * mhHeader = (mach_header*)file_buf;
    vector sectionArray;
    // 查找代码段structsection_64const * sectionTxt_DB = findSection64ByName(mhHeader, "__text", "__BD_TEXT");//if(sectionTxt_DB==NULL){
        NSLog(@"找不到代码段--1");
    }
    else{
        sectionArray.push_back(sectionTxt_DB);
    }

    structsection_64const * sectionTxt = findSection64ByName(mhHeader, "__text", "__TEXT");//if(sectionTxt==NULL){
        NSLog(@"找不到代码段--2");
    }
    else{
        sectionArray.push_back(sectionTxt);
    }

    return sectionArray;
}

因为大部分APP的代码段是在Section64(TEXT,text)中,有的代码段在Section64(BD_TEXT,text)中,因此代码中需要判断代码段的位置。

iOS静态方式绕过svc反动态调试_第4张图片
iOS静态方式绕过svc反动态调试_第5张图片

(3)从app的二进制文件中,搜索指定的汇编数组,如果完全符合则返回最后一条汇编指令的地址,search_svc_from_asm函数会返回一个数组:

vector search_svc_from_asm(void * file_buf, vector asmStrArray){
    vector resultVec;//声明一个int型向量
    vector sectionArray = gain_sections(file_buf);

    if(sectionArray.size()<1){
        NSLog(@"找不到代码段--3");
        return resultVec;
    }

    for(int i=0; ioffset;
        uint64_t text_size = sectionTxt->size;

        int tmp_length = 4;//单条汇编所占内存//反汇编
        Disasm * disasm = [[Disasm alloc] init];

        uint32_t my_offset = (uint32_t)0;
        uint64_t my_addr = (uint64_t)offset;
        uint32_t my_size = 0x640;//400条int asmStrCount = (int)asmStrArray.size();

        while(1){

            my_size = 0x640;

            if(my_offset==0){
                my_offset = (uint32_t)offset;
            }
            else{
                my_offset = my_offset + my_size - (asmStrCount-1)*tmp_length;
            }

            if(my_offset < text_size+offset && my_offset+my_size > text_size+offset)
            {
                my_size = (uint32_t)(text_size+offset-my_offset);
            }
            elseif(my_offset>text_size+offset){
                break;
            }

            NSArray * asmArrayTmp = [disasm disAsmWithBuff:file_buf offset:my_offset size:my_size addr:(uint64_t)my_addr];

            int count = (int)[asmArrayTmp count];

            uint64_t first_addr = (uint32_t)my_offset;

            int samecount = 0;

            for(int i = 0; i

search_svc_from_asm函数第二个参数是汇编指令数组,可以搜索任意汇编指令。

(4)可根据得到的汇编指令地址,修改”svc #0x80”为”nop”:

void * alter_svc_to_nop(void * file_buf, uint64_t target_addr){
    uint8_t *pBegin = (uint8_t*)file_buf;


    uint64_t target = 0xD4001001;//svc 0x80uint64_t textAddr_base = (uint64_t)pBegin+target_addr;

    structSingleAss * sAss = (struct SingleAss *)(textAddr_base);


    if(sAss->singleAss == target)
    {
        sAss->singleAss = 0xD503201F;// nop
    }

    return file_buf;

}

(5)将修改后的二进制存储到指定文件:

//存储二进制到文件voidsave_buf_to_file(void * file_buf, uint64_t file_size, string filePath_save){
        FILE *fp = fopen(filePath_save.c_str(), "w");
        uint8_t *pBegin = (uint8_t*)file_buf;

        fwrite((void*)pBegin, 1, file_size, fp);

        fclose(fp);

        free(file_buf);

        printf("rBuff=写入完成\n");

        printf("**********************************\n");
}

测试效果如下:

iOS静态方式绕过svc反动态调试_第6张图片

可对新生成的app做重签名,就可以做动态调试了。

五、注意事项

(1)search_svc_from_asm 函数可以搜索任意汇编指令,第二个参数是汇编指令数组,就是要搜索的内容;

(2)如果查不到如下arm汇编:

{“mov x0,#0x1f”, “mov x1,#0”, “mov x2,#0”, “mov x3,#0”, “mov w16,#0x1a”, “svc #0x80”}

可以尝试查一下:

{“movz x0,#0x1f”, “movz x1,#0”, “movz x2,#0”, “movz x3,#0”, “movz w16,#0x1a”, “svc #0x80”}

只有完全匹配才判断为找到对应的汇编指令,如果这也找不到.

可尽量减少汇编指令的数量,例如只搜索最后两条指令{“mov w16,#0x1a”, “svc #0x80”},或者只搜索最后一条指令{“svc #0x80”}

(3)有的app中会存在很多”svc #0x80”指令,只用search_svc_from_asm函数搜索”svc #0x80”指令可能会得到很多结果:

2022-12-1500:19:37.636260+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x7736b94svc#0x802022-12-1500:19:37.642945+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x77383b4svc#0x802022-12-1500:19:37.651847+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773a824svc#0x802022-12-1500:19:37.654735+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773b570svc#0x802022-12-1500:19:37.658423+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773c5d0svc#0x802022-12-1500:19:37.660096+0800MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773c66csvc#0x80
......

全部改成”nop”也不能正常运行,尽量不要全部改为nop,可尝试逐个修改为nop后签名并测试效果。

一般情况下svc会在main函数中,可尝试找到APP二进制文件中的main函数,看其中如果有svc,可尝试修改为nop。

(4)本工程可用于搜索macho中的任意汇编指令

源码地址:

https://github.com/luoyanbei/MachConfuse

你可能感兴趣的:(网络安全,服务器,windows,运维,网络,web安全)