├── LICENSE ├── README.md ├── apple-touch-icon.png ├── bower.json ├── css ├── impress-demo.css ├── impress.css └── reverseEngineering.css ├── demo.html ├── favicon.png ├── images ├── Hopper-2.jpeg ├── Hopper.jpeg ├── IDA-1.jpeg ├── IDA-2.jpeg ├── MachOView-1.jpeg ├── MachOView-2.jpeg ├── class-dump.jpeg ├── crack-castle.png ├── cycript.jpeg ├── dumpdecrypt.jpeg ├── iOSOpenDev2.jpeg ├── ifile.PNG ├── iosOpenDev.jpeg ├── overview.jpeg ├── safe-castle.png ├── terminal.PNG ├── theos-1.jpeg └── theos-2.jpeg ├── index.html └── js └── impress.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alex Ao 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS-Reverse-Engineering-presentation 2 | 3 | ## Overview 4 | iOS-Reverse-Engineering-presentation introduce the knowledge and tool chain of reverse engineering of iOS, which covering the basic use of reverse engineering of iOS. 5 | 6 | ![OverView](https://github.com/aozhimin/iOS-Reverse-Engineering-presentation/blob/master/images/overview.jpeg) 7 | 8 | ## Usage 9 | clone the repo and open the index.html to show the slides or click github io link. 10 | 11 | ## Resources 12 | * [class-dump](https://github.com/nygard/class-dump) 13 | * [Theos](https://github.com/theos/theos) 14 | * [iOSOpenDev](https://github.com/kokoabim/iOSOpenDev) 15 | * [IDA](https://www.hex-rays.com/products/ida/) 16 | * [Hopper](https://www.hopperapp.com/) 17 | * [MachOView](https://github.com/gdbinit/MachOView) 18 | * [dumpdecrypted](https://github.com/stefanesser/dumpdecrypted) 19 | * [Clutch](https://github.com/KJCracks/Clutch) 20 | * [cycript](http://www.cycript.org/) 21 | * [LLDB commands](https://objccn.io/issue-19-2/) 22 | * [chisel](https://github.com/facebook/chisel) 23 | * [iosre](http://iosre.com/) 24 | 25 | ## Workaround 26 | when you start iOS Reverse Engineering, you should install many tools, You may encounter a variety of problems. Here is a list of possible workarounds. 27 | 28 | * [The solution to the failure of the iOSOpenDev installation 29 | ](https://www.ianisme.com/ios/2319.html) 30 | * [The solution to the failure of make package](https://blog.sunnyyoung.net/post/ni-xiang/2017-01-11-theosan-zhuang-yu-pei-zhi) 31 | 32 | > when you execute `make package` command to package, you may encounter a failure situation, wrong prompt is "dpkg-deb: warning: deprecated compression type 'lzma'; use xz instead'", you should change value of `_THEOS_PLATFORM_DPKG_DEB_COMPRESSION` to xz 33 | 34 | ## Author 35 | Alex Ao, aozhimin0811@gmail.com 36 | 37 | ## License 38 | 39 | iOS-Reverse-Engineering-presentation is released under the MIT license. See LICENSE for details. 40 | 41 | -------------------------------------------------------------------------------- /apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/apple-touch-icon.png -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "impress-js", 3 | "version": "0.5.3", 4 | "main": "./js/impress.js", 5 | "description": "It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com", 6 | "homepage": "https://github.com/bartaz/impress.js", 7 | "license" : ["http://bartaz.mit-license.org/", "http://www.gnu.org/licenses/"], 8 | "main": [ 9 | "./js/impress.js" 10 | ], 11 | "keywords": [ 12 | "slideshow", 13 | "css3" 14 | ], 15 | "author": { 16 | "name": "Bartek Szopka", 17 | "web": "http://bartaz.github.com" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /css/impress-demo.css: -------------------------------------------------------------------------------- 1 | /* 2 | So you like the style of impress.js demo? 3 | Or maybe you are just curious how it was done? 4 | 5 | You couldn't find a better place to find out! 6 | 7 | Welcome to the stylesheet impress.js demo presentation. 8 | 9 | Please remember that it is not meant to be a part of impress.js and is 10 | not required by impress.js. 11 | I expect that anyone creating a presentation for impress.js would create 12 | their own set of styles. 13 | 14 | But feel free to read through it and learn how to get the most of what 15 | impress.js provides. 16 | 17 | And let me be your guide. 18 | 19 | Shall we begin? 20 | */ 21 | 22 | 23 | /* 24 | We start with a good ol' reset. 25 | That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/ 26 | 27 | You can probably argue if it is needed here, or not, but for sure it 28 | doesn't do any harm and gives us a fresh start. 29 | */ 30 | 31 | html, body, div, span, applet, object, iframe, 32 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 33 | a, abbr, acronym, address, big, cite, code, 34 | del, dfn, em, img, ins, kbd, q, s, samp, 35 | small, strike, strong, sub, sup, tt, var, 36 | b, u, i, center, 37 | dl, dt, dd, ol, ul, li, 38 | fieldset, form, label, legend, 39 | table, caption, tbody, tfoot, thead, tr, th, td, 40 | article, aside, canvas, details, embed, 41 | figure, figcaption, footer, header, hgroup, 42 | menu, nav, output, ruby, section, summary, 43 | time, mark, audio, video { 44 | margin: 0; 45 | padding: 0; 46 | border: 0; 47 | font-size: 100%; 48 | font: inherit; 49 | vertical-align: baseline; 50 | } 51 | 52 | /* HTML5 display-role reset for older browsers */ 53 | article, aside, details, figcaption, figure, 54 | footer, header, hgroup, menu, nav, section { 55 | display: block; 56 | } 57 | body { 58 | line-height: 1; 59 | } 60 | ol, ul { 61 | list-style: none; 62 | } 63 | blockquote, q { 64 | quotes: none; 65 | } 66 | blockquote:before, blockquote:after, 67 | q:before, q:after { 68 | content: ''; 69 | content: none; 70 | } 71 | 72 | table { 73 | border-collapse: collapse; 74 | border-spacing: 0; 75 | } 76 | 77 | /* 78 | Now here is when interesting things start to appear. 79 | 80 | We set up styles with default font and nice gradient in the background. 81 | And yes, there is a lot of repetition there because of -prefixes but we don't 82 | want to leave anybody behind. 83 | */ 84 | body { 85 | font-family: 'PT Sans', sans-serif; 86 | min-height: 740px; 87 | 88 | background: rgb(215, 215, 215); 89 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190))); 90 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 91 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 92 | background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 93 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 94 | background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 95 | } 96 | 97 | /* 98 | Now let's bring some text styles back ... 99 | */ 100 | b, strong { font-weight: bold } 101 | i, em { font-style: italic } 102 | 103 | /* 104 | ... and give links a nice look. 105 | */ 106 | a { 107 | color: inherit; 108 | text-decoration: none; 109 | padding: 0 0.1em; 110 | background: rgba(255,255,255,0.5); 111 | text-shadow: -1px -1px 2px rgba(100,100,100,0.9); 112 | border-radius: 0.2em; 113 | 114 | -webkit-transition: 0.5s; 115 | -moz-transition: 0.5s; 116 | -ms-transition: 0.5s; 117 | -o-transition: 0.5s; 118 | transition: 0.5s; 119 | } 120 | 121 | a:hover, 122 | a:focus { 123 | background: rgba(255,255,255,1); 124 | text-shadow: -1px -1px 2px rgba(100,100,100,0.5); 125 | } 126 | 127 | /* 128 | Because the main point behind the impress.js demo is to demo impress.js 129 | we display a fallback message for users with browsers that don't support 130 | all the features required by it. 131 | 132 | All of the content will be still fully accessible for them, but I want 133 | them to know that they are missing something - that's what the demo is 134 | about, isn't it? 135 | 136 | And then we hide the message, when support is detected in the browser. 137 | */ 138 | 139 | .fallback-message { 140 | font-family: sans-serif; 141 | line-height: 1.3; 142 | 143 | width: 780px; 144 | padding: 10px 10px 0; 145 | margin: 20px auto; 146 | 147 | border: 1px solid #E4C652; 148 | border-radius: 10px; 149 | background: #EEDC94; 150 | } 151 | 152 | .fallback-message p { 153 | margin-bottom: 10px; 154 | } 155 | 156 | .impress-supported .fallback-message { 157 | display: none; 158 | } 159 | 160 | /* 161 | Now let's style the presentation steps. 162 | 163 | We start with basics to make sure it displays correctly in everywhere ... 164 | */ 165 | 166 | .step { 167 | position: relative; 168 | width: 900px; 169 | padding: 40px; 170 | margin: 20px auto; 171 | 172 | -webkit-box-sizing: border-box; 173 | -moz-box-sizing: border-box; 174 | -ms-box-sizing: border-box; 175 | -o-box-sizing: border-box; 176 | box-sizing: border-box; 177 | 178 | font-family: 'PT Serif', georgia, serif; 179 | font-size: 48px; 180 | line-height: 1.5; 181 | } 182 | 183 | /* 184 | ... and we enhance the styles for impress.js. 185 | 186 | Basically we remove the margin and make inactive steps a little bit transparent. 187 | */ 188 | .impress-enabled .step { 189 | margin: 0; 190 | opacity: 0.3; 191 | 192 | -webkit-transition: opacity 1s; 193 | -moz-transition: opacity 1s; 194 | -ms-transition: opacity 1s; 195 | -o-transition: opacity 1s; 196 | transition: opacity 1s; 197 | } 198 | 199 | .impress-enabled .step.active { opacity: 1 } 200 | 201 | /* 202 | These 'slide' step styles were heavily inspired by HTML5 Slides: 203 | http://html5slides.googlecode.com/svn/trunk/styles.css 204 | 205 | ;) 206 | 207 | They cover everything what you see on first three steps of the demo. 208 | */ 209 | .slide { 210 | display: block; 211 | 212 | width: 900px; 213 | height: 700px; 214 | padding: 40px 60px; 215 | 216 | background-color: white; 217 | border: 1px solid rgba(0, 0, 0, .3); 218 | border-radius: 10px; 219 | box-shadow: 0 2px 6px rgba(0, 0, 0, .1); 220 | 221 | color: rgb(102, 102, 102); 222 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1); 223 | 224 | font-family: 'Open Sans', Arial, sans-serif; 225 | font-size: 30px; 226 | line-height: 36px; 227 | letter-spacing: -1px; 228 | } 229 | 230 | .slide q { 231 | display: block; 232 | font-size: 50px; 233 | line-height: 72px; 234 | 235 | margin-top: 100px; 236 | } 237 | 238 | .slide q strong { 239 | white-space: nowrap; 240 | } 241 | 242 | /* 243 | And now we start to style each step separately. 244 | 245 | I agree that this may be not the most efficient, object-oriented and 246 | scalable way of styling, but most of steps have quite a custom look 247 | and typography tricks here and there, so they had to be styled separately. 248 | 249 | First is the title step with a big

