├── .gitignore
├── Makefile
├── README.md
├── README_current.json
├── book.json
├── book_current.json
├── node_modules
└── src
├── README.md
├── SUMMARY.md
├── anti_jb_detect
├── README.md
├── app_self.md
├── debuggable.md
├── dyld
│ ├── README.md
│ ├── _dyld_series.md
│ └── dylib.md
├── env.md
├── file
│ ├── README.md
│ ├── open
│ │ ├── README.md
│ │ ├── c_func
│ │ │ ├── README.md
│ │ │ ├── svc_0x80_asm.md
│ │ │ └── syscall.md
│ │ └── ios_func.md
│ └── write
│ │ ├── README.md
│ │ ├── c_func.md
│ │ └── ios_func.md
├── getsectiondata.md
├── jb_processes.md
├── kernel_level.md
├── objc_runtime.md
├── sandbox.md
├── system.md
└── url_scheme.md
├── appendix
├── README.md
└── reference.md
├── assets
└── favicon.ico
├── detect_common
├── README.md
├── app_load_process.md
└── jb_path
│ ├── README.md
│ └── jb_file_list.md
├── ios_jb_detecton_overview
└── README.md
├── jb_detection
├── README.md
├── app_self.md
├── debuggable.md
├── dyld
│ ├── README.md
│ ├── _dyld_series.md
│ └── dylib.md
├── env.md
├── file
│ ├── README.md
│ ├── attributes.md
│ ├── open
│ │ ├── README.md
│ │ ├── c_func
│ │ │ ├── README.md
│ │ │ ├── svc_0x80_asm.md
│ │ │ └── syscall.md
│ │ └── ios_func.md
│ └── write
│ │ ├── README.md
│ │ ├── c_func.md
│ │ └── ios_func.md
├── getsectiondata.md
├── installed_app.md
├── jb_processes.md
├── objc_runtime.md
├── sandbox.md
├── ssh.md
├── system.md
└── url_scheme.md
└── other_summary
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | output/
3 | debug/
4 |
5 | *.zip
6 |
7 | .DS_Store
8 |
9 | !src/**/output
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | include ../../common/honkit_makefile.mk
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS逆向开发:越狱检测和反越狱检测
2 |
3 | * 最新版本:`v1.0`
4 | * 更新时间:`20221110`
5 |
6 | ## 简介
7 |
8 | 介绍iOS逆向期间涉及到的iOS的越狱检测和反越狱检测方面的内容。包括常用的越狱检测手段,比如URL Scheme、文件方面的:文件属性、文件打开和文件写入;其中文件打开又包括C函数和iOS函数,其中C函数又包括syscall的C函数和svc 0x80内联汇编;以及文件写入包括C函数和iOS函数;以及其他检测手段:环境变量、是否可调试、system、沙箱完整性校验、越狱相关进程、dyld动态库,包括dylib的dladdr、dlopen、dlsym和_dyld开头的系列函数、ObjC运行时、app本身、已安装app、SSH相关、getsectiondata等;以及上述各种方法的反越狱检测的具体实现,以及其他一些额外的反越狱检测手段,比如内核级反越狱;接着整理越狱检测和反越狱检测的通用的内容,比如app的启动过程、越狱路径相关,尤其是越狱路径列表。以及其他一些心得。且均已贴出相关的越狱检测和反越狱检测的代码段和独立的代码仓库。
9 |
10 | ## 源码+浏览+下载
11 |
12 | 本书的各种源码、在线浏览地址、多种格式文件下载如下:
13 |
14 | ### HonKit源码
15 |
16 | * [crifan/ios_re_jb_detection: iOS逆向开发:越狱检测和反越狱检测](https://github.com/crifan/ios_re_jb_detection)
17 |
18 | #### 如何使用此HonKit源码去生成发布为电子书
19 |
20 | 详见:[crifan/honkit_template: demo how to use crifan honkit template and demo](https://github.com/crifan/honkit_template)
21 |
22 | ### 在线浏览
23 |
24 | * [iOS逆向开发:越狱检测和反越狱检测 book.crifan.org](https://book.crifan.org/books/ios_re_jb_detection/website/)
25 | * [iOS逆向开发:越狱检测和反越狱检测 crifan.github.io](https://crifan.github.io/ios_re_jb_detection/website/)
26 |
27 | ### 离线下载阅读
28 |
29 | * [iOS逆向开发:越狱检测和反越狱检测 PDF](https://book.crifan.org/books/ios_re_jb_detection/pdf/ios_re_jb_detection.pdf)
30 | * [iOS逆向开发:越狱检测和反越狱检测 ePub](https://book.crifan.org/books/ios_re_jb_detection/epub/ios_re_jb_detection.epub)
31 | * [iOS逆向开发:越狱检测和反越狱检测 Mobi](https://book.crifan.org/books/ios_re_jb_detection/mobi/ios_re_jb_detection.mobi)
32 |
33 | ## 版权和用途说明
34 |
35 | 此电子书教程的全部内容,如无特别说明,均为本人原创。其中部分内容参考自网络,均已备注了出处。如发现有侵权,请通过邮箱联系我 `admin 艾特 crifan.com`,我会尽快删除。谢谢合作。
36 |
37 | 各种技术类教程,仅作为学习和研究使用。请勿用于任何非法用途。如有非法用途,均与本人无关。
38 |
39 | ## 鸣谢
40 |
41 | 感谢我的老婆**陈雪**的包容理解和悉心照料,才使得我`crifan`有更多精力去专注技术专研和整理归纳出这些电子书和技术教程,特此鸣谢。
42 |
43 | ## 其他
44 |
45 | ### 作者的其他电子书
46 |
47 | 本人`crifan`还写了其他`150+`本电子书教程,感兴趣可移步至:
48 |
49 | [crifan/crifan_ebook_readme: Crifan的电子书的使用说明](https://github.com/crifan/crifan_ebook_readme)
50 |
51 | ### 关于作者
52 |
53 | 关于作者更多介绍,详见:
54 |
55 | [关于CrifanLi李茂 – 在路上](https://www.crifan.org/about/)
56 |
--------------------------------------------------------------------------------
/README_current.json:
--------------------------------------------------------------------------------
1 | {
2 | "latestVersion": "v1.0",
3 | "lastUpdate": "20221110",
4 | "gitRepoName": "ios_re_jb_detection",
5 | "bookName": "iOS逆向开发:越狱检测和反越狱检测",
6 | "bookDescription": "介绍iOS逆向期间涉及到的iOS的越狱检测和反越狱检测方面的内容。包括常用的越狱检测手段,比如URL Scheme、文件方面的:文件属性、文件打开和文件写入;其中文件打开又包括C函数和iOS函数,其中C函数又包括syscall的C函数和svc 0x80内联汇编;以及文件写入包括C函数和iOS函数;以及其他检测手段:环境变量、是否可调试、system、沙箱完整性校验、越狱相关进程、dyld动态库,包括dylib的dladdr、dlopen、dlsym和_dyld开头的系列函数、ObjC运行时、app本身、已安装app、SSH相关、getsectiondata等;以及上述各种方法的反越狱检测的具体实现,以及其他一些额外的反越狱检测手段,比如内核级反越狱;接着整理越狱检测和反越狱检测的通用的内容,比如app的启动过程、越狱路径相关,尤其是越狱路径列表。以及其他一些心得。且均已贴出相关的越狱检测和反越狱检测的代码段和独立的代码仓库。"
7 | }
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "iOS逆向开发:越狱检测和反越狱检测",
3 | "description": "介绍iOS逆向期间涉及到的iOS的越狱检测和反越狱检测方面的内容。包括常用的越狱检测手段,比如URL Scheme、文件方面的:文件属性、文件打开和文件写入;其中文件打开又包括C函数和iOS函数,其中C函数又包括syscall的C函数和svc 0x80内联汇编;以及文件写入包括C函数和iOS函数;以及其他检测手段:环境变量、是否可调试、system、沙箱完整性校验、越狱相关进程、dyld动态库,包括dylib的dladdr、dlopen、dlsym和_dyld开头的系列函数、ObjC运行时、app本身、已安装app、SSH相关、getsectiondata等;以及上述各种方法的反越狱检测的具体实现,以及其他一些额外的反越狱检测手段,比如内核级反越狱;接着整理越狱检测和反越狱检测的通用的内容,比如app的启动过程、越狱路径相关,尤其是越狱路径列表。以及其他一些心得。且均已贴出相关的越狱检测和反越狱检测的代码段和独立的代码仓库。",
4 | "pluginsConfig": {
5 | "github-buttons": {
6 | "buttons": [
7 | {
8 | "repo": "ios_re_jb_detection",
9 | "user": "crifan",
10 | "type": "star",
11 | "count": true,
12 | "size": "small"
13 | },
14 | {
15 | "user": "crifan",
16 | "type": "follow",
17 | "width": "120",
18 | "count": false,
19 | "size": "small"
20 | }
21 | ]
22 | },
23 | "sitemap-general": {
24 | "prefix": "https://book.crifan.org/books/ios_re_jb_detection/website/"
25 | },
26 | "toolbar-button": {
27 | "url": "https://book.crifan.org/books/ios_re_jb_detection/pdf/ios_re_jb_detection.pdf",
28 | "icon": "fa-file-pdf-o",
29 | "label": "下载PDF"
30 | },
31 | "callouts": {
32 | "showTypeInHeader": false
33 | },
34 | "theme-default": {
35 | "showLevel": true
36 | },
37 | "disqus": {
38 | "shortName": "crifan"
39 | },
40 | "prism": {
41 | "css": [
42 | "prism-themes/themes/prism-atom-dark.css"
43 | ]
44 | },
45 | "sharing": {
46 | "douban": false,
47 | "facebook": true,
48 | "google": false,
49 | "hatenaBookmark": false,
50 | "instapaper": false,
51 | "line": false,
52 | "linkedin": false,
53 | "messenger": false,
54 | "pocket": false,
55 | "qq": true,
56 | "qzone": false,
57 | "stumbleupon": false,
58 | "twitter": true,
59 | "viber": false,
60 | "vk": false,
61 | "weibo": true,
62 | "whatsapp": false,
63 | "all": [
64 | "douban",
65 | "facebook",
66 | "google",
67 | "instapaper",
68 | "line",
69 | "linkedin",
70 | "messenger",
71 | "pocket",
72 | "qq",
73 | "qzone",
74 | "stumbleupon",
75 | "twitter",
76 | "viber",
77 | "vk",
78 | "weibo",
79 | "whatsapp"
80 | ]
81 | },
82 | "tbfed-pagefooter": {
83 | "copyright": "crifan.org,使用署名4.0国际(CC BY 4.0)协议发布",
84 | "modify_label": "最后更新:",
85 | "modify_format": "YYYY-MM-DD HH:mm:ss"
86 | },
87 | "donate": {
88 | "wechat": "https://www.crifan.org/files/res/crifan_com/crifan_wechat_pay.jpg",
89 | "alipay": "https://www.crifan.org/files/res/crifan_com/crifan_alipay_pay.jpg",
90 | "title": "",
91 | "button": "打赏",
92 | "alipayText": "支付宝打赏给Crifan",
93 | "wechatText": "微信打赏给Crifan"
94 | }
95 | },
96 | "author": "Crifan Li ",
97 | "language": "zh-hans",
98 | "root": "./src",
99 | "links": {
100 | "sidebar": {
101 | "主页": "http://www.crifan.org"
102 | }
103 | },
104 | "plugins": [
105 | "theme-comscore",
106 | "anchors",
107 | "expandable-menu",
108 | "-lunr",
109 | "-search",
110 | "search-plus",
111 | "disqus",
112 | "-highlight",
113 | "prism",
114 | "prism-themes",
115 | "github-buttons",
116 | "-splitter",
117 | "splitter-nosessionbutcookie",
118 | "-sharing",
119 | "sharing-plus",
120 | "tbfed-pagefooter",
121 | "donate",
122 | "sitemap-general",
123 | "copy-code-button",
124 | "callouts",
125 | "toolbar-button"
126 | ]
127 | }
--------------------------------------------------------------------------------
/book_current.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "iOS逆向开发:越狱检测和反越狱检测",
3 | "description": "介绍iOS逆向期间涉及到的iOS的越狱检测和反越狱检测方面的内容。包括常用的越狱检测手段,比如URL Scheme、文件方面的:文件属性、文件打开和文件写入;其中文件打开又包括C函数和iOS函数,其中C函数又包括syscall的C函数和svc 0x80内联汇编;以及文件写入包括C函数和iOS函数;以及其他检测手段:环境变量、是否可调试、system、沙箱完整性校验、越狱相关进程、dyld动态库,包括dylib的dladdr、dlopen、dlsym和_dyld开头的系列函数、ObjC运行时、app本身、已安装app、SSH相关、getsectiondata等;以及上述各种方法的反越狱检测的具体实现,以及其他一些额外的反越狱检测手段,比如内核级反越狱;接着整理越狱检测和反越狱检测的通用的内容,比如app的启动过程、越狱路径相关,尤其是越狱路径列表。以及其他一些心得。且均已贴出相关的越狱检测和反越狱检测的代码段和独立的代码仓库。",
4 | "pluginsConfig": {
5 | "github-buttons": {
6 | "buttons": [
7 | {
8 | "repo": "ios_re_jb_detection"
9 | }
10 | ]
11 | },
12 | "sitemap-general": {
13 | "prefix": "https://book.crifan.org/books/ios_re_jb_detection/website/"
14 | },
15 | "toolbar-button": {
16 | "url": "https://book.crifan.org/books/ios_re_jb_detection/pdf/ios_re_jb_detection.pdf"
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/node_modules:
--------------------------------------------------------------------------------
1 | ../../generated/honkit/node_modules
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # iOS逆向开发:越狱检测和反越狱检测
2 |
3 | * 最新版本:`v1.0`
4 | * 更新时间:`20221110`
5 |
6 | ## 简介
7 |
8 | 介绍iOS逆向期间涉及到的iOS的越狱检测和反越狱检测方面的内容。包括常用的越狱检测手段,比如URL Scheme、文件方面的:文件属性、文件打开和文件写入;其中文件打开又包括C函数和iOS函数,其中C函数又包括syscall的C函数和svc 0x80内联汇编;以及文件写入包括C函数和iOS函数;以及其他检测手段:环境变量、是否可调试、system、沙箱完整性校验、越狱相关进程、dyld动态库,包括dylib的dladdr、dlopen、dlsym和_dyld开头的系列函数、ObjC运行时、app本身、已安装app、SSH相关、getsectiondata等;以及上述各种方法的反越狱检测的具体实现,以及其他一些额外的反越狱检测手段,比如内核级反越狱;接着整理越狱检测和反越狱检测的通用的内容,比如app的启动过程、越狱路径相关,尤其是越狱路径列表。以及其他一些心得。且均已贴出相关的越狱检测和反越狱检测的代码段和独立的代码仓库。
9 |
10 | ## 源码+浏览+下载
11 |
12 | 本书的各种源码、在线浏览地址、多种格式文件下载如下:
13 |
14 | ### HonKit源码
15 |
16 | * [crifan/ios_re_jb_detection: iOS逆向开发:越狱检测和反越狱检测](https://github.com/crifan/ios_re_jb_detection)
17 |
18 | #### 如何使用此HonKit源码去生成发布为电子书
19 |
20 | 详见:[crifan/honkit_template: demo how to use crifan honkit template and demo](https://github.com/crifan/honkit_template)
21 |
22 | ### 在线浏览
23 |
24 | * [iOS逆向开发:越狱检测和反越狱检测 book.crifan.org](https://book.crifan.org/books/ios_re_jb_detection/website/)
25 | * [iOS逆向开发:越狱检测和反越狱检测 crifan.github.io](https://crifan.github.io/ios_re_jb_detection/website/)
26 |
27 | ### 离线下载阅读
28 |
29 | * [iOS逆向开发:越狱检测和反越狱检测 PDF](https://book.crifan.org/books/ios_re_jb_detection/pdf/ios_re_jb_detection.pdf)
30 | * [iOS逆向开发:越狱检测和反越狱检测 ePub](https://book.crifan.org/books/ios_re_jb_detection/epub/ios_re_jb_detection.epub)
31 | * [iOS逆向开发:越狱检测和反越狱检测 Mobi](https://book.crifan.org/books/ios_re_jb_detection/mobi/ios_re_jb_detection.mobi)
32 |
33 | ## 版权和用途说明
34 |
35 | 此电子书教程的全部内容,如无特别说明,均为本人原创。其中部分内容参考自网络,均已备注了出处。如发现有侵权,请通过邮箱联系我 `admin 艾特 crifan.com`,我会尽快删除。谢谢合作。
36 |
37 | 各种技术类教程,仅作为学习和研究使用。请勿用于任何非法用途。如有非法用途,均与本人无关。
38 |
39 | ## 鸣谢
40 |
41 | 感谢我的老婆**陈雪**的包容理解和悉心照料,才使得我`crifan`有更多精力去专注技术专研和整理归纳出这些电子书和技术教程,特此鸣谢。
42 |
43 | ## 其他
44 |
45 | ### 作者的其他电子书
46 |
47 | 本人`crifan`还写了其他`150+`本电子书教程,感兴趣可移步至:
48 |
49 | [crifan/crifan_ebook_readme: Crifan的电子书的使用说明](https://github.com/crifan/crifan_ebook_readme)
50 |
51 | ### 关于作者
52 |
53 | 关于作者更多介绍,详见:
54 |
55 | [关于CrifanLi李茂 – 在路上](https://www.crifan.org/about/)
56 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # iOS逆向开发:越狱检测和反越狱检测
2 |
3 | * [前言](README.md)
4 | * [iOS越狱检测概览](ios_jb_detecton_overview/README.md)
5 | * [iOS越狱检测](jb_detection/README.md)
6 | * [URL Scheme](jb_detection/url_scheme.md)
7 | * [文件](jb_detection/file/README.md)
8 | * [文件属性](jb_detection/file/attributes.md)
9 | * [文件打开](jb_detection/file/open/README.md)
10 | * [C函数](jb_detection/file/open/c_func/README.md)
11 | * [syscall](jb_detection/file/open/c_func/syscall.md)
12 | * [svc 0x80内联汇编](jb_detection/file/open/c_func/svc_0x80_asm.md)
13 | * [iOS函数](jb_detection/file/open/ios_func.md)
14 | * [文件写入](jb_detection/file/write/README.md)
15 | * [C函数](jb_detection/file/write/c_func.md)
16 | * [iOS函数](jb_detection/file/write/ios_func.md)
17 | * [环境变量](jb_detection/env.md)
18 | * [是否可调试](jb_detection/debuggable.md)
19 | * [system](jb_detection/system.md)
20 | * [沙箱完整性校验](jb_detection/sandbox.md)
21 | * [越狱相关进程](jb_detection/jb_processes.md)
22 | * [dyld动态库](jb_detection/dyld/README.md)
23 | * [_dyld系列](jb_detection/dyld/_dyld_series.md)
24 | * [dylib](jb_detection/dyld/dylib.md)
25 | * [ObjC运行时](jb_detection/objc_runtime.md)
26 | * [app本身](jb_detection/app_self.md)
27 | * [已安装app](jb_detection/installed_app.md)
28 | * [SSH相关](jb_detection/ssh.md)
29 | * [getsectiondata](jb_detection/getsectiondata.md)
30 | * [iOS反越狱检测](anti_jb_detect/README.md)
31 | * [URL Scheme](anti_jb_detect/url_scheme.md)
32 | * [文件](anti_jb_detect/file/README.md)
33 | * [文件打开](anti_jb_detect/file/open/README.md)
34 | * [C函数](anti_jb_detect/file/open/c_func/README.md)
35 | * [syscall](anti_jb_detect/file/open/c_func/syscall.md)
36 | * [svc 0x80内联汇编](anti_jb_detect/file/open/c_func/svc_0x80_asm.md)
37 | * [iOS函数](anti_jb_detect/file/open/ios_func.md)
38 | * [文件写入](anti_jb_detect/file/write/README.md)
39 | * [C函数](anti_jb_detect/file/write/c_func.md)
40 | * [iOS函数](anti_jb_detect/file/write/ios_func.md)
41 | * [环境变量](anti_jb_detect/env.md)
42 | * [是否可调试](anti_jb_detect/debuggable.md)
43 | * [system](anti_jb_detect/system.md)
44 | * [沙箱完整性校验](anti_jb_detect/sandbox.md)
45 | * [越狱相关进程](anti_jb_detect/jb_processes.md)
46 | * [dyld动态库](anti_jb_detect/dyld/README.md)
47 | * [_dyld系列](anti_jb_detect/dyld/_dyld_series.md)
48 | * [dylib](anti_jb_detect/dyld/dylib.md)
49 | * [ObjC运行时](anti_jb_detect/objc_runtime.md)
50 | * [app本身](anti_jb_detect/app_self.md)
51 | * [getsectiondata](anti_jb_detect/getsectiondata.md)
52 | * [内核级反越狱](anti_jb_detect/kernel_level.md)
53 | * [通用内容](detect_common/README.md)
54 | * [app启动过程](detect_common/app_load_process.md)
55 | * [越狱路径相关](detect_common/jb_path/README.md)
56 | * [越狱文件列表](detect_common/jb_path/jb_file_list.md)
57 | * [其他心得](other_summary/README.md)
58 | * [附录](appendix/README.md)
59 | * [参考资料](appendix/reference.md)
60 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/README.md:
--------------------------------------------------------------------------------
1 | # iOS反越狱检测
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS反越狱检测:反越狱插件tweak
6 | * 【已解决】iOS反越狱检测:参考学习借鉴开源代码项目
7 | * 【未解决】iOS反越狱检测:优化逻辑调用被hook的orig函数
8 | * 【未解决】越狱iOS如何实现反越狱检测防越狱检测屏蔽越狱检测
9 | * 其他反越狱检测相关
10 | * 【记录】iOS反越狱检测:hook调试NSBundle的mainBundle的pathForResource
11 |
12 | ---
13 |
14 | 现已公开发布源码:
15 |
16 | [crifan/iOSBypassJailbreak: 越狱iOS的hook插件,实现反越狱检测](https://github.com/crifan/iOSBypassJailbreak)
17 |
18 | 本教程后续章节中贴出的**iOS反越狱检测**的代码片段,均摘录自其中。
19 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/app_self.md:
--------------------------------------------------------------------------------
1 | # app本身
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS逆向:绕过重签名检测embedded.mobileprovision
6 |
7 | ---
8 |
9 | ## NSBundle
10 |
11 | ```c
12 | /*==============================================================================
13 | Hook: debugging embedded.mobileprovision
14 | ==============================================================================*/
15 |
16 | // NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
17 | %hook NSBundle
18 |
19 | - (NSString *)pathForResource:(NSString *)name ofType:(NSString *)ext {
20 | NSString* resPath = %orig(name, ext);
21 |
22 | if (cfgHookEnable_aweme) {
23 | if ([ext isEqualToString: @"mobileprovision"]){
24 | iosLogInfo("name=%{public}@, ext=%{public}@ -> resPath=%{public}@", name, ext, resPath);
25 | if ([name isEqualToString: @"embedded"]){
26 | resPath = NULL;
27 | }
28 | }
29 | }
30 |
31 | return resPath;
32 | }
33 |
34 | // https://developer.apple.com/documentation/foundation/nsbundle/1407973-bundlepath
35 | // @property(readonly, copy) NSString *bundlePath;
36 |
37 | - (NSString *)bundlePath {
38 | NSString* origBundlePath = %orig;
39 | BOOL shouldOmit = [origBundlePath containsString: @"Aweme"] || [origBundlePath containsString: @"/System/Library"];
40 | if (!shouldOmit){
41 | iosLogInfo("origBundlePath=%{public}@", origBundlePath);
42 | }
43 | return origBundlePath;
44 | }
45 |
46 | %end
47 | ```
48 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/debuggable.md:
--------------------------------------------------------------------------------
1 | # 是否可调试
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:是否可被调试
6 |
7 | ---
8 |
9 | ## sysctl
10 |
11 | ```c
12 | /*==============================================================================
13 | Hook: sysctl
14 | ==============================================================================*/
15 |
16 | int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
17 |
18 | %hookf(int, sysctl, int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen){
19 | iosLogDebug("name=%p, namelen=%d, oldp=%p, oldlenp=%p, newp=%p, newlen=%ld", name, namelen, oldp, oldlenp, newp, newlen);
20 |
21 | // int sysctlRet = SYSCTL_FAIL;
22 | // sysctlRet = %orig(name, namelen, oldp, oldlenp, newp, newlen);
23 | int sysctlRet = %orig;
24 |
25 | if (cfgHookEnable_sysctl_sysctl) {
26 | // for Anti-Debug
27 | bool isGetpid = (name[0] == CTL_KERN && name[1] == KERN_PROC && name[2] == KERN_PROC_PID);
28 | if (isGetpid) {
29 | struct kinfo_proc *info = NULL;
30 | info = (struct kinfo_proc *)oldp;
31 | int oldPFlag = info->kp_proc.p_flag;
32 | info->kp_proc.p_flag &= ~(P_TRACED);
33 | int newPFlag = info->kp_proc.p_flag;
34 |
35 | iosLogInfo("name=%p, namelen=%d, oldp=%p, oldlenp=%p, newp=%p, newlen=%ld -> isGetpid=%s -> oldPFlag=0x%x, newPFlag=0x%x -> sysctlRet=%d", name, namelen, oldp, oldlenp, newp, newlen, boolToStr(isGetpid), oldPFlag, newPFlag, sysctlRet);
36 | }
37 | }
38 |
39 | return sysctlRet;
40 | }
41 | ```
42 |
43 | ## sysctlnametomib
44 |
45 | ```c
46 | /*==============================================================================
47 | Hook: sysctlnametomib
48 | ==============================================================================*/
49 |
50 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctlnametomib.3.html
51 | int sysctlnametomib(const char *name, int *mibp, size_t *sizep);
52 |
53 | %hookf(int, sysctlnametomib, const char *name, int *mibp, size_t *sizep){
54 | // iosLogInfo("name=%p, mibp=%p, sizep=%p", name, mibp, sizep);
55 | int retInt = SYSCTL_FAIL;
56 | retInt = %orig;
57 | iosLogInfo("name=%{public}s, mibp=%p, sizep=%p -> retInt=%d", name, mibp, sizep, retInt);
58 | return retInt;
59 | }
60 | ```
--------------------------------------------------------------------------------
/src/anti_jb_detect/dyld/README.md:
--------------------------------------------------------------------------------
1 | # dyld动态库
2 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/dyld/_dyld_series.md:
--------------------------------------------------------------------------------
1 | # _dyld系列
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:优化findRealImageCount改为调用_dyld_get_image_vmaddr_slide计算逻辑
6 | * 【已解决】iOS反越狱检测:_dyld_image_count和_dyld_get_image_name返回hook后的值
7 | * 【已解决】iOS反越狱检测:如何hook绕过_dyld_image_count和_dyld_get_image_name
8 | * 【已解决】iOS反越狱检测:优化findRealImageCount改为调用_dyld_get_image_vmaddr_slide计算逻辑
9 | * 【已解决】iOS反越狱检测:_dyld_get_image_name的hook绕过
10 | * 【已解决】iOS反越狱检测:_dyld_image_count和_dyld_get_image_name改为普通hook逻辑
11 | * 【已解决】iOS反越狱检测:dyld的_dyld_image_count和_dyld_get_image_name
12 | * 【已解决】iOS反越狱检测:_dyld_register_func_for_add_image和_dyld_register_func_for_remove_image
13 | *
14 | * 【已解决】反越狱检测测试抖音:优化dyld的hook逻辑
15 |
16 | ---
17 |
18 | ## 相关工具函数
19 |
20 | ```c
21 | /*==============================================================================
22 | Hook: _dyld_image_count(), _dyld_get_image_name(), _dyld_get_image_header(), _dyld_get_image_vmaddr_slide()
23 | ==============================================================================*/
24 |
25 | /*
26 | https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
27 | _dyld_image_count,
28 | _dyld_get_image_header,
29 | _dyld_get_image_vmaddr_slide,
30 | _dyld_get_image_name,
31 | _dyld_register_func_for_add_image,
32 | _dyld_register_func_for_remove_image,
33 | NSVersionOfRunTimeLibrary,
34 | NSVersionOfLinkTimeLibrary,
35 | _NSGetExecutablePath
36 | */
37 |
38 | uint32_t _dyld_image_count(void);
39 | //uint32_t orig__dyld_image_count(void);
40 | //uint32_t _logos_orig$_ungrouped$_dyld_image_count(void);
41 | //uint32_t (*_logos_orig$_ungrouped$_dyld_image_count)(void);
42 | //static uint32_t (*_logos_orig$_ungrouped$_dyld_image_count)(void);
43 |
44 | const struct mach_header* _dyld_get_image_header(uint32_t image_index);
45 | const char* _dyld_get_image_name(uint32_t image_index);
46 | intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index);
47 |
48 | void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
49 | void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
50 |
51 | int32_t NSVersionOfRunTimeLibrary(const char* libraryName);
52 |
53 | int32_t NSVersionOfLinkTimeLibrary(const char* libraryName);
54 |
55 | int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
56 |
57 | const int IMAGE_INDEX_FAKE_END = IMAGE_INDEX_FAKE_START + IMAGE_INDEX_MAX_VALID_NUMBER;
58 |
59 | // Global Variable
60 | int gOrigImageCount = -1;
61 | int gHookedImageCount = -1;
62 | int gRealOrigImageCount = -1; // after hooked, image name/header/slide got hooked image count -> so need find real original image count
63 |
64 | int* gJbDylibIdxList = NULL;
65 | int gJbDylibIdxListLen = -1;
66 |
67 | int* gHookedImgIdxList = NULL;
68 | int gHookedImgIdxListLen = -1;
69 |
70 | static int generateFakeImageIndex(int origImageIndex){
71 | int fakeImgIdx = origImageIndex + IMAGE_INDEX_FAKE_START;
72 | iosLogDebug("generateFakeImageIndex: origImageIndex=%d -> fakeImgIdx=%d", origImageIndex, fakeImgIdx);
73 | return fakeImgIdx;
74 | }
75 |
76 | static bool isFakeImageIndex(int curImageIndex){
77 | bool isFakeIdx = (curImageIndex >= IMAGE_INDEX_FAKE_START) && (curImageIndex < IMAGE_INDEX_FAKE_END);
78 | iosLogDebug("curImageIndex=%d -> isFakeIdx=%s", curImageIndex, boolToStr(isFakeIdx));
79 | return isFakeIdx;
80 | }
81 |
82 | static int fakeToRealImageIndex(int fakeImgageIndex){
83 | int realImageIndex = fakeImgageIndex - IMAGE_INDEX_FAKE_START;
84 | iosLogDebug("fakeImgageIndex=%d -> realImageIndex=%d", fakeImgageIndex, realImageIndex);
85 | return realImageIndex;
86 | }
87 |
88 | static void dbgPrintImgIdxList(int* imgIdxList){
89 | iosLogDebug("imgIdxList=%p", imgIdxList);
90 |
91 | if (NULL != imgIdxList){
92 | int curListIdx = 0;
93 | int curIdxValue = DYLD_IMAGE_INDEX_INVALID;
94 | curIdxValue = imgIdxList[curListIdx];
95 | if (DYLD_IMAGE_INDEX_INVALID == curIdxValue) {
96 | iosLogDebug("[%d] %d", curListIdx, curIdxValue);
97 | }
98 |
99 | while(DYLD_IMAGE_INDEX_INVALID != curIdxValue){
100 | iosLogDebug("[%d] %d", curListIdx, curIdxValue);
101 |
102 | ++curListIdx;
103 | curIdxValue = imgIdxList[curListIdx];
104 | }
105 |
106 | int listCount = curListIdx;
107 | iosLogDebug("end listCount=%d", listCount);
108 | }
109 | }
110 |
111 | static void getJbDylibImgIdxList(int origImageCount, int** outJbDylibIdxList, int* jbDylibIdxListLen){
112 | iosLogDebug("origImageCount=%d", origImageCount);
113 |
114 | int intSize = sizeof(int);
115 | int mallocCount = IMAGE_INDEX_MAX_JAILBREAK + 1;
116 | int mallocSize = intSize * mallocCount;
117 | iosLogDebug("intSize=%d, mallocCount=%d, mallocSize=%d", intSize, mallocCount, mallocSize);
118 |
119 | int curListIdx = 0;
120 |
121 | int* jbDylibIdxList = (int *)malloc(mallocSize);
122 | iosLogDebug("jbDylibIdxList=%p", jbDylibIdxList);
123 |
124 | if (NULL != jbDylibIdxList) {
125 | for (int origImgIdx = 0 ; origImgIdx < origImageCount; ++origImgIdx) {
126 | int fakeImgIdx = generateFakeImageIndex(origImgIdx);
127 | iosLogDebug("origImgIdx=%d, fakeImgIdx=%d", origImgIdx, fakeImgIdx);
128 | const char* curImageName = _dyld_get_image_name(fakeImgIdx);
129 | iosLogDebug("curImageName=%{public}s", curImageName);
130 |
131 | bool isJbDylib = isJailbreakDylib(curImageName);
132 | iosLogDebug("isJbDylib=%s", boolToStr(isJbDylib));
133 |
134 | if(isJbDylib){
135 | jbDylibIdxList[curListIdx] = origImgIdx;
136 | iosLogInfo("curImageName=%{public}s -> origImgIdx=%d, jbDylibIdxList[%d]=%d", curImageName, origImgIdx, curListIdx, jbDylibIdxList[curListIdx]);
137 | ++curListIdx;
138 | }
139 | }
140 |
141 | int curListCount = curListIdx;
142 |
143 | if (jbDylibIdxListLen) {
144 | *jbDylibIdxListLen = curListCount;
145 | iosLogDebug("*jbDylibIdxListLen=%d", *jbDylibIdxListLen);
146 | }
147 |
148 | int curListEndIdx = curListCount;
149 | jbDylibIdxList[curListEndIdx] = DYLD_IMAGE_INDEX_INVALID;
150 | iosLogDebug("list end, jbDylibIdxList[%d]=%d", curListEndIdx, jbDylibIdxList[curListEndIdx]);
151 |
152 | dbgPrintImgIdxList(jbDylibIdxList);
153 |
154 | if (outJbDylibIdxList) {
155 | // Note: here for 0 jailbreak dylib, also means get OK
156 | *outJbDylibIdxList = jbDylibIdxList;
157 | }
158 | }
159 |
160 | iosLogInfo("origImageCount=%d -> outJbDylibIdxList=%p, *outJbDylibIdxList=%p, jbDylibIdxList=%p, *jbDylibIdxListLen=%d", origImageCount, outJbDylibIdxList, outJbDylibIdxList ? *outJbDylibIdxList : NULL, jbDylibIdxList, jbDylibIdxListLen ? *jbDylibIdxListLen : 0);
161 | }
162 |
163 | static void initDylibImageIdxList(void) {
164 | // init for _dyld_image_count and related
165 | if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
166 | getJbDylibImgIdxList(gOrigImageCount, &gJbDylibIdxList, &gJbDylibIdxListLen);
167 | gHookedImageCount = gOrigImageCount - gJbDylibIdxListLen;
168 | iosLogInfo("gOrigImageCount=%d, gJbDylibIdxList=%p, gJbDylibIdxListLen=%d -> gHookedImageCount=%d", gOrigImageCount, gJbDylibIdxList, gJbDylibIdxListLen, gHookedImageCount);
169 | }
170 | }
171 |
172 | static void generateHookedImageIndexList(int* jbDylibIdxList, int jbDylibIdxListLen, int origImgageCount, int** outHookedImgIdxList, int* outHookedImgIdxListLen){
173 | int* hookedImgIdxList = (int*)malloc(sizeof(int) * (origImgageCount + 1));
174 | int curListIdx = 0;
175 | for(int curImgIdx = 0; curImgIdx < origImgageCount; curImgIdx++){
176 | bool isJbDylibIdx = false;
177 | if (jbDylibIdxListLen > 0){
178 | isJbDylibIdx = isIntInList(curImgIdx, jbDylibIdxList, jbDylibIdxListLen);
179 | }
180 |
181 | iosLogDebug("curImgIdx=%d, isJbDylibIdx=%s", curImgIdx, boolToStr(isJbDylibIdx));
182 |
183 | if(!isJbDylibIdx){
184 | hookedImgIdxList[curListIdx] = curImgIdx;
185 | ++curListIdx;
186 | }
187 | }
188 |
189 | int hookedImgIdxListLen = curListIdx;
190 | // set end
191 | int hookedImgIdxListEndIdx = hookedImgIdxListLen;
192 | hookedImgIdxList[hookedImgIdxListEndIdx] = DYLD_IMAGE_INDEX_INVALID;
193 |
194 | dbgPrintImgIdxList(hookedImgIdxList);
195 |
196 | // return result
197 | *outHookedImgIdxList = hookedImgIdxList;
198 | *outHookedImgIdxListLen = hookedImgIdxListLen;
199 |
200 | iosLogInfo("-> outHookedImgIdxList=%p, *outHookedImgIdxList=%p, *outHookedImgIdxListLen=%d", outHookedImgIdxList, *outHookedImgIdxList, *outHookedImgIdxListLen);
201 | }
202 |
203 | static void reInitImgCountIfNeed(int curOrigCount) {
204 | if (curOrigCount != gOrigImageCount) {
205 | iosLogInfo("curOrigCount=%d != gOrigImageCount=%d, need init", curOrigCount, gOrigImageCount);
206 | gOrigImageCount = curOrigCount;
207 | initDylibImageIdxList();
208 |
209 | bool foundJbLib = (NULL != gJbDylibIdxList) && (gJbDylibIdxListLen > 0);
210 | if (foundJbLib) {
211 | generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, gOrigImageCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
212 | }
213 | }
214 | }
215 |
216 | // static int hookedToOrigImageIndex(int hookedImageIndex, int* jbDylibIdxList, int jbDylibIdxListLen, int origImgageCount){
217 | static int hookedToOrigImageIndex(int hookedImageIndex){
218 | int origImgIdx = DYLD_IMAGE_INDEX_INVALID;
219 |
220 | // int* hookedImgIdxList = NULL;
221 | // int hookedImgIdxListLen = 0;
222 | // generateHookedImageIndexList(jbDylibIdxList, jbDylibIdxListLen, origImgageCount, &hookedImgIdxList, &hookedImgIdxListLen);
223 |
224 | // int hookedImgIdxListMaxIdx = hookedImgIdxListLen - 1;
225 | int hookedImgIdxListMaxIdx = gHookedImgIdxListLen - 1;
226 | iosLogDebug("hookedImgIdxListMaxIdx=%d", hookedImgIdxListMaxIdx);
227 |
228 | if (hookedImageIndex <= hookedImgIdxListMaxIdx){
229 | // origImgIdx = hookedImgIdxList[hookedImageIndex];
230 | origImgIdx = gHookedImgIdxList[hookedImageIndex];
231 | iosLogDebug("hookedImageIndex=%d <= hookedImgIdxListMaxIdx=%d -> origImgIdx=%d", hookedImageIndex, hookedImgIdxListMaxIdx, origImgIdx);
232 | } else {
233 | origImgIdx = DYLD_IMAGE_INDEX_INVALID;
234 | iosLogDebug("hookedImageIndex=%d > hookedImgIdxListMaxIdx=%d -> origImgIdx=%d", hookedImageIndex, hookedImgIdxListMaxIdx, origImgIdx);
235 | }
236 |
237 | // if (NULL != hookedImgIdxList){
238 | // free(hookedImgIdxList);
239 | // }
240 |
241 | return origImgIdx;
242 | }
243 |
244 | static int findRealImageCount(void){
245 | iosLogDebug("%s", "");
246 |
247 | int realImageCount = 0;
248 | int hookedImageCount = _dyld_image_count();
249 | // int origImageCount = orig__dyld_image_count();
250 | iosLogDebug("hookedImageCount=%d", hookedImageCount);
251 |
252 | // find real count
253 | int curImgIdx = hookedImageCount;
254 |
255 | // use: _dyld_get_image_vmaddr_slide
256 |
257 | long retSlide = _dyld_get_image_vmaddr_slide(generateFakeImageIndex(curImgIdx));
258 | iosLogDebug("[%d] -> retSlide=%ld", curImgIdx, retSlide);
259 | while(DYLD_IMAGE_SLIDE_INVALID != retSlide){
260 | ++curImgIdx;
261 | retSlide = _dyld_get_image_vmaddr_slide(generateFakeImageIndex(curImgIdx));
262 | iosLogDebug("[%d] -> retSlide=%ld", curImgIdx, retSlide);
263 | }
264 |
265 | // // use: _dyld_get_image_name
266 | // const char* retImgName = _dyld_get_image_name(generateFakeImageIndex(curImgIdx));
267 | // iosLogDebug("[%d] -> retImgName=%s", curImgIdx, retImgName);
268 | // while(NULL != retImgName){
269 | // ++curImgIdx;
270 | // retImgName = _dyld_get_image_name(generateFakeImageIndex(curImgIdx));
271 | // iosLogDebug("[%d] -> retImgName=%s", curImgIdx, retImgName);
272 | // }
273 |
274 | // // use: _dyld_get_image_header
275 | // const struct mach_header* retImgHeader = _dyld_get_image_header(generateFakeImageIndex(curImgIdx));
276 | // iosLogDebug("[%d] -> retImgHeader=%p", curImgIdx, retImgHeader);
277 | // while(NULL != retImgHeader){
278 | // ++curImgIdx;
279 | // retImgHeader = _dyld_get_image_header(generateFakeImageIndex(curImgIdx));
280 | // iosLogDebug("[%d] -> retImgHeader=%p", curImgIdx, retImgHeader);
281 | // }
282 |
283 | realImageCount = curImgIdx;
284 | iosLogDebug("realImageCount=%d", realImageCount);
285 | return realImageCount;
286 | }
287 |
288 | static void reInitAllRelated(void) {
289 | // if (gRealOrigImageCount <= 0) {
290 | // // invalid, need reinit
291 | // gRealOrigImageCount = findRealImageCount();
292 | // iosLogInfo("gRealOrigImageCount=%d", gRealOrigImageCount);
293 | // }
294 |
295 | int curOrigCount = -1;
296 |
297 | int curHookedImageCount = _dyld_image_count();
298 | if (gJbDylibIdxListLen > 0) {
299 | curOrigCount = curHookedImageCount + gJbDylibIdxListLen;
300 | } else {
301 | curOrigCount = findRealImageCount();
302 | }
303 | iosLogDebug("curHookedImageCount=%d, gJbDylibIdxListLen=%d -> curOrigCount=%d", curHookedImageCount, gJbDylibIdxListLen, curOrigCount);
304 |
305 | if (curOrigCount != gOrigImageCount) {
306 | iosLogInfo("curOrigCount=%d != gOrigImageCount=%d -> reinit image index list", curOrigCount, gOrigImageCount);
307 |
308 | reInitImgCountIfNeed(curOrigCount);
309 | iosLogInfo("after reinit, gOrigImageCount=%d, gHookedImgIdxList=%p, gHookedImgIdxListLen=%d", gOrigImageCount, gHookedImgIdxList, gHookedImgIdxListLen);
310 | } else {
311 | iosLogDebug("gJbDylibIdxList=%p, gJbDylibIdxListLen=%d, gHookedImgIdxList=%p, gHookedImgIdxListLen=%d", gJbDylibIdxList, gJbDylibIdxListLen, gHookedImgIdxList, gHookedImgIdxListLen);
312 |
313 | if ((NULL == gJbDylibIdxList) || (gJbDylibIdxListLen <= 0)) {
314 | reInitImgCountIfNeed(curOrigCount);
315 | }
316 |
317 | if ((NULL == gHookedImgIdxList) || (gHookedImgIdxListLen <= 0)) {
318 | generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, curOrigCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
319 | }
320 | }
321 | }
322 |
323 | static int getOrigImageIndex(int hookedImageIndex){
324 | iosLogDebug("hookedImageIndex=%d", hookedImageIndex);
325 |
326 | int origImgIdx = DYLD_IMAGE_INDEX_INVALID;
327 |
328 | // uint32_t origImageCount_byLogos = (*_logos_orig$_ungrouped$_dyld_image_count)();
329 | // os_log(OS_LOG_DEFAULT, "hook_dyld getOrigImageIndex: origImageCount_byLogos=%d", origImageCount_byLogos);
330 |
331 | // uint32_t origImageCount = findRealImageCount();
332 | // iosLogDebug("origImageCount=%d", origImageCount);
333 | //
334 | // int* jbDylibImgIdxList = NULL;
335 | // int jbDylibImgIdxListLen = -1;
336 | // getJbDylibImgIdxList(origImageCount, &jbDylibImgIdxList, &jbDylibImgIdxListLen);
337 | // iosLogDebug("jbDylibImgIdxList=%p,jbDylibImgIdxListLen=%d", jbDylibImgIdxList, jbDylibImgIdxListLen);
338 |
339 | reInitAllRelated();
340 |
341 | // if (jbDylibImgIdxListLen > 0){
342 | if (gJbDylibIdxListLen > 0){
343 | // check input image index validation
344 | // int origImgMaxIdx = origImageCount - 1;
345 | // int origImgMaxIdx = gRealOrigImageCount - 1;
346 | int origImgMaxIdx = gOrigImageCount - 1;
347 | // int hookedImgMaxIdx = origImgMaxIdx - jbDylibImgIdxListLen;
348 | int hookedImgMaxIdx = origImgMaxIdx - gJbDylibIdxListLen;
349 | iosLogDebug("origImgMaxIdx=%d, hookedImgMaxIdx=%d, hookedImageIndex=%d", origImgMaxIdx, hookedImgMaxIdx, hookedImageIndex);
350 |
351 | if(hookedImageIndex > hookedImgMaxIdx){
352 | // invalid
353 | iosLogError("input image index invalid, hookedImageIndex=%d > hookedImgMaxIdx=%d", hookedImageIndex, hookedImgMaxIdx);
354 | origImgIdx = DYLD_IMAGE_INDEX_INVALID;
355 | } else {
356 | // valid
357 | // origImgIdx = hookedToOrigImageIndex(hookedImageIndex, jbDylibImgIdxList, jbDylibImgIdxListLen, origImageCount);
358 | origImgIdx = hookedToOrigImageIndex(hookedImageIndex);
359 | }
360 | } else {
361 | // no jailbreak dylib image index list
362 | origImgIdx = hookedImageIndex;
363 | }
364 |
365 | iosLogDebug("hookedImageIndex=%d -> origImgIdx=%d", hookedImageIndex, origImgIdx);
366 |
367 | // if (NULL != jbDylibImgIdxList){
368 | // free(jbDylibImgIdxList);
369 | // }
370 |
371 | return origImgIdx;
372 | }
373 |
374 | static int getRealOrOrigImageIndex(int inputImageIndex){
375 | iosLogDebug("inputImageIndex=%d", inputImageIndex);
376 |
377 | int realOrOrigImgIdx = DYLD_IMAGE_INDEX_INVALID;
378 |
379 | bool isFakeImgIdx = isFakeImageIndex(inputImageIndex);
380 | iosLogDebug("isFakeImgIdx=%s", boolToStr(isFakeImgIdx));
381 |
382 | if (isFakeImgIdx){
383 | realOrOrigImgIdx = fakeToRealImageIndex(inputImageIndex);
384 | } else {
385 | realOrOrigImgIdx = getOrigImageIndex(inputImageIndex);
386 | }
387 |
388 | iosLogDebug("inputImageIndex=%d -> realOrOrigImgIdx=%d", inputImageIndex, realOrOrigImgIdx);
389 |
390 | return realOrOrigImgIdx;
391 | }
392 | ```
393 |
394 | ## _dyld_image_count
395 |
396 | ```c
397 | %hookf(uint32_t, _dyld_image_count, void){
398 | // iosLogDebug();
399 | iosLogDebug("%s", "");
400 |
401 | uint32_t origCount = 0;
402 | int retImageCount = 0;
403 |
404 | if (cfgHookEnable_dyld){
405 | origCount = %orig();
406 | iosLogDebug("origCount=%d", origCount);
407 | retImageCount = origCount;
408 |
409 | if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
410 | // int* jbDylibIdxList = NULL;
411 | // int jbDylibIdxListLen = -1;
412 | // getJbDylibImgIdxList(origCount, &jbDylibIdxList, &jbDylibIdxListLen);
413 | // iosLogDebug("jbDylibIdxList=%p, jbDylibIdxListLen=%d", jbDylibIdxList, jbDylibIdxListLen);
414 | // retImageCount = origCount - jbDylibIdxListLen;
415 | //
416 | // if(NULL != jbDylibIdxList){
417 | // free(jbDylibIdxList);
418 | // }
419 |
420 | reInitImgCountIfNeed(origCount);
421 |
422 | // if ((NULL == gHookedImgIdxList) || (gHookedImgIdxListLen <= 0)) {
423 | // generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, origCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
424 | // }
425 |
426 | retImageCount = gHookedImageCount;
427 | // retImageCount = gOrigImageCount;
428 | }
429 | } else {
430 | origCount = %orig();
431 | retImageCount = origCount;
432 | }
433 |
434 | iosLogDebug("%sorigCount=%d -> retImageCount=%d", HOOK_PREFIX(cfgHookEnable_dyld), origCount, retImageCount);
435 | return retImageCount;
436 | }
437 | ```
438 |
439 | ## _dyld_get_image_name
440 |
441 | ```c
442 | %hookf(const char*, _dyld_get_image_name, uint32_t image_index){
443 | iosLogDebug("image_index=%d", image_index);
444 | const char* retImgName = NULL;
445 |
446 | if (cfgHookEnable_dyld){
447 | if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
448 | int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
449 | bool isValidImgIdx = (realOrOrigImgIdx >= 0);
450 | if (isValidImgIdx){
451 | const char* imgName = %orig(realOrOrigImgIdx);
452 | iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> imgName=%{public}s", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), imgName);
453 | retImgName = imgName;
454 | } else {
455 | iosLogError("fail to get real or origin image index for image_index=%d", image_index);
456 | retImgName = NULL;
457 | }
458 | } else {
459 | const char * firstImgName = NULL;
460 | char* randomDylibName = NULL;
461 | const char* imgName = %orig(image_index);
462 | bool isJbDylib = isJailbreakDylib(imgName);
463 | if (isJbDylib){
464 | if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
465 | retImgName = NULL;
466 | } else if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) {
467 | firstImgName = _dyld_get_image_name(0);
468 | // normally is app self
469 | // eg: /private/var/containers/Bundle/Application/B6327617-9ED7-4DED-AFAC-4D9C92D82377/Aweme.app/Aweme
470 | retImgName = firstImgName;
471 | } else if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) {
472 | char* randomName = randomStr(10, NULL);
473 | asprintf(&randomDylibName, "/usr/lib/%s.dylib", randomName);
474 | retImgName = randomDylibName;
475 | }
476 | } else {
477 | retImgName = imgName;
478 | }
479 |
480 | // for debug
481 | if (isJbDylib) {
482 | iosLogInfo("image_index=%d -> imgName=%{public}s -> isJbDylib=%s -> firstImgName=%{public}s, randomDylibName=%{public}s -> retImgName=%{public}s", image_index, imgName, boolToStr(isJbDylib), firstImgName, randomDylibName, retImgName);
483 | }
484 | }
485 | } else {
486 | retImgName = %orig(image_index);
487 | }
488 |
489 | iosLogDebug("%simage_index=%d -> retImgName=%{public}s", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retImgName);
490 | return retImgName;
491 | }
492 | ```
493 |
494 | ## _dyld_get_image_header
495 |
496 | ```c
497 | %hookf(const struct mach_header*, _dyld_get_image_header, uint32_t image_index){
498 | iosLogDebug("image_index=%d", image_index);
499 | // return %orig;
500 | const struct mach_header* retMachHeader = NULL;
501 |
502 | if (cfgHookEnable_dyld){
503 | if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
504 | int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
505 | bool isValidImgIdx = (realOrOrigImgIdx >= 0);
506 | if (isValidImgIdx){
507 | retMachHeader = %orig(realOrOrigImgIdx);
508 | } else {
509 | iosLogError("fail to get real or origin image index for image_index=%d", image_index);
510 | retMachHeader = NULL;
511 | }
512 | iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> retMachHeader=%p", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), retMachHeader);
513 | } else {
514 | bool isJbDylib = false;
515 | const struct mach_header* firstImgHeader = NULL;
516 | const char* imageName = _dyld_get_image_name(image_index);
517 | if (NULL == imageName){
518 | retMachHeader = NULL;
519 | } else {
520 | isJbDylib = isJailbreakDylib(imageName);
521 | if (isJbDylib){
522 | if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
523 | retMachHeader = NULL;
524 | } else if ( (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) || (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) ) {
525 | firstImgHeader = _dyld_get_image_header(0);
526 | // normally is app self
527 | retMachHeader = firstImgHeader;
528 | }
529 | } else {
530 | retMachHeader = %orig(image_index);
531 | }
532 | }
533 |
534 | // for debug
535 | if (isJbDylib) {
536 | iosLogInfo("image_index=%d -> imageName=%{public}s -> isJbDylib=%s -> firstImgHeader=%p -> retMachHeader=%p", image_index, imageName, boolToStr(isJbDylib), firstImgHeader, retMachHeader);
537 | }
538 | }
539 | } else {
540 | retMachHeader = %orig(image_index);
541 | // iosLogDebug("%simage_index=%d -> retMachHeader=%p", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retMachHeader);
542 | }
543 |
544 | iosLogDebug("%simage_index=%d -> retMachHeader=%p", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retMachHeader);
545 | return retMachHeader;
546 | }
547 | ```
548 |
549 | ## _dyld_get_image_vmaddr_slide
550 |
551 | ```c
552 | %hookf(intptr_t, _dyld_get_image_vmaddr_slide, uint32_t image_index){
553 | iosLogDebug("image_index=%d", image_index);
554 | // return %orig;
555 | long retSlide = DYLD_IMAGE_SLIDE_INVALID;
556 |
557 | if (cfgHookEnable_dyld){
558 | if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
559 | int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
560 | bool isValidImgIdx = (realOrOrigImgIdx >= 0);
561 | if (isValidImgIdx){
562 | retSlide = %orig(realOrOrigImgIdx);
563 | } else {
564 | iosLogError("fail to get real or origin image index for image_index=%d", image_index);
565 | retSlide = DYLD_IMAGE_SLIDE_INVALID;
566 | }
567 | iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> retSlide=0x%lx", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), retSlide);
568 | } else {
569 | bool isJbDylib = false;
570 | long firtImgSlide = DYLD_IMAGE_SLIDE_INVALID;
571 | const char* imageName = _dyld_get_image_name(image_index);
572 | if (NULL == imageName){
573 | retSlide = DYLD_IMAGE_SLIDE_INVALID;
574 | } else {
575 | isJbDylib = isJailbreakDylib(imageName);
576 | if (isJbDylib){
577 | if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
578 | retSlide = DYLD_IMAGE_SLIDE_INVALID;
579 | } else if ( (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) || (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) ) {
580 | firtImgSlide = _dyld_get_image_vmaddr_slide(0);
581 | // normally is app self
582 | retSlide = firtImgSlide;
583 | }
584 | } else {
585 | retSlide = %orig(image_index);
586 | }
587 | }
588 |
589 | // for debug
590 | if (isJbDylib) {
591 | iosLogInfo("image_index=%d -> imageName=%{public}s -> isJbDylib=%s -> firtImgSlide=0x%lx -> retSlide=0x%lx", image_index, imageName, boolToStr(isJbDylib), firtImgSlide, retSlide);
592 | }
593 | }
594 | } else {
595 | retSlide = %orig(image_index);
596 | // iosLogDebug("%simage_index=%d -> retSlide=0x%lx", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retSlide);
597 | }
598 |
599 | iosLogDebug("%simage_index=%d -> retSlide=0x%lx", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retSlide);
600 | return retSlide;
601 | }
602 | ```
603 |
604 | ## 其实无法hook,只能调试或注释掉
605 |
606 | ### _dyld_register_func_for_add_image
607 |
608 | ```c
609 | %hookf(void, _dyld_register_func_for_add_image, void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)){
610 | // iosLogInfo("%sfunc=%p -> Omitted", HOOK_PREFIX(cfgHookEnable_dyld), func);
611 | iosLogInfo("%sfunc=%p", HOOK_PREFIX(cfgHookEnable_dyld), func);
612 |
613 | //#ifndef XCODE_DEBUG
614 | %orig;
615 | // %orig(func);
616 | //#endif
617 | }
618 | ```
619 |
620 | ### _dyld_register_func_for_remove_image
621 |
622 | ```c
623 | %hookf(void, _dyld_register_func_for_remove_image, void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)){
624 | // iosLogInfo("%sfunc=%p -> Omitted", HOOK_PREFIX(cfgHookEnable_dyld), func);
625 | iosLogInfo("%sfunc=%p", HOOK_PREFIX(cfgHookEnable_dyld), func);
626 | %orig;
627 | }
628 | ```
629 |
630 | ## 其他调试内容
631 |
632 | ```c
633 | %hookf(int32_t, NSVersionOfRunTimeLibrary, const char* libraryName){
634 | int32_t rtLibVer = %orig;
635 | iosLogInfo("libraryName=%s -> rtLibVer=%d", libraryName, rtLibVer);
636 | return rtLibVer;
637 | }
638 |
639 | %hookf(int32_t, NSVersionOfLinkTimeLibrary, const char* libraryName){
640 | int32_t rtLtLibVer = %orig;
641 | iosLogInfo("libraryName=%s -> rtLtLibVer=%d", libraryName, rtLtLibVer);
642 | return rtLtLibVer;
643 | }
644 |
645 | %hookf(int, _NSGetExecutablePath, char* buf, uint32_t* bufsize){
646 | int extPathCpSize = %orig;
647 | iosLogInfo("buf=%{public}s,*bufsize=%d -> extPathCpSize=%d", buf, *bufsize, extPathCpSize);
648 | return extPathCpSize;
649 | }
650 |
651 | ```
652 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/dyld/dylib.md:
--------------------------------------------------------------------------------
1 | # dylib
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:dladdr的hook绕过
6 | * 【已解决】iOS反越狱检测:逆向hook的dlopen+dlsym
7 |
8 | ---
9 |
10 | ## dladdr
11 |
12 | ```c
13 |
14 | /*==============================================================================
15 | Hook: dladdr()
16 | ==============================================================================*/
17 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dladdr.3.html
18 |
19 |
20 | /*==============================================================================
21 | hook dladdr()
22 | ==============================================================================*/
23 |
24 | void* generateHookedDladdrAddress(void *origAddr);
25 |
26 | const long DLADDR_HOOKED_ADDRESS_BASE = 0xF00000000000;
27 | //const unsigned long DLADDR_HOOKED_ADDRESS_MAX = 0xFFFF000000000000;
28 |
29 | void* generateHookedDladdrAddress(void *origAddr) {
30 | // if ((long)origAddr < (long)DLADDR_HOOKED_ADDRESS_MAX) {
31 | void* hookedAddr = origAddr;
32 | if ((long)origAddr > (long)DLADDR_HOOKED_ADDRESS_BASE) {
33 | hookedAddr = origAddr;
34 | } else {
35 | hookedAddr = (void*)((long)origAddr + DLADDR_HOOKED_ADDRESS_BASE);
36 | }
37 | return hookedAddr;
38 | }
39 |
40 | static bool isHookedDladdrAddress(const void *addr){
41 | bool isHookedAddr = false;
42 | long addrLong = (long) addr;
43 | // if ((addrLong > DLADDR_HOOKED_ADDRESS_BASE) && (addrLong < DLADDR_HOOKED_ADDRESS_MAX)) {
44 | if (addrLong > DLADDR_HOOKED_ADDRESS_BASE) {
45 | isHookedAddr = true;
46 | }
47 |
48 | return isHookedAddr;
49 | }
50 |
51 | static void* hookedToOrigDladdrAddr(const void *hookedAddr){
52 | return (void*) ( (long)hookedAddr - DLADDR_HOOKED_ADDRESS_BASE );
53 | }
54 |
55 | int dladdr(const void *, Dl_info *);
56 | //int dladdr(void *, Dl_info *);
57 | //extern int dladdr(const void *, Dl_info *);
58 |
59 | //%hookf(int, dladdr, void *addr, Dl_info *info){
60 | %hookf(int, dladdr, const void *addr, Dl_info *info){
61 | iosLogDebug("addr=%p,info=%p", addr, info);
62 | int finalRet = DLADDR_FAILED;
63 |
64 | if (NULL == addr) {
65 | iosLogInfo("addr is %s", "NULL");
66 | } else {
67 | void* origAddr = (void*)addr;
68 |
69 | bool isHookedAddr = isHookedDladdrAddress(addr);
70 | if (isHookedAddr) {
71 | origAddr = hookedToOrigDladdrAddr(addr);
72 |
73 | iosLogDebug("addr=%p -> isHookedAddr=%s -> origAddr=%p", addr, boolToStr(isHookedAddr), origAddr);
74 |
75 | if (NULL == origAddr) {
76 | iosLogInfo("addr=%p -> isHookedAddr=%s -> origAddr=%p", addr, boolToStr(isHookedAddr), origAddr);
77 | }
78 | }
79 |
80 | // int origRet = %orig;
81 |
82 | // int origRet = DLADDR_FAILED;
83 | // if (NULL == origAddr) {
84 | // origRet = DLADDR_FAILED;
85 | // } else {
86 | // origRet = %orig(origAddr, info);
87 | // }
88 |
89 | int origRet = %orig(origAddr, info);
90 | finalRet = origRet;
91 |
92 | bool isNotHookedAddr = !isHookedAddr;
93 | bool isNeedHook = cfgHookEnable_dylib_dladdr && isNotHookedAddr;
94 | if (isNeedHook) {
95 | // if (dladdrRetInt > 0) {
96 | if (DLADDR_FAILED != origRet) {
97 | if (NULL != info) {
98 | const char* curImageName = info->dli_fname;
99 | bool isJbDyib = isJailbreakDylib(curImageName);
100 | if (isJbDyib) {
101 | finalRet = DLADDR_FAILED;
102 |
103 | iosLogInfo("addr=%p -> origRet=%d -> dli_fname=%{public}s, dli_fbase=%p, dli_sname=%{public}s, dli_saddr=%p -> isJbDyib=%s -> finalRet=%d", addr, origRet, info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr, boolToStr(isJbDyib), finalRet);
104 | // iosLogInfo("isJbDyib=%s", boolToStr(isJbDyib));
105 | // iosLogInfo("addr=%p -> origRet=%d", addr, origRet);
106 | // iosLogInfo("dli_fname=%{public}s, dli_fbase=%p, dli_sname=%{public}s, dli_saddr=%p", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
107 | // iosLogInfo("finalRet=%d", finalRet);
108 |
109 | size_t dlInfoSize = sizeof(Dl_info);
110 | memset(info, 0, dlInfoSize);
111 | }
112 | }
113 | }
114 | }
115 | }
116 |
117 | return finalRet;
118 | }
119 |
120 | /*
121 | TODO:
122 | https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
123 | https://man7.org/linux/man-pages/man3/dladdr.3.html
124 | may need support:
125 | int dladdr1(const void *addr, Dl_info *info, void **extra_info, int flags);
126 | */
127 | ```
128 |
129 | ## dlopen
130 |
131 | ```c
132 |
133 | /*==============================================================================
134 | Hook: dlopen()
135 | ==============================================================================*/
136 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlopen.3.html
137 | void* dlopen(const char* path, int mode);
138 |
139 | %hookf(void*, dlopen, const char* path, int mode){
140 | iosLogDebug("path=%{public}s, mode=0x%x", path, mode);
141 | void* dlopenRetPtr = NULL;
142 |
143 | if (cfgHookEnable_dylib) {
144 | bool isJbDylib = isJailbreakDylib(path);
145 | if (isJbDylib) {
146 | dlopenRetPtr = NULL;
147 | } else {
148 | // dlopenRetPtr = %orig(path, mode);
149 | dlopenRetPtr = %orig;
150 | }
151 |
152 | if (isJbDylib) {
153 | iosLogInfo("path=%{public}s, mode=0x%x -> isJbDylib=%s -> dlopenRetPtr=%p", path, mode, boolToStr(isJbDylib), dlopenRetPtr);
154 | }
155 | } else {
156 | // dlopenRetPtr = %orig(path, mode);
157 | dlopenRetPtr = %orig;
158 | }
159 |
160 | return dlopenRetPtr;
161 | }
162 |
163 | ////void* _dlopen(const char* path, int mode);
164 | //void* __ZL15dlopen_internalPKciPv(const char* path, int mode);
165 | //
166 | ////%hookf(void*, _dlopen, const char* path, int mode){
167 | //%hookf(void*, __ZL15dlopen_internalPKciPv, const char* path, int mode){
168 | // iosLogInfo("path=%{public}s, mode=0x%x", path, mode);
169 | // return %orig;
170 | //}
171 | ```
172 |
173 | ## dlclose
174 |
175 | ```c
176 |
177 | /*==============================================================================
178 | Hook: dlclose()
179 | ==============================================================================*/
180 |
181 | int dlclose(void* handle);
182 |
183 | %hookf(int, dlclose, void* handle){
184 | bool isJbLib = false;
185 |
186 | Dl_info info;
187 | size_t dlInfoSize = sizeof(Dl_info);
188 | memset(&info, 0, dlInfoSize);
189 |
190 | // dladdr(mhp, &info);
191 | void* hookedAddr = generateHookedDladdrAddress(handle);
192 | dladdr(hookedAddr, &info);
193 |
194 | const char* curImgName = info.dli_fname;
195 | if(curImgName != NULL) {
196 | isJbLib = isJailbreakDylib(curImgName);
197 | }
198 |
199 | if (isJbLib) {
200 | iosLogInfo("handle=%p -> is jb lib: %s", handle, curImgName);
201 | }
202 |
203 | int closeRet = %orig;
204 | iosLogInfo("handle=%p -> closeRet=%d", handle, closeRet);
205 | return closeRet;
206 | }
207 | ```
208 |
209 | ## dlsym
210 |
211 | ```c
212 | /*==============================================================================
213 | Hook: dlsym()
214 | ==============================================================================*/
215 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
216 | void* dlsym(void* handle, const char* symbol);
217 |
218 | %hookf(void*, dlsym, void* handle, const char* symbol) {
219 | iosLogDebug("handle=%p, symbol=%{public}s", handle, symbol);
220 | void* dlsymRetPtr = NULL;
221 |
222 | if (cfgHookEnable_dylib) {
223 | bool shouldHook = false;
224 | bool isJbFuncName = isJailbreakDylibFunctionName(symbol);
225 | bool isPtrace = 0 == strcmp(symbol, "ptrace");
226 | shouldHook = isJbFuncName || isPtrace;
227 | iosLogDebug("isPtrace=%s, shouldHook=%s", boolToStr(isPtrace), boolToStr(shouldHook));
228 | // if (isJbFuncName) {
229 | if (shouldHook) {
230 | dlsymRetPtr = NULL;
231 | } else {
232 | // dlsymRetPtr = %orig(handle, symbol);
233 | dlsymRetPtr = %orig;
234 | }
235 |
236 | // if (isJbFuncName) {
237 | if (shouldHook) {
238 | // iosLogInfo("handle=%p, symbol=%{public}s -> isJbFuncName=%s -> dlsymRetPtr=%p", handle, symbol, boolToStr(isJbFuncName), dlsymRetPtr);
239 | iosLogInfo("handle=%p, symbol=%{public}s -> isJbFuncName=%s, isPtrace=%s -> shouldHook=%s -> dlsymRetPtr=%p", handle, symbol, boolToStr(isJbFuncName), boolToStr(isPtrace), boolToStr(shouldHook), dlsymRetPtr);
240 | }
241 | } else {
242 | // dlsymRetPtr = %orig(handle, symbol);
243 | dlsymRetPtr = %orig;
244 | }
245 |
246 | return dlsymRetPtr;
247 | }
248 | ```
249 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/env.md:
--------------------------------------------------------------------------------
1 | # 环境变量
2 |
3 | ```c
4 | /*==============================================================================
5 | Hook: getenv(DYLD_INSERT_LIBRARIES)
6 | ==============================================================================*/
7 |
8 | char * getenv(const char* name);
9 | const char* DYLD_INSERT_LIBRARIES = "DYLD_INSERT_LIBRARIES";
10 |
11 | %hookf(char *, getenv, const char* name){
12 | // char* getenvRetStr = %orig(name);
13 | char* getenvRetStr = %orig;
14 |
15 | if (cfgHookEnable_misc) {
16 | // iosLogDebug("name=%s", name);
17 | // NSLog(@"getenv name");
18 |
19 | // "_CFXNOTIFICATIONREGISTAR2_ENABLED" will cause crash
20 | if (strStartsWith(name, "DYLD_")){
21 | // if (!strStartsWith(name, "_")){
22 | // iosLogInfo("not start with '_', name=%s", name);
23 | iosLogInfo("DYLD_ name=%s", name);
24 | }
25 |
26 | if(0 == strcmp(name, DYLD_INSERT_LIBRARIES)){
27 | iosLogInfo("name=%s -> getenvRetStr=%{public}s", name, getenvRetStr);
28 | getenvRetStr = NULL;
29 | } else {
30 | if (strStartsWith(name, "DYLD_")){
31 | iosLogInfo("name=%s -> getenvRetStr=%{public}s", name, getenvRetStr);
32 | }
33 | }
34 | }
35 |
36 | return getenvRetStr;
37 | }
38 | ```
39 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/README.md:
--------------------------------------------------------------------------------
1 | # 文件
2 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/open/README.md:
--------------------------------------------------------------------------------
1 | # 文件打开
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS程序崩溃:strdup报错Thread 1 EXC_BAD_ACCESS code 2 address
6 | * 【未解决】iOS反越狱检测:优化findRealImageCount改为调用_dyld_get_image_vmaddr_slide计算逻辑
7 | * 【已解决】iOS报错:libsystem_malloc.dylib nanov2_allocate_from_block VARIANT mp
8 | * 【已解决】iOS的tweak中open报错:Address of overloaded function open does not match required type void
9 | * 【已解决】iOS的tweak中open的hook报错:%orig requires arguments when hooking variadic functions
10 |
11 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/open/c_func/README.md:
--------------------------------------------------------------------------------
1 | # C函数
2 |
3 | TODO:
4 |
5 | 整理下面多个帖子的内容
6 |
7 | ## open系列
8 |
9 | ### open
10 |
11 | TODO:
12 |
13 | * 【基本解决】iOS反越狱检测之打开文件:open
14 | * 【已解决】iOS反越狱检测:tweak插件中hook绕过open函数
15 |
16 | ---
17 |
18 | ```c
19 | /*==============================================================================
20 | Hook: open()
21 | ==============================================================================*/
22 |
23 | /*
24 | TODO: maybe need support more version:
25 | int creat(const char *pathname, mode_t mode);
26 | int openat(int dirfd, const char *pathname, int flags);
27 | int openat(int dirfd, const char *pathname, int flags, mode_t mode);
28 | int openat2(int dirfd, const char *pathname, const struct open_how *how, size_t size);
29 | refer:
30 | https://man7.org/linux/man-pages/man2/open.2.html
31 | */
32 |
33 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/open.2.html
34 | // normally max number of open parameter is 1
35 | int MaxSupportArgNum_open = 2;
36 | int open(const char *path, int oflag, ...);
37 |
38 | %hookf(int, open, const char *path, int oflag, ...){
39 | iosLogDebug("path=%{public}s, oflag=%d", path, oflag);
40 | bool isJbPath = false;
41 |
42 | if(cfgHookEnable_openFileC_open){
43 | isJbPath = isJailbreakPath(path);
44 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
45 |
46 | // // for debug: for system() call, temp allow /bin/sh
47 | // if (0 == strcmp(path, "/bin/sh")){
48 | // os_log(OS_LOG_DEFAULT, "hook_open_system: temp allow /bin/sh");
49 | // isJbPath = false;
50 | // }
51 |
52 | if (isJbPath){
53 | iosLogInfo("path=%{public}s -> isJbPath=%{bool}d", path, isJbPath);
54 | errno = ENOENT;
55 | iosLogDebug("set errno=%d", errno);
56 | return OPEN_FAILED;
57 | }
58 | }
59 |
60 | // Setting up some variables to get all the parameters from open
61 | mode_t curPara, paraList[MaxSupportArgNum_open];
62 | va_list argList;
63 | int curParaNum = 0;
64 |
65 | va_start(argList, oflag);
66 | // while ((curPara = (mode_t) va_arg(argList, mode_t))) {
67 | // paraList[curParaNum] = curPara;
68 | // curParaNum += 1;
69 | // os_log(OS_LOG_DEFAULT, "hook_open: [%d] curPara=%d", curParaNum, curPara);
70 | // }
71 | // unsigned int firstArg = va_arg(argList, mode_t)
72 | // curPara = (mode_t) va_arg(argList, mode_t);
73 | curPara = (mode_t) va_arg(argList, unsigned int);
74 | // maxium is only extra 1 para, so no need while
75 | if (0 != (int)curPara) {
76 | paraList[curParaNum] = curPara;
77 | curParaNum += 1;
78 | iosLogDebug("[%d] para=%d", curParaNum, curPara);
79 | }
80 | va_end(argList);
81 |
82 | iosLogDebug("curParaNum=%d, argList=%{public}s", curParaNum, argList);
83 |
84 | // return %orig;
85 | int openRetValue = OPEN_FAILED;
86 | if (0 == curParaNum){
87 | openRetValue = %orig(path, oflag);
88 | if (isJbPath) {
89 | iosLogInfo("%spath=%{public}s, oflag=0x%x -> isJbPath=%{bool}d -> openRetValue=%d", HOOK_PREFIX(cfgHookEnable_openFileC_open), path, oflag, isJbPath, openRetValue);
90 | }
91 | } else if (1 == curParaNum){
92 | mode_t mode = paraList[0];
93 | // os_log(OS_LOG_DEFAULT, "hook_open: mode=0x%x", mode);
94 | openRetValue = %orig(path, oflag, mode);
95 | if (isJbPath) {
96 | iosLogInfo("%spath=%{public}s, oflag=0x%x, mode=0x%x -> isJbPath=%{bool}d -> openRetValue=%d", HOOK_PREFIX(cfgHookEnable_openFileC_open), path, oflag, mode, isJbPath, openRetValue);
97 | }
98 | }
99 | return openRetValue;
100 | }
101 | ```
102 |
103 | ### fopen
104 |
105 | TODO:
106 |
107 | 【已解决】iOS反越狱检测之打开文件:hook绕过fopen
108 |
109 | ---
110 |
111 | ```c
112 | /*==============================================================================
113 | Hook: fopen()
114 | ==============================================================================*/
115 |
116 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fopen.3.html
117 | FILE* fopen(const char *filename, const char *mode);
118 |
119 | %hookf(int, fopen, const char *filename, const char *mode){
120 | iosLogDebug("filename=%{public}s, mode=%{public}s", filename, mode);
121 | int retValue = FOPEN_OPEN_FAILED;
122 | bool isJbPath = false;
123 |
124 | if(cfgHookEnable_openFileC_fopen){
125 | isJbPath = isJailbreakPath(filename);
126 | if (isJbPath){
127 | retValue = FOPEN_OPEN_FAILED;
128 | } else {
129 | retValue = %orig;
130 | }
131 | } else {
132 | retValue = %orig;
133 | }
134 |
135 | // for debug
136 | if (isJbPath) {
137 | iosLogInfo("filename=%{public}s, mode=%{public}s -> isJbPath=%s -> retValue=%d", filename, mode, boolToStr(isJbPath), retValue);
138 | }
139 | return retValue;
140 | }
141 | ```
142 |
143 | ### opendir + __opendir2
144 |
145 | ```c
146 | /*==============================================================================
147 | Hook: opendir()
148 | ==============================================================================*/
149 |
150 | // NOTES: !!! hook opendir will cause app crash.
151 | // detail log: SubstituteLog: SubHookFunction: substitute_hook_functions returned SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START
152 |
153 | //// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/opendir.3.html
154 | //DIR* opendir(const char *dirname);
155 | //
156 | //%hookf(DIR*, opendir, const char *dirname){
157 | // iosLogInfo("dirname=%s", dirname);
158 | //// return %orig;
159 | // return %orig(dirname);
160 | //}
161 |
162 | /*==============================================================================
163 | Hook: __opendir2()
164 | ==============================================================================*/
165 |
166 | // https://opensource.apple.com/source/Libc/Libc-186/gen.subproj/opendir.c.auto.html
167 | // https://opensource.apple.com/source/Libc/Libc-320/include/dirent.h.auto.html
168 | DIR *__opendir2(const char *name, int flags);
169 |
170 | %hookf(DIR*, __opendir2, const char *name, int flags){
171 | iosLogDebug("name=%{public}s, flags=0x%x", name, flags);
172 | DIR* openedDir = OPENDIR_FAILED;
173 | bool isJbPath = false;
174 |
175 | if (cfgHookEnable_openFileC___opendir2){
176 | isJbPath = isJailbreakPath(name);
177 | if (isJbPath) {
178 | openedDir = OPENDIR_FAILED;
179 | } else {
180 | // openedDir = %orig(name, flags);
181 | openedDir = %orig;
182 | }
183 | } else {
184 | // openedDir = %orig(name, flags);
185 | openedDir = %orig;
186 | }
187 |
188 | // for debug
189 | if (isJbPath) {
190 | iosLogInfo("name=%{public}s, flags=0x%x -> isJbPath=%{bool}d -> openedDir=%p", name, flags, isJbPath, openedDir);
191 | }
192 |
193 | return openedDir;
194 | }
195 | ```
196 |
197 | ## access系列
198 |
199 | TODO:
200 |
201 | * 【未解决】iOS反越狱检测之打开文件:access系列函数
202 |
203 | ### access
204 |
205 | ```c
206 | /*==============================================================================
207 | Hook: access()
208 | ==============================================================================*/
209 |
210 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/access.2.html
211 | int access(const char *path, int amode);
212 |
213 | %hookf(int, access, const char *path, int amode){
214 | iosLogDebug("path=%{public}s, amode=0x%x", path, amode);
215 | int retValue = ACCESS_FAILED;
216 | bool isJbPath = false;
217 |
218 | if (cfgHookEnable_openFileC) {
219 | isJbPath = isJailbreakPath(path);
220 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
221 | if (isJbPath){
222 | iosLogDebug("isJbPath=%{bool}d, %{public}s", isJbPath, path);
223 | errno = ENOENT;
224 | iosLogDebug("set errno=%d", errno);
225 | retValue = ACCESS_FAILED;
226 | } else {
227 | retValue = %orig;
228 | }
229 | } else {
230 | retValue = %orig;
231 | }
232 |
233 | // for debug
234 | if(isJbPath){
235 | iosLogInfo("path=%{public}s, amode=0x%x -> isJbPath=%{bool}d, retValue=%d", path, amode, isJbPath, retValue);
236 | }
237 | return retValue;
238 | }
239 | ```
240 |
241 | ### faccessat
242 |
243 | ```c
244 | /*==============================================================================
245 | Hook: faccessat()
246 | ==============================================================================*/
247 |
248 | // https://man7.org/linux/man-pages/man2/access.2.html
249 | // https://linux.die.net/man/2/faccessat
250 | int faccessat(int dirfd, const char *pathname, int mode, int flags);
251 |
252 | %hookf(int, faccessat, int dirfd, const char *pathname, int mode, int flags){
253 | iosLogDebug("dirfd=%d, pathname=%{public}s, mode=0x%x, flags=0x%x", dirfd, pathname, mode, flags);
254 | int retInt = ACCESS_FAILED;
255 | bool isJbPath = false;
256 |
257 | if(cfgHookEnable_openFileC_faccessat){
258 | const char* absPath = NULL;
259 | bool isAbsPath = strStartsWith(pathname, "/");
260 | iosLogDebug("isAbsPath=%{bool}d", isAbsPath);
261 | if (isAbsPath) {
262 | absPath = pathname;
263 | } else {
264 | // is relative path
265 | if (dirfd == AT_FDCWD){
266 | iosLogDebug("dirfd is AT_FDCWD=%d", AT_FDCWD);
267 |
268 | // pathname is interpreted relative to the current working directory of the calling process (like access())
269 | // TODO: try get current working directory -> avoid caller pass the special path, finnaly is jailbreak path
270 | // eg: current working directory is "/usr/xxx/yyy/", then pass in "../../libexec/cydia/zzz"
271 | // finnal path is "/usr/libexec/cydia/zzz", match jailbreak path: "/usr/libexec/cydia/", is jaibreak path
272 | // but use "../../libexec/cydia/zzz" can not check whether is jailbreak path
273 | } else {
274 | // get file path from dir fd
275 | char filePath[PATH_MAX];
276 | bool isGetPathOk = getFilePath(dirfd, filePath);
277 | iosLogDebug("isGetPathOk=%s", boolToStr(isGetPathOk));
278 | if (isGetPathOk) {
279 | char* fullPath = strPathJoin(filePath, pathname)
280 | iosLogDebug("fullPath=%{public}s", fullPath);
281 | absPath = fullPath;
282 | }
283 | }
284 | }
285 |
286 | if (NULL != absPath){
287 | isJbPath = isJailbreakPath(absPath);
288 | iosLogDebug("absPath=%{public}s -> isJbPath=%{bool}d", absPath, isJbPath);
289 | if (isJbPath) {
290 | iosLogDebug("hook jailbreak path: %s", absPath);
291 | retInt = ACCESS_FAILED;
292 | } else {
293 | retInt = %orig;
294 | }
295 | } else {
296 | retInt = %orig;
297 | }
298 | } else {
299 | retInt = %orig;
300 | }
301 |
302 | // for debug
303 | if (isJbPath) {
304 | iosLogInfo("%sdirfd=%d, pathname=%{public}s, mode=0x%x, flags=0x%x -> isJbPath=%{bool}d, retInt=%d",
305 | HOOK_PREFIX(cfgHookEnable_openFileC_faccessat), dirfd, pathname, mode, flags, isJbPath, retInt);
306 | }
307 |
308 | return retInt;
309 | }
310 | ```
311 |
312 | ## realpath
313 |
314 | ```c
315 | /*==============================================================================
316 | Hook: realpath()
317 | ==============================================================================*/
318 |
319 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/realpath.3.html
320 | char* realpath(const char* file_name, char* resolved_name);
321 |
322 | %hookf(char*, realpath, const char* file_name, char* resolved_name){
323 | iosLogDebug("file_name=%{public}s, resolved_name=%p", file_name, resolved_name);
324 | char* resolvedPath = REALPATH_FAILED;
325 | bool isJbPath = false;
326 |
327 | if (cfgHookEnable_openFileC) {
328 | isJbPath = isJailbreakPath(file_name);
329 | if (isJbPath) {
330 | resolvedPath = REALPATH_FAILED;
331 | } else {
332 | // resolvedPath = %orig(file_name, resolved_name);
333 | resolvedPath = %orig;
334 | }
335 | } else {
336 | resolvedPath = %orig;
337 | }
338 |
339 | // for debug
340 | if (isJbPath) {
341 | iosLogInfo("file_name=%{public}s, resolved_name=%{public}s -> isJbPath=%{bool}d -> resolvedPath=%{public}s", file_name, resolved_name, isJbPath, resolvedPath);
342 | }
343 |
344 | return resolvedPath;
345 | }
346 | ```
347 |
348 | ## stat系列
349 |
350 | TODO:
351 |
352 | * 【已解决】iOS反越狱检测之stat:支持路径是否带斜杠结尾以及包含点和两个点
353 | * 【已解决】iOS反越狱之stat函数测试机文件列表对于非越狱手是否正常
354 | * 【部分解决】iOS反越狱检测的其他版本stat函数:stat64
355 | * 【已解决】iOS反越狱检测:hook绕过stat函数的实现机制和方式
356 |
357 | ---
358 |
359 | ### stat
360 |
361 | ```c
362 | /*==============================================================================
363 | Hook: stat()
364 | ==============================================================================*/
365 |
366 | int stat(const char *pathname, struct stat *buf);
367 |
368 | %hookf(int, stat, const char *pathname, struct stat *buf){
369 | int statRet = STAT_FAILED;
370 | bool isJbPath = false;
371 |
372 | if (cfgHookEnable_openFileC) {
373 | isJbPath = isJailbreakPath(pathname);
374 | iosLogDebug("pathname=%{public}s -> isJbPath=%s", pathname, boolToStr(isJbPath));
375 |
376 | if (isJbPath){
377 | statRet = STAT_FAILED;
378 | } else {
379 | statRet = %orig;
380 | }
381 | } else {
382 | statRet = %orig;
383 | }
384 |
385 | // for debug
386 | if(isJbPath){
387 | iosLogInfo("pathname=%{public}s -> isJbPath=%s -> statRet=%d", pathname, boolToStr(isJbPath), statRet);
388 | }
389 |
390 | return statRet;
391 | }
392 | ```
393 |
394 | ### lstat
395 |
396 | ```c
397 | /*==============================================================================
398 | Hook: lstat()
399 | ==============================================================================*/
400 |
401 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/lstat.2.html
402 | int lstat(const char* path, struct stat* buf);
403 |
404 | %hookf(int, lstat, const char* path, struct stat* buf){
405 | iosLogDebug("path=%{public}s, buf=%p", path, buf);
406 | int lstatRet = STAT_FAILED;
407 | bool isJbPath = false;
408 |
409 | if (cfgHookEnable_openFileC) {
410 | isJbPath = isJailbreakPath(path);
411 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
412 |
413 | if (isJbPath){
414 | lstatRet = STAT_FAILED;
415 | } else {
416 | lstatRet = %orig;
417 | }
418 | } else {
419 | lstatRet = %orig;
420 | }
421 |
422 | // for debug
423 | if(isJbPath){
424 | iosLogInfo("path=%{public}s -> isJbPath=%s -> lstatRet=%d", path, boolToStr(isJbPath), lstatRet);
425 | }
426 |
427 | return lstatRet;
428 | }
429 | ```
430 |
431 | ### fstat
432 |
433 | ```c
434 | /*==============================================================================
435 | Hook: fstat()
436 | ==============================================================================*/
437 |
438 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fstat.2.html
439 |
440 | int fstat(int fd, struct stat *buf);
441 |
442 | %hookf(int, fstat, int fd, struct stat *buf){
443 | iosLogDebug("fd=%d, buf=%p", fd, buf);
444 | int retInt = STAT_FAILED;
445 | bool isJbPath = false;
446 |
447 | char parsedPath[PATH_MAX];
448 | memset(parsedPath, 0, PATH_MAX);
449 |
450 | if (cfgHookEnable_openFileC) {
451 | // get file path from fd
452 | bool isGetPathOk = getFilePath(fd, parsedPath);
453 | iosLogDebug("isGetPathOk=%s, parsedPath=%s", boolToStr(isGetPathOk), parsedPath);
454 | if (isGetPathOk) {
455 | isJbPath = isJailbreakPath(parsedPath);
456 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
457 |
458 | if (isJbPath){
459 | retInt = STAT_FAILED;
460 | } else {
461 | retInt = %orig;
462 | }
463 | } else {
464 | // can not get path -> can not check is jailbreak or not -> not hook
465 | retInt = %orig;
466 | }
467 | } else {
468 | retInt = %orig;
469 | }
470 |
471 | // for debug
472 | if(isJbPath){
473 | iosLogInfo("fd=%d,buf=%p -> parsedPath=%{public}s -> isJbPath=%s -> retInt=%d", fd, buf, parsedPath, boolToStr(isJbPath), retInt);
474 | }
475 |
476 | return retInt;
477 | }
478 | ```
479 |
480 | ### fstatat
481 |
482 | ```c
483 | /*==============================================================================
484 | Hook: fstatat()
485 | ==============================================================================*/
486 |
487 | // https://man7.org/linux/man-pages/man3/fstatat.3p.html
488 | // https://linux.die.net/man/2/fstatat
489 |
490 | //int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
491 | //int fstatat(int fd, const char *restrict path, struct stat *restrict buf, int flag);
492 | int fstatat(int fd, const char* pathname, struct stat* buf, int flags);
493 |
494 | %hookf(int, fstatat, int fd, const char* pathname, struct stat* buf, int flags){
495 | iosLogDebug("fd=%d, pathname=%{public}s, buf=%p, flags=0x%x", fd, pathname, buf, flags);
496 | int fstatatRet = STATFS_FAILED;
497 | bool isJbPath = false;
498 |
499 | if(cfgHookEnable_openFileC){
500 | const char* absPath = NULL;
501 | bool isAbsPath = strStartsWith(pathname, "/");
502 | iosLogDebug("isAbsPath=%{bool}d", isAbsPath);
503 | if (isAbsPath) {
504 | absPath = pathname;
505 | } else {
506 | // is relative path
507 | if (fd == AT_FDCWD){
508 | iosLogDebug("fd is AT_FDCWD=%d", AT_FDCWD);
509 |
510 | // pathname is interpreted relative to the current working directory of the calling process (like access())
511 | // TODO: try get current working directory -> avoid caller pass the special path, finnaly is jailbreak path
512 | // eg: current working directory is "/usr/xxx/yyy/", then pass in "../../libexec/cydia/zzz"
513 | // finnal path is "/usr/libexec/cydia/zzz", match jailbreak path: "/usr/libexec/cydia/", is jaibreak path
514 | // but use "../../libexec/cydia/zzz" can not check whether is jailbreak path
515 | } else {
516 | // get file path from dir fd
517 | char filePath[PATH_MAX];
518 | bool isGetPathOk = getFilePath(fd, filePath);
519 | iosLogDebug("isGetPathOk=%s", boolToStr(isGetPathOk));
520 | if (isGetPathOk) {
521 | char* fullPath = strPathJoin(filePath, pathname)
522 | iosLogDebug("fullPath=%{public}s", fullPath);
523 | absPath = fullPath;
524 | }
525 | }
526 | }
527 |
528 | if (NULL != absPath){
529 | isJbPath = isJailbreakPath(absPath);
530 | iosLogDebug("absPath=%{public}s -> isJbPath=%{bool}d", absPath, isJbPath);
531 | if (isJbPath) {
532 | iosLogDebug("hook jailbreak path: %s", absPath);
533 | fstatatRet = STATFS_FAILED;
534 | } else {
535 | fstatatRet = %orig;
536 | }
537 | } else {
538 | fstatatRet = %orig;
539 | }
540 | } else {
541 | fstatatRet = %orig;
542 | }
543 |
544 | // for debug
545 | if (isJbPath) {
546 | iosLogInfo("fd=%d, pathname=%{public}s, buf=%p, flags=0x%x -> isJbPath=%{bool}d -> fstatatRet=%d", fd, pathname, buf, flags, isJbPath, fstatatRet);
547 | }
548 |
549 | return fstatatRet;
550 | }
551 | ```
552 |
553 | ### statfs
554 |
555 | ```c
556 | /*==============================================================================
557 | Hook: statfs()
558 | ==============================================================================*/
559 |
560 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html
561 |
562 | int statfs(const char *path, struct statfs *buf);
563 |
564 | %hookf(int, statfs, const char *path, struct statfs *buf){
565 | iosLogDebug("path=%{public}s, buf=%p", path, buf);
566 | int statfsRet = STATFS_FAILED;
567 | bool isJbPath = false;
568 |
569 | if (cfgHookEnable_openFileC) {
570 | isJbPath = isJailbreakPath(path);
571 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
572 |
573 | if (isJbPath){
574 | statfsRet = STATFS_FAILED;
575 | } else {
576 | statfsRet = %orig;
577 | }
578 | } else {
579 | statfsRet = %orig;
580 | }
581 |
582 | // for debug
583 | if(isJbPath){
584 | iosLogInfo("found jailbreak path: path=%{public}s -> isJbPath=%s -> statfsRet=%d", path, boolToStr(isJbPath), statfsRet);
585 | }
586 |
587 | return statfsRet;
588 | }
589 | ```
590 |
591 | ### fstatfs
592 |
593 | ```c
594 | /*==============================================================================
595 | Hook: fstatfs()
596 | ==============================================================================*/
597 |
598 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs64.2.html
599 |
600 | int fstatfs(int fd, struct statfs *buf);
601 |
602 | %hookf(int, fstatfs, int fd, struct statfs *buf){
603 | iosLogDebug("fd=%d, buf=%p", fd, buf);
604 | int fstatfsRet = STATFS_FAILED;
605 | bool isJbPath = false;
606 |
607 | char parsedPath[PATH_MAX];
608 | memset(parsedPath, 0, PATH_MAX);
609 |
610 | if (cfgHookEnable_openFileC) {
611 | // get file path from fd
612 | bool isGetPathOk = getFilePath(fd, parsedPath);
613 | iosLogDebug("isGetPathOk=%s, parsedPath=%s", boolToStr(isGetPathOk), parsedPath);
614 | if (isGetPathOk) {
615 | isJbPath = isJailbreakPath(parsedPath);
616 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
617 |
618 | if (isJbPath){
619 | fstatfsRet = STATFS_FAILED;
620 | } else {
621 | fstatfsRet = %orig;
622 | }
623 | } else {
624 | // can not get path -> can not check is jailbreak or not -> not hook
625 | fstatfsRet = %orig;
626 | }
627 | } else {
628 | fstatfsRet = %orig;
629 | }
630 |
631 | // for debug
632 | if(isJbPath){
633 | iosLogInfo("fd=%d,buf=%p -> parsedPath=%{public}s -> isJbPath=%s -> fstatfsRet=%d", fd, buf, parsedPath, boolToStr(isJbPath), fstatfsRet);
634 | }
635 |
636 | return fstatfsRet;
637 | }
638 | ```
639 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/open/c_func/svc_0x80_asm.md:
--------------------------------------------------------------------------------
1 | # svc 0x80内联汇编
2 |
3 | TODO:
4 |
5 | * 目前(暂时)无法实现
6 | * 【未解决】iOS反越狱检测:svc 0x80 call
7 | * 【未解决】iOS反越狱检测之:svc 0x80的open
8 | * 【未解决】iOS逆向反越狱检测:插件tweak中绕过内联汇编svc 0x80的系统调用
9 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/open/c_func/syscall.md:
--------------------------------------------------------------------------------
1 | # syscall
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS去hook绕过syscall函数异常:可变参数计算个数再次异常12个13个
6 | * 【已解决】iOS的tweak插件去hook函数syscall出现递归调用死循环
7 | * 【已解决】iOS的tweak插件Logos的%orig的实现原理如何规避绕开原函数的递归调用
8 | * 【未解决】iOS反越狱检测:syscall
9 |
10 | ## syscall的fork
11 |
12 | * 【已解决】iOS反越狱检测:syscall的fork
13 |
14 | TODO:
15 |
16 | ## syscall的stat和stat64
17 |
18 | TODO:
19 |
20 | * 【已解决】iOS反越狱检测hook绕过:syscall的stat和stat64
21 |
22 | ---
23 |
24 | ```c
25 |
26 | /*==============================================================================
27 | Define
28 | ==============================================================================*/
29 |
30 | #if !defined(PT_DENY_ATTACH)
31 | #define PT_DENY_ATTACH 31
32 | #endif // !defined(PT_DENY_ATTACH)
33 |
34 | /*==============================================================================
35 | Const
36 | ==============================================================================*/
37 |
38 | /*==============================================================================
39 | Hook: syscall()
40 | ==============================================================================*/
41 |
42 | /*
43 | https://www.theiphonewiki.com/wiki/Kernel_Syscalls
44 | TODO: support syscall(access_extended)
45 | */
46 |
47 |
48 | int syscall(int, ...);
49 |
50 | // normally max number of syscall parameter is not exceed 8
51 | // refer: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/kern/syscalls.master
52 | int MaxSupportArgNum_syscall = 16;
53 |
54 | %hookf(int, syscall, int number, ...){
55 | iosLogDebug("number=%d", number);
56 |
57 | int syscallRetValue = -1;
58 |
59 | // Setting up some variables to get all the parameters from syscall
60 | void *paraPtr, *paraList[MaxSupportArgNum_syscall];
61 | // char *paraPtr, *paraList[MaxSupportArgNum_syscall];
62 | va_list argList;
63 | int curParaNum = 0;
64 |
65 | if (cfgHookEnable_syscall) {
66 | // #define SYS_fork 2
67 | bool isFork = (SYS_fork == number);
68 | if (isFork){
69 | iosLogInfo("number=%d -> return %d", number, FORK_FAILED);
70 | return FORK_FAILED;
71 | }
72 |
73 | // #define SYS_open 5
74 | bool isOpen = (SYS_open == number);
75 | if (isOpen){
76 | //int open(const char *path, int oflag, ...);
77 | // ->
78 | // int open(const char *pathname, int flags);
79 | // int open(const char *pathname, int flags, mode_t mode);
80 |
81 | //5 AUE_OPEN_RWTC ALL { int open(user_addr_t path, int flags, int mode) NO_SYSCALL_STUB; }
82 | va_start(argList, number);
83 | const char * fisrtPath = va_arg(argList, const char *);
84 | int secondFlags = va_arg(argList, int);
85 | // mode_t thirdMode = va_arg(argList, mode_t);
86 | mode_t thirdMode = (mode_t)va_arg(argList, unsigned int);
87 | va_end(argList);
88 | iosLogDebug("fisrtPath=%{public}s, secondFlags=%d, thirdMode=%d", fisrtPath, secondFlags, thirdMode);
89 |
90 | bool isJbPath = isJailbreakPath(fisrtPath);
91 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
92 | if (isJbPath){
93 | errno = ENOENT;
94 | iosLogDebug("set errno=%d", errno);
95 | syscallRetValue = OPEN_FAILED;
96 | } else {
97 | syscallRetValue = %orig(number, fisrtPath, secondFlags, thirdMode);
98 | }
99 | iosLogInfo("SYS_open: number=%d -> isJbPath=%{bool}d, fisrtPath=%{public}s -> syscallRetValue=%d", number, isJbPath, fisrtPath, syscallRetValue);
100 | return syscallRetValue;
101 | }
102 |
103 | // #define SYS_ptrace 26
104 | bool isPtrace = (SYS_ptrace == number);
105 | if (isPtrace){
106 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/ptrace.2.html
107 | // int ptrace(int request, pid_t pid, caddr_t addr, int data);
108 | va_start(argList, number);
109 | int request = va_arg(argList, int);
110 | int pid = va_arg(argList, int);
111 | char* addr = va_arg(argList, char*);
112 | int data = va_arg(argList, int);
113 | va_end(argList);
114 |
115 | iosLogInfo("request=%d, pid=%d, addr=%p, data=%d", request, pid, addr, data);
116 |
117 | if (PT_DENY_ATTACH == request){
118 | syscallRetValue = PTRACE_FAILED;
119 | } else {
120 | syscallRetValue = %orig(request, pid, addr, data);
121 | }
122 |
123 | iosLogInfo("SYS_ptrace: request=%d, pid=%d, addr=%p, data=%d -> syscallRetValue=%d", request, pid, addr, data, syscallRetValue);
124 | return syscallRetValue;
125 | }
126 |
127 | // #define SYS_access 33
128 | bool isAccess = (SYS_access == number);
129 | if (isAccess) {
130 | // int access(const char *path, int amode);
131 | va_start(argList, number);
132 | const char* path = va_arg(argList, const char *);
133 | int amode = va_arg(argList, int);
134 | va_end(argList);
135 |
136 | iosLogDebug("isAccess=%{bool}d, path=%{public}s, amode=0x%x", isAccess, path, amode);
137 |
138 | bool isJbPath = isJailbreakPath(path);
139 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
140 | if (isJbPath){
141 | syscallRetValue = ACCESS_FAILED;
142 | } else {
143 | syscallRetValue = %orig(number, path, amode);
144 | }
145 | iosLogInfo("SYS_access: number=%d -> path=%{public}s, amode=0x%x -> isJbPath=%{bool}d -> syscallRetValue=%d", number, path, amode, isJbPath, syscallRetValue);
146 | return syscallRetValue;
147 | }
148 |
149 | // #define SYS_statfs 157
150 | bool isStatfs = (SYS_statfs == number);
151 | if (isStatfs) {
152 | // int statfs(const char *path, struct statfs *buf);
153 | va_start(argList, number);
154 | const char* path = va_arg(argList, const char *);
155 | struct stat* buf = va_arg(argList, struct stat*);
156 | va_end(argList);
157 |
158 | iosLogDebug("isStatfs=%{bool}d, path=%{public}s, buf=%p", isStatfs, path, buf);
159 |
160 | bool isJbPath = isJailbreakPath(path);
161 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
162 | if (isJbPath){
163 | syscallRetValue = STATFS_FAILED;
164 | } else {
165 | syscallRetValue = %orig(number, path, buf);
166 | }
167 | iosLogInfo("SYS_statfs: number=%d -> path=%{public}s, buf=%p -> isJbPath=%{bool}d -> syscallRetValue=%d", number, path, buf, isJbPath, syscallRetValue);
168 | return syscallRetValue;
169 | }
170 |
171 | // #define SYS_fstatfs 158
172 | bool isFstatfs = (SYS_fstatfs == number);
173 | if (isFstatfs) {
174 | bool isGetPathOk = false;
175 | bool isJbPath = false;
176 | char parsedPath[PATH_MAX];
177 | memset(parsedPath, 0, PATH_MAX);
178 |
179 | // int fstatfs(int fd, struct statfs *buf);
180 | va_start(argList, number);
181 | int fd = va_arg(argList, int);
182 | struct stat* buf = va_arg(argList, struct stat*);
183 | va_end(argList);
184 |
185 | iosLogDebug("isFstatfs=%{bool}d, fd=%d, buf=%p", isFstatfs, fd, buf);
186 |
187 | isGetPathOk = getFilePath(fd, parsedPath);
188 | iosLogDebug("isGetPathOk=%s, parsedPath=%s", boolToStr(isGetPathOk), parsedPath);
189 | if (isGetPathOk) {
190 | isJbPath = isJailbreakPath(parsedPath);
191 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
192 |
193 | if (isJbPath){
194 | syscallRetValue = STATFS_FAILED;
195 | } else {
196 | syscallRetValue = %orig(number, fd, buf);
197 | }
198 | } else {
199 | // can not get path -> can not check is jailbreak or not -> not hook
200 | syscallRetValue = %orig(number, fd, buf);
201 | }
202 |
203 | iosLogInfo("SYS_fstatfs: number=%d -> fd=%d, buf=%p -> isJbPath=%{bool}d -> syscallRetValue=%d", number, fd, buf, isJbPath, syscallRetValue);
204 | return syscallRetValue;
205 | }
206 |
207 | // #define SYS_stat 188
208 | // #define SYS_stat64 338
209 | bool isStat = (SYS_stat == number);
210 | bool isStat64 = (SYS_stat64 == number);
211 | if (isStat || isStat64){
212 | //int stat(const char *, struct stat *) __DARWIN_INODE64(stat);
213 | //int stat64(const char *, struct stat64 *) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_6, __IPHONE_NA, __IPHONE_NA);
214 | va_start(argList, number);
215 | const char * fisrtPath = va_arg(argList, const char *);
216 | void *secondStat = va_arg(argList, void *);
217 | va_end(argList);
218 |
219 | iosLogDebug("isStat=%{bool}d, isStat64=%{BOOL}d, fisrtPath=%{public}s, secondStat=%p", isStat, isStat64, fisrtPath, secondStat);
220 |
221 | bool isJbPath = isJailbreakPath(fisrtPath);
222 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
223 | if (isJbPath){
224 | syscallRetValue = OPEN_FAILED;
225 | } else {
226 | // if (isStat){
227 | // struct stat *statInfo = (struct stat *)secondStat;
228 | // syscallRetValue = %orig(number, fisrtPath, statInfo);
229 | // } else if(isStat64){
230 | // struct stat64 *stat64Info = (struct stat64 *)secondStat;
231 | // syscallRetValue = %orig(number, fisrtPath, stat64Info);
232 | // }
233 | syscallRetValue = %orig(number, fisrtPath, secondStat);
234 | }
235 | iosLogInfo("SYS_stat/SYS_stat64: number=%d -> isJbPath=%{bool}d, fisrtPath=%{public}s -> syscallRetValue=%d", number, isJbPath, fisrtPath, syscallRetValue);
236 | return syscallRetValue;
237 | }
238 |
239 | // #define SYS_fstat 189
240 | bool isFstat = (SYS_fstat == number);
241 | if (isFstat) {
242 | bool isGetPathOk = false;
243 | bool isJbPath = false;
244 | char parsedPath[PATH_MAX];
245 | memset(parsedPath, 0, PATH_MAX);
246 |
247 | // int fstat(int fd, struct stat *buf);
248 | va_start(argList, number);
249 | int fd = va_arg(argList, int);
250 | struct stat* buf = (struct stat*)va_arg(argList, void *);
251 | va_end(argList);
252 |
253 | iosLogDebug("isFstat=%{bool}d, fd=%d, buf=%p", isFstat, fd, buf);
254 |
255 | isGetPathOk = getFilePath(fd, parsedPath);
256 | iosLogDebug("isGetPathOk=%{bool}d, parsedPath=%s", isGetPathOk, parsedPath);
257 | if (isGetPathOk) {
258 | isJbPath = isJailbreakPath(parsedPath);
259 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
260 |
261 | if (isJbPath){
262 | syscallRetValue = STAT_FAILED;
263 | } else {
264 | syscallRetValue = %orig(number, fd, buf);
265 | }
266 | } else {
267 | syscallRetValue = %orig(number, fd, buf);
268 | }
269 |
270 | iosLogInfo("SYS_fstat: number=%d -> fd=%d -> isGetPathOk=%{bool}d, parsedPath=%{public}s -> isJbPath=%{bool}d -> syscallRetValue=%d", number, fd, isGetPathOk, parsedPath, isJbPath, syscallRetValue);
271 | return syscallRetValue;
272 | }
273 |
274 | // #define SYS_lstat 190
275 | bool isLstat = (SYS_lstat == number);
276 | if (isLstat) {
277 | // int lstat(const char* path, struct stat* buf);
278 | va_start(argList, number);
279 | const char* fisrtPath = va_arg(argList, const char *);
280 | struct stat* secondBuf = (struct stat*)va_arg(argList, void *);
281 | va_end(argList);
282 |
283 | iosLogDebug("isLstat=%{bool}d, fisrtPath=%{public}s, secondBuf=%p", isLstat, fisrtPath, secondBuf);
284 |
285 | bool isJbPath = isJailbreakPath(fisrtPath);
286 | iosLogDebug("isJbPath=%{bool}d", isJbPath);
287 | if (isJbPath){
288 | syscallRetValue = STAT_FAILED;
289 | } else {
290 | syscallRetValue = %orig(number, fisrtPath, secondBuf);
291 | }
292 | iosLogInfo("SYS_lstat: number=%d -> isJbPath=%{bool}d, fisrtPath=%{public}s -> syscallRetValue=%d", number, isJbPath, fisrtPath, syscallRetValue);
293 | return syscallRetValue;
294 | }
295 |
296 | // #define SYS_fstatat 469
297 | bool isFstatat = (SYS_fstatat == number);
298 | if (isFstatat) {
299 | bool isJbPath = false;
300 |
301 | // int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
302 | va_start(argList, number);
303 | int dirfd = va_arg(argList, int);
304 | const char *pathname = (const char *)va_arg(argList, void *);
305 | struct stat *buf = (struct stat*)va_arg(argList, void *);
306 | int flags = va_arg(argList, int);
307 | va_end(argList);
308 |
309 | iosLogDebug("isFstatat=%{bool}d, dirfd=%d, pathname=%{public}s, buf=%p, flags=%d", isFstatat, dirfd, pathname, buf, flags);
310 |
311 | const char* absPath = NULL;
312 | bool isAbsPath = strStartsWith(pathname, "/");
313 | iosLogDebug("isAbsPath=%{bool}d", isAbsPath);
314 | if (isAbsPath) {
315 | absPath = pathname;
316 | } else {
317 | // is relative path
318 | if (dirfd == AT_FDCWD){
319 | iosLogDebug("dirfd is AT_FDCWD=%d", AT_FDCWD);
320 |
321 | // pathname is interpreted relative to the current working directory of the calling process (like access())
322 | // TODO: try get current working directory -> avoid caller pass the special path, finnaly is jailbreak path
323 | // eg: current working directory is "/usr/xxx/yyy/", then pass in "../../libexec/cydia/zzz"
324 | // finnal path is "/usr/libexec/cydia/zzz", match jailbreak path: "/usr/libexec/cydia/", is jaibreak path
325 | // but use "../../libexec/cydia/zzz" can not check whether is jailbreak path
326 | } else {
327 | // get file path from dir fd
328 | char filePath[PATH_MAX];
329 | bool isGetPathOk = getFilePath(dirfd, filePath);
330 | iosLogDebug("isGetPathOk=%s", boolToStr(isGetPathOk));
331 | if (isGetPathOk) {
332 | char* fullPath = strPathJoin(filePath, pathname)
333 | iosLogDebug("fullPath=%{public}s", fullPath);
334 | absPath = fullPath;
335 | }
336 | }
337 | }
338 |
339 | if (NULL != absPath){
340 | isJbPath = isJailbreakPath(absPath);
341 | iosLogDebug("absPath=%{public}s -> isJbPath=%{bool}d", absPath, isJbPath);
342 | if (isJbPath) {
343 | iosLogDebug("hook jailbreak path: %s", absPath);
344 | syscallRetValue = STATFS_FAILED;
345 | } else {
346 | syscallRetValue = %orig(number, dirfd, pathname, buf, flags);
347 | }
348 | } else {
349 | syscallRetValue = %orig(number, dirfd, pathname, buf, flags);
350 | }
351 |
352 | iosLogInfo("SYS_fstatat: number=%d -> dirfd=%d, pathname=%{public}s, buf=%p, flags=0x%x -> isJbPath=%{bool}d -> syscallRetValue=%d", number, dirfd, pathname, buf, flags, isJbPath, syscallRetValue);
353 | return syscallRetValue;
354 | }
355 |
356 | }
357 |
358 | va_start(argList, number);
359 | while ((paraPtr = (void *) va_arg(argList, void *))) {
360 | // while ((paraPtr = (char *) va_arg(argList, char *))) {
361 | paraList[curParaNum] = paraPtr;
362 | curParaNum += 1;
363 | iosLogDebug("[%d] paraPtr=%p", curParaNum, paraPtr);
364 | }
365 | va_end(argList);
366 |
367 | // iosLogDebug("argList=%{public}s", argList);
368 | iosLogDebug("curParaNum=%d", curParaNum);
369 |
370 | // return %orig;
371 | // return %orig(number, ...);
372 | // int retValue = %orig();
373 |
374 | // int retValue = callOriginSyscall(number, curParaNum, paraList);
375 | //// int retValue = callOriginSyscall(number, curParaNum, (void *)paraList);
376 | // iosLogDebug("retValue=%d", retValue);
377 | // return retValue;
378 |
379 | int paraNum = curParaNum;
380 |
381 | if (0 == paraNum){
382 | syscallRetValue = %orig(number);
383 | } else if (1 == paraNum){
384 | void* para1 = paraList[0];
385 | iosLogDebug("para1=%p", para1);
386 | syscallRetValue = %orig(number, para1);
387 | } else if (2 == paraNum){
388 | void* para1 = paraList[0];
389 | void* para2 = paraList[1];
390 | iosLogDebug("para1=%p,para2=%p", para1, para2);
391 | syscallRetValue = %orig(number, para1, para2);
392 | } else if (3 == paraNum){
393 | void* para1 = paraList[0];
394 | void* para2 = paraList[1];
395 | void* para3 = paraList[2];
396 | iosLogDebug("para1=%p,para2=%p,para3=%p", para1, para2, para3);
397 | syscallRetValue = %orig(number, para1, para2, para3);
398 | } else if (4 == paraNum){
399 | void* para1 = paraList[0];
400 | void* para2 = paraList[1];
401 | void* para3 = paraList[2];
402 | void* para4 = paraList[3];
403 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p", para1, para2, para3, para4);
404 | syscallRetValue = %orig(number, para1, para2, para3, para4);
405 | } else if (5 == paraNum){
406 | void* para1 = paraList[0];
407 | void* para2 = paraList[1];
408 | void* para3 = paraList[2];
409 | void* para4 = paraList[3];
410 | void* para5 = paraList[4];
411 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p,para5=%p", para1, para2, para3, para4, para5);
412 | syscallRetValue = %orig(number, para1, para2, para3, para4, para5);
413 | } else if (6 == paraNum){
414 | void* para1 = paraList[0];
415 | void* para2 = paraList[1];
416 | void* para3 = paraList[2];
417 | void* para4 = paraList[3];
418 | void* para5 = paraList[4];
419 | void* para6 = paraList[5];
420 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p", para1, para2, para3, para4, para5, para6);
421 | syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6);
422 | } else if (7 == paraNum){
423 | void* para1 = paraList[0];
424 | void* para2 = paraList[1];
425 | void* para3 = paraList[2];
426 | void* para4 = paraList[3];
427 | void* para5 = paraList[4];
428 | void* para6 = paraList[5];
429 | void* para7 = paraList[6];
430 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p", para1, para2, para3, para4, para5, para6, para7);
431 | syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7);
432 | } else if (8 == paraNum){
433 | void* para1 = paraList[0];
434 | void* para2 = paraList[1];
435 | void* para3 = paraList[2];
436 | void* para4 = paraList[3];
437 | void* para5 = paraList[4];
438 | void* para6 = paraList[5];
439 | void* para7 = paraList[6];
440 | void* para8 = paraList[7];
441 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p,para8=%p", para1, para2, para3, para4, para5, para6, para7, para8);
442 | syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7, para8);
443 | } else if (9 == paraNum){
444 | void* para1 = paraList[0];
445 | void* para2 = paraList[1];
446 | void* para3 = paraList[2];
447 | void* para4 = paraList[3];
448 | void* para5 = paraList[4];
449 | void* para6 = paraList[5];
450 | void* para7 = paraList[6];
451 | void* para8 = paraList[7];
452 | void* para9 = paraList[8];
453 | iosLogDebug("para1=%p,para2=%p,para3=%p,para4=%p,para5=%p,para6=%p,para7=%p,para8=%p,para9=%p", para1, para2, para3, para4, para5, para6, para7, para8, para9);
454 | syscallRetValue = %orig(number, para1, para2, para3, para4, para5, para6, para7, para8, para9);
455 | }
456 |
457 | iosLogInfo("number=%d -> syscallRetValue=%d", number, syscallRetValue);
458 | return syscallRetValue;
459 | }
460 | ```
461 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/open/ios_func.md:
--------------------------------------------------------------------------------
1 | # iOS函数
2 |
3 | ## NSFileManager
4 |
5 | TODO:
6 |
7 | * 【已解决】iOS反越狱检测:NSFileManager的fileExistsAtPath
8 |
9 | ```c
10 | /*==============================================================================
11 | Hook: NSFileManager
12 | ==============================================================================*/
13 |
14 | //@interface NSFileManager (TweakMethods)
15 | //+ (BOOL) isJailbreakPath_iOS: (NSString*)curPath;
16 | //@end
17 |
18 | %hook NSFileManager
19 |
20 | ///* Common Util Function */
21 | //
22 | //%new
23 | //+ (BOOL) isJailbreakPath_iOS: (NSString*)curPath{
24 | ////- (BOOL) isJailbreakPath_iOS: (NSString*)curPath{
25 | // BOOL isJbPath = FALSE;
26 | //
27 | // if (NULL != curPath){
28 | // const char* curPathStr = [curPath UTF8String];
29 | //// isJbPath = isJailbreakPath(curPathStr);
30 | // const char* FILE_PREFIX = "file://";
31 | //
32 | //// const char* pathNoFilePrefix = removeHead(curPathStr, FILE_PREFIX);
33 | // char* toFreePtr = NULL;
34 | // const char* pathNoFilePrefix = removeHead(curPathStr, FILE_PREFIX, &toFreePtr);
35 | //
36 | // isJbPath = isJailbreakPath(pathNoFilePrefix);
37 | //
38 | //// free(pathNoFilePrefix);
39 | //// if (NULL != toFreePtr) {
40 | // iosLogDebug("now to free: toFreePtr=%p", toFreePtr);
41 | // free(toFreePtr);
42 | //// }
43 | // }
44 | // iosLogDebug("curPath=%{public}@ -> isJbPath=%s", curPath, boolToStr(isJbPath));
45 | // return isJbPath;
46 | //}
47 |
48 | - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError * _Nullable *)error
49 | {
50 | iosLogDebug("path=%{public}@, *error=%@", path, ERROR_STR(error));
51 | NSArray * retContentList = NULL;
52 | BOOL isJbPath = FALSE;
53 |
54 | if (cfgHookEnable_openFileiOS) {
55 | if (NULL != path) {
56 | isJbPath = [JailbreakiOS isJailbreakPath_iOS: path];
57 | if (isJbPath){
58 | retContentList = NULL;
59 | } else {
60 | // retContentList = %orig(path, error);
61 | retContentList = %orig;
62 | }
63 | }
64 | } else {
65 | retContentList = %orig;
66 | }
67 |
68 | // for debug
69 | if (isJbPath){
70 | iosLogInfo("path=%{public}@, *error=%@ -> isJbPath=%{bool}d -> retContentList=%p", path, ERROR_STR(error), isJbPath, retContentList);
71 | }
72 | return retContentList;
73 | }
74 |
75 | - (BOOL)fileExistsAtPath:(NSString *)path
76 | {
77 | iosLogDebug("path=%{public}@", path);
78 | bool isExists = FALSE;
79 | BOOL isJbPath = FALSE;
80 |
81 | if (cfgHookEnable_openFileiOS) {
82 | if (NULL != path){
83 | isJbPath = [JailbreakiOS isJailbreakPath_iOS: path];
84 | if(isJbPath){
85 | isExists = FALSE;
86 | } else{
87 | // isExists = %orig(path);
88 | isExists = %orig;
89 | }
90 | }
91 | } else {
92 | isExists = %orig;
93 | }
94 |
95 | // for debug
96 | if (isJbPath){
97 | iosLogInfo("path=%{public}@ -> isJbPath=%s -> isExists=%s", path, boolToStr(isJbPath), boolToStr(isExists));
98 | }
99 |
100 | return isExists;
101 | }
102 |
103 | - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory
104 | {
105 | iosLogDebug("path=%{public}@, isDirectory=%p", path, isDirectory);
106 | BOOL isJbPath = FALSE;
107 | BOOL isExists = FALSE;
108 |
109 | if (cfgHookEnable_openFileiOS) {
110 | if (NULL != path) {
111 | isJbPath = [JailbreakiOS isJailbreakPath_iOS: path];
112 | if(isJbPath){
113 | isExists = FALSE;
114 | } else{
115 | // isExists = %orig(path, isDirectory);
116 | isExists = %orig;
117 | }
118 | }
119 | } else {
120 | isExists = %orig;
121 | }
122 |
123 | // for debug
124 | if (isJbPath){
125 | iosLogInfo("path=%{public}@, isDirectory=%p -> isJbPath=%s -> isExists=%s", path, isDirectory, boolToStr(isJbPath), boolToStr(isExists));
126 | }
127 |
128 | return isExists;
129 | }
130 |
131 | %end
132 | ```
133 |
134 | ## NSURL
135 |
136 | ```c
137 | /*==============================================================================
138 | Hook: NSURL
139 | ==============================================================================*/
140 |
141 | %hook NSURL
142 |
143 | - (BOOL)checkResourceIsReachableAndReturnError:(NSError * _Nullable *)error{
144 | NSString* curUrlStr = [self absoluteString];
145 | iosLogDebug("curUrlStr=%{public}@, error=%p", curUrlStr, error);
146 | BOOL isJbPath = FALSE;
147 | BOOL isReachable = FALSE;
148 |
149 | if (cfgHookEnable_openFileiOS) {
150 | isJbPath = [JailbreakiOS isJailbreakPath_iOS: curUrlStr];
151 | if(isJbPath){
152 | isReachable = FALSE;
153 | } else{
154 | // isReachable = %orig(error);
155 | isReachable = %orig;
156 | }
157 | } else {
158 | isReachable = %orig;
159 | }
160 |
161 | // for debug
162 | if (isJbPath) {
163 | iosLogInfo("curUrlStr=%{public}@, error=%p -> isJbPath=%s -> isReachable=%s", curUrlStr, error, boolToStr(isJbPath), boolToStr(isReachable));
164 | }
165 | return isReachable;
166 | }
167 |
168 | %end
169 | ```
170 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/write/README.md:
--------------------------------------------------------------------------------
1 | # 文件写入
2 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/write/c_func.md:
--------------------------------------------------------------------------------
1 | # C函数
2 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/file/write/ios_func.md:
--------------------------------------------------------------------------------
1 | # iOS函数
2 |
3 | TODO:
4 |
5 | 【已解决】iOS反越狱检测之iOS层函数写入/private的hook绕过
6 |
7 | ---
8 |
9 | ## 工具类函数
10 |
11 | ```c
12 | bool shouldHookWritePath(const char* path);
13 | bool shouldHookWritePath_NSString(NSString* pathNs);
14 | bool shouldHookWritePath_NSURL(NSURL* url);
15 |
16 | /*==============================================================================
17 | Common Functions
18 | ==============================================================================*/
19 |
20 | // /private/testWriteToFile.txt -> true
21 | // /private/var/mobile/Containers/Data/Application/EEFACEA4-2ADB-4D25-9DB4-B5D643EA8943/Documents/bd.turing/ -> false
22 | bool shouldHookWritePath(const char* path){
23 | const char* Path_Private = "/private/";
24 | const char* Path_FilePrivate = "file:///private/";
25 |
26 | bool shouldHook = false;
27 |
28 | char* purePath = toPurePath(path);
29 | iosLogDebug("path=%{public}s -> purePath=%s", path, purePath);
30 | bool isStartWithPrivate = strStartsWith(purePath, Path_Private);
31 | bool isStartWithFilePrivate = strStartsWith(purePath, Path_FilePrivate);
32 | iosLogDebug("isStartWithPrivate=%s, isStartWithFilePrivate=%s",boolToStr(isStartWithPrivate), boolToStr(isStartWithFilePrivate));
33 |
34 | if (isStartWithPrivate || isStartWithFilePrivate){
35 | // is /private/ path
36 | char* pathNoPrivateHead = NULL;
37 | // int origMallocStrPointerMovePrevLen = 0;
38 | char* toFreeRemoveHeadPathPrivate = NULL;
39 | char* toFreeRemoveHeadPathFilePrivate = NULL;
40 |
41 | if(isStartWithPrivate){
42 | // pathNoPrivateHead = removeHead(purePath, Path_Private);
43 | // origMallocStrPointerMovePrevLen = strlen(Path_Private);
44 | pathNoPrivateHead = removeHead(purePath, Path_Private, &toFreeRemoveHeadPathPrivate);
45 | }
46 |
47 | if(isStartWithFilePrivate){
48 | // pathNoPrivateHead = removeHead(purePath, Path_FilePrivate);
49 | // origMallocStrPointerMovePrevLen = strlen(Path_FilePrivate);
50 | pathNoPrivateHead = removeHead(purePath, Path_FilePrivate, &toFreeRemoveHeadPathFilePrivate);
51 | }
52 | // iosLogDebug("purePath=%s -> pathNoPrivateHead=%s, origMallocStrPointerMovePrevLen=%d", purePath, pathNoPrivateHead, origMallocStrPointerMovePrevLen);
53 | iosLogDebug("purePath=%s -> pathNoPrivateHead=%s, toFreeRemoveHeadPathPrivate=%p, toFreeRemoveHeadPathFilePrivate=%p", purePath, pathNoPrivateHead, toFreeRemoveHeadPathPrivate, toFreeRemoveHeadPathFilePrivate);
54 |
55 | // testWriteToFile.txt
56 | // var/mobile/Containers/Data/Application/EEFACEA4-2ADB-4D25-9DB4-B5D643EA8943/Documents/xxx
57 | if (NULL != pathNoPrivateHead){
58 | char* foundSlash = strstr(pathNoPrivateHead, "/");
59 | iosLogDebug("foundSlash=%s", foundSlash);
60 | if (NULL != foundSlash){
61 | // var/mobile/Containers/Data/Application/EEFACEA4-2ADB-4D25-9DB4-B5D643EA8943/Documents/xxx
62 | shouldHook = false;
63 | } else {
64 | // testWriteToFile.txt
65 | shouldHook = true;
66 | }
67 |
68 | // free(pathNoPrivateHead); // will crash !!!
69 | // char* toFreePtr = pathNoPrivateHead - origMallocStrPointerMovePrevLen;
70 | // iosLogDebug("pathNoPrivateHead=%p, toFreePtr=%p", pathNoPrivateHead, toFreePtr);
71 | // free(toFreePtr);
72 | if (NULL != toFreeRemoveHeadPathPrivate){
73 | free(toFreeRemoveHeadPathPrivate);
74 | iosLogDebug("has free toFreeRemoveHeadPathPrivate=%p", toFreeRemoveHeadPathPrivate);
75 | }
76 |
77 | if (NULL != toFreeRemoveHeadPathFilePrivate){
78 | free(toFreeRemoveHeadPathFilePrivate);
79 | iosLogDebug("has free toFreeRemoveHeadPathFilePrivate=%p", toFreeRemoveHeadPathFilePrivate);
80 | }
81 | } else {
82 | shouldHook = false;
83 | }
84 | } else {
85 | // not /private/ path
86 | shouldHook = false;
87 | }
88 |
89 | free(purePath);
90 |
91 | // for debug
92 | if (shouldHook) {
93 | iosLogInfo("path=%{public}s -> shouldHook=%s", path, boolToStr(shouldHook));
94 | // /private/testWriteToFile.txt
95 | }
96 |
97 | // // for debug
98 | // shouldHook = false;
99 |
100 | return shouldHook;
101 | }
102 |
103 | bool shouldHookWritePath_NSString(NSString* pathNs){
104 | const char* pathStr = [pathNs UTF8String];
105 | BOOL shouldHook = shouldHookWritePath(pathStr);
106 |
107 | // // for debug
108 | // shouldHook = false;
109 |
110 | iosLogDebug("pathNs=%@ -> shouldHook=%s", pathNs, boolToStr(shouldHook));
111 | return shouldHook;
112 | }
113 |
114 | bool shouldHookWritePath_NSURL(NSURL* url){
115 | NSString *urlNSStr = [url absoluteString];
116 | const char* urlStr = [urlNSStr UTF8String];
117 | BOOL shouldHook = shouldHookWritePath(urlStr);
118 |
119 | // // for debug
120 | // shouldHook = false;
121 |
122 | iosLogDebug("url=%@ -> shouldHook=%s", url, boolToStr(shouldHook));
123 | return shouldHook;
124 | }
125 | ```
126 |
127 | ## NSString
128 |
129 | ```c
130 | /*==============================================================================
131 | Hook: NSString
132 | ==============================================================================*/
133 |
134 | %hook NSString
135 |
136 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
137 | {
138 | BOOL isWriteOk = FALSE;
139 |
140 | if(cfgHookEnable_writeFileiOS){
141 | if(shouldHookWritePath_NSString(path)){
142 | isWriteOk = FALSE;
143 | iosLogInfo("hooked path=%{public}@ -> isWriteOk=%s", path, boolToStr(isWriteOk));
144 | } else {
145 | // isWriteOk = %orig(path, useAuxiliaryFile);
146 | isWriteOk = %orig;
147 | }
148 | } else {
149 | // isWriteOk = %orig(path, useAuxiliaryFile);
150 | isWriteOk = %orig;
151 | }
152 |
153 | iosLogDebug("%spath=%{public}@, useAuxiliaryFile=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, boolToStr(useAuxiliaryFile), boolToStr(isWriteOk));
154 | return isWriteOk;
155 | }
156 |
157 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error{
158 | iosLogDebug("path=%@, useAuxiliaryFile=%s, enc=%ld, *error=%@", path, boolToStr(useAuxiliaryFile), enc, ERROR_STR(error));
159 | BOOL isWriteOk = FALSE;
160 |
161 | if (cfgHookEnable_writeFileiOS) {
162 | if(shouldHookWritePath_NSString(path)){
163 | isWriteOk = FALSE;
164 | iosLogInfo("hooked path=%{public}@ -> isWriteOk=%s", path, boolToStr(isWriteOk));
165 | } else {
166 | // isWriteOk = %orig(path, useAuxiliaryFile, enc, error);
167 | isWriteOk = %orig;
168 | }
169 | } else {
170 | // isWriteOk = %orig(path, useAuxiliaryFile, enc, error);
171 | isWriteOk = %orig;
172 | }
173 | iosLogDebug("%spath=%{public}@, useAuxiliaryFile=%s, enc=%lu, *error=%@-> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, boolToStr(useAuxiliaryFile), enc, ERROR_STR(error), boolToStr(isWriteOk));
174 | return isWriteOk;
175 | }
176 |
177 | - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically{
178 | BOOL isWriteOk = FALSE;
179 |
180 | if (cfgHookEnable_writeFileiOS) {
181 | if(shouldHookWritePath_NSURL(url)){
182 | isWriteOk = FALSE;
183 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
184 | } else {
185 | // isWriteOk = %orig(url, atomically);
186 | isWriteOk = %orig;
187 | }
188 | } else {
189 | // isWriteOk = %orig(url, atomically);
190 | isWriteOk = %orig;
191 | }
192 | iosLogDebug("%surl=%{public}@, atomically=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, boolToStr(atomically), boolToStr(isWriteOk));
193 | return isWriteOk;
194 | }
195 |
196 | - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error{
197 | BOOL isWriteOk = FALSE;
198 |
199 | if (cfgHookEnable_writeFileiOS) {
200 | if(shouldHookWritePath_NSURL(url)){
201 | isWriteOk = FALSE;
202 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
203 | } else {
204 | // isWriteOk = %orig(url, useAuxiliaryFile, enc, error);
205 | isWriteOk = %orig;
206 | }
207 | } else {
208 | // isWriteOk = %orig(url, useAuxiliaryFile, enc, error);
209 | isWriteOk = %orig;
210 | }
211 | iosLogDebug("%surl=%{public}@, useAuxiliaryFile=%s, enc=%lu, *error=%@-> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, boolToStr(useAuxiliaryFile), enc, ERROR_STR(error), boolToStr(isWriteOk));
212 | return isWriteOk;
213 | }
214 |
215 | %end
216 | ```
217 |
218 | ## NSData
219 |
220 | ```c
221 | /*==============================================================================
222 | Hook: NSData
223 | ==============================================================================*/
224 |
225 | %hook NSData
226 |
227 | - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically{
228 | BOOL isWriteOk = FALSE;
229 |
230 | if (cfgHookEnable_writeFileiOS) {
231 | if(shouldHookWritePath_NSURL(url)){
232 | isWriteOk = FALSE;
233 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
234 | } else {
235 | // isWriteOk = %orig(url, atomically);
236 | isWriteOk = %orig;
237 | }
238 | } else {
239 | // isWriteOk = %orig(url, atomically);
240 | isWriteOk = %orig;
241 | }
242 | iosLogDebug("%surl=%{public}@, atomically=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, boolToStr(atomically), boolToStr(isWriteOk));
243 | return isWriteOk;
244 | }
245 |
246 | //- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)errorPtr{
247 | - (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)error{
248 | BOOL isWriteOk = FALSE;
249 |
250 | if (cfgHookEnable_writeFileiOS) {
251 | if(shouldHookWritePath_NSString(path)){
252 | isWriteOk = FALSE;
253 | iosLogInfo("hooked path=%{public}@ -> isWriteOk=%s", path, boolToStr(isWriteOk));
254 | } else {
255 | // isWriteOk = %orig(path, writeOptionsMask, error);
256 | isWriteOk = %orig;
257 | }
258 | } else {
259 | // isWriteOk = %orig(path, writeOptionsMask, error);
260 | isWriteOk = %orig;
261 | }
262 | iosLogDebug("%spath=%{public}@, writeOptionsMask=0x%lx, *error=%@-> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, writeOptionsMask, ERROR_STR(error), boolToStr(isWriteOk));
263 | return isWriteOk;
264 | }
265 |
266 | //- (BOOL)writeToURL:(NSURL *)url options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)errorPtr{
267 | - (BOOL)writeToURL:(NSURL *)url options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)error{
268 | BOOL isWriteOk = FALSE;
269 |
270 | if (cfgHookEnable_writeFileiOS) {
271 | if(shouldHookWritePath_NSURL(url)){
272 | isWriteOk = FALSE;
273 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
274 | } else {
275 | // isWriteOk = %orig(url, writeOptionsMask, error);
276 | isWriteOk = %orig;
277 | }
278 | } else {
279 | // isWriteOk = %orig(url, writeOptionsMask, error);
280 | isWriteOk = %orig;
281 | }
282 | iosLogDebug("%surl=%{public}@, writeOptionsMask=0x%lx, *error=%@-> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, writeOptionsMask, ERROR_STR(error), boolToStr(isWriteOk));
283 | return isWriteOk;
284 | }
285 |
286 | %end
287 | ```
288 |
289 | ## NSArray
290 |
291 | ```c
292 | /*==============================================================================
293 | Hook: NSArray
294 | ==============================================================================*/
295 |
296 | %hook NSArray
297 |
298 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile{
299 | BOOL isWriteOk = FALSE;
300 |
301 | if (cfgHookEnable_writeFileiOS) {
302 | if(shouldHookWritePath_NSString(path)){
303 | isWriteOk = FALSE;
304 | iosLogInfo("hooked path=%{public}@ -> isWriteOk=%s", path, boolToStr(isWriteOk));
305 | } else {
306 | // isWriteOk = %orig(path, useAuxiliaryFile);
307 | isWriteOk = %orig;
308 | }
309 | } else {
310 | // isWriteOk = %orig(path, useAuxiliaryFile);
311 | isWriteOk = %orig;
312 | }
313 | iosLogDebug("%spath=%{public}@, useAuxiliaryFile=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, boolToStr(useAuxiliaryFile), boolToStr(isWriteOk));
314 | return isWriteOk;
315 | }
316 |
317 | - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically{
318 | BOOL isWriteOk = FALSE;
319 |
320 | if (cfgHookEnable_writeFileiOS) {
321 | if(shouldHookWritePath_NSURL(url)){
322 | isWriteOk = FALSE;
323 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
324 | } else {
325 | // isWriteOk = %orig(url, atomically);
326 | isWriteOk = %orig;
327 | }
328 | } else {
329 | // isWriteOk = %orig(url, atomically);
330 | isWriteOk = %orig;
331 | }
332 | iosLogDebug("%surl=%{public}@, atomically=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, boolToStr(atomically), boolToStr(isWriteOk));
333 | return isWriteOk;
334 | }
335 |
336 | - (BOOL)writeToURL:(NSURL *)url error:(NSError **)error{
337 | BOOL isWriteOk = FALSE;
338 |
339 | if (cfgHookEnable_writeFileiOS) {
340 | if(shouldHookWritePath_NSURL(url)){
341 | isWriteOk = FALSE;
342 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
343 | } else {
344 | // isWriteOk = %orig(url, error);
345 | isWriteOk = %orig;
346 | }
347 | } else {
348 | // isWriteOk = %orig(url, error);
349 | isWriteOk = %orig;
350 | }
351 | iosLogDebug("%surl=%{public}@, *error=%@ -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, ERROR_STR(error), boolToStr(isWriteOk));
352 | return isWriteOk;
353 | }
354 |
355 | %end
356 | ```
357 |
358 | ## NSDictionary
359 |
360 | ```c
361 | /*==============================================================================
362 | Hook: NSDictionary
363 | ==============================================================================*/
364 |
365 | %hook NSDictionary
366 |
367 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile{
368 | BOOL isWriteOk = FALSE;
369 |
370 | if (cfgHookEnable_writeFileiOS) {
371 | if(shouldHookWritePath_NSString(path)){
372 | isWriteOk = FALSE;
373 | iosLogInfo("hooked path=%{public}@ -> isWriteOk=%s", path, boolToStr(isWriteOk));
374 | } else {
375 | // isWriteOk = %orig(path, useAuxiliaryFile);
376 | isWriteOk = %orig;
377 | }
378 | } else {
379 | // isWriteOk = %orig(path, useAuxiliaryFile);
380 | isWriteOk = %orig;
381 | }
382 | iosLogDebug("%spath=%{public}@, useAuxiliaryFile=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, boolToStr(useAuxiliaryFile), boolToStr(isWriteOk));
383 | return isWriteOk;
384 | }
385 |
386 | - (BOOL)writeToURL:(NSURL *)url error:(NSError **)error{
387 | BOOL isWriteOk = FALSE;
388 |
389 | if (cfgHookEnable_writeFileiOS) {
390 | if(shouldHookWritePath_NSURL(url)){
391 | isWriteOk = FALSE;
392 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
393 | } else {
394 | // isWriteOk = %orig(url, error);
395 | isWriteOk = %orig;
396 | }
397 | } else {
398 | // isWriteOk = %orig(url, error);
399 | isWriteOk = %orig;
400 | }
401 | iosLogDebug("%surl=%{public}@, *error=%@ -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, ERROR_STR(error), boolToStr(isWriteOk));
402 | return isWriteOk;
403 | }
404 |
405 | - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically{
406 | BOOL isWriteOk = FALSE;
407 |
408 | if (cfgHookEnable_writeFileiOS) {
409 | if(shouldHookWritePath_NSURL(url)){
410 | isWriteOk = FALSE;
411 | iosLogInfo("hooked url=%{public}@ -> isWriteOk=%s", url, boolToStr(isWriteOk));
412 | } else {
413 | // isWriteOk = %orig(url, atomically);
414 | isWriteOk = %orig;
415 | }
416 | } else {
417 | // isWriteOk = %orig(url, atomically);
418 | isWriteOk = %orig;
419 | }
420 | iosLogDebug("%surl=%{public}@, atomically=%s -> isWriteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, boolToStr(atomically), boolToStr(isWriteOk));
421 | return isWriteOk;
422 | }
423 |
424 | %end
425 | ```
426 |
427 | ## NSFileManager
428 |
429 | ```c
430 | /*==============================================================================
431 | Hook: NSFileManager
432 | ==============================================================================*/
433 |
434 | %hook NSFileManager
435 |
436 | - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error {
437 | BOOL isDeleteOk = FALSE;
438 |
439 | if (cfgHookEnable_writeFileiOS) {
440 | if(shouldHookWritePath_NSString(path)){
441 | isDeleteOk = FALSE;
442 | iosLogInfo("hooked path=%{public}@ -> isDeleteOk=%s", path, boolToStr(isDeleteOk));
443 | } else {
444 | // isDeleteOk = %orig(path, error);
445 | isDeleteOk = %orig;
446 | }
447 | } else {
448 | // isDeleteOk = %orig(path, error);
449 | isDeleteOk = %orig;
450 | }
451 | iosLogDebug("%spath=%{public}@, *error=%@-> isDeleteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), path, ERROR_STR(error), boolToStr(isDeleteOk));
452 | return isDeleteOk;
453 | }
454 |
455 | //- (BOOL)removeItemAtURL:(NSURL *)URL error:(NSError **)error {
456 | - (BOOL)removeItemAtURL:(NSURL *)url error:(NSError **)error {
457 | BOOL isDeleteOk = FALSE;
458 |
459 | if (cfgHookEnable_writeFileiOS) {
460 | if(shouldHookWritePath_NSURL(url)){
461 | isDeleteOk = FALSE;
462 | iosLogInfo("hooked url=%{public}@ -> isDeleteOk=%s", url, boolToStr(isDeleteOk));
463 | } else {
464 | // isDeleteOk = %orig(url, error);
465 | isDeleteOk = %orig;
466 | }
467 | } else {
468 | // isDeleteOk = %orig(url, error);
469 | isDeleteOk = %orig;
470 | }
471 | iosLogDebug("%surl=%{public}@, *error=%@-> isDeleteOk=%s", HOOK_PREFIX(cfgHookEnable_writeFileiOS), url, ERROR_STR(error), boolToStr(isDeleteOk));
472 | return isDeleteOk;
473 | }
474 |
475 | %end
476 | ```
477 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/getsectiondata.md:
--------------------------------------------------------------------------------
1 | # getsectiondata
2 |
3 | TODO:
4 |
5 | * 【已解决】hook函数getsectiondata时不hook函数dladdr看看是否还是导致抖音崩溃
6 |
7 | ---
8 |
9 | ## getsectiondata的hook
10 |
11 | ```c
12 | /*==============================================================================
13 | Hook: getsectiondata
14 | ==============================================================================*/
15 |
16 | extern uint8_t *getsectiondata(
17 | const struct mach_header_64 *mhp,
18 | const char *segname,
19 | const char *sectname,
20 | unsigned long *size);
21 |
22 | //extern uint8_t *getsectiondata(
23 | // const struct mach_header *mhp,
24 | // const char *segname,
25 | // const char *sectname,
26 | // unsigned long *size);
27 |
28 | %hookf(uint8_t*, getsectiondata, const struct mach_header_64 *mhp, const char *segname, const char *sectname, unsigned long *size){
29 | iosLogDebug("mhp=%p,segname=%{public}s,sectname=%{public}s,size=%p", mhp, segname, sectname, size);
30 |
31 | uint8_t* origRetIntP = %orig;
32 |
33 | if (cfgHookEnable_macho) {
34 | bool isJbLib = false;
35 | bool isShowLog = false;
36 |
37 | Dl_info info;
38 | size_t dlInfoSize = sizeof(Dl_info);
39 | memset(&info, 0, dlInfoSize);
40 |
41 | // dladdr(mhp, &info);
42 | void* hookedAddr = generateHookedDladdrAddress((void*)mhp);
43 | dladdr(hookedAddr, &info);
44 |
45 | const char* curImgName = info.dli_fname;
46 | if(curImgName != NULL) {
47 | isJbLib = isJailbreakDylib(curImgName);
48 | }
49 |
50 | if (isJbLib) {
51 | // isShowLog = true;
52 | if( size && (*size > 0) ) {
53 | isShowLog = true;
54 |
55 | //#ifdef XCODE_DEBUG
56 | // Note: MUST filter out following log, otherwise Aweme will crash
57 |
58 | // // getsectiondata: mhp=0x114af0000,segname=__TEXT,sectname=__swift5_replace,size=0x16fbf7df8 ===> *size=6169788088, curImgName=/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-FrontBoard.dylib, isJbLib=True
59 | if (
60 | strstr(curImgName, "AppSyncUnified") && \
61 | (0==strcmp(segname, "__TEXT"))
62 | // ( (0==strcmp(sectname, "__swift5_replace")) || (0==strcmp(sectname, "__swift5_types")) ) \
63 | ) {
64 | isShowLog = false;
65 | }
66 |
67 | // "/Library/MobileSubstrate/DynamicLibraries/ Choicy.dylib"
68 | if (strstr(curImgName, "Choicy")) {
69 | isShowLog = false;
70 | }
71 |
72 | // /usr/lib/librocketbootstrap.dylib
73 | if (strstr(curImgName, "librocketbootstrap")) {
74 | isShowLog = false;
75 | }
76 | //#endif
77 |
78 | if (isShowLog) {
79 | iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s,size=%p ===> *size=%lu, curImgName=%{public}s, isJbLib=%s", mhp, segname, sectname, size, size ? *size : 0, curImgName, boolToStr(isJbLib));
80 | }
81 | }
82 | }
83 |
84 | if (isJbLib) {
85 | origRetIntP = NULL;
86 | if (NULL != size) {
87 | *size = 0;
88 | }
89 | }
90 |
91 | // if (NULL != size) {
92 | // if (*size > 0) {
93 | // isShowLog = true;
94 | // }
95 | // }
96 |
97 | // if (isShowLog) {
98 | // iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s,size=%p ===> *size=%lu, curImgName=%{public}s, isJbLib=%s", mhp, segname, sectname, size, size ? *size : 0, curImgName, boolToStr(isJbLib));
99 | // }
100 | }
101 |
102 | // // for debug
103 | // if (origRetIntP != NULL) {
104 | // printf("origRetIntP=%p", origRetIntP);
105 | // }
106 |
107 | return origRetIntP;
108 | }
109 | ```
110 |
111 | ## getsectbynamefromheader_64
112 |
113 | ```c
114 | const struct section_64* getsectbynamefromheader_64(const struct mach_header_64 *mhp, const char *segname, const char *sectname);
115 |
116 | %hookf(const struct section_64 *, getsectbynamefromheader_64, const struct mach_header_64 *mhp, const char *segname, const char *sectname){
117 | const struct section_64* retSection64 = %orig;
118 |
119 | bool isJbLib = false;
120 |
121 | Dl_info info;
122 | size_t dlInfoSize = sizeof(Dl_info);
123 | memset(&info, 0, dlInfoSize);
124 |
125 | // dladdr(mhp, &info);
126 | void* hookedAddr = generateHookedDladdrAddress((void*)mhp);
127 | dladdr(hookedAddr, &info);
128 |
129 | const char* curImgName = info.dli_fname;
130 | if(curImgName != NULL) {
131 | isJbLib = isJailbreakDylib(curImgName);
132 | }
133 |
134 | if (isJbLib) {
135 | iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s -> retSection64=%p -> isJbLib=%s", mhp, segname, sectname, retSection64, boolToStr(isJbLib));
136 | retSection64 = NULL;
137 | } else {
138 | iosLogDebug("mhp=%p,segname=%{public}s,sectname=%{public}s -> retSection64=%p", mhp, segname, sectname, retSection64);
139 | }
140 |
141 | return retSection64;
142 | }
143 | ```
144 |
145 | ## 其他相关
146 |
147 | ```c
148 |
149 | // https://opensource.apple.com/source/cctools/cctools-895/include/mach-o/getsect.h.auto.html
150 |
151 | /*==============================================================================
152 | Hook: getsegbyname
153 | ==============================================================================*/
154 |
155 | // Note: if add log, Aweme will crash
156 |
157 | uint8_t* getsegmentdata(const struct mach_header_64 *mhp, const char *segname, unsigned long *size);
158 |
159 | %hookf(uint8_t*, getsegmentdata, const struct mach_header_64 *mhp, const char *segname, unsigned long *size){
160 | // iosLogInfo("mhp=%p,segname=%{public}s,size=%p", mhp, segname, size);
161 | uint8_t* retSegData = %orig;
162 | // iosLogInfo("mhp=%p,segname=%{public}s,*size=%lu -> retSegCmd=%p", mhp, segname, *size, retSegData);
163 | return retSegData;
164 | }
165 |
166 | /*==============================================================================
167 | Hook: getsectdatafromFramework
168 | ==============================================================================*/
169 |
170 | const struct section_64* getsectbyname(const char *segname, const char *sectname);
171 |
172 | %hookf(const struct section_64*, getsectbyname, const char *segname, const char *sectname){
173 | const struct section_64* retSection = %orig;
174 | iosLogInfo("segname=%{public}s,sectname=%{public}s -> retSection=%p", segname, sectname, retSection);
175 | return retSection;
176 | }
177 |
178 | /*==============================================================================
179 | Hook: getsegbyname
180 | ==============================================================================*/
181 |
182 | const struct segment_command_64* getsegbyname(const char *segname);
183 |
184 | %hookf(const struct segment_command_64*, getsegbyname, const char *segname){
185 | const struct segment_command_64* retSegCmd = %orig;
186 | iosLogInfo("segname=%{public}s -> retSegCmd=%p", segname, retSegCmd);
187 | return retSegCmd;
188 | }
189 |
190 | /*==============================================================================
191 | Hook: getsectbynamefromheaderwithswap_64
192 | ==============================================================================*/
193 |
194 | const struct section* getsectbynamefromheaderwithswap_64(struct mach_header_64 *mhp, const char *segname, const char *sectname, int fSwap);
195 |
196 | %hookf(const struct section*, getsectbynamefromheaderwithswap_64, struct mach_header_64 *mhp, const char *segname, const char *sectname, int fSwap){
197 | const struct section* retSection = %orig;
198 | iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s,fSwap=%d -> retSection=%p", mhp, segname, sectname, fSwap, retSection);
199 | return retSection;
200 | }
201 |
202 | /*==============================================================================
203 | Hook: getsectdata
204 | ==============================================================================*/
205 |
206 | extern char* getsectdata(const char *segname, const char *sectname, unsigned long *size);
207 |
208 | %hookf(char*, getsectdata, const char *segname, const char *sectname, unsigned long *size){
209 | char* sectDataStr = %orig;
210 | iosLogInfo("segname=%{public}s,sectname=%{public}s,*size=%lu -> sectDataStr=%s", segname, sectname, *size, sectDataStr);
211 | return sectDataStr;
212 | }
213 |
214 | /*==============================================================================
215 | Hook: getsectdatafromheader_64
216 | ==============================================================================*/
217 |
218 | char* getsectdatafromheader_64(const struct mach_header_64 *mhp, const char *segname, const char *sectname, uint64_t *size);
219 |
220 | %hookf(char*, getsectdatafromheader_64, const struct mach_header_64 *mhp, const char *segname, const char *sectname, uint64_t *size){
221 | char* retSectDataStr = %orig;
222 | iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s,*size=%llu -> retSectData=%{public}s", mhp, segname, sectname, *size, retSectDataStr);
223 | return retSectDataStr;
224 | }
225 |
226 | /*==============================================================================
227 | Hook: getsectdatafromFramework
228 | ==============================================================================*/
229 |
230 | char* getsectdatafromFramework(const char *FrameworkName, const char *segname, const char *sectname, unsigned long *size);
231 |
232 | %hookf(char *, getsectdatafromFramework, const char *FrameworkName, const char *segname, const char *sectname, unsigned long *size){
233 | char* sectDataFrameworkStr = %orig;
234 | iosLogInfo("FrameworkName=%{public}s,segname=%{public}s,sectname=%{public}s,*size=%lu -> sectDataFrameworkStr=%s", FrameworkName, segname, sectname, *size, sectDataFrameworkStr);
235 | return sectDataFrameworkStr;
236 | }
237 |
238 | /*==============================================================================
239 | Hook: getsectbynamefromheader getsectbynamefromheader_64
240 | ==============================================================================*/
241 |
242 | // Not found: Aweme call getsectbynamefromheader
243 | const struct section* getsectbynamefromheader(const struct mach_header *mhp, const char *segname, const char *sectname);
244 |
245 | %hookf(const struct section*, getsectbynamefromheader, const struct mach_header *mhp, const char *segname, const char *sectname){
246 | const struct section* retSection = %orig;
247 | iosLogInfo("mhp=%p,segname=%{public}s,sectname=%{public}s -> retSection=%p", mhp, segname, sectname, retSection);
248 | return retSection;
249 | }
250 | ```
251 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/jb_processes.md:
--------------------------------------------------------------------------------
1 | # 越狱相关进程
2 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/kernel_level.md:
--------------------------------------------------------------------------------
1 | # 内核级反越狱
2 |
3 | TODO:
4 |
5 | * 【未解决】研究和尝试内核级反越狱检测:KernBypass
6 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/objc_runtime.md:
--------------------------------------------------------------------------------
1 | # ObjC运行时
2 |
3 | ## objc_copyImageNames
4 |
5 | ```c
6 | /*==============================================================================
7 | Hook: objc_copyImageNames
8 | ==============================================================================*/
9 |
10 | //const char * _Nonnull * objc_copyImageNames(unsigned int *outCount);
11 | const char ** objc_copyImageNames(unsigned int *outCount);
12 |
13 | %hookf(const char **, objc_copyImageNames, unsigned int *outCount){
14 | iosLogInfo("outCount=%p", outCount);
15 | const char** imageList = %orig(outCount);
16 | iosLogInfo("*outCount=%d, imageList=%p", *outCount, imageList);
17 | if (cfgHookEnable_aweme) {
18 | // TODO: add support
19 |
20 | if ((*outCount > 0) && (imageList != NULL)) {
21 | for (int i = 0; i < *outCount; i++) {
22 | const char* curImagePath = imageList[i];
23 | bool isJbPath = isJailbreakPath(curImagePath);
24 | if (isJbPath) {
25 | iosLogInfo("[%d] %s -> isJbPath=%s", i, curImagePath, boolToStr(isJbPath));
26 | }
27 | }
28 | }
29 | }
30 | return imageList;
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/sandbox.md:
--------------------------------------------------------------------------------
1 | # 沙箱完整性校验
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:fork()进程即沙箱完整性检测
6 | * 【已解决】iOS反越狱检测:fork()的hook绕过
7 |
8 | ----
9 |
10 | ```c
11 | /*==============================================================================
12 | Hook: fork()
13 | ==============================================================================*/
14 |
15 | pid_t fork(void);
16 |
17 | %hookf(int, fork, void){
18 | int retForkValue = FORK_FAILED;
19 | if (cfgHookEnable_misc) {
20 | retForkValue = FORK_FAILED;
21 | } else {
22 | retForkValue = %orig;
23 | }
24 | iosLogInfo("retForkValue=%d", retForkValue);
25 | return retForkValue;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/src/anti_jb_detect/system.md:
--------------------------------------------------------------------------------
1 | # system
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:system()
6 | *
--------------------------------------------------------------------------------
/src/anti_jb_detect/url_scheme.md:
--------------------------------------------------------------------------------
1 | # URL Scheme
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS反越狱检测:绕过cydia://的url scheme
6 | * 【未解决】iOS反越狱检测:能否打开特定开头的URL scheme
7 |
8 | ---
9 |
10 | ```c
11 | const char* CydiaPrefix = "cydia://";
12 |
13 | /*==============================================================================
14 | Hook: UIApplication canOpenURL:
15 | ==============================================================================*/
16 |
17 | /*
18 | hook url scheme, eg: cydia://
19 | */
20 |
21 | %hook UIApplication
22 |
23 | const char* CydiaPrefix = "cydia://";
24 |
25 | - (BOOL)canOpenURL:(NSURL *)url
26 | {
27 | iosLogDebug("url=%{public}@", url);
28 | bool couldOpen = false;
29 | bool isCydia = false;
30 |
31 | if (cfgHookEnable_misc) {
32 | NSString *urlNSStr = [url absoluteString];
33 | const char* urlStr = [urlNSStr UTF8String];
34 | char* urlStrLower = strToLowercase(urlStr);
35 | iosLogDebug("urlStrLower=%s", urlStrLower);
36 | isCydia = strStartsWith(urlStrLower, CydiaPrefix);
37 | free(urlStrLower);
38 | iosLogDebug("isCydia=%{public}s", boolToStr(isCydia));
39 |
40 | if(isCydia){
41 | couldOpen = false;
42 | } else{
43 | // couldOpen = %orig(url);
44 | couldOpen = %orig;
45 | }
46 | } else {
47 | couldOpen = %orig;
48 | }
49 |
50 | // for debug
51 | // if (isCydia) {
52 | iosLogInfo("url=%{public}@ -> isCydia=%{public}s -> couldOpen=%{public}s", url, boolToStr(isCydia), boolToStr(couldOpen));
53 | // }
54 | return couldOpen;
55 | }
56 |
57 | %end
58 |
59 | ```
--------------------------------------------------------------------------------
/src/appendix/README.md:
--------------------------------------------------------------------------------
1 | # 附录
2 |
3 | 下面列出相关参考资料。
4 |
--------------------------------------------------------------------------------
/src/appendix/reference.md:
--------------------------------------------------------------------------------
1 | # 参考资料
2 |
3 | *
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crifan/ios_re_jb_detection/6e11042a6013c13805ec1228cb37725fad87dc0c/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/detect_common/README.md:
--------------------------------------------------------------------------------
1 | # 通用内容
2 |
3 | 此处整理,正向的iOS越狱检测和逆向的iOS反越狱检测,都用得到的部分,通用的内容。
4 |
--------------------------------------------------------------------------------
/src/detect_common/app_load_process.md:
--------------------------------------------------------------------------------
1 | # app启动过程
2 |
3 | TODO:
4 |
5 | * 越狱检测和反越狱检测 会涉及到的:启动的阶段和过程
6 | * 【未解决】iOS的app的启动流程启动过程
7 |
--------------------------------------------------------------------------------
/src/detect_common/jb_path/README.md:
--------------------------------------------------------------------------------
1 | # 越狱路径相关
2 |
3 | TODO:
4 |
5 | * 【已解决】反越狱插件中解决内存泄漏OOM:isPathInList
6 | * 【未解决】反越狱相关路径:/Library/MobileSubstrate/DynamicLibraries/
7 | * 【已解决】越狱iOS中动态库/usr/lib/libsubstrate.dylib是哪个插件的
8 |
--------------------------------------------------------------------------------
/src/detect_common/jb_path/jb_file_list.md:
--------------------------------------------------------------------------------
1 | # 越狱文件列表
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS越狱检测:越狱文件列表
6 | * 【记录】iOS越狱文件列表更新和维护
7 | * 【已解决】iOS反越狱:优化越狱文件列表的NSString版本从JailbreakFileList.c文件中生成
8 |
9 | 最新内容已整理至独立的文件
10 |
11 | * `JailbreakPathList`
12 | * 最新版详见
13 | * https://github.com/crifan/crifanLib/blob/master/c/JailbreakPathList.c
14 | * https://github.com/crifan/crifanLib/blob/master/c/JailbreakPathList.h
15 |
16 | ---
17 |
18 | 截止目前`20221104`的最新版是:
19 |
20 | * `JailbreakPathList.c`
21 |
22 | ```c
23 | /*
24 | File: JailbreakPathList.c
25 | Function: crifan's common jailbreak file path list
26 | Author: Crifan Li
27 | Latest: https://github.com/crifan/crifanLib/blob/master/c/JailbreakPathList.c
28 | Updated: 20221104_1730
29 | */
30 |
31 | #include "JailbreakPathList.h"
32 |
33 | /*==============================================================================
34 | Jailbreak Path List
35 | ==============================================================================*/
36 |
37 | // when use isJailbreakPath_realpath, should/could disable KEEP_SOFT_LINK -> internally will convert soft link to real link, so no need soft link
38 | // when use isJailbreakPath_pureC, shold enable KEEP_SOFT_LINK -> to include other soft link jailbreak path for later compare
39 | #define KEEP_SOFT_LINK
40 |
41 | const char* jailbreakDylibFuncNameList[] = {
42 | "MSGetImageByName",
43 | "MSFindSymbol",
44 | "MSHookFunction",
45 | "MSHookMessageEx",
46 |
47 | "SubGetImageByName",
48 | "SubFindSymbol",
49 | "SubHookFunction",
50 | "SubHookMessageEx",
51 | };
52 |
53 | const char* jailbreakPathList_Dylib[] = {
54 | //char* jailbreakPathList_Dylib[] = {
55 | // common: tweak plugin libs
56 | "/Library/Frameworks/Cephei.framework/Cephei", // -> /usr/lib/CepheiUI.framework/CepheiUI ?
57 |
58 | #ifdef KEEP_SOFT_LINK
59 | "/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate", // -> /usr/lib/libsubstrate.dylib
60 | #endif
61 |
62 | "/Library/MobileSubstrate/DynamicLibraries/ Choicy.dylib",
63 | "/Library/MobileSubstrate/DynamicLibraries/0Shadow.dylib",
64 | "/Library/MobileSubstrate/DynamicLibraries/afc2dService.dylib",
65 | "/Library/MobileSubstrate/DynamicLibraries/afc2dSupport.dylib",
66 | "/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-FrontBoard.dylib",
67 | "/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-installd.dylib",
68 | "/Library/MobileSubstrate/DynamicLibraries/ChoicySB.dylib",
69 | "/Library/MobileSubstrate/DynamicLibraries/dygz.dylib",
70 | "/Library/MobileSubstrate/DynamicLibraries/LiveClock.dylib",
71 | "/Library/MobileSubstrate/DynamicLibraries/MobileSafety.dylib",
72 | "/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.dylib",
73 | "/Library/MobileSubstrate/DynamicLibraries/RocketBootstrap.dylib",
74 | "/Library/MobileSubstrate/DynamicLibraries/Veency.dylib",
75 | "/Library/MobileSubstrate/DynamicLibraries/xCon.dylib",
76 | "/Library/MobileSubstrate/DynamicLibraries/zorro.dylib",
77 | "/Library/MobileSubstrate/DynamicLibraries/zzzzHeiBaoLib.dylib",
78 |
79 | "/usr/lib/libsubstrate.dylib",
80 |
81 | // Cydia Substrate libs
82 | "/Library/MobileSubstrate/MobileSubstrate.dylib",
83 | "/usr/lib/CepheiUI.framework/CepheiUI",
84 | "/usr/lib/substrate/SubstrateInserter.dylib",
85 | "/usr/lib/substrate/SubstrateLoader.dylib",
86 | "/usr/lib/substrate/SubstrateBootstrap.dylib",
87 |
88 | // Substitute libs
89 | "/usr/lib/libsubstitute.dylib",
90 | #ifdef KEEP_SOFT_LINK
91 | "/usr/lib/libsubstitute.0.dylib", // -> /usr/lib/libsubstitute.dylib
92 | #endif
93 | "/usr/lib/substitute-inserter.dylib",
94 | "/usr/lib/substitute-loader.dylib",
95 | #ifdef KEEP_SOFT_LINK
96 | "/Library/Frameworks/CydiaSubstrate.framework/SubstrateLoader.dylib", // -> /usr/lib/substitute-loader.dylib
97 | #endif
98 |
99 | // Other libs
100 | "/private/var/lib/clutch/overdrive.dylib",
101 | "/usr/lib/frida/frida-agent.dylib",
102 |
103 | #ifdef KEEP_SOFT_LINK
104 | "/usr/lib/libapt-inst.2.0.dylib",
105 | "/usr/lib/libapt-pkg.5.0.dylib",
106 | "/usr/lib/libapt-private.0.0.dylib",
107 | #endif
108 | "/usr/lib/libapt-inst.2.0.0.dylib",
109 | "/usr/lib/libapt-pkg.5.0.2.dylib",
110 | "/usr/lib/libapt-private.0.0.0.dylib",
111 |
112 | "/usr/lib/libcycript.dylib",
113 | "/usr/lib/librocketbootstrap.dylib",
114 | "/usr/lib/tweakloader.dylib",
115 | };
116 |
117 | const char* jailbreakPathList_Other[] = {
118 | //char* jailbreakPathList_Other[] = {
119 | "/Applications/Activator.app",
120 | "/Applications/ALS.app",
121 | "/Applications/blackra1n.app",
122 | "/Applications/Cydia.app",
123 | "/Applications/FakeCarrier.app",
124 | "/Applications/Filza.app",
125 | "/Applications/FlyJB.app",
126 | "/Applications/Icy.app",
127 | "/Applications/iFile.app",
128 | "/Applications/Iny.app",
129 | "/Applications/IntelliScreen.app",
130 | "/Applications/MTerminal.app",
131 | "/Applications/MxTube.app",
132 | "/Applications/RockApp.app",
133 | "/Applications/SBSettings.app",
134 | "/Applications/SubstituteSettings.app"
135 | "/Applications/SubstituteSettings.app/Info.plist",
136 | "/Applications/SubstituteSettings.app/SubstituteSettings",
137 | "/Applications/Snoop-itConfig.app",
138 | "/Applications/WinterBoard.app",
139 |
140 | #ifdef KEEP_SOFT_LINK
141 | "/bin/sh",
142 | #endif
143 | "/bin/bash",
144 |
145 | #ifdef KEEP_SOFT_LINK
146 | // Note: etc -> private/etc/ !!!
147 | "/etc/alternatives/sh",
148 | "/etc/apt",
149 | "/etc/apt/preferences.d/checkra1n",
150 | "/etc/apt/preferences.d/cydia",
151 | "/etc/clutch.conf",
152 | "/etc/clutch_cracked.plist",
153 | "/etc/dpkg/origins/debian",
154 | "/etc/rc.d/substitute-launcher",
155 | "/etc/ssh/sshd_config",
156 | #endif
157 |
158 | "/Library/Activator",
159 | "/Library/Flipswitch",
160 | "/Library/dpkg/",
161 |
162 | "/Library/Frameworks/CydiaSubstrate.framework/",
163 | "/Library/Frameworks/CydiaSubstrate.framework/Headers/"
164 | "/Library/Frameworks/CydiaSubstrate.framework/Headers/CydiaSubstrate.h",
165 | "/Library/Frameworks/CydiaSubstrate.framework/Info.plist",
166 |
167 | "/Library/LaunchDaemons/ai.akemi.asu_inject.plist",
168 | "/Library/LaunchDaemons/com.openssh.sshd.plist",
169 | "/Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist",
170 | "/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
171 | "/Library/LaunchDaemons/com.tigisoftware.filza.helper.plist",
172 | "/Library/LaunchDaemons/dhpdaemon.plist",
173 | "/Library/LaunchDaemons/re.frida.server.plist",
174 |
175 | // for debug: try avoid 抖音(Aweme) crash
176 | "/Library/MobileSubstrate/",
177 | "/Library/MobileSubstrate/DynamicLibraries/",
178 |
179 | "/Library/MobileSubstrate/DynamicLibraries/ Choicy.plist",
180 | "/Library/MobileSubstrate/DynamicLibraries/afc2dService.plist",
181 | "/Library/MobileSubstrate/DynamicLibraries/afc2dSupport.plist",
182 | "/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-FrontBoard.plist",
183 | "/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-installd.plist",
184 | "/Library/MobileSubstrate/DynamicLibraries/ChoicySB.plist",
185 | "/Library/MobileSubstrate/DynamicLibraries/dygz.plist",
186 | "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
187 | "/Library/MobileSubstrate/DynamicLibraries/MobileSafety.plist",
188 | "/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.plist",
189 | "/Library/MobileSubstrate/DynamicLibraries/RocketBootstrap.plist",
190 | "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
191 | "/Library/MobileSubstrate/DynamicLibraries/xCon.plist",
192 | "/Library/MobileSubstrate/DynamicLibraries/zorro.plist",
193 | "/Library/MobileSubstrate/DynamicLibraries/zzzzHeiBaoLib.plist",
194 |
195 | "/Library/PreferenceBundles/SubstitutePrefs.bundle/",
196 | "/Library/PreferenceBundles/SubstitutePrefs.bundle/Info.plist",
197 | "/Library/PreferenceBundles/SubstitutePrefs.bundle/SubstitutePrefs",
198 |
199 | "/Library/PreferenceLoader/Preferences/SubstituteSettings.plist",
200 |
201 | "/private/etc/alternatives/sh",
202 | "/private/etc/apt",
203 | "/private/etc/apt/preferences.d/checkra1n",
204 | "/private/etc/apt/preferences.d/cydia",
205 | "/private/etc/clutch.conf",
206 | "/private/etc/clutch_cracked.plist",
207 | "/private/etc/dpkg/origins/debian",
208 | "/private/etc/rc.d/substitute-launcher",
209 | "/private/etc/ssh/sshd_config",
210 |
211 | "/private/var/cache/apt/",
212 | "/private/var/cache/clutch.plist",
213 | "/private/var/cache/clutch_cracked.plist",
214 | "/private/var/db/stash",
215 | "/private/var/evasi0n",
216 | "/private/var/lib/apt/",
217 | "/private/var/lib/cydia/",
218 | "/private/var/lib/dpkg/",
219 |
220 | "/private/var/mobile/Applications/", //TODO: non-jailbreak can normally open?
221 | "/private/var/mobile/Library/Filza/",
222 | "/private/var/mobile/Library/Filza/pasteboard.plist",
223 | "/private/var/mobile/Library/Cydia/",
224 | "/private/var/mobile/Library/Preferences/com.ex.substitute.plist",
225 | "/private/var/mobile/Library/SBSettingsThemes/",
226 | "/private/var/MobileSoftwareUpdate/mnt1/System/Library/PrivateFrameworks/DictionaryServices.framework/SubstituteCharacters.plist",
227 | "/private/var/root/Documents/Cracked/",
228 | "/private/var/stash",
229 | "/private/var/tmp/cydia.log",
230 |
231 | "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
232 | "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
233 | "/System/Library/PrivateFrameworks/DictionaryServices.framework/SubstituteCharacters.plist",
234 |
235 | #ifdef KEEP_SOFT_LINK
236 | // Note: /User -> /var/mobile/
237 | "/User/Applications/", //TODO: non-jailbreak can normally open?
238 | "/User/Library/Filza/",
239 | "/User/Library/Filza/pasteboard.plist",
240 | "/User/Library/Cydia/",
241 | #endif
242 |
243 | "/usr/bin/asu_inject",
244 | "/usr/bin/cycc",
245 | "/usr/bin/cycript",
246 | #ifdef KEEP_SOFT_LINK
247 | "/usr/bin/cynject", // -> /usr/bin/sinject
248 | "/usr/bin/Filza", // -> /usr/libexec/filza/Filza
249 | #endif
250 | "/usr/bin/scp",
251 | "/usr/bin/sftp",
252 | "/usr/bin/ssh",
253 | "/usr/bin/ssh-add",
254 | "/usr/bin/ssh-agent",
255 | "/usr/bin/ssh-keygen",
256 | "/usr/bin/ssh-keyscan",
257 | "/usr/bin/sshd",
258 | "/usr/bin/sinject",
259 |
260 | "/usr/include/substrate.h",
261 |
262 | "/usr/lib/cycript0.9/",
263 | "/usr/lib/cycript0.9/com/",
264 | "/usr/lib/cycript0.9/com/saurik/"
265 | "/usr/lib/cycript0.9/com/saurik/substrate/",
266 | "/usr/lib/cycript0.9/com/saurik/substrate/MS.cy",
267 | "/usr/libexec/filza/Filza",
268 | "/usr/libexec/substituted",
269 | "/usr/libexec/sinject-vpa",
270 |
271 | "/usr/lib/substrate/",
272 |
273 | "/usr/lib/TweakInject",
274 |
275 | "/usr/libexec/cydia/",
276 | "/usr/libexec/sftp-server",
277 | "/usr/libexec/substrate",
278 | "/usr/libexec/substrated",
279 | "/usr/libexec/ssh-keysign",
280 |
281 | "/usr/local/bin/cycript",
282 |
283 | "/usr/sbin/frida-server",
284 | "/usr/sbin/sshd",
285 |
286 | #ifdef KEEP_SOFT_LINK
287 | // /var -> /private/var/
288 |
289 | // TODO: add more /var/xxx path
290 | "/var/cache/apt/",
291 | "/var/cache/clutch.plist",
292 | "/var/cache/clutch_cracked.plist",
293 | "/var/db/stash",
294 | "/var/evasi0n",
295 | "/var/lib/apt/",
296 | "/var/lib/cydia/",
297 | "/var/lib/dpkg/",
298 |
299 | "/var/mobile/Applications/", //TODO: non-jailbreak can normally open?
300 | "/var/mobile/Library/Filza/",
301 | "/var/mobile/Library/Filza/pasteboard.plist",
302 | "/var/mobile/Library/Cydia/",
303 | "/var/mobile/Library/Preferences/com.ex.substitute.plist",
304 | "/var/mobile/Library/SBSettingsThemes/",
305 | "/var/MobileSoftwareUpdate/mnt1/System/Library/PrivateFrameworks/DictionaryServices.framework/SubstituteCharacters.plist",
306 | "/var/root/Documents/Cracked/",
307 | "/var/stash",
308 | "/var/tmp/cydia.log",
309 |
310 | #endif
311 | };
312 |
313 | const int StrSize = sizeof(const char *);
314 | const int jailbreakPathListLen_Dylib = sizeof(jailbreakPathList_Dylib) / StrSize;
315 | const int jailbreakPathListLen_Other = sizeof(jailbreakPathList_Other) / StrSize;
316 |
317 | //int jailbreakPathListLen = sizeof(jailbreakPathList) / StrSize;
318 | const int jailbreakPathListLen = jailbreakPathListLen_Dylib + jailbreakPathListLen_Other;
319 |
320 | const int jailbreakDylibFuncNameListLen = sizeof(jailbreakDylibFuncNameList) / StrSize;
321 |
322 | const char** getJailbreakPathList(void){
323 | int strPtrMaxIdx = jailbreakPathListLen; // 133
324 | int strPtrNum = strPtrMaxIdx + 1; // 134
325 | int singleSize = sizeof(const char *); // 8
326 | size_t mallocSize = singleSize * strPtrNum; // 1072
327 | const char** jailbreakPathStrPtrList = malloc(mallocSize);
328 | // jailbreakPathStrPtrList=0x000000011e840c00
329 |
330 | // set each string
331 | for(int curStrIdx = 0; curStrIdx < jailbreakPathListLen_Dylib; curStrIdx++){
332 | const char* curStrPtr = jailbreakPathList_Dylib[curStrIdx];
333 | jailbreakPathStrPtrList[curStrIdx] = curStrPtr;
334 | }
335 |
336 | for(int curStrIdx = 0; curStrIdx < jailbreakPathListLen_Other; curStrIdx++){
337 | int totalIndex = jailbreakPathListLen_Dylib + curStrIdx;
338 | const char* curStrPtr = jailbreakPathList_Other[curStrIdx];
339 | jailbreakPathStrPtrList[totalIndex] = curStrPtr;
340 | }
341 | // set end
342 | jailbreakPathStrPtrList[strPtrMaxIdx] = NULL;
343 |
344 | return jailbreakPathStrPtrList;
345 | }
346 |
347 |
348 | /*==============================================================================
349 | Jailbreak Function
350 | ==============================================================================*/
351 |
352 | bool isPathInList(
353 | const char* inputPath,
354 | // char* inputPath,
355 | const char** pathList,
356 | // char** pathList,
357 | int pathListLen,
358 | bool isConvertToPurePath, // is convert to pure path or not
359 | bool isCmpSubFolder // is compare sub foder or not
360 | ){
361 | bool isInside = false;
362 | if (!inputPath) {
363 | return isInside;
364 | }
365 |
366 | char* inputOrigOrPurePath = NULL;
367 | if (isConvertToPurePath){
368 | inputOrigOrPurePath = toPurePath(inputPath);
369 | }else{
370 | inputOrigOrPurePath = strdup(inputPath);
371 | }
372 |
373 | char* matchedPath = NULL;
374 |
375 | char* curPathNoEndSlash = NULL;
376 | char * curPathWithEndSlash = NULL;
377 | for (int i=0; i < pathListLen; i++) {
378 | const char* curPath = pathList[i];
379 | // char* curPath = pathList[i];
380 | if (isPathEaqual(inputOrigOrPurePath, curPath)){
381 | isInside = true;
382 | matchedPath = (char *)curPath;
383 | break;
384 | }
385 |
386 | if (isCmpSubFolder){
387 | // check sub folder
388 | // "/Applications/Cydia.app/Info.plist" belong to "/Applications/Cydia.app/", should bypass
389 | // but avoid: '/usr/bin/ssh-keyscan' starts with '/usr/bin/ssh'
390 | curPathNoEndSlash = removeEndSlash(curPath);
391 | curPathWithEndSlash = NULL;
392 | asprintf(&curPathWithEndSlash, "%s/", curPathNoEndSlash);
393 |
394 | if (strStartsWith(inputOrigOrPurePath, curPathWithEndSlash)){
395 | isInside = true;
396 | matchedPath = (char *)curPath;
397 | break;
398 | }
399 | }
400 |
401 | if(NULL != curPathNoEndSlash){
402 | free(curPathNoEndSlash);
403 | curPathNoEndSlash = NULL;
404 | }
405 |
406 | if(NULL != curPathWithEndSlash){
407 | free(curPathWithEndSlash);
408 | curPathWithEndSlash = NULL;
409 | }
410 | }
411 |
412 | if (NULL != inputOrigOrPurePath){
413 | free(inputOrigOrPurePath);
414 | }
415 |
416 | return isInside;
417 | }
418 |
419 | bool isPathInJailbreakPathList(const char *curPath){
420 | bool isInJbPathList = false;
421 |
422 | const char** jailbreakPathList = getJailbreakPathList();
423 | if(jailbreakPathList) {
424 | isInJbPathList = isPathInList(curPath, jailbreakPathList, jailbreakPathListLen, true, true);
425 | // final: free char** self
426 | free(jailbreakPathList);
427 | }
428 |
429 | return isInJbPathList;
430 | }
431 |
432 | bool isJailbreakPath_pureC(const char *curPath){
433 | bool isJbPath = false;
434 | if (!curPath) {
435 | return isJbPath;
436 | }
437 |
438 | isJbPath = isPathInJailbreakPathList(curPath);
439 |
440 | return isJbPath;
441 | }
442 |
443 | bool isJailbreakPath_realpath(const char *curPath){
444 | bool isJbPath = false;
445 | if (!curPath) {
446 | return isJbPath;
447 | }
448 |
449 | char gotRealPath[PATH_MAX];
450 | bool isParseRealPathOk = parseRealPath(curPath, gotRealPath);
451 | // os_log(OS_LOG_DEFAULT, "isJailbreakPath: isParseRealPathOk=%{bool}d", isParseRealPathOk);
452 |
453 | char curRealPath[PATH_MAX];
454 | if (isParseRealPathOk) {
455 | strcpy(curRealPath, gotRealPath);
456 | } else {
457 | strcpy(curRealPath, curPath);
458 | }
459 | // os_log(OS_LOG_DEFAULT, "isJailbreakPath: curRealPath=%{public}s", curRealPath);
460 | isJbPath = isPathInJailbreakPathList(curRealPath);
461 |
462 | return isJbPath;
463 | }
464 |
465 | // "/Applications/Cydia.app" -> true
466 | bool isJailbreakPath(const char *pathname){
467 | if (!pathname) {
468 | return false;
469 | } else {
470 | // return isJailbreakPath_realpath(pathname);
471 | return isJailbreakPath_pureC(pathname);
472 | }
473 | }
474 |
475 | // "/Library/MobileSubstrate/MobileSubstrate.dylib" -> true
476 | bool isJailbreakDylib(const char *pathname){
477 | bool isJbDylib = false;
478 |
479 | if (NULL != pathname){
480 | isJbDylib = isPathInList(pathname, jailbreakPathList_Dylib, jailbreakPathListLen_Dylib, true, false);
481 | }
482 |
483 | return isJbDylib;
484 | }
485 |
486 | // "MSHookFunction" -> true
487 | bool isJailbreakDylibFunctionName(const char *libFuncName){
488 | bool isJbDylibFuncName = false;
489 |
490 | if (NULL != libFuncName){
491 | isJbDylibFuncName = isPathInList(libFuncName, jailbreakDylibFuncNameList, jailbreakDylibFuncNameListLen, false, false);
492 | }
493 |
494 | return isJbDylibFuncName;
495 | }
496 | ```
497 |
498 | * `JailbreakPathList.h`
499 |
500 | ```c
501 | /*
502 | File: JailbreakPathList.h
503 | Function: crifan's common jailbreak file path list header file
504 | Author: Crifan Li
505 | Latest: https://github.com/crifan/crifanLib/blob/master/c/JailbreakPathList.h
506 | Updated: 20211230_1049
507 | */
508 |
509 | // This will not work with all C++ compilers, but it works with clang and gcc
510 | #ifdef __cplusplus
511 | extern "C" {
512 | #endif
513 |
514 | #ifndef JailbreakPathList_h
515 | #define JailbreakPathList_h
516 |
517 | #include
518 |
519 | #include "CrifanLib.h"
520 |
521 | extern const int jailbreakPathListLen;
522 | extern const char* jailbreakPathList_Dylib[];
523 | extern const char* jailbreakPathList_Other[];
524 | //extern char* jailbreakPathList_Dylib[];
525 | //extern char* jailbreakPathList_Other[];
526 | extern const int jailbreakPathListLen_Dylib;
527 | extern const int jailbreakPathListLen_Other;
528 |
529 | //extern const char* jailbreakPathList[];
530 | const char** getJailbreakPathList(void);
531 | //char** getJailbreakPathList(void);
532 |
533 | bool isPathInJailbreakPathList(const char *curPath);
534 | bool isJailbreakPath_pureC(const char *curPath);
535 | bool isJailbreakPath_realpath(const char *pathname);
536 | bool isJailbreakPath(const char *pathname);
537 | bool isJailbreakDylib(const char *pathname);
538 | bool isJailbreakDylibFunctionName(const char *libFuncName);
539 |
540 | bool isPathInList(
541 | const char* inputPath,
542 | // char* inputPath,
543 | const char** pathList,
544 | // char** pathList,
545 | int pathListLen,
546 | bool isConvertToPurePath, // is convert to pure path or not
547 | bool isCmpSubFolder // is compare sub foder or not
548 | );
549 |
550 | #endif /* JailbreakPathList_h */
551 |
552 | #ifdef __cplusplus
553 | }
554 | #endif
555 | ```
556 |
--------------------------------------------------------------------------------
/src/ios_jb_detecton_overview/README.md:
--------------------------------------------------------------------------------
1 | # iOS越狱检测概览
2 |
3 | TODO:
4 |
5 | * 【未解决】越狱iOS如何实现反越狱检测
6 | * 【整理】越狱检测和反越狱检测相关手段及进度
7 |
8 | ---
9 |
10 | iOS逆向的所涉及的内容和领域,其中就有:
11 |
12 | * `防` 的 `iOS越狱检测`:检测iOS设备(iPhone等)是否越狱
13 | * 如果发现越狱,则轻则提示和警告,重则禁用部分功能,甚至完全不可用
14 | * `攻` 的 `iOS反越狱检测`
15 | * 想办法绕过越狱检测,从而对应目的,比如实现技术上的攻防研究、甚至是刷机改机等手段去赚黑灰产的钱。
16 |
--------------------------------------------------------------------------------
/src/jb_detection/README.md:
--------------------------------------------------------------------------------
1 | # iOS越狱检测
2 |
3 | TODO:
4 |
5 | * 【已解决】Mac中如何判断iPhone手机中iOS系统已越狱
6 | * 【已解决】iOS中被测app实现越狱检测
7 |
8 | ---
9 |
10 | 现已公开发布源码:
11 |
12 | [crifan/iOSJailbreakDetection: iOS的ObjC的app,实现iOS越狱检测](https://github.com/crifan/iOSJailbreakDetection)
13 |
14 | 本教程后续章节中贴出的**iOS越狱检测**的代码片段,均摘录自其中。
15 |
--------------------------------------------------------------------------------
/src/jb_detection/app_self.md:
--------------------------------------------------------------------------------
1 | # app本身
2 |
3 | ## 重签名
4 |
5 | TODO:
6 |
7 | * 【已解决】iOS防护:签名校验重签名检测
8 |
9 | ```c
10 |
11 | - (IBAction)reCodeSignBtnClicked:(UIButton *)sender {
12 | _curBtnLbl.text = sender.titleLabel.text;
13 | NSLog(@"re-CodeSign check");
14 | NSString* resultStr = @"TODO";
15 |
16 | // NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]; // embeddedPath __NSCFString * @"/private/var/containers/Bundle/Application/4366136E-242E-4C5D-9CC8-CF100A0B6FB2/ShowSysInfo.app/embedded.mobileprovision" 0x0000000282c11830
17 | // if (![[NSFileManager defaultManager] fileExistsAtPath:embeddedPath]) {
18 | // return;
19 | // }
20 |
21 | // // 读取application-identifier 注意描述文件的编码要使用:NSASCIIStringEncoding
22 | // NSStringEncoding fileEncoding = NSASCIIStringEncoding;
23 | //// NSStringEncoding fileEncoding = NSUTF8StringEncoding;
24 | // NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:fileEncoding error:nil];
25 | // NSArray *embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
26 | // for (int i = 0; i < embeddedProvisioningLines.count; i++) {
27 | // if ([embeddedProvisioningLines[i] rangeOfString:@"application-identifier"].location != NSNotFound) {
28 | // NSString *identifierString = embeddedProvisioningLines[i + 1]; // 类似:L2ZY2L7GYS.com.xx.xxx
29 | // NSRange fromRange = [identifierString rangeOfString:@""];
30 | // NSInteger fromPosition = fromRange.location + fromRange.length;
31 | // NSInteger toPosition = [identifierString rangeOfString:@""].location;
32 | // NSRange range;
33 | // range.location = fromPosition;
34 | // range.length = toPosition - fromPosition;
35 | // NSString *fullIdentifier = [identifierString substringWithRange:range];
36 | // NSScanner *scanner = [NSScanner scannerWithString:fullIdentifier];
37 | // NSString *teamIdString;
38 | // [scanner scanUpToString:@"." intoString:&teamIdString];
39 | // NSRange teamIdRange = [fullIdentifier rangeOfString:teamIdString];
40 | // NSString *appIdentifier = [fullIdentifier substringFromIndex:teamIdRange.length + 1];
41 | // // 对比签名teamID或者identifier信息
42 | // // if (![appIdentifier isEqualToString:identifier] || ![teamId isEqualToString:teamIdString]) {
43 | //
44 | // if (![appIdentifier isEqualToString: curAppId]) {
45 | // // exit(0)
46 | // asm(
47 | // "mov X0,#0\n"
48 | // "mov w16,#1\n"
49 | // "svc #0x80"
50 | // );
51 | // }
52 | // break;
53 | // }
54 | // }
55 |
56 | BOOL isExistCodesign = [CrifanLibiOS isCodeSignExist];
57 |
58 | if (isExistCodesign) {
59 | // NSString* curAppId = @"com.crifan.ShowSystemInfo";
60 | NSString* selfAppId = @"3WRHBBSBW4.*";
61 | NSString* gotAddId = [CrifanLibiOS getAppId];
62 | // BOOL isSelfId = [CrifanLibiOS isSelfAppId: curAppId];
63 | // BOOL isSelfId = FALSE;
64 | if ([gotAddId isEqualToString: selfAppId]) {
65 | // isSelfId = TRUE;
66 | resultStr = [NSString stringWithFormat: @"embedded.mobileprovision中是自己app的ID:%@ -> 合法app", selfAppId];
67 | } else {
68 | // isSelfId = FALSE;
69 | resultStr = [NSString stringWithFormat: @"embedded.mobileprovision中的app的ID是%@ != 自己的AppId %@ -> 非法app", gotAddId, selfAppId];
70 | }
71 | } else {
72 | resultStr = @"不存在embedded.mobileprovision";
73 | }
74 |
75 | NSLog(@"resultStr=%@", resultStr);
76 | _detectResultTv.text = resultStr;
77 | }
78 | ```
79 |
80 | ## __RESTRICT
81 |
82 | TODO:
83 |
84 | * 【未解决】iOS越狱检测手段:__RESTRICT
85 |
--------------------------------------------------------------------------------
/src/jb_detection/debuggable.md:
--------------------------------------------------------------------------------
1 | # 是否可调试
2 |
3 | ```c
4 | - (IBAction)isDebugableBtnClicked:(UIButton *)sender {
5 | _curBtnLbl.text = sender.titleLabel.text;
6 | NSLog(@"is debugable check");
7 |
8 | // /* tmp to debug getuid */
9 | // uid_t curUid = getuid();
10 | // NSLog(@"curUid=%d", curUid);
11 |
12 | int SYSCTL_OK = 0;
13 | NSString* resultStr = @"";
14 | BOOL isDebugable = FALSE;
15 |
16 | // Initialize mib, which tells sysctl the info we want, in this case
17 | // we're looking for information about a specific process ID.
18 | int name[4]; //里面放字节码。查询的信息
19 | name[0] = CTL_KERN; //内核查询
20 | name[1] = KERN_PROC; //查询进程
21 | name[2] = KERN_PROC_PID; //传递的参数是进程的ID
22 | // name[3] = getpid(); //获取当前进程ID
23 |
24 | int pidToCheck = -1;
25 |
26 | int currentPID = getpid();
27 | NSLog(@"currentPID=%d", currentPID);
28 | pidToCheck = currentPID;
29 |
30 | // //for debug
31 | // int parentPID = getppid();
32 | // NSLog(@"parentPID=%d", parentPID);
33 | // pidToCheck = parentPID;
34 |
35 | NSLog(@"pidToCheck=%d", pidToCheck);
36 | name[3] = pidToCheck;
37 |
38 | // [3] int 13679
39 |
40 | // size_t infoSize = sizeof(kernelInfoProc); // 结构体大小
41 | size_t infoSize = sizeof(struct kinfo_proc);
42 | struct kinfo_proc kernelInfoProc; //接受查询结果的结构体
43 | // Initialize the flags so that, if sysctl fails for some bizarre reason, we get a predictable result.
44 | // kernelInfoProc.kp_proc.p_flag = 0;
45 | memset(&kernelInfoProc, 0, infoSize);
46 |
47 | // infoSize size_t 648
48 | // int sysctlRet = sysctl(name, 4, &kernelInfoProc, &infoSize, 0, 0);
49 | int sysctlRet = sysctl(name, 4, &kernelInfoProc, &infoSize, NULL, 0);
50 | NSLog(@"sysctlRet=%d", sysctlRet);
51 | if(sysctlRet == SYSCTL_OK){
52 | int pFlag = kernelInfoProc.kp_proc.p_flag;
53 | NSLog(@"pFlag=0x%x", pFlag);
54 | isDebugable = ((pFlag & P_TRACED) != 0);
55 | NSLog(@"isDebugable=%s", boolToStr(isDebugable));
56 | if (isDebugable) {
57 | resultStr = @"可被调试 -> 越狱手机";
58 | } else {
59 | resultStr = @"不可被调试 -> 非越狱手机";
60 | }
61 | } else {
62 | NSLog(@"errno=%d\n", errno);
63 | char *errMsg = strerror(errno);
64 | NSLog(@"errMsg=%s\n", errMsg);
65 | resultStr = [NSString stringWithFormat:@"检测失败: %s", errMsg];
66 | }
67 | NSLog(@"resultStr=%@\n", resultStr);
68 | _detectResultTv.text = resultStr;
69 | }
70 | ```
71 |
72 |
--------------------------------------------------------------------------------
/src/jb_detection/dyld/README.md:
--------------------------------------------------------------------------------
1 | # dyld动态库
2 |
3 | TODO:
4 |
5 | * 抖音
6 | * 【未解决】iOS中如何检测抖音app当前进程加载或注入了哪些dylib动态库
7 |
8 | ---
9 |
--------------------------------------------------------------------------------
/src/jb_detection/dyld/_dyld_series.md:
--------------------------------------------------------------------------------
1 | # _dyld系列
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS越狱检测:辅助用_dyld_get_image_header解析动态库文件信息
6 | * 【已解决】iOS越狱检测:优化dyld的动态库文件和其他越狱文件列表
7 | * 【已解决】iOS越狱检测:用_dyld_image_count() 和 _dyld_get_image_name()检测越狱相关动态库
8 | * 【已解决】iOS正向越狱检测:_dyld_register_func_for_add_image及相关
9 |
10 | ---
11 |
12 | * `_dyld`系列 = `_dyld`开头的一系列函数
13 | * 最基础也最常用的:`_dyld_image_count` + `_dyld_get_image_name`
14 | * 很少用到的:`_dyld_get_image_vmaddr_slide`
15 | * 更高级的:`_dyld_register_func_for_add_image` 和 `_dyld_register_func_for_remove_image`
16 |
17 | ## `_dyld_image_count` + `_dyld_get_image_name`
18 |
19 | ```c
20 | - (void) dbgPrintLibInfo: (int)curImgIdx{
21 | // debug slide
22 | intptr_t curSlide = _dyld_get_image_vmaddr_slide(curImgIdx);
23 | NSLog(@"[%d] curSlide=0x%lx", curImgIdx, curSlide);
24 |
25 | // debug header info
26 | const struct mach_header* libHeader = _dyld_get_image_header(curImgIdx);
27 | if (NULL != libHeader){
28 | int magic = libHeader->magic;
29 | int cputype = libHeader->cputype;
30 | int cpusubtype = libHeader->cpusubtype;
31 | int filetype = libHeader->filetype;
32 | int ncmds = libHeader->ncmds;
33 | int sizeofcmds = libHeader->sizeofcmds;
34 | int flags = libHeader->flags;
35 |
36 | NSLog(@"[%d] magic=0x%x,cputype=0x%x,cpusubtype=0x%x,filetype=%d,ncmds=%d,sizeofcmds=%d,flags=0x%x",
37 | curImgIdx,
38 | magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags);
39 | // 2021-12-17 09:37:46.814810+0800 ShowSysInfo[11192:1067220] [0] magic=0xfeedfacf,cputype=0x100000c,cpusubtype=0x0,filetype=2,ncmds=23,sizeofcmds=3072,flags=0x200085
40 | } else {
41 | NSLog(@"[%d] mach_header is NULL", curImgIdx);
42 | }
43 | }
44 |
45 | - (IBAction)dyldImgCntNameBtnClicked:(UIButton *)sender {
46 | _curBtnLbl.text = sender.titleLabel.text;
47 | NSLog(@"_dyld_image_count and _dyld_get_image_name check");
48 |
49 | // //for debug
50 | // int testImgIdx = 282; // hooked:279 ~ real: 284
51 | // [self dbgPrintLibInfo: testImgIdx];
52 |
53 | uint32_t imageCount = _dyld_image_count();
54 | NSLog(@"dyld: imageCount=%d", imageCount);
55 |
56 | NSMutableArray *loadedDylibList = [NSMutableArray array];
57 |
58 | NSMutableArray *jbDylibList = [NSMutableArray array];
59 |
60 | for (uint32_t i = 0 ; i < imageCount; ++i) {
61 | const char* curImageName = _dyld_get_image_name(i);
62 |
63 | // for debug
64 | // bool isNeedDebug = (0 == i) || (1 == i) || (2 == i) || (275 == i);
65 | bool isNeedDebug = (277 == i) || (278 == i);
66 |
67 | if (NULL != curImageName){
68 | NSString *curImageNameStr = [[NSString alloc]initWithUTF8String: curImageName];
69 | NSLog(@"[%d] %@", i, curImageNameStr);
70 |
71 | [loadedDylibList addObject:curImageNameStr];
72 |
73 | // if([JbPathList.jbDylibList containsObject:curImageNameStr]){
74 | // if([JbPathList isJbDylib: curImageNameStr]){
75 |
76 | if(isJailbreakDylib(curImageName)){
77 | [jbDylibList addObject: curImageNameStr];
78 |
79 | // for debug
80 | isNeedDebug = true;
81 | }
82 | } else {
83 | NSLog(@"[%d] %s", i, curImageName);
84 | }
85 |
86 | // for debug
87 | if (isNeedDebug){
88 | [self dbgPrintLibInfo: i];
89 | }
90 | }
91 |
92 | // NSString *loadedDylibListStr = [CrifanLibiOS nsStrListToStr:loadedDylibList];
93 | NSString *loadedDylibListStr = [CrifanLibiOS nsStrListToStr:loadedDylibList isSortList:TRUE isAddIndexPrefix:TRUE];
94 | NSLog(@"dyld: loadedDylibListStr=%@", loadedDylibListStr);
95 |
96 | NSString *jbLibListStr = [CrifanLibiOS nsStrListToStr:jbDylibList isSortList:TRUE isAddIndexPrefix:TRUE];
97 | NSLog(@"dyld: jbDylibList=%@", jbLibListStr);
98 |
99 | NSString* dyldLibResultStr = @"";
100 | if (jbDylibList.count > 0){
101 | dyldLibResultStr = [NSString stringWithFormat: @"检测出越狱动态库 -> 越狱手机; 越狱动态库列表:\n%@", jbLibListStr];
102 | } else{
103 | dyldLibResultStr = @"未检测出越狱动态库 -> 非越狱手机";
104 | }
105 | NSLog(@"dyld: dyldLibResultStr=%@", dyldLibResultStr);
106 |
107 | _detectResultTv.text = dyldLibResultStr;
108 | }
109 | ```
110 |
111 | ## `_dyld_register_func_for_add_image`
112 |
113 | ```c
114 | static NSString* checkImageResult = @"未发现越狱库 -> 非越狱手机";
115 | NSMutableArray *checkImageFoundJbLibList = NULL;
116 |
117 | + (void)load {
118 | static dispatch_once_t onceToken;
119 | dispatch_once(&onceToken, ^{
120 | checkImageFoundJbLibList = [NSMutableArray array];
121 | _dyld_register_func_for_add_image(_check_image);
122 | });
123 | }
124 |
125 | static void _check_image(const struct mach_header *header, intptr_t slide) {
126 | Dl_info info;
127 | size_t dlInfoSize = sizeof(Dl_info);
128 | memset(&info, 0, dlInfoSize);
129 |
130 | dladdr(header, &info);
131 | const char* curImgName = info.dli_fname;
132 | if(curImgName != NULL) {
133 | if (isJailbreakDylib(curImgName)) {
134 | NSString *curImgNameNs = [NSString stringWithUTF8String: curImgName];
135 | [checkImageFoundJbLibList addObject: curImgNameNs];
136 | NSString *jbLibListStr = [CrifanLibiOS nsStrListToStr:checkImageFoundJbLibList isSortList:TRUE isAddIndexPrefix:TRUE];
137 | checkImageResult = [NSString stringWithFormat: @"发现越狱动态库 -> 越狱手机\n%@", jbLibListStr];
138 | NSLog(@"%@", checkImageResult);
139 | // "Found Jailbreak dylib: /usr/lib/substitute-inserter.dylib -> 越狱手机"
140 | }
141 | }
142 | return;
143 | }
144 |
145 | - (IBAction)dyldRegImgBtnClicked:(UIButton *)sender {
146 | _curBtnLbl.text = sender.titleLabel.text;
147 | NSLog(@"dlyd register image add/remove check");
148 | NSString* resultStr = @"TODO";
149 |
150 | resultStr = checkImageResult;
151 |
152 | NSLog(@"resultStr=%@", resultStr);
153 | _detectResultTv.text = resultStr;
154 | }
155 | ```
156 |
--------------------------------------------------------------------------------
/src/jb_detection/dyld/dylib.md:
--------------------------------------------------------------------------------
1 | # dylib
2 |
3 | ## `dladdr`
4 |
5 | TODO:
6 |
7 | * 【部分解决】iOS越狱检测:用dladdr解析函数所属动态库
8 | * 【无需解决】已越狱iOS且已反越狱但dladdr仍是系统库libsystem_kernel.dylib
9 | * 【已解决】iOS反越狱检测:dyld的dladdr
10 |
11 | ---
12 |
13 | ```c
14 | - (IBAction)dladdrBtnClicked:(UIButton *)sender {
15 | _curBtnLbl.text = sender.titleLabel.text;
16 | NSLog(@"dladdr check");
17 |
18 | const int DLADDR_FAILED = 0;
19 |
20 | const char* curSystemLib = NULL;
21 | char* curTestFuncName = NULL;
22 | Dl_info dylib_info;
23 |
24 | const char* SystemLib_kernel = "/usr/lib/system/libsystem_kernel.dylib";
25 | curSystemLib = SystemLib_kernel;
26 | curTestFuncName = "stat";
27 | int (*func_stat)(const char *, struct stat *) = stat;
28 | int ret = dladdr(func_stat, &dylib_info);
29 |
30 | // const char* SystemLib_c = "/usr/lib/system/libsystem_c.dylib";
31 | // curSystemLib = SystemLib_c;
32 | // curTestFuncName = "fopen";
33 | // FILE* (*func_fopen)(const char *filename, const char *mode) = fopen;
34 | // int ret = dladdr(func_fopen, &dylib_info);
35 |
36 | NSLog(@"dladdr ret=%d", ret);
37 |
38 | NSString *dladdrResultStr = @"";
39 | if (DLADDR_FAILED != ret){
40 | NSString* conclusionStr = @"";
41 | const char* libName = dylib_info.dli_fname;
42 | NSLog(@"dladdr dli_fname=%s, dli_fbase=%p, dli_sname=%s, dli_saddr=%p", libName, dylib_info.dli_fbase, dylib_info.dli_sname, dylib_info.dli_saddr);
43 |
44 | if (0 == strcmp(libName, curSystemLib)){
45 | conclusionStr = @"是系统库 -> 非越狱手机";
46 | } else {
47 | conclusionStr = @"不是系统库 -> 越狱手机";
48 | }
49 |
50 | dladdrResultStr = [NSString stringWithFormat:@"解析成功 -> %s 所属动态库: %s -> %@", curTestFuncName, libName, conclusionStr];
51 | } else{
52 | NSLog(@"dladdr failed: ret=%d", ret);
53 | dladdrResultStr = [NSString stringWithFormat:@"无法解析,返回值=%d", ret];
54 | }
55 | NSLog(@"dladdr: %@", dladdrResultStr);
56 |
57 | _detectResultTv.text = dladdrResultStr;
58 | }
59 | ```
60 |
61 | ## dlopen+dlsym
62 |
63 | TODO:
64 |
65 | * 【已解决】iOS越狱检测:正向的dlopen+dlsym
66 | * 【记录】iOS中尝试dlopen和dlsym的system函数传入其他参数确保运行正常
67 | * 【已解决】iOS中调用system返回值始终是32512
68 | * 【已解决】iOS反越狱检测:dyld的dlopen和dlsym
69 |
70 | ---
71 |
72 | ```c
73 | - (IBAction)dlopenDlsymBtnClicked:(UIButton *)sender {
74 | _curBtnLbl.text = sender.titleLabel.text;
75 | NSLog(@"dlopen + dlsym check");
76 |
77 | typedef void (*function_common) (void *para);
78 | // typedef void (*lib_MSHookFunction)(void *symbol, void *hook, void **old);
79 |
80 | char* dylibPathList[] = {
81 | // // for debug
82 | // "/usr/lib/libstdc++.dylib",
83 | // "/usr/lib/libstdc++.6.dylib",
84 | // "/usr/lib/libstdc++.6.0.9.dylib",
85 |
86 | // common: tweak plugin libs
87 | "/usr/lib/libsubstrate.dylib",
88 |
89 | // Cydia Substrate libs
90 | "/Library/MobileSubstrate/MobileSubstrate.dylib",
91 | "/usr/lib/substrate/SubstrateInserter.dylib",
92 | "/usr/lib/substrate/SubstrateLoader.dylib",
93 | "/usr/lib/substrate/SubstrateBootstrap.dylib",
94 |
95 | // Substitute libs
96 | "/usr/lib/libsubstitute.dylib",
97 | "/usr/lib/substitute-inserter.dylib",
98 | "/usr/lib/substitute-loader.dylib",
99 |
100 | // Other libs
101 | "/usr/lib/tweakloader.dylib",
102 | };
103 | const int StrSize = sizeof(const char *);
104 | const int DylibLen = sizeof(dylibPathList) / StrSize;
105 |
106 | char* libFuncNameList[] = {
107 | "MSGetImageByName",
108 | "MSFindSymbol",
109 | "MSHookFunction",
110 | "MSHookMessageEx",
111 |
112 | "SubGetImageByName",
113 | "SubFindSymbol",
114 | "SubHookFunction",
115 | "SubHookMessageEx",
116 | };
117 | const int LibFuncLen = sizeof(libFuncNameList) / StrSize;
118 |
119 | // NSMutableArray *detectedJbDylibList = [NSMutableArray array];
120 | // NSMutableArray *detectedJbFuncNameList = [NSMutableArray array];
121 | NSMutableArray *detectedJbLibAndFuncList = [NSMutableArray array];
122 |
123 | for(int libIdx = 0; libIdx < DylibLen; libIdx++) {
124 | char* curDylib = dylibPathList[libIdx];
125 | void *curLibHandle = dlopen(curDylib, RTLD_GLOBAL | RTLD_NOW);
126 | if (NULL == curLibHandle) {
127 | char* errStr = dlerror();
128 | NSLog(@"Failed to open dylib %s, error: %s", curDylib, errStr);
129 | } else {
130 | // NSString* curDylibNs = [NSString stringWithFormat:@"%s", curDylib];
131 | // [detectedJbDylibList addObject:curDylibNs];
132 |
133 | for(int funcIdx = 0; funcIdx < LibFuncLen; funcIdx++) {
134 | char* curFuncName = libFuncNameList[funcIdx];
135 | function_common funcInLib = dlsym(curLibHandle, curFuncName);
136 | if (NULL != funcInLib){
137 | NSLog(@"Found func %s=%p in dylib %s\n", curFuncName, funcInLib, curDylib);
138 |
139 | // NSString* curFuncNameNs = [NSString stringWithFormat:@"%s", curFuncName];
140 | // [detectedJbFuncNameList addObject:curFuncNameNs];
141 | NSString* curLibAndFuncNs = [NSString stringWithFormat:@"%s -> %s", curDylib, curFuncName];
142 | [detectedJbLibAndFuncList addObject:curLibAndFuncNs];
143 | }
144 | }
145 |
146 | dlclose(curLibHandle);
147 | }
148 | }
149 |
150 | NSString* finalResult = @"";
151 | // BOOL isJb = (detectedJbDylibList.count > 0) || (detectedJbFuncNameList.count > 0);
152 | // NSString *detectedJbDylibListStr = [CrifanLibiOS nsStrListToStr:detectedJbDylibList isSortList:FALSE isAddIndexPrefix:TRUE];
153 | // NSString *detectedJbFuncNameListStr = [CrifanLibiOS nsStrListToStr:detectedJbFuncNameList isSortList:FALSE isAddIndexPrefix:TRUE];
154 | // NSString* detectedLibAndFuncNameStr = [NSString stringWithFormat:@"越狱库=%@\n库函数=%@", detectedJbDylibListStr, detectedJbFuncNameListStr] ;
155 |
156 | BOOL isJb = (detectedJbLibAndFuncList.count > 0);
157 | NSString *detectedJbLibAndFuncListStr = [CrifanLibiOS nsStrListToStr:detectedJbLibAndFuncList isSortList:FALSE isAddIndexPrefix:TRUE];
158 | NSString* detectedLibAndFuncNameStr = [NSString stringWithFormat:@"越狱库和库函数=%@", detectedJbLibAndFuncListStr];
159 |
160 | if (isJb){
161 | finalResult = [NSString stringWithFormat:@"检测出越狱库或库函数 -> 越狱手机\n%@", detectedLibAndFuncNameStr] ;
162 | } else {
163 | finalResult = @"未检测出越狱库和库函数 -> 非越狱手机";
164 | }
165 | NSLog(@"finalResult=%@", finalResult);
166 | _detectResultTv.text = finalResult;
167 | }
168 | ```
--------------------------------------------------------------------------------
/src/jb_detection/env.md:
--------------------------------------------------------------------------------
1 | # 环境变量
2 |
3 | TODO
4 |
5 | * 【已解决】iOS越狱测试:getenv获取其他DYLD的环境变量值
6 | * 【未解决】越狱iPhone中getenv获取DYLD_INSERT_LIBRARIES返回为空
7 | * 【已解决】iOS反越狱检测:dyld的getenv获取环境变量DYLD_INSERT_LIBRARIES
8 |
9 | ---
10 |
11 | ```c
12 | - (IBAction)getenvDyInsLibBtnClicked:(UIButton *)sender {
13 | _curBtnLbl.text = sender.titleLabel.text;
14 | NSLog(@"getenv(DYLD_INSERT_LIBRARIES) check");
15 |
16 | char* dyldPrintEnv = getenv("DYLD_PRINT_ENV");
17 | NSLog(@"dyldPrintEnv=%s", dyldPrintEnv);
18 |
19 | char* insertLibs = getenv("DYLD_INSERT_LIBRARIES");
20 | NSLog(@"insertLibs=%s", insertLibs);
21 |
22 | const char* dyldEnvList[] = {
23 | "DYLD_FRAMEWORK_PATH",
24 | "DYLD_FALLBACK_FRAMEWORK_PATH",
25 | "DYLD_VERSIONED_FRAMEWORK_PATH",
26 | "DYLD_LIBRARY_PATH",
27 | "DYLD_FALLBACK_LIBRARY_PATH",
28 | "DYLD_VERSIONED_LIBRARY_PATH",
29 | "DYLD_ROOT_PATH",
30 | "DYLD_SHARED_REGION",
31 | "DYLD_INSERT_LIBRARIES",
32 | "DYLD_FORCE_FLAT_NAMESPACE",
33 | "DYLD_IMAGE_SUFFIX",
34 | "DYLD_PRINT_OPTS",
35 | "DYLD_PRINT_ENV",
36 | "DYLD_PRINT_LIBRARIES",
37 | "DYLD_PRINT_LIBRARIES_POST_LAUNCH",
38 | "DYLD_BIND_AT_LAUNCH",
39 | "DYLD_NO_FIX_PREBINDING",
40 | "DYLD_DISABLE_DOFS",
41 | "DYLD_PRINT_APIS",
42 | "DYLD_PRINT_BINDINGS",
43 | "DYLD_PRINT_INITIALIZERS",
44 | "DYLD_PRINT_REBASINGS",
45 | "DYLD_PRINT_SEGMENTS",
46 | "DYLD_PRINT_STATISTICS",
47 | "DYLD_PRINT_DOFS",
48 | "DYLD_PRINT_RPATHS",
49 | "DYLD_SHARED_CACHE_DIR",
50 | "DYLD_SHARED_CACHE_DONT_VALIDATE",
51 | };
52 | const int dyldEnvListLen = sizeof(dyldEnvList)/sizeof(const char *);
53 |
54 | for(int curIdx = 0; curIdx < dyldEnvListLen; curIdx++){
55 | const char* curDyldEnv = dyldEnvList[curIdx];
56 | char* curEnvRet = getenv(curDyldEnv);
57 | NSLog(@"dyld: [%d] %s -> %s", curIdx, curDyldEnv, curEnvRet);
58 | }
59 |
60 | NSString* insertLibResultStr = @"";
61 |
62 | if (NULL != insertLibs){
63 | insertLibResultStr = [NSString stringWithFormat: @"检测出DYLD_INSERT_LIBRARIES -> 越狱手机; DYLD_INSERT_LIBRARIES=%s", insertLibs];
64 | } else{
65 | insertLibResultStr = @"未检测出DYLD_INSERT_LIBRARIES -> 非越狱手机";
66 | }
67 | NSLog(@"dyld: insertLibResultStr=%@", insertLibResultStr);
68 |
69 | _detectResultTv.text = insertLibResultStr;
70 | }
71 | ```
72 |
--------------------------------------------------------------------------------
/src/jb_detection/file/README.md:
--------------------------------------------------------------------------------
1 | # 文件
2 |
3 | iOS越狱检测手段中,常见的一种方式,是基于文件的检测:
4 |
5 | * 基于`文件属性`的
6 | * 举例
7 | * `/etc/fstab`的大小
8 | * 基于`文件打开`的
9 | * 逻辑:能否打开某个(越狱后才会存在的)文件
10 | * 如果能打开,说明该文件存在,说明是越狱手机
11 | * 函数类型
12 | * 基于`C`语言的(系列)函数
13 | * `open`、`stat`、`access`等
14 | * 引申:同样的函数,可以换成更高级的调用方式
15 | * 通过`syscall`,传递不同的`number`编号,实现对应函数的调用
16 | * 更高级的:把`syscall`的调用,改为通过svc 0x80的内联汇编,增加被hook拦截的难度
17 | * 基于`iOS`语言的
18 | * `NSFileManager`、`NSURL`等
19 | * 基于`文件写入`的
20 | * 举例
21 | * 向`/private`中尝试能否写入
22 |
--------------------------------------------------------------------------------
/src/jb_detection/file/attributes.md:
--------------------------------------------------------------------------------
1 | # 文件属性
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS反越狱检测:/etc/fstab大小是否异常
6 |
--------------------------------------------------------------------------------
/src/jb_detection/file/open/README.md:
--------------------------------------------------------------------------------
1 | # 文件打开
2 |
3 | TODO:
4 |
5 | * 【记录】研究越狱iPhone中有哪些lib库
6 | * 【记录】整理和对比不同iPhone的dyld输出的动态库
7 |
8 | * 【已解决】越狱iOS中/bin/sh和/bin/bash以及/etc/alternatives/sh关系
9 |
--------------------------------------------------------------------------------
/src/jb_detection/file/open/c_func/README.md:
--------------------------------------------------------------------------------
1 | # C函数
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS越狱检测之打开文件方式
6 |
7 | ## open系列
8 |
9 | TODO:
10 |
11 | 【未解决】iOS越狱检测之打开文件:open系列函数
12 |
13 | ### open
14 |
15 | TODO:
16 |
17 | * 【已解决】iOS越狱检测:iOS的app中用open打开文件
18 | * 【基本解决】iOS越狱检测之打开文件:正向调用fopen
19 |
20 | ```c
21 | isUseFd = TRUE;
22 | retFd = open(filePathStr, O_RDONLY);
23 | ```
24 |
25 | ### opendir
26 |
27 | TODO:
28 |
29 | 【已解决】iOS越狱检测之打开文件之底层C函数:opendir
30 |
31 | ```c
32 | } else if (FUNC_OPENDIR == funcType) {
33 | DIR* retDir = opendir(filePathStr);
34 | if (NULL != retDir){
35 | NSLog(@"opendir OK: filePathStr=%s -> retDir=%p", filePathStr, retDir);
36 | NSLog(@"\tDIR: __dd_fd=%d,__dd_loc=%ld,__dd_size=%ld,__dd_buf=%s,__dd_len=%d,__dd_seek=%ld,__padding=%ld,__dd_flags=%d",
37 | retDir->__dd_fd, retDir->__dd_loc, retDir->__dd_size, retDir->__dd_buf, retDir->__dd_len, retDir->__dd_seek, retDir->__padding, retDir->__dd_flags);
38 | isOpenOk = TRUE;
39 | } else {
40 | NSLog(@"opendir fail for filePathStr=%s", filePathStr);
41 | isOpenOk = FALSE;
42 | }
43 |
44 | NSLog(@"opendir filePathStr=%s -> retDir=%p -> isOpenOk=%s", filePathStr, retDir, boolToStr(isOpenOk));
45 | ```
46 |
47 | ### __opendir2
48 |
49 | ```c
50 | } else if (FUNC___OPENDIR2 == funcType) {
51 | DIR* retDir = __opendir2(filePathStr, DTF_HIDEW|DTF_NODUP);
52 | if (NULL != retDir){
53 | NSLog(@"__opendir2 OK: filePathStr=%s -> retDir=%p", filePathStr, retDir);
54 | NSLog(@"\tDIR: __dd_fd=%d,__dd_loc=%ld,__dd_size=%ld,__dd_buf=%s,__dd_len=%d,__dd_seek=%ld,__padding=%ld,__dd_flags=%d",
55 | retDir->__dd_fd, retDir->__dd_loc, retDir->__dd_size, retDir->__dd_buf, retDir->__dd_len, retDir->__dd_seek, retDir->__padding, retDir->__dd_flags);
56 | isOpenOk = TRUE;
57 | } else {
58 | NSLog(@"__opendir2 fail for filePathStr=%s", filePathStr);
59 | isOpenOk = FALSE;
60 | }
61 |
62 | NSLog(@"__opendir2 filePathStr=%s -> retDir=%p -> isOpenOk=%s", filePathStr, retDir, boolToStr(isOpenOk));
63 | }
64 | ```
65 |
66 | ## access系列
67 |
68 | ### access
69 |
70 | TODO:
71 |
72 | * 【已解决】iOS越狱检测之打开文件:access
73 |
74 | ```c
75 | } else if (FUNC_ACCESS == funcType) {
76 | int retValue = access(filePathStr, F_OK);
77 | NSLog(@"access %s -> %d", filePathStr, retValue);
78 |
79 | if (retValue != ACCESS_OK){
80 | isOpenOk = FALSE;
81 | } else {
82 | isOpenOk = TRUE;
83 | }
84 | ```
85 |
86 | ### faccessat
87 |
88 | TODO:
89 |
90 | * 【已解决】iOS越狱检测之打开文件:faccessat
91 | * 【已解决】iOS中越狱检测之打开文件:faccessat正向检测
92 |
93 | ```c
94 | } else if (FUNC_FACCESSAT == funcType) {
95 | int curDirFd = 0;
96 | int retValue = ACCESS_FAILED;
97 |
98 | // // 1. test relative path
99 | //// const char* curDir = "/private/var/mobile/Library/Filza/";
100 | //// const char* curFile = "scripts/README.url";
101 | //
102 | //// const char* curDir = "/private/var/mobile/Library/";
103 | //// const char* curFile = "Filza/scripts/README.url";
104 | //// const char* curDir = "/private/./var/../var/mobile/Library/./";
105 | //// const char* curFile = "Filza/./scripts/../scripts/README.url";
106 | // const char* curDir = "/usr/lib";
107 | // const char* curFile = "libsubstrate.dylib";
108 | //
109 | // curDirFd = open(curDir, O_RDONLY);
110 | // NSLog(@"curDir=%s -> curDirFd=%d", curDir, curDirFd);
111 | //
112 | //// // for debug: get file path from fd
113 | //// char filePath[PATH_MAX];
114 | //// int fcntlRet = fcntl(curDirFd, F_GETPATH, filePath);
115 | //// const int FCNTL_FAILED = -1;
116 | //// if (fcntlRet != FCNTL_FAILED){
117 | //// NSLog(@"fcntl OK: curDirFd=%d -> filePath=%s", curDirFd, filePath);
118 | //// } else {
119 | //// NSLog(@"fcntl fail for curDirFd=%d", curDirFd);
120 | //// }
121 | //
122 | // retValue = faccessat(curDirFd, curFile, F_OK, AT_EACCESS);
123 | // NSLog(@"faccessat curDir=%s,curFile=%s -> %d", curDir, curFile, retValue);
124 |
125 | // 2. test input path
126 | const int FAKE_FD = 0;
127 | curDirFd = FAKE_FD;
128 | retValue = faccessat(curDirFd, filePathStr, F_OK, AT_EACCESS);
129 | NSLog(@"faccessat curDirFd=%d, filePathStr=%s -> %d", curDirFd, filePathStr, retValue);
130 |
131 | if (retValue != ACCESS_FAILED){
132 | isOpenOk = TRUE;
133 | } else {
134 | isOpenOk = FALSE;
135 | }
136 | ```
137 |
138 | ## stat系列
139 |
140 | TODO:
141 |
142 | * 【未解决】iOS越狱检测之打开文件之stat系列函数
143 |
144 | ### stat
145 |
146 | TODO:
147 |
148 | * 【已解决】iOS越狱检测之打开文件:stat函数
149 | * 【已解决】iOS用stat打开和检测文件是否存在检测是否越狱
150 |
151 | ```c
152 | if (FUNC_STAT == funcType){
153 | isUseStatInfo = TRUE;
154 | openResult = stat(filePathStr, &stat_info);
155 | ```
156 |
157 | ### lstat
158 |
159 | TODO:
160 |
161 | * 【已解决】iOS越狱检测之打开文件:lstat正向越狱检测
162 | * 【已解决】lstat检测普通文件但却通过S_IFLNK误判出是软链接
163 |
164 | ```c
165 | } else if (FUNC_LSTAT == funcType) {
166 | isOpenOk = FALSE;
167 | bool isLink = FALSE;
168 |
169 | struct stat statInfo;
170 | int lstatRet = lstat(filePathStr, &statInfo);
171 | if (STAT_OK == lstatRet){
172 | // isLink = statInfo.st_mode & S_IFLNK;
173 | isLink = S_ISLNK(statInfo.st_mode);
174 | if (isLink) {
175 | isOpenOk = TRUE;
176 | }
177 | }
178 |
179 | NSLog(@"lstat filePathStr=%s -> isLink=%s -> isOpenOk=%s", filePathStr, boolToStr(isLink), boolToStr(isOpenOk));
180 | ```
181 |
182 | ### fstat
183 |
184 | ```c
185 | } else if (FUNC_FSTAT == funcType) {
186 | isOpenOk = FALSE;
187 | int tmpFd = open(filePathStr, O_RDONLY);
188 |
189 | if (tmpFd > 0){
190 | isOpenOk = TRUE;
191 |
192 | struct stat statInfo;
193 | memset(&statInfo, 0, sizeof(struct stat));
194 | int fstatRet = fstat(tmpFd, &statInfo);
195 | if (STAT_OK == fstatRet) {
196 | isOpenOk = TRUE;
197 | } else {
198 | isOpenOk = FALSE;
199 | }
200 | } else {
201 | // when fd < 0, normally is -1, means open file failed
202 | isOpenOk = FALSE;
203 | NSLog(@"open() failed for %@", filePath);
204 | }
205 | ```
206 |
207 | ### fstatfs
208 |
209 | ```c
210 | } else if (FUNC_FSTATFS == funcType) {
211 | isOpenOk = FALSE;
212 | int tmpFd = open(filePathStr, O_RDONLY);
213 |
214 | if (tmpFd > 0){
215 | isOpenOk = TRUE;
216 |
217 | struct statfs statfsInfo;
218 | memset(&statfsInfo, 0, sizeof(struct statfs));
219 | int fstatfsRet = fstatfs(tmpFd, &statfsInfo);
220 | if (STATFS_OK == fstatfsRet) {
221 | isOpenOk = TRUE;
222 | } else {
223 | isOpenOk = FALSE;
224 | }
225 | } else {
226 | // when fd < 0, normally is -1, means open file failed
227 | isOpenOk = FALSE;
228 | NSLog(@"open() failed for %@", filePath);
229 | }
230 | ```
231 |
232 | ## realpath
233 |
234 | ```c
235 | } else if (FUNC_REALPATH == funcType) {
236 | char parsedRealPath[PATH_MAX];
237 | char *resolvedPtr = realpath(filePathStr, parsedRealPath);
238 | if (NULL != resolvedPtr){
239 | NSLog(@"realpath OK: filePathStr=%s -> parsedRealPath=%s", filePathStr, parsedRealPath);
240 | isOpenOk = TRUE;
241 | } else {
242 | NSLog(@"realpath fail for filePathStr=%s", filePathStr);
243 | isOpenOk = FALSE;
244 | }
245 | NSLog(@"realpath filePathStr=%s -> isOpenOk=%s", filePathStr, boolToStr(isOpenOk));
246 | ```
247 |
248 |
--------------------------------------------------------------------------------
/src/jb_detection/file/open/c_func/svc_0x80_asm.md:
--------------------------------------------------------------------------------
1 | # svc 0x80内联汇编
2 |
3 | TODO:
4 |
5 | * 【整理】syscall内核系统调用和svc 0x80相关基础知识
6 | * 【已解决】iOS正向越狱检测:app中实现svc 0x80实现系统调用
7 | * 【已解决】iOS中优化asm汇编代码新增syscall的number参数
8 | * 【整理】iOS中syscall的系统调用编号number的定义
9 |
10 | ---
11 |
12 | ```c
13 |
14 | //---------- svc 0x80 define ----------
15 |
16 | //#define asm_set_syscall_number(SYSCALL_NUMBER) "mov x16, #SYSCALL_NUMBER\n"
17 | //
18 | //#define asm_svc_0x80_stat64() \
19 | // "mov x0, %[pathname_p]\n" \
20 | // "mov x1, %[stat_info_p]\n" \
21 | // asm_set_syscall_number(SYS_stat64) \
22 | // "svc #0x80\n" \
23 | // "mov %[ret_p], x0\n"
24 |
25 | // "mov x16, #338\n" \
26 |
27 | //__attribute__((always_inline)) long svc_0x80_stat_stat64(int syscall_number, const char * pathname, struct stat * stat_info) {
28 | // long ret = 0;
29 | // long long_syscall_number = syscall_number;
30 | // __asm__ volatile(
31 | // "mov x0, %[pathname_p]\n"
32 | // "mov x1, %[stat_info_p]\n"
33 | // "mov x16, %[long_syscall_number_p]\n"
34 | // "svc #0x80\n"
35 | // "mov %[ret_p], x0\n"
36 | // : [ret_p]"=r"(ret)
37 | // : [long_syscall_number_p]"r"(long_syscall_number), [pathname_p]"r"(pathname), [stat_info_p]"r"(stat_info)
38 | // : "x0", "x1", "x16"
39 | // );
40 | // return ret == 0 ? ret : -1;
41 | //}
42 |
43 | __attribute__((always_inline)) int svc_0x80_stat_stat64(int syscall_number, const char * pathname, struct stat * stat_info) {
44 | register const char * x0_pathname asm ("x0") = pathname; // first arg
45 | register struct stat * x1_stat_info asm ("x1") = stat_info; // second arg
46 | register int x16_syscall_number asm ("x16") = syscall_number; // special syscall number store to x16
47 | register int x4_ret asm("x4") = OPEN_FAILED; // store result
48 | __asm__ volatile(
49 | "svc #0x80\n"
50 | "mov x4, x0\n"
51 | : "=r"(x4_ret)
52 | : "r"(x0_pathname), "r"(x1_stat_info), "r"(x16_syscall_number)
53 | // : "x0", "x1", "x4", "x16"
54 | );
55 | return x4_ret;
56 | }
57 |
58 | //__attribute__((always_inline)) int svc_0x80_open(const char * pathname, int flags, mode_t mode) {
59 | __attribute__((always_inline)) int svc_0x80_open(const char * pathname, int flags) {
60 | register const char * x0_pathname asm ("x0") = pathname; // first arg
61 | register int x1_flags asm ("x1") = flags; // second arg
62 | // register unsigned int x2_mode asm ("x2") = (unsigned int)mode; // third arg
63 | register int x16_syscall_number asm ("x16") = SYS_open; // special syscall number store to x16
64 | register int x4_ret asm("x4") = OPEN_FD_INVALID; // store result
65 | __asm__ volatile(
66 | // "mov x16, #5\n" // SYS_open
67 | "svc #0x80\n"
68 | "mov x4, x0\n"
69 | : "=r"(x4_ret)
70 | : "r"(x0_pathname), "r"(x1_flags), "r"(x16_syscall_number)
71 | // : "r"(x0_pathname), "r"(x1_flags), "r"(x2_mode), "r"(x16_syscall_number)
72 | // : "x16"
73 | // : "x0", "x1", "x5", "x16"
74 | );
75 | return x4_ret;
76 | }
77 |
78 | //---------- svc 0x80 call ----------
79 |
80 | ...
81 | } else if (FUNC_SVC_0X80_STAT == funcType) {
82 | isUseStatInfo = TRUE;
83 | //Note: for open normal file, return 0 is OK, but st_mode is abnormal !
84 | openResult = svc_0x80_stat_stat64(SYS_stat, filePathStr, &stat_info);
85 | } else if (FUNC_SVC_0X80_STAT64 == funcType) {
86 | isUseStatInfo = TRUE;
87 | openResult = svc_0x80_stat_stat64(SYS_stat64, filePathStr, &stat_info);
88 | ...
89 | } else if (FUNC_SVC_0X80_OPEN == funcType) {
90 | isUseFd = TRUE;
91 | // retFd = svc_0x80_open(filePathStr, O_RDONLY, MODE_NONE);
92 | retFd = svc_0x80_open(filePathStr, O_RDONLY);
93 |
94 | ```
95 |
--------------------------------------------------------------------------------
/src/jb_detection/file/open/c_func/syscall.md:
--------------------------------------------------------------------------------
1 | # syscall
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS的app中如何实现syscall函数调用
6 | * 【已解决】iOS中用syscall调用stat64实现越狱文件检测
7 | * 【整理】syscall内核系统调用和svc 0x80相关基础知识
8 | * 【整理】iOS中syscall的系统调用编号number的定义
9 | * 【整理】iOS中的带函数原型和说明的系统调用system call
10 |
11 | ---
12 |
13 | ## SYS_fork
14 |
15 | ```c
16 | NSString * parseForkResult(int forkRetPid){
17 | NSString *forkResultStr = NULL;
18 | if (forkRetPid < 0){
19 | forkResultStr = @"无法fork->旧版iOS:非越狱, 新版iOS:无法判断";
20 |
21 | // log print erro info
22 | NSLog(@"errno=%d\n", errno);
23 | char *errMsg = strerror(errno);
24 | NSLog(@"errMsg=%s\n", errMsg);
25 | } else{
26 | forkResultStr = @"可以fork -> 旧版iOS:越狱手机";
27 | }
28 |
29 | return forkResultStr;
30 | }
31 |
32 | - (IBAction)syscallForkBtnClicked:(UIButton *)sender {
33 | _curBtnLbl.text = sender.titleLabel.text;
34 | NSLog(@"syscall(fork) check");
35 | int retPid = syscall(SYS_fork);
36 |
37 | NSString *forkResultStr = parseForkResult(retPid);
38 | NSLog(@"syscall(fork) return retPid=%d, forkResultStr=%@", retPid, forkResultStr);
39 | _detectResultTv.text = [NSString stringWithFormat:@"%@ -> %@", @"syscall(fork)", forkResultStr];
40 | }
41 | ```
42 |
43 | ## SYS_stat
44 |
45 | ```c
46 | } else if (FUNC_SYSCALL_STAT == funcType) {
47 | isUseStatInfo = TRUE;
48 | //Note: for open normal file, return 0 is OK, but st_mode is abnormal !
49 | openResult = syscall(SYS_stat, filePathStr, &stat_info);
50 | ```
51 |
52 | ## SYS_stat64
53 |
54 | ```c
55 | } else if (FUNC_SYSCALL_STAT64 == funcType){
56 | isUseStatInfo = TRUE;
57 | openResult = syscall(SYS_stat64, filePathStr, &stat_info);
58 | ```
59 |
60 | ## SYS_lstat
61 |
62 | ```c
63 | } else if (FUNC_SYSCALL_LSTAT == funcType){
64 | isUseStatInfo = TRUE;
65 | openResult = syscall(SYS_lstat, filePathStr, &stat_info);
66 | ```
67 |
68 | ## SYS_fstat
69 |
70 | ```c
71 | } else if (FUNC_SYSCALL_FSTAT == funcType){
72 | isUseStatInfo = TRUE;
73 |
74 | int curFd = open(filePathStr, O_RDONLY);
75 | if (curFd > 0){
76 | openResult = syscall(SYS_fstat, curFd, &stat_info);
77 | } else {
78 | isOpenOk = FALSE;
79 | }
80 | ```
81 |
82 | ## SYS_fstatat
83 |
84 | ```c
85 | } else if (FUNC_SYSCALL_FSTATAT == funcType){
86 | // NOTE: syscall(SYS_fstatat) not work until 20220316 -> awalys return -1
87 |
88 | // int fstatat(int fd, const char* pathname, struct stat* buf, int flags);
89 | isUseStatInfo = TRUE;
90 |
91 | // int curFd = open(filePathStr, O_RDONLY);
92 | // if (curFd > 0){
93 | // openResult = syscall(SYS_fstatat, curFd, filePathStr, &stat_info, F_DUPFD);
94 | // } else {
95 | // isOpenOk = FALSE;
96 | // }
97 |
98 | // int notUsedDirfd = -1;
99 | // openResult = syscall(SYS_fstatat, notUsedDirfd, filePathStr, &stat_info, F_DUPFD);
100 | // openResult = syscall(SYS_fstatat, notUsedDirfd, filePathStr, &stat_info, 0);
101 | openResult = syscall(SYS_fstatat, AT_FDCWD, filePathStr, &stat_info, 0);
102 | ```
103 |
104 | ## SYS_statfs
105 |
106 | ```c
107 | } else if (FUNC_SYSCALL_STATFS == funcType){
108 | isUseStatInfo = TRUE;
109 | // int statfs(const char *path, struct statfs *buf);
110 | openResult = syscall(SYS_statfs, filePathStr, &stat_info);
111 | ```
112 |
113 | ## SYS_fstatfs
114 |
115 | ```c
116 | } else if (FUNC_SYSCALL_FSTATFS == funcType){
117 | isUseStatInfo = TRUE;
118 | // int fstatfs(int fd, struct statfs *buf);
119 | int curFd = open(filePathStr, O_RDONLY);
120 | if (curFd > 0){
121 | openResult = syscall(SYS_fstatfs, curFd, &stat_info);
122 | } else {
123 | isOpenOk = FALSE;
124 | }
125 | }
126 | ```
127 |
128 | ## SYS_open
129 |
130 | ```c
131 | } else if (FUNC_SYSCALL_OPEN == funcType){
132 | isUseFd = TRUE;
133 | // retFd = syscall(SYS_open, filePathStr, O_RDONLY);
134 | retFd = syscall(SYS_open, filePathStr, O_RDONLY, MODE_NONE);
135 | }
136 | ```
137 |
138 | ## SYS_access
139 |
140 | ```c
141 | } else if (FUNC_SYSCALL_ACCESS == funcType) {
142 | int retValue = syscall(SYS_access, filePathStr, F_OK);
143 | NSLog(@"SYS_access %s -> %d", filePathStr, retValue);
144 | if (retValue != ACCESS_OK){
145 | isOpenOk = FALSE;
146 | } else {
147 | isOpenOk = TRUE;
148 | }
149 | }
150 | ```
151 |
152 | ## SYS_faccessat
153 |
154 | ```c
155 | } else if (FUNC_SYSCALL_FACCESSAT == funcType) {
156 | // NOTE: syscall(SYS_faccessat) not work until 20220317 -> awalys return -1
157 |
158 | int curDirFd = 0;
159 | int retValue = ACCESS_FAILED;
160 |
161 | const int FAKE_FD = 0;
162 | curDirFd = FAKE_FD;
163 | // retValue = syscall(SYS_faccessat, curDirFd, filePathStr, F_OK, AT_EACCESS);
164 | retValue = syscall(SYS_faccessat, curDirFd, filePathStr, F_OK, 0);
165 | if (retValue != ACCESS_FAILED){
166 | isOpenOk = TRUE;
167 | } else {
168 | isOpenOk = FALSE;
169 | }
170 | }
171 | ```
172 |
--------------------------------------------------------------------------------
/src/jb_detection/file/open/ios_func.md:
--------------------------------------------------------------------------------
1 | # iOS函数
2 |
3 | TODO:
4 |
5 | 【已解决】iOS中NSURL的checkResourceIsReachableAndReturnError底层调用lstat返回文件结果异常
6 |
7 | ---
8 |
9 | ## NSFileManager
10 |
11 | ```objc
12 | } else if (FUNC_NSFILEMANAGER == funcType) {
13 | NSFileManager *defaultManager = [NSFileManager defaultManager];
14 | NSString* filePathNsStr = [NSString stringWithFormat:@"%s", filePathStr];
15 |
16 | BOOL isExisted = [defaultManager fileExistsAtPath: filePathNsStr];
17 | NSLog(@"isExisted=%s", boolToStr(isExisted));
18 |
19 | BOOL isDir = FALSE;
20 | BOOL isExistedWithDir = [defaultManager fileExistsAtPath:filePathNsStr isDirectory: &isDir];
21 | NSLog(@"isExistedWithDir=%s, isDir=%s", boolToStr(isExistedWithDir), boolToStr(isDir));
22 |
23 | isOpenOk = isExisted || isExistedWithDir;
24 |
25 | NSString* curResultStr = @"";
26 |
27 | if(isExisted){
28 | curResultStr = [NSString stringWithFormat:@"%@ 是否是目录:%@", @"路径存在", isDir ? @"是":@"否"];
29 | } else{
30 | curResultStr = [NSString stringWithFormat:@"%@", @"路径不存在"];
31 | }
32 |
33 | NSLog(@"fileExistsAtPath %@ -> %@", filePathNsStr, curResultStr);
34 | ```
35 |
36 | ## NSURL
37 |
38 | ```objc
39 | } else if (FUNC_NSURL == funcType) {
40 | NSString* fileStr = [NSString stringWithUTF8String:filePathStr];
41 | NSString* fileWithFilePrefix = [NSString stringWithFormat:@"file://%@", fileStr];
42 | NSURL* fileUrl = [NSURL URLWithString:fileWithFilePrefix];
43 | NSError* error = NULL;
44 | BOOL isReachable = [fileUrl checkResourceIsReachableAndReturnError:&error];
45 | NSLog(@"isReachable=%s, error=%@", boolToStr(isReachable), (error != NULL) ? error : @"");
46 |
47 | // for debug
48 | if (isReachable){
49 | NSLog(@"fileStr=%@", fileStr);
50 | }
51 |
52 | isOpenOk = isReachable;
53 | NSLog(@"NSURL checkResourceIsReachableAndReturnError %@ -> %s", fileStr, boolToStr(isReachable));
54 | ```
55 |
--------------------------------------------------------------------------------
/src/jb_detection/file/write/README.md:
--------------------------------------------------------------------------------
1 | # 文件写入
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS越狱检测:尝试向/private写入文件
6 | * 【未解决】iOS越狱检测之是否能写入文件到特定目录
7 |
--------------------------------------------------------------------------------
/src/jb_detection/file/write/c_func.md:
--------------------------------------------------------------------------------
1 | # C函数
2 |
--------------------------------------------------------------------------------
/src/jb_detection/file/write/ios_func.md:
--------------------------------------------------------------------------------
1 | # iOS函数
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS越狱检测之打开文件之上层iOS层函数
6 | * 【已解决】iOS越狱检测之iOS层函数:尝试向/private写入文件
7 | * 【无法解决】越狱iOS中用writeToFile去写入/private报错:NSCocoaErrorDomain Code 513 You don’t have permission to save the file in the folder private
8 |
9 | ## NSFileManager
10 |
11 | TODO:
12 |
13 | * 【未解决】iOS越狱检测之打开文件之iOS层函数之NSFileManager
14 |
15 | ---
16 |
17 | ```c
18 | - (IBAction)writeFileBtnClicked:(UIButton *)sender {
19 | _curBtnLbl.text = sender.titleLabel.text;
20 | NSLog(@"write file check");
21 |
22 | // NSStringEncoding strEncoding = NSStringEncodingConversionAllowLossy;
23 | NSStringEncoding strEncoding = NSUTF8StringEncoding;
24 | BOOL isAtomicWriteFile = YES;
25 | BOOL isUseAuxiliaryFile = NO;
26 | NSDataWritingOptions writeOption = NSDataWritingAtomic;
27 |
28 | NSString *testFile = @"/private/testWriteToFile.txt";
29 | // for debug
30 | // NSString *testFile = @"/private/var/mobile/Containers/Data/Application/EEFACEA4-2ADB-4D25-9DB4-B5D643EA8943/Documents/bd.turing/";
31 | // NSString *testFile = @"/private/var/mobile/Containers/Data/Application/EEFACEA4-2ADB-4D25-9DB4-B5D643EA8943/Documents/test_douyin_write.txt";
32 | NSString* withPrefixTestFile = [NSString stringWithFormat:@"file://%@", testFile];
33 | NSURL* testFileUrl = [NSURL URLWithString:withPrefixTestFile];
34 | NSString *testStr = @"just some test string for test write file";
35 | NSData* testData = [testStr dataUsingEncoding:strEncoding];
36 |
37 | id objects[] = { @"demo string", @123, @45.67 };
38 | NSUInteger count = sizeof(objects) / sizeof(id);
39 | NSArray *testArr = [NSArray arrayWithObjects:objects count:count];
40 |
41 | NSDictionary* testDict = @{
42 | @"intValue": @123,
43 | @"floatValue": @45.678,
44 | @"strValue": @"test write file",
45 | };
46 |
47 | NSFileManager *fileManager = [NSFileManager defaultManager];
48 | NSError* error = NULL;
49 | BOOL isWriteOk = FALSE;
50 | BOOL isFinalWriteOk = FALSE;
51 |
52 | // 1. NSString
53 | // // (1) [NSString writeToFile:atomically:]
54 | // isWriteOk = [testStr writeToFile:testFile atomically:isAtomicWriteFile];
55 | // NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
56 | // isFinalWriteOk = isWriteOk || isFinalWriteOk;
57 |
58 | // (2) [NSString writeToFile:atomically:encoding:error:]
59 | [testStr writeToFile:testFile atomically:isAtomicWriteFile encoding:strEncoding error:&error];
60 | // [testStr writeToFile:testFile atomically:isAtomicWriteFile encoding:strEncoding error:NULL];
61 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
62 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
63 |
64 | // (3) [NSString writeToURL:atomically:]
65 | isWriteOk = [testStr writeToURL:testFileUrl atomically:isAtomicWriteFile];
66 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
67 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
68 |
69 | // (4) [NSString writeToURL:atomically:encoding:error:]
70 | isWriteOk = [testFile writeToURL:testFileUrl atomically:isAtomicWriteFile encoding:strEncoding error:&error];
71 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
72 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
73 |
74 | // 2. NSData
75 | // (1) [NSData writeToFile:atomically:]
76 | isWriteOk = [testData writeToURL:testFileUrl atomically:isAtomicWriteFile];
77 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
78 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
79 |
80 | // (2) [NSData writeToFile:options:error:]
81 | isWriteOk = [testData writeToFile:testFile options:writeOption error:&error];
82 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
83 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
84 |
85 | // (3) [NSData writeToURL:atomically:]
86 | isWriteOk = [testData writeToURL:testFileUrl atomically:isAtomicWriteFile];
87 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
88 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
89 |
90 | // (4) [NSData writeToURL:options:error:]
91 | isWriteOk = [testData writeToURL:testFileUrl options:writeOption error:&error];
92 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
93 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
94 |
95 | // 3. NSArray
96 | // (1) [NSArray writeToFile:atomically:]
97 | isWriteOk = [testArr writeToFile:testFile atomically:isUseAuxiliaryFile];
98 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
99 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
100 |
101 | // (2) [NSArray writeToFile:atomically:]
102 | isWriteOk = [testArr writeToURL:testFileUrl atomically:isAtomicWriteFile];
103 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
104 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
105 |
106 | // (3) [NSArray writeToFile:error:]
107 | isWriteOk = [testArr writeToURL:testFileUrl error:&error];
108 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
109 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
110 |
111 | // 4. NSDictionary
112 | // (1) [NSDictionary writeToFile:atomically:]
113 | isWriteOk = [testDict writeToFile:testFile atomically:isUseAuxiliaryFile];
114 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
115 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
116 |
117 | // (2) [NSDictionary writeToURL:error:]
118 | isWriteOk = [testDict writeToURL:testFileUrl error:&error];
119 | NSLog(@"isWriteOk=%s, error=%@", boolToStr(isWriteOk), error);
120 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
121 |
122 | // (3) [NSDictionary writeToURL:atomically:]
123 | isWriteOk = [testDict writeToURL:testFileUrl atomically:isAtomicWriteFile];
124 | NSLog(@"isWriteOk=%s", boolToStr(isWriteOk));
125 | isFinalWriteOk = isWriteOk || isFinalWriteOk;
126 |
127 | // // for debug: test removeItemAtPath
128 | // isWriteOk = TRUE;
129 |
130 | NSLog(@"isFinalWriteOk=%s", boolToStr(isFinalWriteOk));
131 | if (isFinalWriteOk)
132 | {
133 | NSLog(@"Ok to write file %@", testFile);
134 |
135 | BOOL isDeleteOk = FALSE;
136 |
137 | isDeleteOk = [fileManager removeItemAtPath:testFile error:&error];
138 | NSLog(@"isDeleteOk=%s, *error=%@", boolToStr(isDeleteOk), error);
139 | // if(error == nil){
140 | // isDeleteOk = TRUE;
141 | // }
142 |
143 | isDeleteOk = [fileManager removeItemAtURL:testFileUrl error:&error];
144 | NSLog(@"isDeleteOk=%s, *error=%@", boolToStr(isDeleteOk), error);
145 | // if(error == nil){
146 | // isDeleteOk = TRUE;
147 | // }
148 |
149 | if (isDeleteOk){
150 | NSLog(@"Ok to delete file %@", testFile);
151 | } else {
152 | NSLog(@"Fail to delete file %@", testFile);
153 | }
154 | } else{
155 | NSLog(@"Fail to write file %@", testFile);
156 | }
157 | NSString* finalResult = @"";
158 | if (isFinalWriteOk){
159 | finalResult = @"可以写入 -> 越狱手机";
160 | } else {
161 | finalResult = @"无法写入 -> 很可能是非越狱手机";
162 | }
163 | _detectResultTv.text = finalResult;
164 | }
165 | ```
166 |
--------------------------------------------------------------------------------
/src/jb_detection/getsectiondata.md:
--------------------------------------------------------------------------------
1 | # getsectiondata
2 |
--------------------------------------------------------------------------------
/src/jb_detection/installed_app.md:
--------------------------------------------------------------------------------
1 | # 已安装app
2 |
3 | ```c
4 | - (IBAction)lsapplicationBtnClicked:(UIButton *)sender {
5 | _curBtnLbl.text = sender.titleLabel.text;
6 | NSLog(@"LSApplication check");
7 | NSString* resultStr = @"TODO";
8 |
9 | Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
10 | NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];
11 | NSArray *allAppList = [workspace performSelector:@selector(allApplications)]; //这样就能获取到手机中安装的所有App
12 |
13 | resultStr = [NSString stringWithFormat: @"已安装app总数: %d", [allAppList count]];
14 | resultStr = [NSString stringWithFormat: @"%@\n非系统app列表:", resultStr];
15 |
16 | for (int i=0; i<[allAppList count]; i++) {
17 | // LSApplicationProxy *appProxy = [allAppList objectAtIndex:i];
18 | // LSApplicationProxy_class *appProxy = [allAppList objectAtIndex:i];
19 | // NSString* bundleId =[appProxy applicationIdentifier];
20 | // NSString* name = [appProxy localizedName];
21 |
22 | id appProxy = [allAppList objectAtIndex:i];
23 | NSString* bundleId =[appProxy performSelector:@selector(applicationIdentifier)];
24 | NSString* name = [appProxy performSelector:@selector(localizedName)];
25 | NSString* version = [appProxy performSelector:@selector(bundleVersion)];
26 | NSObject *description = [appProxy performSelector:@selector(description)];
27 | NSArray *plugInKitPlugins = [appProxy performSelector:@selector(plugInKitPlugins)];
28 | if(![bundleId hasPrefix: @"com.apple."]) {
29 | resultStr = [NSString stringWithFormat: @"%@\n[%d] bundleId=%@, name=%@, version=%@, description=%@, plugInKitPlugins=%@", resultStr, i, bundleId, name, version, description, plugInKitPlugins];
30 | }
31 | }
32 |
33 | // Class LSApplicationProxy_class = object_getClass(@"LSApplicationProxy");
34 | //
35 | // for (LSApplicationProxy_class in allAppList) {
36 | // NSString *bundleId = [LSApplicationProxy_class performSelector:@selector(applicationIdentifier)];
37 | // NSString *version = [LSApplicationProxy_class performSelector:@selector(bundleVersion)];
38 | // }
39 |
40 | NSLog(@"resultStr=%@", resultStr);
41 | _detectResultTv.text = resultStr;
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/src/jb_detection/jb_processes.md:
--------------------------------------------------------------------------------
1 | # 越狱相关进程
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS越狱检测:检测是否有越狱相关进程
6 |
7 | ---
8 |
9 | ```c
10 |
11 | - (IBAction)processCheckBtnClicked:(UIButton *)sender {
12 | _curBtnLbl.text = sender.titleLabel.text;
13 | NSLog(@"process check");
14 | NSString* resultStr = @"TODO";
15 |
16 | NSArray *processes = [CrifanLibiOS runningProcesses];
17 | NSLog(@"processes=%@", processes);
18 |
19 | if (NULL == processes) {
20 | resultStr = @"此检测手段已失效:sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL)";
21 | }
22 |
23 | // proc_listpids(type, typeinfo, buffer, buffersize)
24 | // type = PROC_ALL_PIDS, typeinfo = 0 (use proc_listallpids)
25 | // type = PROC_PGRP_ONLY, typeinfo = process group id (use proc_listpgrppids)
26 | // type = PROC_TTY_ONLY, typeinfo = tty
27 | // type = PROC_UID_ONLY, typeinfo = uid
28 | // type = PROC_RUID_ONLY, typeinfo = ruid
29 | // type = PROC_PPID_ONLY, typeinfo = ppid (use proc_listchildpids)
30 | // Call with buffer = NULL to return number of pids.
31 | // int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
32 | // NSLog(@"numberOfProcesses=%d", numberOfProcesses);
33 |
34 | NSLog(@"resultStr=%@", resultStr);
35 | _detectResultTv.text = resultStr;
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/src/jb_detection/objc_runtime.md:
--------------------------------------------------------------------------------
1 | # ObjC运行时
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS越狱检测:objc_copyImageNames检测image
6 | * 【无法解决】iOS越狱检测和反越狱检测:objc_getClass
7 | * 【无需解决】iOS越狱检测和反越狱检测:NSClassFromString
8 |
9 | ---
10 |
11 | ## `objc_copyImageNames`
12 |
13 | ```c
14 |
15 | - (IBAction)objCopyBtnClicked:(UIButton *)sender {
16 | _curBtnLbl.text = sender.titleLabel.text;
17 | NSLog(@"objc_copyImageNames check");
18 | unsigned int outImageCount = 0;
19 | const char **imageList = objc_copyImageNames(&outImageCount);
20 | NSLog(@"outImageCount=%d, imageList=%p", outImageCount, imageList);
21 |
22 | NSMutableArray *jbImageList = [NSMutableArray array];
23 |
24 | if ((outImageCount > 0) && (imageList != NULL)) {
25 | for (int i = 0; i < outImageCount; i++) {
26 | const char* curImagePath = imageList[i];
27 | bool isJbPath = isJailbreakPath(curImagePath);
28 | NSLog(@"[%d] %s -> isJbPath=%s", i, curImagePath, boolToStr(isJbPath));
29 | if (isJbPath) {
30 | NSString* curImagePathNs = [NSString stringWithFormat:@"%s", curImagePath];
31 | [jbImageList addObject: curImagePathNs];
32 | }
33 | }
34 | }
35 |
36 | NSString *jbImageListStr = [CrifanLibiOS nsStrListToStr:jbImageList isSortList:TRUE isAddIndexPrefix:TRUE];
37 | NSLog(@"jbImageListStr=%@", jbImageListStr);
38 |
39 | NSString* resultStr = @"";
40 | if (jbImageList.count > 0) {
41 | resultStr = [NSString stringWithFormat:@"检测出越狱库image -> 越狱手机\n%@", jbImageListStr] ;
42 | } else {
43 | resultStr = @"未检测出越狱库image -> 非越狱手机";
44 | }
45 | NSLog(@"resultStr=%@", resultStr);
46 | _detectResultTv.text = resultStr;
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/src/jb_detection/sandbox.md:
--------------------------------------------------------------------------------
1 | # 沙箱完整性校验
2 |
3 | TODO:
4 |
5 | * 【无需解决】已越狱iOS中fork()失败返回-1
6 | * 【已解决】iOS中系统调用fork返回值的含义和逻辑
7 |
8 | ---
9 |
10 | ```c
11 | - (IBAction)forkBtnClicked:(UIButton *)sender {
12 | _curBtnLbl.text = sender.titleLabel.text;
13 | NSLog(@"Fork() check");
14 | // SandBox Integrity Check
15 | int retPid = fork(); //返回值:子进程返回0,父进程中返回子进程ID,出错则返回-1
16 | NSString *forkResultStr = parseForkResult(retPid);
17 | NSLog(@"fork() return retPid=%d, forkResultStr=%@", retPid, forkResultStr);
18 | _detectResultTv.text = [NSString stringWithFormat:@"%@ -> %@", @"fork()", forkResultStr];
19 | }
20 | ```
21 |
--------------------------------------------------------------------------------
/src/jb_detection/ssh.md:
--------------------------------------------------------------------------------
1 | # SSH相关
2 |
3 | TODO:
4 |
5 | * 【未解决】iOS越狱检测之ssh相关
6 | * 【未解决】iOS中如何用C语言代码实现ssh调用
7 |
8 | ---
9 |
10 | ```c
11 | - (IBAction)sshBtnClicked:(UIButton *)sender {
12 | _curBtnLbl.text = sender.titleLabel.text;
13 | NSLog(@"ssh check");
14 | const char* sshCmd = "ssh root@127.0.0.1";
15 | // int systemRet = system(sshCmd);
16 | int systemRet = iOS_system(sshCmd);
17 | NSLog(@"sshCmd=%s -> systemRet=%d", sshCmd, systemRet);
18 |
19 | _detectResultTv.text = @"TODO";
20 | }
21 | ```
22 |
23 | * 调用的函数:`iOS_system`
24 | * https://github.com/crifan/crifanLib/blob/master/c/crifanLib.c
25 |
26 | ```c
27 | /*==============================================================================
28 | iOS: implement deprecated system()
29 | ==============================================================================*/
30 |
31 | int iOS_system(const char* command){
32 | const int SYSTEM_FAIL = -1;
33 | int systemRet = SYSTEM_FAIL;
34 |
35 | // if (NULL == command) {
36 | // return systemRet;
37 | // }
38 |
39 | typedef int (*function_system) (const char *command);
40 | char* dyLibSystem = "/usr/lib/libSystem.dylib";
41 | void *libHandle = dlopen(dyLibSystem, RTLD_GLOBAL | RTLD_NOW);
42 | if (NULL == libHandle) {
43 | char* errStr = dlerror();
44 | printf("Failed to open %s, error: %s", dyLibSystem, errStr);
45 | } else {
46 | function_system libSystem_system = dlsym(libHandle, "system");
47 | if (NULL != libSystem_system){
48 | systemRet = libSystem_system(command);
49 | // return systemRet;
50 | }
51 | dlclose(libHandle);
52 | }
53 |
54 | return systemRet;
55 | }
56 | ```
57 |
58 |
--------------------------------------------------------------------------------
/src/jb_detection/system.md:
--------------------------------------------------------------------------------
1 | # system
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS越狱检测:system(NULL)
6 | * 【已解决】iOS代码XCode中报错:system is unavailable not available on iOS
7 | * 【已解决】iOS中传入fork调用system的返回值的含义和逻辑
8 |
9 | ---
10 |
11 | ```c
12 | - (IBAction)systemBtnClicked:(UIButton *)sender {
13 | _curBtnLbl.text = sender.titleLabel.text;
14 | NSLog(@"system() check");
15 |
16 | // int systemRet = system(NULL);
17 | const char* command = NULL;
18 | // command = "ls -lh";
19 | // command = "fork";
20 | int systemRet = iOS_system(command);
21 |
22 | const int SYSTEM_RET_SHELL_EXEC_CMD_FAIL = 32512; // == 0x7F00 -> bit 15-8 is 0x7F = 127
23 | const int SYSTEM_RET_FORK_FAIL = -1;
24 |
25 | NSString* conclusionStr = @"未知结果";
26 | if (NULL == command){
27 | // if (0 == systemRet){
28 | if (systemRet > 0){
29 | conclusionStr = @"sh存在 -> 越狱手机";
30 | } else {
31 | conclusionStr = @"sh不存在 -> 非越狱手机";
32 | }
33 | } else {
34 | if (SYSTEM_RET_SHELL_EXEC_CMD_FAIL == systemRet){
35 | conclusionStr = @"shell执行命令失败 -> 可能是非越狱手机";
36 | } else if (SYSTEM_RET_FORK_FAIL == systemRet){
37 | conclusionStr = @"fork或waitpid失败 -> 可能是非越狱手机";
38 | } else {
39 | conclusionStr = [NSString stringWithFormat: @"shell退出状态值为%d -> 无法判断", systemRet];
40 | }
41 | }
42 | NSString* systemResultStr = [NSString stringWithFormat: @"system(%s)返回: %d -> %@", command, systemRet, conclusionStr];
43 | NSLog(@"%@", systemResultStr);
44 | _detectResultTv.text = systemResultStr;
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/src/jb_detection/url_scheme.md:
--------------------------------------------------------------------------------
1 | # URL Scheme
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS的canOpenURL报错:failed for URL OSStatus error -10814
6 | * 【已解决】iOS越狱检测:cydia://开头的URL scheme
7 | * 【已解决】iOS中canOpenURL测试普通可以打开的app的url scheme
8 | * 【已解决】iOS的canOpenURL报错:This app is not allowed to query for scheme weixin
9 | *
10 |
11 | ---
12 |
13 | ```c
14 |
15 | - (IBAction)detectCydiaBtnClicked:(UIButton *)sender {
16 | _curBtnLbl.text = sender.titleLabel.text;
17 | NSLog(@"Clicked detect cydia://");
18 | BOOL canOpen = FALSE;
19 |
20 | // NSString *fakeCydiaStr = @"cydia://package/com.fake.packagename";
21 | // NSString *fakeCydiaStr = @"CYDIA://package/com.fake.packagename";
22 | // NSString *fakeCydiaStr = @"Cydia://package/xxx";
23 | // NSString *openPrefAbout = @"Prefs:root=General&path=About";
24 | // NSString *openPrefAbout = @"prefs:root=General&path=About";
25 |
26 | NSString *curToOpenStr = NULL;
27 |
28 | // curToOpenStr = @"weixin://";
29 | curToOpenStr = @"cydia://";
30 |
31 | NSURL *curToOpenUrl = [NSURL URLWithString:curToOpenStr];
32 | canOpen = [[UIApplication sharedApplication] canOpenURL:curToOpenUrl];
33 | NSString *canOpenStr = canOpen ? @"可以打开": @"无法打开";
34 | NSString *conclusionStr = canOpen ? @"可能是越狱手机": @"很可能不是越狱手机";
35 | NSString *resultStr = [NSString stringWithFormat:@"%@: %@\n-> %@", canOpenStr, curToOpenUrl, conclusionStr];
36 | NSLog(@"resultStr=%@", resultStr);
37 | _detectResultTv.text = resultStr;
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/src/other_summary/README.md:
--------------------------------------------------------------------------------
1 | # 其他心得
2 |
3 | TODO:
4 |
5 | * 【已解决】iOS中Choicy中变量的定义:kCFCoreFoundationVersionNumber
6 | *
--------------------------------------------------------------------------------