├── .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 | * --------------------------------------------------------------------------------