(no room for padding) and some 250 | 3D positioning along Z axis. 251 | */ 252 | 253 | #title { 254 | padding: 0; 255 | } 256 | 257 | #title .try { 258 | font-size: 64px; 259 | position: absolute; 260 | top: -0.5em; 261 | left: 1.5em; 262 | 263 | -webkit-transform: translateZ(20px); 264 | -moz-transform: translateZ(20px); 265 | -ms-transform: translateZ(20px); 266 | -o-transform: translateZ(20px); 267 | transform: translateZ(20px); 268 | } 269 | 270 | #title h1 { 271 | font-size: 190px; 272 | 273 | -webkit-transform: translateZ(50px); 274 | -moz-transform: translateZ(50px); 275 | -ms-transform: translateZ(50px); 276 | -o-transform: translateZ(50px); 277 | transform: translateZ(50px); 278 | } 279 | 280 | #title .footnote { 281 | font-size: 32px; 282 | } 283 | 284 | /* 285 | Second step is nothing special, just a text with a link, so it doesn't need 286 | any special styling. 287 | 288 | Let's move to 'big thoughts' with centered text and custom font sizes. 289 | */ 290 | #big { 291 | width: 600px; 292 | text-align: center; 293 | font-size: 60px; 294 | line-height: 1; 295 | } 296 | 297 | #big b { 298 | display: block; 299 | font-size: 250px; 300 | line-height: 250px; 301 | } 302 | 303 | #big .thoughts { 304 | font-size: 90px; 305 | line-height: 150px; 306 | } 307 | 308 | /* 309 | 'Tiny ideas' just need some tiny styling. 310 | */ 311 | #tiny { 312 | width: 500px; 313 | text-align: center; 314 | } 315 | 316 | /* 317 | This step has some animated text ... 318 | */ 319 | #ing { width: 500px } 320 | 321 | /* 322 | ... so we define display to `inline-block` to enable transforms and 323 | transition duration to 0.5s ... 324 | */ 325 | #ing b { 326 | display: inline-block; 327 | -webkit-transition: 0.5s; 328 | -moz-transition: 0.5s; 329 | -ms-transition: 0.5s; 330 | -o-transition: 0.5s; 331 | transition: 0.5s; 332 | } 333 | 334 | /* 335 | ... and we want 'positioning` word to move up a bit when the step gets 336 | `present` class ... 337 | */ 338 | #ing.present .positioning { 339 | -webkit-transform: translateY(-10px); 340 | -moz-transform: translateY(-10px); 341 | -ms-transform: translateY(-10px); 342 | -o-transform: translateY(-10px); 343 | transform: translateY(-10px); 344 | } 345 | 346 | /* 347 | ... 'rotating' to rotate a quarter of a second later ... 348 | */ 349 | #ing.present .rotating { 350 | -webkit-transform: rotate(-10deg); 351 | -moz-transform: rotate(-10deg); 352 | -ms-transform: rotate(-10deg); 353 | -o-transform: rotate(-10deg); 354 | transform: rotate(-10deg); 355 | 356 | -webkit-transition-delay: 0.25s; 357 | -moz-transition-delay: 0.25s; 358 | -ms-transition-delay: 0.25s; 359 | -o-transition-delay: 0.25s; 360 | transition-delay: 0.25s; 361 | } 362 | 363 | /* 364 | ... and 'scaling' to scale down after another quarter of a second. 365 | */ 366 | #ing.present .scaling { 367 | -webkit-transform: scale(0.7); 368 | -moz-transform: scale(0.7); 369 | -ms-transform: scale(0.7); 370 | -o-transform: scale(0.7); 371 | transform: scale(0.7); 372 | 373 | -webkit-transition-delay: 0.5s; 374 | -moz-transition-delay: 0.5s; 375 | -ms-transition-delay: 0.5s; 376 | -o-transition-delay: 0.5s; 377 | transition-delay: 0.5s; 378 | } 379 | 380 | /* 381 | The 'imagination' step is again some boring font-sizing. 382 | */ 383 | 384 | #imagination { 385 | width: 600px; 386 | } 387 | 388 | #imagination .imagination { 389 | font-size: 78px; 390 | } 391 | 392 | /* 393 | There is nothing really special about 'use the source, Luke' step, too, 394 | except maybe of the Yoda background. 395 | 396 | As you can see below I've 'hard-coded' it in data URL. 397 | That's not the best way to serve images, but because that's just this one 398 | I decided it will be OK to have it this way. 399 | 400 | Just make sure you don't blindly copy this approach. 401 | */ 402 | #source { 403 | width: 700px; 404 | padding-bottom: 300px; 405 | 406 | /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */ 407 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==); 408 | background-position: bottom right; 409 | background-repeat: no-repeat; 410 | } 411 | 412 | #source q { 413 | font-size: 60px; 414 | } 415 | 416 | /* 417 | And the "it's in 3D" step again brings some 3D typography - just for fun. 418 | 419 | Because we want to position elements in 3D we set transform-style to 420 | `preserve-3d` on the paragraph. 421 | It is not needed by webkit browsers, but it is in Firefox. It's hard to say 422 | which behaviour is correct as 3D transforms spec is not very clear about it. 423 | */ 424 | #its-in-3d p { 425 | -webkit-transform-style: preserve-3d; 426 | -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */ 427 | -ms-transform-style: preserve-3d; 428 | -o-transform-style: preserve-3d; 429 | transform-style: preserve-3d; 430 | } 431 | 432 | /* 433 | Below we position each word separately along Z axis and we want it to transition 434 | to default position in 0.5s when the step gets `present` class. 435 | 436 | Quite a simple idea, but lot's of styles and prefixes. 437 | */ 438 | #its-in-3d span, 439 | #its-in-3d b { 440 | display: inline-block; 441 | -webkit-transform: translateZ(40px); 442 | -moz-transform: translateZ(40px); 443 | -ms-transform: translateZ(40px); 444 | -o-transform: translateZ(40px); 445 | transform: translateZ(40px); 446 | 447 | -webkit-transition: 0.5s; 448 | -moz-transition: 0.5s; 449 | -ms-transition: 0.5s; 450 | -o-transition: 0.5s; 451 | transition: 0.5s; 452 | } 453 | 454 | #its-in-3d .have { 455 | -webkit-transform: translateZ(-40px); 456 | -moz-transform: translateZ(-40px); 457 | -ms-transform: translateZ(-40px); 458 | -o-transform: translateZ(-40px); 459 | transform: translateZ(-40px); 460 | } 461 | 462 | #its-in-3d .you { 463 | -webkit-transform: translateZ(20px); 464 | -moz-transform: translateZ(20px); 465 | -ms-transform: translateZ(20px); 466 | -o-transform: translateZ(20px); 467 | transform: translateZ(20px); 468 | } 469 | 470 | #its-in-3d .noticed { 471 | -webkit-transform: translateZ(-40px); 472 | -moz-transform: translateZ(-40px); 473 | -ms-transform: translateZ(-40px); 474 | -o-transform: translateZ(-40px); 475 | transform: translateZ(-40px); 476 | } 477 | 478 | #its-in-3d .its { 479 | -webkit-transform: translateZ(60px); 480 | -moz-transform: translateZ(60px); 481 | -ms-transform: translateZ(60px); 482 | -o-transform: translateZ(60px); 483 | transform: translateZ(60px); 484 | } 485 | 486 | #its-in-3d .in { 487 | -webkit-transform: translateZ(-10px); 488 | -moz-transform: translateZ(-10px); 489 | -ms-transform: translateZ(-10px); 490 | -o-transform: translateZ(-10px); 491 | transform: translateZ(-10px); 492 | } 493 | 494 | #its-in-3d .footnote { 495 | font-size: 32px; 496 | 497 | -webkit-transform: translateZ(-10px); 498 | -moz-transform: translateZ(-10px); 499 | -ms-transform: translateZ(-10px); 500 | -o-transform: translateZ(-10px); 501 | transform: translateZ(-10px); 502 | } 503 | 504 | #its-in-3d.present span, 505 | #its-in-3d.present b { 506 | -webkit-transform: translateZ(0px); 507 | -moz-transform: translateZ(0px); 508 | -ms-transform: translateZ(0px); 509 | -o-transform: translateZ(0px); 510 | transform: translateZ(0px); 511 | } 512 | 513 | /* 514 | The last step is an overview. 515 | There is no content in it, so we make sure it's not visible because we want 516 | to be able to click on other steps. 517 | 518 | */ 519 | #overview { display: none } 520 | 521 | /* 522 | We also make other steps visible and give them a pointer cursor using the 523 | `impress-on-` class. 524 | */ 525 | .impress-on-overview .step { 526 | opacity: 1; 527 | cursor: pointer; 528 | } 529 | 530 | 531 | /* 532 | Now, when we have all the steps styled let's give users a hint how to navigate 533 | around the presentation. 534 | 535 | The best way to do this would be to use JavaScript, show a delayed hint for a 536 | first time users, then hide it and store a status in cookie or localStorage... 537 | 538 | But I wanted to have some CSS fun and avoid additional scripting... 539 | 540 | Let me explain it first, so maybe the transition magic will be more readable 541 | when you read the code. 542 | 543 | First of all I wanted the hint to appear only when user is idle for a while. 544 | You can't detect the 'idle' state in CSS, but I delayed a appearing of the 545 | hint by 5s using transition-delay. 546 | 547 | You also can't detect in CSS if the user is a first-time visitor, so I had to 548 | make an assumption that I'll only show the hint on the first step. And when 549 | the step is changed hide the hint, because I can assume that user already 550 | knows how to navigate. 551 | 552 | To summarize it - hint is shown when the user is on the first step for longer 553 | than 5 seconds. 554 | 555 | The other problem I had was caused by the fact that I wanted the hint to fade 556 | in and out. It can be easily achieved by transitioning the opacity property. 557 | But that also meant that the hint was always on the screen, even if totally 558 | transparent. It covered part of the screen and you couldn't correctly clicked 559 | through it. 560 | Unfortunately you cannot transition between display `block` and `none` in pure 561 | CSS, so I needed a way to not only fade out the hint but also move it out of 562 | the screen. 563 | 564 | I solved this problem by positioning the hint below the bottom of the screen 565 | with CSS transform and moving it up to show it. But I also didn't want this move 566 | to be visible. I wanted the hint only to fade in and out visually, so I delayed 567 | the fade in transition, so it starts when the hint is already in its correct 568 | position on the screen. 569 | 570 | I know, it sounds complicated ... maybe it would be easier with the code? 571 | */ 572 | 573 | .hint { 574 | /* 575 | We hide the hint until presentation is started and from browsers not supporting 576 | impress.js, as they will have a linear scrollable view ... 577 | */ 578 | display: none; 579 | 580 | /* 581 | ... and give it some fixed position and nice styles. 582 | */ 583 | position: fixed; 584 | left: 0; 585 | right: 0; 586 | bottom: 200px; 587 | 588 | background: rgba(0,0,0,0.5); 589 | color: #EEE; 590 | text-align: center; 591 | 592 | font-size: 50px; 593 | padding: 20px; 594 | 595 | z-index: 100; 596 | 597 | /* 598 | By default we don't want the hint to be visible, so we make it transparent ... 599 | */ 600 | opacity: 0; 601 | 602 | /* 603 | ... and position it below the bottom of the screen (relative to it's fixed position) 604 | */ 605 | -webkit-transform: translateY(400px); 606 | -moz-transform: translateY(400px); 607 | -ms-transform: translateY(400px); 608 | -o-transform: translateY(400px); 609 | transform: translateY(400px); 610 | 611 | /* 612 | Now let's imagine that the hint is visible and we want to fade it out and move out 613 | of the screen. 614 | 615 | So we define the transition on the opacity property with 1s duration and another 616 | transition on transform property delayed by 1s so it will happen after the fade out 617 | on opacity finished. 618 | 619 | This way user will not see the hint moving down. 620 | */ 621 | -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s; 622 | -moz-transition: opacity 1s, -moz-transform 0.5s 1s; 623 | -ms-transition: opacity 1s, -ms-transform 0.5s 1s; 624 | -o-transition: opacity 1s, -o-transform 0.5s 1s; 625 | transition: opacity 1s, transform 0.5s 1s; 626 | } 627 | 628 | /* 629 | Now we 'enable' the hint when presentation is initialized ... 630 | */ 631 | .impress-enabled .hint { display: block } 632 | 633 | /* 634 | ... and we will show it when the first step (with id 'bored') is active. 635 | */ 636 | .impress-on-bored .hint { 637 | /* 638 | We remove the transparency and position the hint in its default fixed 639 | position. 640 | */ 641 | opacity: 1; 642 | 643 | -webkit-transform: translateY(0px); 644 | -moz-transform: translateY(0px); 645 | -ms-transform: translateY(0px); 646 | -o-transform: translateY(0px); 647 | transform: translateY(0px); 648 | 649 | /* 650 | Now for fade in transition we have the oposite situation from the one 651 | above. 652 | 653 | First after 4.5s delay we animate the transform property to move the hint 654 | into its correct position and after that we fade it in with opacity 655 | transition. 656 | */ 657 | -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s; 658 | -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s; 659 | -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s; 660 | -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s; 661 | transition: opacity 1s 5s, transform 0.5s 4.5s; 662 | } 663 | 664 | /* 665 | And as the last thing there is a workaround for quite strange bug. 666 | It happens a lot in Chrome. I don't remember if I've seen it in Firefox. 667 | 668 | Sometimes the element positioned in 3D (especially when it's moved back 669 | along Z axis) is not clickable, because it falls 'behind' the 670 | element. 671 | 672 | To prevent this, I decided to make non clickable by setting 673 | pointer-events property to `none` value. 674 | Value if this property is inherited, so to make everything else clickable 675 | I bring it back on the #impress element. 676 | 677 | If you want to know more about `pointer-events` here are some docs: 678 | https://developer.mozilla.org/en/CSS/pointer-events 679 | 680 | There is one very important thing to notice about this workaround - it makes 681 | everything 'unclickable' except what's in #impress element. 682 | 683 | So use it wisely ... or don't use at all. 684 | */ 685 | .impress-enabled { pointer-events: none } 686 | .impress-enabled #impress { pointer-events: auto } 687 | 688 | /* 689 | There is one funny thing I just realized. 690 | 691 | Thanks to this workaround above everything except #impress element is invisible 692 | for click events. That means that the hint element is also not clickable. 693 | So basically all of this transforms and delayed transitions trickery was probably 694 | not needed at all... 695 | 696 | But it was fun to learn about it, wasn't it? 697 | */ 698 | 699 | /* 700 | That's all I have for you in this file. 701 | Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it 702 | for you. 703 | */ 704 | -------------------------------------------------------------------------------- /css/impress.css: -------------------------------------------------------------------------------- 1 | /* 2 | So you like the style of impress.js demo? 3 | Or maybe you are just curious how it was done? 4 | 5 | You couldn't find a better place to find out! 6 | 7 | Welcome to the stylesheet impress.js demo presentation. 8 | 9 | Please remember that it is not meant to be a part of impress.js and is 10 | not required by impress.js. 11 | I expect that anyone creating a presentation for impress.js would create 12 | their own set of styles. 13 | 14 | But feel free to read through it and learn how to get the most of what 15 | impress.js provides. 16 | 17 | And let me be your guide. 18 | 19 | Shall we begin? 20 | */ 21 | 22 | 23 | /* 24 | We start with a good ol' reset. 25 | That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/ 26 | 27 | You can probably argue if it is needed here, or not, but for sure it 28 | doesn't do any harm and gives us a fresh start. 29 | */ 30 | 31 | html, body, div, span, applet, object, iframe, 32 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 33 | a, abbr, acronym, address, big, cite, code, 34 | del, dfn, em, img, ins, kbd, q, s, samp, 35 | small, strike, strong, sub, sup, tt, var, 36 | b, u, i, center, 37 | dl, dt, dd, ol, ul, li, 38 | fieldset, form, label, legend, 39 | table, caption, tbody, tfoot, thead, tr, th, td, 40 | article, aside, canvas, details, embed, 41 | figure, figcaption, footer, header, hgroup, 42 | menu, nav, output, ruby, section, summary, 43 | time, mark, audio, video { 44 | margin: 0; 45 | padding: 0; 46 | border: 0; 47 | font-size: 100%; 48 | font: inherit; 49 | vertical-align: baseline; 50 | } 51 | 52 | /* HTML5 display-role reset for older browsers */ 53 | article, aside, details, figcaption, figure, 54 | footer, header, hgroup, menu, nav, section { 55 | display: block; 56 | } 57 | body { 58 | line-height: 1; 59 | } 60 | ol, ul { 61 | list-style: none; 62 | } 63 | blockquote, q { 64 | quotes: none; 65 | } 66 | blockquote:before, blockquote:after, 67 | q:before, q:after { 68 | content: ''; 69 | content: none; 70 | } 71 | 72 | table { 73 | border-collapse: collapse; 74 | border-spacing: 0; 75 | } 76 | 77 | /* 78 | Now here is when interesting things start to appear. 79 | 80 | We set up styles with default font and nice gradient in the background. 81 | And yes, there is a lot of repetition there because of -prefixes but we don't 82 | want to leave anybody behind. 83 | */ 84 | body { 85 | font-family: 'PT Sans', sans-serif; 86 | min-height: 740px; 87 | 88 | background: rgb(215, 215, 215); 89 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190))); 90 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 91 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 92 | background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 93 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 94 | background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 95 | } 96 | 97 | /* 98 | Now let's bring some text styles back ... 99 | */ 100 | b, strong { font-weight: bold } 101 | i, em { font-style: italic } 102 | 103 | /* 104 | ... and give links a nice look. 105 | */ 106 | a { 107 | color: inherit; 108 | text-decoration: none; 109 | padding: 0 0.1em; 110 | background: rgba(255,255,255,0.5); 111 | text-shadow: -1px -1px 2px rgba(100,100,100,0.9); 112 | border-radius: 0.2em; 113 | 114 | -webkit-transition: 0.5s; 115 | -moz-transition: 0.5s; 116 | -ms-transition: 0.5s; 117 | -o-transition: 0.5s; 118 | transition: 0.5s; 119 | } 120 | 121 | a:hover, 122 | a:focus { 123 | background: rgba(255,255,255,1); 124 | text-shadow: -1px -1px 2px rgba(100,100,100,0.5); 125 | } 126 | 127 | /* 128 | Because the main point behind the impress.js demo is to demo impress.js 129 | we display a fallback message for users with browsers that don't support 130 | all the features required by it. 131 | 132 | All of the content will be still fully accessible for them, but I want 133 | them to know that they are missing something - that's what the demo is 134 | about, isn't it? 135 | 136 | And then we hide the message, when support is detected in the browser. 137 | */ 138 | 139 | .fallback-message { 140 | font-family: sans-serif; 141 | line-height: 1.3; 142 | 143 | width: 780px; 144 | padding: 10px 10px 0; 145 | margin: 20px auto; 146 | 147 | border: 1px solid #E4C652; 148 | border-radius: 10px; 149 | background: #EEDC94; 150 | } 151 | 152 | .fallback-message p { 153 | margin-bottom: 10px; 154 | } 155 | 156 | .impress-supported .fallback-message { 157 | display: none; 158 | } 159 | 160 | /* 161 | Now let's style the presentation steps. 162 | 163 | We start with basics to make sure it displays correctly in everywhere ... 164 | */ 165 | 166 | .step { 167 | position: relative; 168 | width: 900px; 169 | padding: 40px; 170 | margin: 20px auto; 171 | 172 | -webkit-box-sizing: border-box; 173 | -moz-box-sizing: border-box; 174 | -ms-box-sizing: border-box; 175 | -o-box-sizing: border-box; 176 | box-sizing: border-box; 177 | 178 | font-family: 'PT Serif', georgia, serif; 179 | font-size: 48px; 180 | line-height: 1.5; 181 | } 182 | 183 | /* 184 | ... and we enhance the styles for impress.js. 185 | 186 | Basically we remove the margin and make inactive steps a little bit transparent. 187 | */ 188 | .impress-enabled .step { 189 | margin: 0; 190 | opacity: 0.3; 191 | 192 | -webkit-transition: opacity 1s; 193 | -moz-transition: opacity 1s; 194 | -ms-transition: opacity 1s; 195 | -o-transition: opacity 1s; 196 | transition: opacity 1s; 197 | } 198 | 199 | .impress-enabled .step.active { opacity: 1 } 200 | 201 | /* 202 | These 'slide' step styles were heavily inspired by HTML5 Slides: 203 | http://html5slides.googlecode.com/svn/trunk/styles.css 204 | 205 | ;) 206 | 207 | They cover everything what you see on first three steps of the demo. 208 | */ 209 | .slide { 210 | display: block; 211 | 212 | width: 900px; 213 | height: 700px; 214 | padding: 40px 60px; 215 | 216 | background-color: white; 217 | border: 1px solid rgba(0, 0, 0, .3); 218 | border-radius: 10px; 219 | box-shadow: 0 2px 6px rgba(0, 0, 0, .1); 220 | 221 | color: rgb(102, 102, 102); 222 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1); 223 | 224 | font-family: 'Open Sans', Arial, sans-serif; 225 | font-size: 30px; 226 | line-height: 36px; 227 | letter-spacing: -1px; 228 | } 229 | 230 | .slide q { 231 | display: block; 232 | font-size: 50px; 233 | line-height: 72px; 234 | 235 | margin-top: 100px; 236 | } 237 | 238 | .slide q strong { 239 | white-space: nowrap; 240 | } 241 | 242 | /* 243 | And now we start to style each step separately. 244 | 245 | I agree that this may be not the most efficient, object-oriented and 246 | scalable way of styling, but most of steps have quite a custom look 247 | and typography tricks here and there, so they had to be styled separately. 248 | 249 | First is the title step with a big

