首页
社区
课程
招聘
[原创]iOS搜索汇编绕过svc反动态调试
发表于: 2023-1-6 14:42 17982

[原创]iOS搜索汇编绕过svc反动态调试

2023-1-6 14:42
17982

在iOS反动态调试中,常用到"svc #0x80",通过svc汇编实现对ptrace、syscall的调用,实现反动态调试,使得lldb无法附加到app进程,不易定位到代码位置,增加反调试绕过难度。
如何绕过这种反调试手段呢?
本文通过搜索app的可执行文件,查找svc相关汇编指令的位置,并修改"svc #0x80" 为 "nop" 从而绕过反动态调试。

一、svc反动态调试代码

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

 

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

 

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

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

二、查找svc汇编指令流程

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

三、主要代码解析

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, const char * argv[])
{
    //原文件路径
    string sFilePath = "/path/to/TestSpace";
    //存储路径
    string sFilePath_save = "/path/to/TestSpace_2";
    //将要搜索的汇编指令
    vector<string> 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<uint64_t> 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为nop
        save_buf_to_file(file_buf_save, file_size, sFilePath_save);//存储修改后的二进制到本地文件
    }
 
    return 0;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//获取二进制文件中的代码段
vector<struct section_64 const *> gain_text_sections(void * file_buf)
{
    // 判断是否为胖文件
    mach_header * mhHeader = (mach_header*)file_buf;
    vector<struct section_64 const *> sectionArray;
    // 查找代码段
    struct section_64 const * sectionTxt_DB = findSection64ByName(mhHeader, "__text", "__BD_TEXT");//
 
    if(sectionTxt_DB==NULL){
        NSLog(@"找不到代码段--1");
    }
    else{
        sectionArray.push_back(sectionTxt_DB);
    }
 
    struct section_64 const * 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)中,因此代码中需要判断代码段的位置。

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
vector<uint64_t> search_svc_from_asm(void * file_buf, vector<string> asmStrArray)
{
    vector<uint64_t> resultVec;//声明一个int型向量
    vector<struct section_64 const *> sectionArray = gain_sections(file_buf);
 
    if(sectionArray.size()<1){
        NSLog(@"找不到代码段--3");
        return resultVec;
    }
 
    for(int i=0; i<sectionArray.size(); i++){
        struct section_64 const * sectionTxt = sectionArray[i];
 
        // 展示 Section64_Header中的 offset 和 size
        uint64_t offset = sectionTxt->offset;
        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);
            }
            else if(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<count; i++){
                NSString * curAsm = [asmArrayTmp objectAtIndex:i];
 
                for(int j=0; j<asmStrCount; j++){
                    if(j==samecount){
                        NSString * strTmp = [NSString stringWithCString:(asmStrArray[j]).c_str()  encoding:NSUTF8StringEncoding];
 
                        if([strTmp isEqualToString:curAsm])
                        {
                            samecount = samecount+1;
                            break;
                        }
                        else
                        {
                            samecount = 0;
                            break;
                        }
                    }
                }
 
                if(samecount==asmStrCount){
 
                    uint64_t target_addr = (uint64_t)(first_addr+i*4);
                    NSLog(@"查找到svc调用反动态调试:0x%lx    %@", target_addr, curAsm);
 
                    resultVec.push_back(target_addr);
 
                    break;
                }
 
            }
 
        }
    }
 
    return resultVec;
}

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

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void * alter_svc_to_nop(void * file_buf, uint64_t target_addr)
{
    uint8_t *pBegin = (uint8_t*)file_buf;
 
 
    uint64_t target = 0xD4001001;//svc 0x80
 
    uint64_t textAddr_base = (uint64_t)pBegin+target_addr;
 
    struct SingleAss * sAss = (struct SingleAss *)(textAddr_base);
 
 
    if(sAss->singleAss == target)
    {
        sAss->singleAss = 0xD503201F;// nop
    }
 
    return file_buf;
 
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//存储二进制到文件
void save_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");
}

测试效果如下:

可对新生成的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"指令可能会得到很多结果:

1
2
3
4
5
6
7
2022-12-15 00:19:37.636260+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x7736b94    svc #0x80
2022-12-15 00:19:37.642945+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x77383b4    svc #0x80
2022-12-15 00:19:37.651847+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773a824    svc #0x80
2022-12-15 00:19:37.654735+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773b570    svc #0x80
2022-12-15 00:19:37.658423+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773c5d0    svc #0x80
2022-12-15 00:19:37.660096+0800 MachConfuse[4456:36033721] 查找到svc调用反动态调试:0x773c66c    svc #0x80
......

全部改成"nop"也不能正常运行,尽量不要全部改为nop,可尝试逐个修改为nop后签名并测试效果。
一般情况下svc会在main函数中,可尝试找到APP二进制文件中的main函数,看其中如果有svc,可尝试修改为nop。

 

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

 

源码地址:
https://github.com/luoyanbei/MachConfuse


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 31
活跃值: (3254)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
奈斯的分析, 条例清晰。 
1 , 提问
2, 流程图,说明思路
3,举例
4,证明测试,验证自己的猜想

结论: 作者,专业iOS安全分析工作者。
2023-1-7 11:25
0
雪    币: 20
活跃值: (201)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
好家伙 学习到了
2024-11-11 03:35
0
雪    币: 565
活跃值: (3024)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学到了
2024-11-12 22:28
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
大佬这个好像不太支持 动态库的中的指令搜索
5天前
0
游客
登录 | 注册 方可回帖
返回
//