TikTok越狱检测之一 <代码模块污染检测>

话说某天在国外论坛闲逛,有一国外小哥,发帖交流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 =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 = 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 = 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 =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 = 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值