(no room for padding) and some 250 | 3D positioning along Z axis. 251 | */ 252 | 253 | /* 254 | We also make other steps visible and give them a pointer cursor using the 255 | `impress-on-` class. 256 | */ 257 | .impress-on-overview .step { 258 | opacity: 1; 259 | cursor: pointer; 260 | } 261 | 262 | 263 | /* 264 | Now, when we have all the steps styled let's give users a hint how to navigate 265 | around the presentation. 266 | 267 | The best way to do this would be to use JavaScript, show a delayed hint for a 268 | first time users, then hide it and store a status in cookie or localStorage... 269 | 270 | But I wanted to have some CSS fun and avoid additional scripting... 271 | 272 | Let me explain it first, so maybe the transition magic will be more readable 273 | when you read the code. 274 | 275 | First of all I wanted the hint to appear only when user is idle for a while. 276 | You can't detect the 'idle' state in CSS, but I delayed a appearing of the 277 | hint by 5s using transition-delay. 278 | 279 | You also can't detect in CSS if the user is a first-time visitor, so I had to 280 | make an assumption that I'll only show the hint on the first step. And when 281 | the step is changed hide the hint, because I can assume that user already 282 | knows how to navigate. 283 | 284 | To summarize it - hint is shown when the user is on the first step for longer 285 | than 5 seconds. 286 | 287 | The other problem I had was caused by the fact that I wanted the hint to fade 288 | in and out. It can be easily achieved by transitioning the opacity property. 289 | But that also meant that the hint was always on the screen, even if totally 290 | transparent. It covered part of the screen and you couldn't correctly clicked 291 | through it. 292 | Unfortunately you cannot transition between display `block` and `none` in pure 293 | CSS, so I needed a way to not only fade out the hint but also move it out of 294 | the screen. 295 | 296 | I solved this problem by positioning the hint below the bottom of the screen 297 | with CSS transform and moving it up to show it. But I also didn't want this move 298 | to be visible. I wanted the hint only to fade in and out visually, so I delayed 299 | the fade in transition, so it starts when the hint is already in its correct 300 | position on the screen. 301 | 302 | I know, it sounds complicated ... maybe it would be easier with the code? 303 | */ 304 | 305 | .hint { 306 | /* 307 | We hide the hint until presentation is started and from browsers not supporting 308 | impress.js, as they will have a linear scrollable view ... 309 | */ 310 | display: none; 311 | 312 | /* 313 | ... and give it some fixed position and nice styles. 314 | */ 315 | position: fixed; 316 | left: 0; 317 | right: 0; 318 | bottom: 200px; 319 | 320 | background: rgba(0,0,0,0.5); 321 | color: #EEE; 322 | text-align: center; 323 | 324 | font-size: 50px; 325 | padding: 20px; 326 | 327 | z-index: 100; 328 | 329 | /* 330 | By default we don't want the hint to be visible, so we make it transparent ... 331 | */ 332 | opacity: 0; 333 | 334 | /* 335 | ... and position it below the bottom of the screen (relative to it's fixed position) 336 | */ 337 | -webkit-transform: translateY(400px); 338 | -moz-transform: translateY(400px); 339 | -ms-transform: translateY(400px); 340 | -o-transform: translateY(400px); 341 | transform: translateY(400px); 342 | 343 | /* 344 | Now let's imagine that the hint is visible and we want to fade it out and move out 345 | of the screen. 346 | 347 | So we define the transition on the opacity property with 1s duration and another 348 | transition on transform property delayed by 1s so it will happen after the fade out 349 | on opacity finished. 350 | 351 | This way user will not see the hint moving down. 352 | */ 353 | -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s; 354 | -moz-transition: opacity 1s, -moz-transform 0.5s 1s; 355 | -ms-transition: opacity 1s, -ms-transform 0.5s 1s; 356 | -o-transition: opacity 1s, -o-transform 0.5s 1s; 357 | transition: opacity 1s, transform 0.5s 1s; 358 | } 359 | 360 | /* 361 | Now we 'enable' the hint when presentation is initialized ... 362 | */ 363 | .impress-enabled .hint { display: block } 364 | 365 | /* 366 | And as the last thing there is a workaround for quite strange bug. 367 | It happens a lot in Chrome. I don't remember if I've seen it in Firefox. 368 | 369 | Sometimes the element positioned in 3D (especially when it's moved back 370 | along Z axis) is not clickable, because it falls 'behind' the 371 | element. 372 | 373 | To prevent this, I decided to make non clickable by setting 374 | pointer-events property to `none` value. 375 | Value if this property is inherited, so to make everything else clickable 376 | I bring it back on the #impress element. 377 | 378 | If you want to know more about `pointer-events` here are some docs: 379 | https://developer.mozilla.org/en/CSS/pointer-events 380 | 381 | There is one very important thing to notice about this workaround - it makes 382 | everything 'unclickable' except what's in #impress element. 383 | 384 | So use it wisely ... or don't use at all. 385 | */ 386 | .impress-enabled { pointer-events: none } 387 | .impress-enabled #impress { pointer-events: auto } 388 | 389 | /* 390 | There is one funny thing I just realized. 391 | 392 | Thanks to this workaround above everything except #impress element is invisible 393 | for click events. That means that the hint element is also not clickable. 394 | So basically all of this transforms and delayed transitions trickery was probably 395 | not needed at all... 396 | 397 | But it was fun to learn about it, wasn't it? 398 | */ 399 | 400 | /* 401 | That's all I have for you in this file. 402 | Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it 403 | for you. 404 | */ 405 | -------------------------------------------------------------------------------- /css/reverseEngineering.css: -------------------------------------------------------------------------------- 1 | /* 2 | ... and we will show it when the first step (with id 'index') is active. 3 | */ 4 | .impress-on-title .hint { 5 | /* 6 | We remove the transparency and position the hint in its default fixed 7 | position. 8 | */ 9 | opacity: 1; 10 | 11 | -webkit-transform: translateY(0px); 12 | -moz-transform: translateY(0px); 13 | -ms-transform: translateY(0px); 14 | -o-transform: translateY(0px); 15 | transform: translateY(0px); 16 | 17 | /* 18 | Now for fade in transition we have the oposite situation from the one 19 | above. 20 | 21 | First after 4.5s delay we animate the transform property to move the hint 22 | into its correct position and after that we fade it in with opacity 23 | transition. 24 | */ 25 | -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s; 26 | -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s; 27 | -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s; 28 | -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s; 29 | transition: opacity 1s 5s, transform 0.5s 4.5s; 30 | } 31 | 32 | .step h1 { 33 | font-weight: bold; 34 | text-align: center; 35 | } 36 | 37 | .step p, .step q, .step ul { 38 | font-size: 25px; 39 | } 40 | 41 | .step ul ul { 42 | font-size: 22px; 43 | padding-left: 30px; 44 | } 45 | 46 | .step ul li { 47 | list-style-type: disc; 48 | list-style-position: inside; 49 | } 50 | 51 | .step u { 52 | font-size: 30px; 53 | padding: 10px; 54 | border: 1px solid black; 55 | border-radius: 10px; 56 | text-decoration: none; 57 | } 58 | 59 | #title { 60 | padding: 0; 61 | } 62 | 63 | #title .try { 64 | font-size: 30px; 65 | position: absolute; 66 | top: -0.5em; 67 | left: 1.5em; 68 | 69 | -webkit-transform: translateZ(20px); 70 | -moz-transform: translateZ(20px); 71 | -ms-transform: translateZ(20px); 72 | -o-transform: translateZ(20px); 73 | transform: translateZ(20px); 74 | } 75 | 76 | #title h1 { 77 | font-size: 80px; 78 | 79 | -webkit-transform: translateZ(50px); 80 | -moz-transform: translateZ(50px); 81 | -ms-transform: translateZ(50px); 82 | -o-transform: translateZ(50px); 83 | transform: translateZ(50px); 84 | } 85 | 86 | #title .footnote { 87 | font-size: 32px; 88 | } 89 | 90 | #family p { 91 | margin-bottom: 30px; 92 | } 93 | 94 | /* 95 | Second step is nothing special, just a text with a link, so it doesn't need 96 | any special styling. 97 | 98 | Let's move to 'big thoughts' with centered text and custom font sizes. 99 | */ 100 | #big { 101 | width: 600px; 102 | text-align: center; 103 | font-size: 60px; 104 | line-height: 1; 105 | } 106 | 107 | #big b { 108 | display: block; 109 | font-size: 250px; 110 | line-height: 250px; 111 | } 112 | 113 | #big .thoughts { 114 | font-size: 90px; 115 | line-height: 150px; 116 | } 117 | 118 | /* 119 | 'Tiny ideas' just need some tiny styling. 120 | */ 121 | #tiny { 122 | width: 500px; 123 | text-align: center; 124 | } 125 | 126 | /* 127 | This step has some animated text ... 128 | */ 129 | #ing { width: 500px } 130 | 131 | /* 132 | ... so we define display to `inline-block` to enable transforms and 133 | transition duration to 0.5s ... 134 | */ 135 | #ing b { 136 | display: inline-block; 137 | -webkit-transition: 0.5s; 138 | -moz-transition: 0.5s; 139 | -ms-transition: 0.5s; 140 | -o-transition: 0.5s; 141 | transition: 0.5s; 142 | } 143 | 144 | /* 145 | ... and we want 'positioning` word to move up a bit when the step gets 146 | `present` class ... 147 | */ 148 | #ing.present .positioning { 149 | -webkit-transform: translateY(-10px); 150 | -moz-transform: translateY(-10px); 151 | -ms-transform: translateY(-10px); 152 | -o-transform: translateY(-10px); 153 | transform: translateY(-10px); 154 | } 155 | 156 | /* 157 | ... 'rotating' to rotate a quarter of a second later ... 158 | */ 159 | #ing.present .rotating { 160 | -webkit-transform: rotate(-10deg); 161 | -moz-transform: rotate(-10deg); 162 | -ms-transform: rotate(-10deg); 163 | -o-transform: rotate(-10deg); 164 | transform: rotate(-10deg); 165 | 166 | -webkit-transition-delay: 0.25s; 167 | -moz-transition-delay: 0.25s; 168 | -ms-transition-delay: 0.25s; 169 | -o-transition-delay: 0.25s; 170 | transition-delay: 0.25s; 171 | } 172 | 173 | /* 174 | ... and 'scaling' to scale down after another quarter of a second. 175 | */ 176 | #ing.present .scaling { 177 | -webkit-transform: scale(0.7); 178 | -moz-transform: scale(0.7); 179 | -ms-transform: scale(0.7); 180 | -o-transform: scale(0.7); 181 | transform: scale(0.7); 182 | 183 | -webkit-transition-delay: 0.5s; 184 | -moz-transition-delay: 0.5s; 185 | -ms-transition-delay: 0.5s; 186 | -o-transition-delay: 0.5s; 187 | transition-delay: 0.5s; 188 | } 189 | 190 | /* 191 | The 'imagination' step is again some boring font-sizing. 192 | */ 193 | 194 | #imagination { 195 | width: 600px; 196 | } 197 | 198 | #imagination .imagination { 199 | font-size: 78px; 200 | } 201 | 202 | /* 203 | There is nothing really special about 'use the source, Luke' step, too, 204 | except maybe of the Yoda background. 205 | 206 | As you can see below I've 'hard-coded' it in data URL. 207 | That's not the best way to serve images, but because that's just this one 208 | I decided it will be OK to have it this way. 209 | 210 | Just make sure you don't blindly copy this approach. 211 | */ 212 | #source { 213 | width: 700px; 214 | padding-bottom: 300px; 215 | 216 | /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */ 217 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==); 218 | background-position: bottom right; 219 | background-repeat: no-repeat; 220 | } 221 | 222 | #source q { 223 | font-size: 60px; 224 | } 225 | 226 | /* 227 | And the "it's in 3D" step again brings some 3D typography - just for fun. 228 | 229 | Because we want to position elements in 3D we set transform-style to 230 | `preserve-3d` on the paragraph. 231 | It is not needed by webkit browsers, but it is in Firefox. It's hard to say 232 | which behaviour is correct as 3D transforms spec is not very clear about it. 233 | */ 234 | #its-in-3d p { 235 | -webkit-transform-style: preserve-3d; 236 | -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */ 237 | -ms-transform-style: preserve-3d; 238 | -o-transform-style: preserve-3d; 239 | transform-style: preserve-3d; 240 | } 241 | 242 | /* 243 | Below we position each word separately along Z axis and we want it to transition 244 | to default position in 0.5s when the step gets `present` class. 245 | 246 | Quite a simple idea, but lot's of styles and prefixes. 247 | */ 248 | #its-in-3d span, 249 | #its-in-3d b { 250 | display: inline-block; 251 | -webkit-transform: translateZ(40px); 252 | -moz-transform: translateZ(40px); 253 | -ms-transform: translateZ(40px); 254 | -o-transform: translateZ(40px); 255 | transform: translateZ(40px); 256 | 257 | -webkit-transition: 0.5s; 258 | -moz-transition: 0.5s; 259 | -ms-transition: 0.5s; 260 | -o-transition: 0.5s; 261 | transition: 0.5s; 262 | } 263 | 264 | #its-in-3d .have { 265 | -webkit-transform: translateZ(-40px); 266 | -moz-transform: translateZ(-40px); 267 | -ms-transform: translateZ(-40px); 268 | -o-transform: translateZ(-40px); 269 | transform: translateZ(-40px); 270 | } 271 | 272 | #its-in-3d .you { 273 | -webkit-transform: translateZ(20px); 274 | -moz-transform: translateZ(20px); 275 | -ms-transform: translateZ(20px); 276 | -o-transform: translateZ(20px); 277 | transform: translateZ(20px); 278 | } 279 | 280 | #its-in-3d .noticed { 281 | -webkit-transform: translateZ(-40px); 282 | -moz-transform: translateZ(-40px); 283 | -ms-transform: translateZ(-40px); 284 | -o-transform: translateZ(-40px); 285 | transform: translateZ(-40px); 286 | } 287 | 288 | #its-in-3d .its { 289 | -webkit-transform: translateZ(60px); 290 | -moz-transform: translateZ(60px); 291 | -ms-transform: translateZ(60px); 292 | -o-transform: translateZ(60px); 293 | transform: translateZ(60px); 294 | } 295 | 296 | #its-in-3d .in { 297 | -webkit-transform: translateZ(-10px); 298 | -moz-transform: translateZ(-10px); 299 | -ms-transform: translateZ(-10px); 300 | -o-transform: translateZ(-10px); 301 | transform: translateZ(-10px); 302 | } 303 | 304 | #its-in-3d .footnote { 305 | font-size: 32px; 306 | 307 | -webkit-transform: translateZ(-10px); 308 | -moz-transform: translateZ(-10px); 309 | -ms-transform: translateZ(-10px); 310 | -o-transform: translateZ(-10px); 311 | transform: translateZ(-10px); 312 | } 313 | 314 | #its-in-3d.present span, 315 | #its-in-3d.present b { 316 | -webkit-transform: translateZ(0px); 317 | -moz-transform: translateZ(0px); 318 | -ms-transform: translateZ(0px); 319 | -o-transform: translateZ(0px); 320 | transform: translateZ(0px); 321 | } 322 | 323 | /* 324 | The last step is an overview. 325 | There is no content in it, so we make sure it's not visible because we want 326 | to be able to click on other steps. 327 | 328 | */ 329 | #overview { display: none } 330 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46 | 47 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | impress.js | presentation tool based on the power of CSS3 transforms and transitions in modern browsers | by Bartek Szopka @bartaz 72 | 73 | 74 | 75 | 76 | 77 | 78 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 117 | 118 | 119 | 122 |
123 |

Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

124 |

For the best experience please use the latest Chrome, Safari or Firefox browser.

125 |
126 | 127 | 148 |
149 | 150 | 167 |
168 | Aren't you just bored with all those slides-based presentations? 169 |
170 | 171 | 186 |
187 | Don't you think that presentations given in modern browsers shouldn't copy the limits of 'classic' slide decks? 188 |
189 | 190 |
191 | Would you like to impress your audience with stunning visualization of your talk? 192 |
193 | 194 | 204 |
205 | then you should try 206 |

impress.js*

207 | * no rhyme intended 208 |
209 | 210 | 218 |
219 |

It's a presentation tool
220 | inspired by the idea behind prezi.com
221 | and based on the power of CSS3 transforms and transitions in modern browsers.

222 |
223 | 224 |
225 |

visualize your big thoughts

226 |
227 | 228 | 237 |
238 |

and tiny ideas

239 |
240 | 241 | 261 |
262 |

by positioning, rotating and scaling them on an infinite canvas

263 |
264 | 265 |
266 |

the only limit is your imagination

267 |
268 | 269 |
270 |

want to know more?

271 | use the source, Luke! 272 |
273 | 274 |
275 |

one more thing...

276 |
277 | 278 | 290 |
291 |

have you noticed it's in 3D*?

292 | * beat that, prezi ;) 293 |
294 | 295 | 310 |
311 |
312 | 313 |
314 | 315 | 339 |
340 |

