首页
社区
课程
招聘
[原创]TikTok越狱检测之一 <代码模块污染检测>
2024-3-16 16:58 3483

[原创]TikTok越狱检测之一 <代码模块污染检测>

2024-3-16 16:58
3483

话说某天在国外论坛闲逛,有一国外小哥,发帖交流TikTok 的相关越狱检测,对TikTok的安全保护极其变态。
好奇心驱使之下,便下载TikTok,注册完账号,竟然什么都做不了,只能浏览视频。
基于逆向的本能,越狱手机应该被检测出来了。
于是便开始了分析该软件。
果然,TikTok的保护极其完善,很多保护都是笔者之前没有接触过的。
把检测的一些函数逆向出来,以后正向开发也有参考价值。
新加坡singpass 政务app也有该相关检测方法
特此记录.

1.<代码模块污染检测>
原理:TikTok加载的内存模块和官方的缓存模块

1
2
3
const char *sharedCachePaths[] = {    "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64",      "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e",
"/System/Library/dyld/dyld_shared_cache_arm64e"
};

进行比较,不一致就说明了TikTok加载的内存模块被篡改了,函数被Hook,或者代码被patch

//根据checkAddress判断该地址所在内存加载模块和 "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64" 官方模块比较
//是否已经被修改过,模块被修改过,返回true,模块没有被修改过返回false

  1. 调用dladdr函数 获取待检测checkAddress内存地址所在的模块
  2. 获取模块名称checkfname,模块代码地址checkTextCommand,模块大小 checkTextVmSize
  3. 映射官方的缓存模块 sharedCachePaths
  4. 从官方的缓存模块中匹配到checkfname模块
  5. 获取匹配模块,代码地址 matchTextCommand
  6. 对待检测的checkTextCommand 和 已经匹配的matchTextCommand进行逐字节比较,一致则说明没有被修改过,不一致则说明被污染了