Use a spacebar or arrow keys to navigate

341 |
342 | 347 | 348 | 363 | 364 | 365 | 366 | 390 | 391 | 392 | 393 | 394 | 422 | 423 | 438 | 439 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/favicon.png -------------------------------------------------------------------------------- /images/Hopper-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/Hopper-2.jpeg -------------------------------------------------------------------------------- /images/Hopper.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/Hopper.jpeg -------------------------------------------------------------------------------- /images/IDA-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/IDA-1.jpeg -------------------------------------------------------------------------------- /images/IDA-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/IDA-2.jpeg -------------------------------------------------------------------------------- /images/MachOView-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/MachOView-1.jpeg -------------------------------------------------------------------------------- /images/MachOView-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/MachOView-2.jpeg -------------------------------------------------------------------------------- /images/class-dump.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/class-dump.jpeg -------------------------------------------------------------------------------- /images/crack-castle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/crack-castle.png -------------------------------------------------------------------------------- /images/cycript.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/cycript.jpeg -------------------------------------------------------------------------------- /images/dumpdecrypt.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/dumpdecrypt.jpeg -------------------------------------------------------------------------------- /images/iOSOpenDev2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/iOSOpenDev2.jpeg -------------------------------------------------------------------------------- /images/ifile.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/ifile.PNG -------------------------------------------------------------------------------- /images/iosOpenDev.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/iosOpenDev.jpeg -------------------------------------------------------------------------------- /images/overview.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/overview.jpeg -------------------------------------------------------------------------------- /images/safe-castle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/safe-castle.png -------------------------------------------------------------------------------- /images/terminal.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/terminal.PNG -------------------------------------------------------------------------------- /images/theos-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/theos-1.jpeg -------------------------------------------------------------------------------- /images/theos-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aozhimin/iOS-Reverse-Engineering-presentation/2f217ec856a2823ce51e9bacbb6faf6e203abe5c/images/theos-2.jpeg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | iOS 逆向工程和应用安全 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

尊驾的浏览器太老土啦!无法满足impress.js的要求,现在你只能看到一些最朴素的演示画面。

20 |

为了欣赏漂亮的演示画面,请使用先进、优雅、快速、美丽、免费的现代浏览器,比如 Chrome, Safari or Firefox

21 |
22 |
23 | 24 |
25 |
26 |

iOS 逆向工程

应用安全

27 |

@aozhimin

28 |
29 |
30 | 31 |
32 |

大纲

33 |
    34 |
  • iOS 逆向工程
  • 35 |
      36 |
    • 简介
    • 37 |
    • 工具集
    • 38 |
    • iOS 逆向开发的套路
    • 39 |
    40 |
  • iOS 应用安全
  • 41 |
      42 |
    • 网络安全
    • 43 |
    • 本地文件和数据安全
    • 44 |
    • 代码安全
    • 45 |
    46 |
47 |
48 | 49 |
50 |

简介

51 |
    52 |
  • 什么是 iOS 逆向工程
  • 53 |
  • iOS 逆向工程的作用
  • 54 |
  • iOS 逆向分析方法
  • 55 |
56 | 57 |
58 | 59 |
60 |

什么是 iOS 逆向工程

61 |

软件的逆向工程指的是通过分析一个程序或系统的功能、结构或行为,将它的技术实现或设计细节推导出来的过程。当我们因为工作需要,或是对一个软件的功能很感兴趣,却又拿不到它的源代码时,往往可以通过逆向工程的方式对它进行分析。


62 |

63 | 照着配方包饺子,是正向开发 吃着饺子推配方,是逆向工程 64 |

65 | 66 |

67 | 配方:API 调用顺序
68 | 效果:实现原理 69 |

70 | 71 |
72 | 73 |
74 |

iOS 逆向工程的作用

75 |
76 |
    77 |
  • 与安全相关的 iOS 逆向工程
  • 78 |
      79 |
    • 评定安全等级
    • 80 |
    • 逆向恶意软件
    • 81 |
    • 检查软件后门
    • 82 |
    • 去除使用限制
    • 83 |
    84 |
  • 与开发相关的 iOS 逆向工程
  • 85 |
      86 |
    • 逆向系统调用
    • 87 |
    • 借鉴别的软件
    • 88 |
    89 |
  • 提升个人能力和素养
  • 90 |
91 | 95 |
96 | 97 |
98 |
99 | 100 |
101 |

固若金汤的城堡

102 |

103 | 104 | 105 |
106 | 107 |
108 |
109 | 110 |
111 |

上帝模式

112 | 113 |
114 | 115 | 116 |
117 |

iOS 逆向分析方法

118 |
    119 |
  • 网络分析
  • 120 |

    通过分析和篡改接口数据,可以有效的通过接口数据来控制客户端行为,常用的抓包工具有 tcpdump, WireShark, Charles 等,Windows平台有 Fiddler

    121 |
  • 静态分析
  • 122 |

    静态分析法是在不运行行 iOS 应用的情形下,对应用进行静态分析的一种方法。比如获取应用的文件系统结构,本地文件的分析、使用反汇编工具(Disassembler,比如 Hopper Disassembler)查看内部代码,分析代码结构也是静态分析

    123 |
  • 动态分析
  • 124 |

    动态分析法是在 iOS 应用的运行过程中进行动态分析的一种方法,通过调试来分析代码,获得内存的状态等等。通过动态分析法,可以在观察应用的文件、网络等。动态分析中还常使用调试器(Debugger,比如LLDB)分析应用的内部结构与原理。甚至可以使用工具(比如Cycript,后面会详细介绍该工具)动态修改内存,给内存打补丁。

    125 |
126 |
127 | 128 |
129 |

工具集

130 |
    131 |
  • Mac 工具集
  • 132 |
  • iOS 工具集
  • 133 |
134 |
135 | 136 |
137 |

Mac 工具集

138 |
    139 |
  • class-dump
  • 140 |
  • Thoes 和 iOSOpenDev
  • 141 |
  • Reveal
  • 142 |
  • IDA 和 Hopper Disassembler
  • 143 |
  • MachOView
  • 144 |
145 |
146 | 147 |
148 |

class-dump

149 |

class-dump 是用来 dump 目标对象的 class 信息的工具。它利用 Objective-C 语言的 runtime 特性,将存储在 Mach-O 文件中的 @interface 和 @protocol 信息提取出来,并生成相应的 .h 文件

150 | 151 | 152 |
153 | 154 |
155 |

Theos

156 |