+(bool)IsRiskModule:(uint64_t)checkAddress {

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
Dl_info checkDlinfo;
if(!dladdr((const void *)checkAddress, &checkDlinfo)){
    return false;
}
const char * checkfname = checkDlinfo.dli_fname;
struct mach_header_64 * checkMachHeader  = (struct mach_header_64 *) checkDlinfo.dli_fbase;
if (checkMachHeader->magic != MH_MAGIC_64)
    return false;
if(checkMachHeader->ncmds == 0)
    return false;
struct segment_command_64 * checkCommand  = (struct segment_command_64 *) ((char *)checkMachHeader + sizeof(struct mach_header_64));
struct segment_command_64 * checkTextCommand  = NULL;
for (int i =0; i< checkMachHeader->ncmds; i++) {
    if ((checkCommand->cmd == LC_SEGMENT_64)   && (strcmp(checkCommand->segname, "__TEXT") == 0))
    {
        checkTextCommand = checkCommand;
        break;
    }
    checkCommand =(struct segment_command_64 *) ((uint64_t)checkCommand + checkCommand->cmdsize);
}
if (!checkTextCommand)
    return false;
uint64_t checkTextVmSize = checkTextCommand->vmsize;
 
 
kern_return_t kernReturn = KERN_SUCCESS;
const char *sharedCachePaths[] = {
    "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64",
    "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e",
    "/System/Library/dyld/dyld_shared_cache_arm64e",
};
int fd = -1;
for (int i = 0;i < sizeof(sharedCachePaths) / sizeof(char *);i++) {
    fd = open(sharedCachePaths[i], O_RDONLY);
    if (fd != -1) {
        break;
    }
}
if (fd == -1)
    return false;
 
vm_size_t vmPageSize = vm_page_size;
unsigned char* p_map =( unsigned char*) mmap(0, vmPageSize, PROT_READ,MAP_NOCACHE|MAP_PRIVATE, fd, 0);
if (p_map == MAP_FAILED) {
    //映射失败
    close(fd);
    return false;
}
 
struct dyld_cache_header * cacheHeader = ( struct dyld_cache_header *)p_map;
if(strcmp(cacheHeader->magic, "dyld_v1   arm64") != 0){
    munmap(p_map, vmPageSize);
    close(fd);
    return false;
}
struct dyld_cache_mapping_info* mappings = (struct dyld_cache_mapping_info*)(cacheHeader->mappingOffset + (uintptr_t)cacheHeader);
uintptr_t length =  mappings[0].size;
munmap(p_map, vmPageSize);
 
vmPageSize = length;
p_map =( unsigned char *) mmap(0, vmPageSize, PROT_READ, MAP_NOCACHE|MAP_PRIVATE|MAP_NORESERVE, fd, 0);
if (p_map == MAP_FAILED) {
    //映射失败
    printf("error:%s\n", strerror(errno));
    close(fd);
    return false;
}
 
cacheHeader = ( struct dyld_cache_header *)p_map;
mappings = (struct dyld_cache_mapping_info*)(cacheHeader->mappingOffset + (uintptr_t)cacheHeader);
//非越狱系统 imagesCount = 0  越狱系统 imagesCount > 0
uint32_t imagesCount = cacheHeader->imagesCount;
if (imagesCount == 0) {
    munmap(p_map, vmPageSize);
    close(fd);
    return false;
}
 
 
struct dyld_cache_image_info* dylibs = (struct dyld_cache_image_info*)((uintptr_t)cacheHeader + cacheHeader->imagesOffset);
struct dyld_cache_image_info * matchDylib = NULL;
for (uint32_t i=0; i < imagesCount; i++) {
    const char* dylibPath  = (char*)cacheHeader + dylibs[i].pathFileOffset;
    if (strcmp(checkfname, dylibPath) == 0) {
        matchDylib = (struct dyld_cache_image_info*)&dylibs[i];
        //NSLog(@"IsRiskModule 函数,匹配到 filename =%s",dylibPath);
        break;
    }
}
 
if (!matchDylib) {
    NSLog(@"IsRiskModule 函数,找不到 filename =%s",checkfname);
    munmap(p_map, vmPageSize);
    close(fd);
    return false;
}
uint64_t offset = 0;
bool bMatch = false;
for (int i = 0 ; i< cacheHeader->mappingCount; i++) {
    uint64_t StartAddress =mappings[i].address;
    if (matchDylib->address >= StartAddress){
        uint64_t EndAddress = mappings[i].address +mappings[i].size;
        if (matchDylib->address <= EndAddress) {
            offset =  matchDylib->address - mappings[i].address + mappings[i].fileOffset ;
            bMatch = true;
            break;;
        }
    }
}
if (!bMatch) {
    munmap(p_map, vmPageSize);
    close(fd);
    return false;
}
struct mach_header_64* matchHeader = (struct mach_header_64*)((uintptr_t)cacheHeader + offset);
if(matchHeader->ncmds == 0)
    return false;
struct segment_command_64 * matchCommand  = (struct segment_command_64 *) ((char *)matchHeader + sizeof(struct mach_header_64));
struct segment_command_64 * matchTextCommand  = NULL;
for (int i =0; i< matchHeader->ncmds; i++) {
    if ((matchCommand->cmd == LC_SEGMENT_64)   && (strcmp(matchCommand->segname, "__TEXT") == 0))
    {
        matchTextCommand = matchCommand;
        break;
    }
 
matchCommand =(struct segment_command_64 *) ((uint64_t)matchCommand + matchCommand->cmdsize);
}
if (!matchTextCommand) {
    munmap(p_map, vmPageSize);
    close(fd);
    return false;
}
 
if (matchTextCommand->vmsize != checkTextVmSize ) {
    munmap(p_map, vmPageSize);
    close(fd);
    return true;
}
bool bIsRisk = false;
for (int i = 0; i< checkTextVmSize ; i++) {
    unsigned char Byte1 = *(unsigned char*) ((uint64_t)matchHeader+i);
    unsigned char Byte2 = *(unsigned char*) ((uint64_t)checkMachHeader+i);
    if (Byte1 != Byte2)
    {
        bIsRisk = true;
        NSLog(@"IsRiskModule 被污染的库,filename =%s,基地址 = 0x%llX,函数地址=0x%llX",checkfname,((uint64_t)checkMachHeader),(uint64_t)i);
        break;
    }
}
munmap(p_map, vmPageSize);
close(fd);
return bIsRisk;

}


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞4
打赏
分享
最新回复 (7)
雪    币: 3797
活跃值: (5570)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huangjw 2024-3-16 20:55
2
0
66666
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ktywcpki 2024-3-16 21:56
3
0
厉害厉害
雪    币: 964
活跃值: (1250)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mb_fssslkzs 2024-3-17 13:39
4
0
好奇国外小哥的 原始帖子
雪    币: 19349
活跃值: (28971)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2024-3-17 20:38
5
1
感谢分享
雪    币: 355
活跃值: (336)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
INightElf 2 2024-3-17 23:01
6
0
偶然发现的
雪    币: 0
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
WadeCong 2024-3-21 19:18
7
0
请问是哪个版本的TT
雪    币: 355
活跃值: (336)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
INightElf 2 2024-3-25 18:07
8
0
哈哈,从TT还原度为98%,可以一起研究研究,提高自己app 的安全性。Q:1793260563
游客
登录 | 注册 方可回帖
返回