Theos 是一个基于 Unix 平台(OS X,iOS…)和大多数的 Linux 平台下进行越狱开发的集成开发环境,由 Dustin Howett 大神开发并开源到 GitHub 上,它的特点是搭建简单、Logos语法简单、编译发布简单

157 | 158 |
159 | 160 |
161 |

Theos

162 | 163 |

编译打包,这个过程可能会遇到问题,比如 make package 过程,可以通过 make package message=yes 输出详细错误信息

164 | 165 |
166 | 167 |
168 |

iOSOpenDev

169 |

iOSOpenDev 是基于 Theos 开发的,被整合到 Xcode 中,编译更方便,不用自己写 Makefile,另外同样提供了很多模版

170 | 171 |

iOSOpenDev 提供的模板

172 |
173 | 174 |
175 |

iOSOpenDev

176 | 177 |

安装过程中可能遇到的问题

178 |
179 | 180 | 181 |
182 |

IDA

183 |

IDA 是逆向工程中最负盛名的神奇之一,它可以把 class-dump 的点连成线,它支持 Windows、Linux 和 Mac OS X 的多平台反汇编器/调试器,功能非常强大

184 |
185 | 186 |

IDA 启动界面

187 |
188 | 189 |
190 | 191 | 192 |
193 |

IDA

194 |
195 | 196 |

IDA 主界面

197 |
198 | 199 | 200 |
201 | 202 |
203 |

Hopper Disassembler

204 |

Hopper,它有Mac OS X和 Linux 版本,能够反汇编32/64位 Mac,Linux,Windows 和 iOS 可执行文件。

205 | 206 | 207 | 208 |
209 | 210 | 211 |
212 |

Hopper Disassembler

213 |
214 | 215 |

Hopper 生成的伪代码

216 |
217 |
218 | 219 | 220 |
221 |

MachOView

222 |

MachOView 是可视化的 Mach-O 文件浏览器,它提供了浏览和编辑 Intel 和 ARM 二进制的功能 223 |

224 | 225 |

MachOView 主界面

226 |
227 | 228 |
229 | 230 |
231 |

MachOView

232 |
233 | 234 |

MachOView 查看加密信息

235 |
236 |
237 | 238 | 239 | 240 |
241 |

iOS 工具集

242 |
    243 |
  • iFile
  • 244 |
  • MobileTerminal
  • 245 |
  • AppCrackr dumpdecrypted 和 Clutch
  • 246 |
  • Cycript
  • 247 |
  • GDB 和 LLDB
  • 248 |
249 |
250 | 251 |
252 |

iFile

253 |

iFile 是运行于 iOS 上的文件管理软件,它能实现文件的各类操作,甚至可以安装 deb 插件, 安装源是 BigBoss。

254 |
255 | 256 |
257 |
258 | 259 |
260 |

MobileTerminal

261 |

MobileTerminal 是开源的 iOS 版 Terminal,比较实用的场景是在没有计算机的情况下结合 Cycript 进行代码测试。 262 |

263 | 264 |
265 |
266 | 267 |
268 |

AppCrackr dumpdecrypted 和 Clutch

269 |

从 AppStore 获取 IPA 文件无法直接通过 class-dump 获取 .h 文件,也无法通过前面介绍的静态分析工具 IDA 进行反汇编。这种情况我们就需要对文件砸壳,AppCrackr 就是一款对 App 破解去壳的工具,AppCrackr 的成功率要远远高于 dumpdecrypted 和 Clutch。但正是由于它功能太过强大,引起公愤,导致其核心功能被迫关闭。 270 | 271 | 272 |

273 | 274 |
275 | 276 |

解密二进制文件

277 |
    278 |
  • 分析二进制文件以确定其加密部分的位置
  • 279 |
  • 用 LLDB 运行应用程序
  • 280 |
  • 将未加密部分转储到磁盘
  • 281 |
  • 复制一份原始的二进制文件
  • 282 |
  • 把上一步复制的二进制文件中的 cryptid 标识删除
  • 283 |
  • 把未加密的段复制到上一步的二进制文件中
  • 284 |
285 |
286 | 287 | 288 |
289 | 290 |

解密二进制文件

291 |
292 | 293 |

dumpdecrypted 脱壳 App 的过程

294 |
295 |
296 | 297 |
298 | 299 |

Cycript

300 |

Cycript 是一门脚本语言,可以看做Objective-JavaScript,它可以很方便的帮忙测试函数和验证一下猜想。

301 |
302 | 303 |
304 |
305 | 306 |
307 | 308 |

GDB 和 LLDB

309 |

LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。

310 |
    311 |
  • help
  • 312 |
  • print
  • 313 |
  • expression
  • 314 |
  • 变量
  • 315 |
  • register write
  • 316 |
  • 断点
  • 317 |
  • script
  • 318 |
319 |
320 | 321 |
322 |

iOS 逆向开发的套路

323 |
    324 |
  • 观察、猜测,寻找分析切入点
  • 325 |
  • 用 dumpdecrypted 砸壳
  • 326 |
  • 用 class-dump 导出 Objective-C 头文件
  • 327 |
  • 用 Cycript 定位目标视图
  • 328 |
  • 获取目标视图的 UIViewController 或 delegate
  • 329 |
  • 在 controller 的头文件中寻找蛛丝马迹
  • 330 |
  • 用 Hopper 和 LLDB 的组合还原调用逻辑
  • 331 |
  • 用 Theos 或 iOSOpenDev 编写插件
  • 332 |
333 |
334 | 335 |
336 |

iOS 应用安全

337 |
    338 |
  • 网络安全
  • 339 |
  • 本地文件和数据安全
  • 340 |
  • 代码安全
  • 341 |
342 |
343 | 344 |
345 |

网络安全

346 |

截获网络请求,破解通信协议并模拟客户端登录,伪造用户行为,对 iOS 用户数据造成危害

347 |

怎么防?

348 |
    349 |
  • 加密
  • 350 |
  • 使用二进制协议传输数据
  • 351 |
352 |
353 | 354 |
355 |

本地文件和数据安全

356 |

移动端本地文件中常常会存储一些敏感信息,包括 NSUserDefaults,Plist,缓存和日志文件,而如果没有做好防御措施,就会造成数据泄露

357 |

怎么防?

358 |
    359 |
  • 尽量不存储敏感信息
  • 360 |
  • 数据加密
  • 361 |
362 |
363 | 364 |
365 |

代码安全

366 |
    367 |
  • 字符串加密
  • 368 |
  • 类名方法名混淆
  • 369 |
  • 程序代码混淆
  • 370 |
  • 越狱检测
  • 371 |
  • 核心代码加密,用 C 或 C++ 实现
  • 372 |
373 |
374 | 375 | 376 |
377 |                         Q && A 378 | 379 |
380 | 381 |
382 |
383 | 384 |
385 | 390 | 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /js/impress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * impress.js 3 | * 4 | * impress.js is a presentation tool based on the power of CSS3 transforms and transitions 5 | * in modern browsers and inspired by the idea behind prezi.com. 6 | * 7 | * 8 | * Copyright 2011-2012 Bartek Szopka (@bartaz) 9 | * 10 | * Released under the MIT and GPL Licenses. 11 | * 12 | * ------------------------------------------------ 13 | * author: Bartek Szopka 14 | * version: 0.5.3 15 | * url: http://bartaz.github.com/impress.js/ 16 | * source: http://github.com/bartaz/impress.js/ 17 | */ 18 | 19 | /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, latedef:true, newcap:true, 20 | noarg:true, noempty:true, undef:true, strict:true, browser:true */ 21 | 22 | // You are one of those who like to know how things work inside? 23 | // Let me show you the cogs that make impress.js run... 24 | (function ( document, window ) { 25 | 'use strict'; 26 | 27 | // HELPER FUNCTIONS 28 | 29 | // `pfx` is a function that takes a standard CSS property name as a parameter 30 | // and returns it's prefixed version valid for current browser it runs in. 31 | // The code is heavily inspired by Modernizr http://www.modernizr.com/ 32 | var pfx = (function () { 33 | 34 | var style = document.createElement('dummy').style, 35 | prefixes = 'Webkit Moz O ms Khtml'.split(' '), 36 | memory = {}; 37 | 38 | return function ( prop ) { 39 | if ( typeof memory[ prop ] === "undefined" ) { 40 | 41 | var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), 42 | props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); 43 | 44 | memory[ prop ] = null; 45 | for ( var i in props ) { 46 | if ( style[ props[i] ] !== undefined ) { 47 | memory[ prop ] = props[i]; 48 | break; 49 | } 50 | } 51 | 52 | } 53 | 54 | return memory[ prop ]; 55 | }; 56 | 57 | })(); 58 | 59 | // `arraify` takes an array-like object and turns it into real Array 60 | // to make all the Array.prototype goodness available. 61 | var arrayify = function ( a ) { 62 | return [].slice.call( a ); 63 | }; 64 | 65 | // `css` function applies the styles given in `props` object to the element 66 | // given as `el`. It runs all property names through `pfx` function to make 67 | // sure proper prefixed version of the property is used. 68 | var css = function ( el, props ) { 69 | var key, pkey; 70 | for ( key in props ) { 71 | if ( props.hasOwnProperty(key) ) { 72 | pkey = pfx(key); 73 | if ( pkey !== null ) { 74 | el.style[pkey] = props[key]; 75 | } 76 | } 77 | } 78 | return el; 79 | }; 80 | 81 | // `toNumber` takes a value given as `numeric` parameter and tries to turn 82 | // it into a number. If it is not possible it returns 0 (or other value 83 | // given as `fallback`). 84 | var toNumber = function (numeric, fallback) { 85 | return isNaN(numeric) ? (fallback || 0) : Number(numeric); 86 | }; 87 | 88 | // `byId` returns element with given `id` - you probably have guessed that ;) 89 | var byId = function ( id ) { 90 | return document.getElementById(id); 91 | }; 92 | 93 | // `$` returns first element for given CSS `selector` in the `context` of 94 | // the given element or whole document. 95 | var $ = function ( selector, context ) { 96 | context = context || document; 97 | return context.querySelector(selector); 98 | }; 99 | 100 | // `$$` return an array of elements for given CSS `selector` in the `context` of 101 | // the given element or whole document. 102 | var $$ = function ( selector, context ) { 103 | context = context || document; 104 | return arrayify( context.querySelectorAll(selector) ); 105 | }; 106 | 107 | // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data 108 | // and triggers it on element given as `el`. 109 | var triggerEvent = function (el, eventName, detail) { 110 | var event = document.createEvent("CustomEvent"); 111 | event.initCustomEvent(eventName, true, true, detail); 112 | el.dispatchEvent(event); 113 | }; 114 | 115 | // `translate` builds a translate transform string for given data. 116 | var translate = function ( t ) { 117 | return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) "; 118 | }; 119 | 120 | // `rotate` builds a rotate transform string for given data. 121 | // By default the rotations are in X Y Z order that can be reverted by passing `true` 122 | // as second parameter. 123 | var rotate = function ( r, revert ) { 124 | var rX = " rotateX(" + r.x + "deg) ", 125 | rY = " rotateY(" + r.y + "deg) ", 126 | rZ = " rotateZ(" + r.z + "deg) "; 127 | 128 | return revert ? rZ+rY+rX : rX+rY+rZ; 129 | }; 130 | 131 | // `scale` builds a scale transform string for given data. 132 | var scale = function ( s ) { 133 | return " scale(" + s + ") "; 134 | }; 135 | 136 | // `perspective` builds a perspective transform string for given data. 137 | var perspective = function ( p ) { 138 | return " perspective(" + p + "px) "; 139 | }; 140 | 141 | // `getElementFromHash` returns an element located by id from hash part of 142 | // window location. 143 | var getElementFromHash = function () { 144 | // get id from url # by removing `#` or `#/` from the beginning, 145 | // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work 146 | return byId( window.location.hash.replace(/^#\/?/,"") ); 147 | }; 148 | 149 | // `computeWindowScale` counts the scale factor between window size and size 150 | // defined for the presentation in the config. 151 | var computeWindowScale = function ( config ) { 152 | var hScale = window.innerHeight / config.height, 153 | wScale = window.innerWidth / config.width, 154 | scale = hScale > wScale ? wScale : hScale; 155 | 156 | if (config.maxScale && scale > config.maxScale) { 157 | scale = config.maxScale; 158 | } 159 | 160 | if (config.minScale && scale < config.minScale) { 161 | scale = config.minScale; 162 | } 163 | 164 | return scale; 165 | }; 166 | 167 | // CHECK SUPPORT 168 | var body = document.body; 169 | 170 | var ua = navigator.userAgent.toLowerCase(); 171 | var impressSupported = 172 | // browser should support CSS 3D transtorms 173 | ( pfx("perspective") !== null ) && 174 | 175 | // and `classList` and `dataset` APIs 176 | ( body.classList ) && 177 | ( body.dataset ) && 178 | 179 | // but some mobile devices need to be blacklisted, 180 | // because their CSS 3D support or hardware is not 181 | // good enough to run impress.js properly, sorry... 182 | ( ua.search(/(iphone)|(ipod)|(android)/) === -1 ); 183 | 184 | if (!impressSupported) { 185 | // we can't be sure that `classList` is supported 186 | body.className += " impress-not-supported "; 187 | } else { 188 | body.classList.remove("impress-not-supported"); 189 | body.classList.add("impress-supported"); 190 | } 191 | 192 | // GLOBALS AND DEFAULTS 193 | 194 | // This is were the root elements of all impress.js instances will be kept. 195 | // Yes, this means you can have more than one instance on a page, but I'm not 196 | // sure if it makes any sense in practice ;) 197 | var roots = {}; 198 | 199 | // some default config values. 200 | var defaults = { 201 | width: 1024, 202 | height: 768, 203 | maxScale: 1, 204 | minScale: 0, 205 | 206 | perspective: 1000, 207 | 208 | transitionDuration: 1000 209 | }; 210 | 211 | // it's just an empty function ... and a useless comment. 212 | var empty = function () { return false; }; 213 | 214 | // IMPRESS.JS API 215 | 216 | // And that's where interesting things will start to happen. 217 | // It's the core `impress` function that returns the impress.js API 218 | // for a presentation based on the element with given id ('impress' 219 | // by default). 220 | var impress = window.impress = function ( rootId ) { 221 | 222 | // If impress.js is not supported by the browser return a dummy API 223 | // it may not be a perfect solution but we return early and avoid 224 | // running code that may use features not implemented in the browser. 225 | if (!impressSupported) { 226 | return { 227 | init: empty, 228 | goto: empty, 229 | prev: empty, 230 | next: empty 231 | }; 232 | } 233 | 234 | rootId = rootId || "impress"; 235 | 236 | // if given root is already initialized just return the API 237 | if (roots["impress-root-" + rootId]) { 238 | return roots["impress-root-" + rootId]; 239 | } 240 | 241 | // data of all presentation steps 242 | var stepsData = {}; 243 | 244 | // element of currently active step 245 | var activeStep = null; 246 | 247 | // current state (position, rotation and scale) of the presentation 248 | var currentState = null; 249 | 250 | // array of step elements 251 | var steps = null; 252 | 253 | // configuration options 254 | var config = null; 255 | 256 | // scale factor of the browser window 257 | var windowScale = null; 258 | 259 | // root presentation elements 260 | var root = byId( rootId ); 261 | var canvas = document.createElement("div"); 262 | 263 | var initialized = false; 264 | 265 | // STEP EVENTS 266 | // 267 | // There are currently two step events triggered by impress.js 268 | // `impress:stepenter` is triggered when the step is shown on the 269 | // screen (the transition from the previous one is finished) and 270 | // `impress:stepleave` is triggered when the step is left (the 271 | // transition to next step just starts). 272 | 273 | // reference to last entered step 274 | var lastEntered = null; 275 | 276 | // `onStepEnter` is called whenever the step element is entered 277 | // but the event is triggered only if the step is different than 278 | // last entered step. 279 | var onStepEnter = function (step) { 280 | if (lastEntered !== step) { 281 | triggerEvent(step, "impress:stepenter"); 282 | lastEntered = step; 283 | } 284 | }; 285 | 286 | // `onStepLeave` is called whenever the step element is left 287 | // but the event is triggered only if the step is the same as 288 | // last entered step. 289 | var onStepLeave = function (step) { 290 | if (lastEntered === step) { 291 | triggerEvent(step, "impress:stepleave"); 292 | lastEntered = null; 293 | } 294 | }; 295 | 296 | // `initStep` initializes given step element by reading data from its 297 | // data attributes and setting correct styles. 298 | var initStep = function ( el, idx ) { 299 | var data = el.dataset, 300 | step = { 301 | translate: { 302 | x: toNumber(data.x), 303 | y: toNumber(data.y), 304 | z: toNumber(data.z) 305 | }, 306 | rotate: { 307 | x: toNumber(data.rotateX), 308 | y: toNumber(data.rotateY), 309 | z: toNumber(data.rotateZ || data.rotate) 310 | }, 311 | scale: toNumber(data.scale, 1), 312 | el: el 313 | }; 314 | 315 | if ( !el.id ) { 316 | el.id = "step-" + (idx + 1); 317 | } 318 | 319 | stepsData["impress-" + el.id] = step; 320 | 321 | css(el, { 322 | position: "absolute", 323 | transform: "translate(-50%,-50%)" + 324 | translate(step.translate) + 325 | rotate(step.rotate) + 326 | scale(step.scale), 327 | transformStyle: "preserve-3d" 328 | }); 329 | }; 330 | 331 | // `init` API function that initializes (and runs) the presentation. 332 | var init = function () { 333 | if (initialized) { return; } 334 | 335 | // First we set up the viewport for mobile devices. 336 | // For some reason iPad goes nuts when it is not done properly. 337 | var meta = $("meta[name='viewport']") || document.createElement("meta"); 338 | meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no"; 339 | if (meta.parentNode !== document.head) { 340 | meta.name = 'viewport'; 341 | document.head.appendChild(meta); 342 | } 343 | 344 | // initialize configuration object 345 | var rootData = root.dataset; 346 | config = { 347 | width: toNumber( rootData.width, defaults.width ), 348 | height: toNumber( rootData.height, defaults.height ), 349 | maxScale: toNumber( rootData.maxScale, defaults.maxScale ), 350 | minScale: toNumber( rootData.minScale, defaults.minScale ), 351 | perspective: toNumber( rootData.perspective, defaults.perspective ), 352 | transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration ) 353 | }; 354 | 355 | windowScale = computeWindowScale( config ); 356 | 357 | // wrap steps with "canvas" element 358 | arrayify( root.childNodes ).forEach(function ( el ) { 359 | canvas.appendChild( el ); 360 | }); 361 | root.appendChild(canvas); 362 | 363 | // set initial styles 364 | document.documentElement.style.height = "100%"; 365 | 366 | css(body, { 367 | height: "100%", 368 | overflow: "hidden" 369 | }); 370 | 371 | var rootStyles = { 372 | position: "absolute", 373 | transformOrigin: "top left", 374 | transition: "all 0s ease-in-out", 375 | transformStyle: "preserve-3d" 376 | }; 377 | 378 | css(root, rootStyles); 379 | css(root, { 380 | top: "50%", 381 | left: "50%", 382 | transform: perspective( config.perspective/windowScale ) + scale( windowScale ) 383 | }); 384 | css(canvas, rootStyles); 385 | 386 | body.classList.remove("impress-disabled"); 387 | body.classList.add("impress-enabled"); 388 | 389 | // get and init steps 390 | steps = $$(".step", root); 391 | steps.forEach( initStep ); 392 | 393 | // set a default initial state of the canvas 394 | currentState = { 395 | translate: { x: 0, y: 0, z: 0 }, 396 | rotate: { x: 0, y: 0, z: 0 }, 397 | scale: 1 398 | }; 399 | 400 | initialized = true; 401 | 402 | triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] }); 403 | }; 404 | 405 | // `getStep` is a helper function that returns a step element defined by parameter. 406 | // If a number is given, step with index given by the number is returned, if a string 407 | // is given step element with such id is returned, if DOM element is given it is returned 408 | // if it is a correct step element. 409 | var getStep = function ( step ) { 410 | if (typeof step === "number") { 411 | step = step < 0 ? steps[ steps.length + step] : steps[ step ]; 412 | } else if (typeof step === "string") { 413 | step = byId(step); 414 | } 415 | return (step && step.id && stepsData["impress-" + step.id]) ? step : null; 416 | }; 417 | 418 | // used to reset timeout for `impress:stepenter` event 419 | var stepEnterTimeout = null; 420 | 421 | // `goto` API function that moves to step given with `el` parameter (by index, id or element), 422 | // with a transition `duration` optionally given as second parameter. 423 | var goto = function ( el, duration ) { 424 | 425 | if ( !initialized || !(el = getStep(el)) ) { 426 | // presentation not initialized or given element is not a step 427 | return false; 428 | } 429 | 430 | // Sometimes it's possible to trigger focus on first link with some keyboard action. 431 | // Browser in such a case tries to scroll the page to make this element visible 432 | // (even that body overflow is set to hidden) and it breaks our careful positioning. 433 | // 434 | // So, as a lousy (and lazy) workaround we will make the page scroll back to the top 435 | // whenever slide is selected 436 | // 437 | // If you are reading this and know any better way to handle it, I'll be glad to hear about it! 438 | window.scrollTo(0, 0); 439 | 440 | var step = stepsData["impress-" + el.id]; 441 | 442 | if ( activeStep ) { 443 | activeStep.classList.remove("active"); 444 | body.classList.remove("impress-on-" + activeStep.id); 445 | } 446 | el.classList.add("active"); 447 | 448 | body.classList.add("impress-on-" + el.id); 449 | 450 | // compute target state of the canvas based on given step 451 | var target = { 452 | rotate: { 453 | x: -step.rotate.x, 454 | y: -step.rotate.y, 455 | z: -step.rotate.z 456 | }, 457 | translate: { 458 | x: -step.translate.x, 459 | y: -step.translate.y, 460 | z: -step.translate.z 461 | }, 462 | scale: 1 / step.scale 463 | }; 464 | 465 | // Check if the transition is zooming in or not. 466 | // 467 | // This information is used to alter the transition style: 468 | // when we are zooming in - we start with move and rotate transition 469 | // and the scaling is delayed, but when we are zooming out we start 470 | // with scaling down and move and rotation are delayed. 471 | var zoomin = target.scale >= currentState.scale; 472 | 473 | duration = toNumber(duration, config.transitionDuration); 474 | var delay = (duration / 2); 475 | 476 | // if the same step is re-selected, force computing window scaling, 477 | // because it is likely to be caused by window resize 478 | if (el === activeStep) { 479 | windowScale = computeWindowScale(config); 480 | } 481 | 482 | var targetScale = target.scale * windowScale; 483 | 484 | // trigger leave of currently active element (if it's not the same step again) 485 | if (activeStep && activeStep !== el) { 486 | onStepLeave(activeStep); 487 | } 488 | 489 | // Now we alter transforms of `root` and `canvas` to trigger transitions. 490 | // 491 | // And here is why there are two elements: `root` and `canvas` - they are 492 | // being animated separately: 493 | // `root` is used for scaling and `canvas` for translate and rotations. 494 | // Transitions on them are triggered with different delays (to make 495 | // visually nice and 'natural' looking transitions), so we need to know 496 | // that both of them are finished. 497 | css(root, { 498 | // to keep the perspective look similar for different scales 499 | // we need to 'scale' the perspective, too 500 | transform: perspective( config.perspective / targetScale ) + scale( targetScale ), 501 | transitionDuration: duration + "ms", 502 | transitionDelay: (zoomin ? delay : 0) + "ms" 503 | }); 504 | 505 | css(canvas, { 506 | transform: rotate(target.rotate, true) + translate(target.translate), 507 | transitionDuration: duration + "ms", 508 | transitionDelay: (zoomin ? 0 : delay) + "ms" 509 | }); 510 | 511 | // Here is a tricky part... 512 | // 513 | // If there is no change in scale or no change in rotation and translation, it means there was actually 514 | // no delay - because there was no transition on `root` or `canvas` elements. 515 | // We want to trigger `impress:stepenter` event in the correct moment, so here we compare the current 516 | // and target values to check if delay should be taken into account. 517 | // 518 | // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on 519 | // - it's simply comparing all the values. 520 | if ( currentState.scale === target.scale || 521 | (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y && 522 | currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x && 523 | currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { 524 | delay = 0; 525 | } 526 | 527 | // store current state 528 | currentState = target; 529 | activeStep = el; 530 | 531 | // And here is where we trigger `impress:stepenter` event. 532 | // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. 533 | // 534 | // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way 535 | // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` 536 | // event is only triggered when there was a transition (change in the values) caused some bugs and 537 | // made the code really complicated, cause I had to handle all the conditions separately. And it still 538 | // needed a `setTimeout` fallback for the situations when there is no transition at all. 539 | // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. 540 | // 541 | // If you want learn something interesting and see how it was done with `transitionend` go back to 542 | // version 0.5.2 of impress.js: http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js 543 | window.clearTimeout(stepEnterTimeout); 544 | stepEnterTimeout = window.setTimeout(function() { 545 | onStepEnter(activeStep); 546 | }, duration + delay); 547 | 548 | return el; 549 | }; 550 | 551 | // `prev` API function goes to previous step (in document order) 552 | var prev = function () { 553 | var prev = steps.indexOf( activeStep ) - 1; 554 | prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; 555 | 556 | return goto(prev); 557 | }; 558 | 559 | // `next` API function goes to next step (in document order) 560 | var next = function () { 561 | var next = steps.indexOf( activeStep ) + 1; 562 | next = next < steps.length ? steps[ next ] : steps[ 0 ]; 563 | 564 | return goto(next); 565 | }; 566 | 567 | // Adding some useful classes to step elements. 568 | // 569 | // All the steps that have not been shown yet are given `future` class. 570 | // When the step is entered the `future` class is removed and the `present` 571 | // class is given. When the step is left `present` class is replaced with 572 | // `past` class. 573 | // 574 | // So every step element is always in one of three possible states: 575 | // `future`, `present` and `past`. 576 | // 577 | // There classes can be used in CSS to style different types of steps. 578 | // For example the `present` class can be used to trigger some custom 579 | // animations when step is shown. 580 | root.addEventListener("impress:init", function(){ 581 | // STEP CLASSES 582 | steps.forEach(function (step) { 583 | step.classList.add("future"); 584 | }); 585 | 586 | root.addEventListener("impress:stepenter", function (event) { 587 | event.target.classList.remove("past"); 588 | event.target.classList.remove("future"); 589 | event.target.classList.add("present"); 590 | }, false); 591 | 592 | root.addEventListener("impress:stepleave", function (event) { 593 | event.target.classList.remove("present"); 594 | event.target.classList.add("past"); 595 | }, false); 596 | 597 | }, false); 598 | 599 | // Adding hash change support. 600 | root.addEventListener("impress:init", function(){ 601 | 602 | // last hash detected 603 | var lastHash = ""; 604 | 605 | // `#/step-id` is used instead of `#step-id` to prevent default browser 606 | // scrolling to element in hash. 607 | // 608 | // And it has to be set after animation finishes, because in Chrome it 609 | // makes transtion laggy. 610 | // BUG: http://code.google.com/p/chromium/issues/detail?id=62820 611 | root.addEventListener("impress:stepenter", function (event) { 612 | window.location.hash = lastHash = "#/" + event.target.id; 613 | }, false); 614 | 615 | window.addEventListener("hashchange", function () { 616 | // When the step is entered hash in the location is updated 617 | // (just few lines above from here), so the hash change is 618 | // triggered and we would call `goto` again on the same element. 619 | // 620 | // To avoid this we store last entered hash and compare. 621 | if (window.location.hash !== lastHash) { 622 | goto( getElementFromHash() ); 623 | } 624 | }, false); 625 | 626 | // START 627 | // by selecting step defined in url or first step of the presentation 628 | goto(getElementFromHash() || steps[0], 0); 629 | }, false); 630 | 631 | body.classList.add("impress-disabled"); 632 | 633 | // store and return API for given impress.js root element 634 | return (roots[ "impress-root-" + rootId ] = { 635 | init: init, 636 | goto: goto, 637 | next: next, 638 | prev: prev 639 | }); 640 | 641 | }; 642 | 643 | // flag that can be used in JS to check if browser have passed the support test 644 | impress.supported = impressSupported; 645 | 646 | })(document, window); 647 | 648 | // NAVIGATION EVENTS 649 | 650 | // As you can see this part is separate from the impress.js core code. 651 | // It's because these navigation actions only need what impress.js provides with 652 | // its simple API. 653 | // 654 | // In future I think about moving it to make them optional, move to separate files 655 | // and treat more like a 'plugins'. 656 | (function ( document, window ) { 657 | 'use strict'; 658 | 659 | // throttling function calls, by Remy Sharp 660 | // http://remysharp.com/2010/07/21/throttling-function-calls/ 661 | var throttle = function (fn, delay) { 662 | var timer = null; 663 | return function () { 664 | var context = this, args = arguments; 665 | clearTimeout(timer); 666 | timer = setTimeout(function () { 667 | fn.apply(context, args); 668 | }, delay); 669 | }; 670 | }; 671 | 672 | // wait for impress.js to be initialized 673 | document.addEventListener("impress:init", function (event) { 674 | // Getting API from event data. 675 | // So you don't event need to know what is the id of the root element 676 | // or anything. `impress:init` event data gives you everything you 677 | // need to control the presentation that was just initialized. 678 | var api = event.detail.api; 679 | 680 | // KEYBOARD NAVIGATION HANDLERS 681 | 682 | // Prevent default keydown action when one of supported key is pressed. 683 | document.addEventListener("keydown", function ( event ) { 684 | if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { 685 | event.preventDefault(); 686 | } 687 | }, false); 688 | 689 | // Trigger impress action (next or prev) on keyup. 690 | 691 | // Supported keys are: 692 | // [space] - quite common in presentation software to move forward 693 | // [up] [right] / [down] [left] - again common and natural addition, 694 | // [pgdown] / [pgup] - often triggered by remote controllers, 695 | // [tab] - this one is quite controversial, but the reason it ended up on 696 | // this list is quite an interesting story... Remember that strange part 697 | // in the impress.js code where window is scrolled to 0,0 on every presentation 698 | // step, because sometimes browser scrolls viewport because of the focused element? 699 | // Well, the [tab] key by default navigates around focusable elements, so clicking 700 | // it very often caused scrolling to focused element and breaking impress.js 701 | // positioning. I didn't want to just prevent this default action, so I used [tab] 702 | // as another way to moving to next step... And yes, I know that for the sake of 703 | // consistency I should add [shift+tab] as opposite action... 704 | document.addEventListener("keyup", function ( event ) { 705 | if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { 706 | switch( event.keyCode ) { 707 | case 33: // pg up 708 | case 37: // left 709 | case 38: // up 710 | api.prev(); 711 | break; 712 | case 9: // tab 713 | case 32: // space 714 | case 34: // pg down 715 | case 39: // right 716 | case 40: // down 717 | api.next(); 718 | break; 719 | } 720 | 721 | event.preventDefault(); 722 | } 723 | }, false); 724 | 725 | // delegated handler for clicking on the links to presentation steps 726 | document.addEventListener("click", function ( event ) { 727 | // event delegation with "bubbling" 728 | // check if event target (or any of its parents is a link) 729 | var target = event.target; 730 | while ( (target.tagName !== "A") && 731 | (target !== document.documentElement) ) { 732 | target = target.parentNode; 733 | } 734 | 735 | if ( target.tagName === "A" ) { 736 | var href = target.getAttribute("href"); 737 | 738 | // if it's a link to presentation step, target this step 739 | if ( href && href[0] === '#' ) { 740 | target = document.getElementById( href.slice(1) ); 741 | } 742 | } 743 | 744 | if ( api.goto(target) ) { 745 | event.stopImmediatePropagation(); 746 | event.preventDefault(); 747 | } 748 | }, false); 749 | 750 | // delegated handler for clicking on step elements 751 | document.addEventListener("click", function ( event ) { 752 | var target = event.target; 753 | // find closest step element that is not active 754 | while ( !(target.classList.contains("step") && !target.classList.contains("active")) && 755 | (target !== document.documentElement) ) { 756 | target = target.parentNode; 757 | } 758 | 759 | if ( api.goto(target) ) { 760 | event.preventDefault(); 761 | } 762 | }, false); 763 | 764 | // touch handler to detect taps on the left and right side of the screen 765 | // based on awesome work of @hakimel: https://github.com/hakimel/reveal.js 766 | document.addEventListener("touchstart", function ( event ) { 767 | if (event.touches.length === 1) { 768 | var x = event.touches[0].clientX, 769 | width = window.innerWidth * 0.3, 770 | result = null; 771 | 772 | if ( x < width ) { 773 | result = api.prev(); 774 | } else if ( x > window.innerWidth - width ) { 775 | result = api.next(); 776 | } 777 | 778 | if (result) { 779 | event.preventDefault(); 780 | } 781 | } 782 | }, false); 783 | 784 | // rescale presentation when window is resized 785 | window.addEventListener("resize", throttle(function () { 786 | // force going to active step again, to trigger rescaling 787 | api.goto( document.querySelector(".step.active"), 500 ); 788 | }, 250), false); 789 | 790 | }, false); 791 | 792 | })(document, window); 793 | 794 | // THAT'S ALL FOLKS! 795 | // 796 | // Thanks for reading it all. 797 | // Or thanks for scrolling down and reading the last part. 798 | // 799 | // I've learnt a lot when building impress.js and I hope this code and comments 800 | // will help somebody learn at least some part of it. 801 | --------------------------------------------------------------------------------