├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── src ├── .gitignore ├── CallStackInspector.h ├── CallStackInspector.m ├── CallTracer.h ├── CallTracer.m ├── Makefile ├── PlistObjectConverter.h ├── PlistObjectConverter.m ├── SQLiteStorage.h ├── SQLiteStorage.m ├── Tweak.xm ├── control ├── hooks │ ├── CommonCryptorHooks.h │ ├── CommonCryptorHooks.m │ ├── CommonDigestHooks.h │ ├── CommonDigestHooks.m │ ├── CommonHMACHooks.h │ ├── CommonHMACHooks.m │ ├── CommonKeyDerivationHooks.h │ ├── CommonKeyDerivationHooks.m │ ├── DelegateProxies.h │ ├── DelegateProxies.m │ ├── KeychainHooks.h │ ├── KeychainHooks.m │ ├── LibCHooks.h │ ├── LibCHooks.m │ ├── SecurityHooks.h │ └── SecurityHooks.m ├── introspy.plist └── layout │ ├── DEBIAN │ └── control │ └── Library │ └── PreferenceLoader │ └── Preferences │ ├── Introspy.plist │ ├── Introspy2.plist │ └── introspy.png └── testapp ├── .gitignore ├── CryptoTester.h ├── CryptoTester.m ├── FileSystemTester.h ├── FileSystemTester.m ├── HTTPTester.h ├── HTTPTester.m ├── IntrospyTestAppApplication.mm ├── KeyChainTester.h ├── KeyChainTester.m ├── Makefile ├── PasteboardTester.h ├── PasteboardTester.m ├── Resources ├── Info.plist └── introspy.p12 ├── RootViewController.h ├── RootViewController.mm ├── SchemeTester.h ├── SchemeTester.m ├── UserPreferencesTester.h ├── UserPreferencesTester.m ├── XMLTester.h ├── XMLTester.m ├── control └── main.m /.gitattributes: -------------------------------------------------------------------------------- 1 | # These files are text and should be normalized (convert crlf => lf) 2 | *.h text 3 | *.m text 4 | *.xm text 5 | *.txt text 6 | *.md text 7 | Makefile text 8 | control text 9 | *.plist text 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | .gitconfig 4 | *.sublime-workspace -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Introspy-iOS Changelog 2 | ====================== 3 | 4 | ## v 0.5.1 5 | * Changed to be compatible with the new theos - https://github.com/theos/theos 6 | * Works on iOS 9 (Tested on iOS 9.3.3) 7 | 8 | ## v 0.4 9 | * Added support for iOS 7, including fixes to accommodate with seatbelt profile changes for System and AppStore apps in iOS 7. 10 | * Clarified output for arguments and return values Introspy-iOS cannot serialize (such as NSError). 11 | * Various bug fixes. 12 | 13 | 14 | ## v 0.3 15 | Initial public release. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introspy-iOS 2 | ============ 3 | 4 | Blackbox tool to help understand what an iOS application is doing at runtime 5 | and assist in the identification of potential security issues. 6 | 7 | See http://isecpartners.github.io/Introspy-iOS/ for a quick introduction. 8 | 9 | 10 | Description 11 | ----------- 12 | 13 | This is the repository for the Introspy-iOS tracer. 14 | 15 | The tracer can be installed on a jailbroken device to hook and log 16 | security-sensitive iOS APIs called by applications running on the device. The 17 | tool records details of relevant API calls, including arguments and return 18 | values and persists them in a database. 19 | Additionally, the calls are also sent to the Console for real-time analysis. 20 | 21 | The database can then be fed to Introspy-Analyzer, a Python script to generate 22 | HTML reports containing the list of logged function calls as well as a list of 23 | potential vulnerabilities affecting the application. Introspy-Analyzer is hosted 24 | on a separate repository: 25 | https://github.com/iSECPartners/Introspy-Analyzer 26 | 27 | 28 | Installation 29 | ------------ 30 | 31 | Users should first download the latest pre-compiled Debian package available 32 | in the release section of the project page at: 33 | https://github.com/integrity-sa/Introspy-iOS/releases or for older releases at 34 | https://github.com/iSECPartners/Introspy-iOS/releases 35 | 36 | ### Dependencies 37 | 38 | The tracer will only run on a jailbroken device. Using Cydia, make 39 | sure the following packages are installed: 40 | - dpkg 41 | - Cydia Substrate 42 | - PreferenceLoader 43 | - Applist 44 | 45 | ### How to install 46 | 47 | Download and copy the Debian package to the device; install it: 48 | 49 | scp root@:~ 50 | ssh root@ 51 | dpkg -i 52 | 53 | Respring the device: 54 | 55 | killall -HUP SpringBoard 56 | 57 | There should be two new menus in the device's Settings. The Apps menu allows you 58 | to select which applications will be profiled while the Settings menu defines 59 | which API groups are being hooked. 60 | 61 | Finally, kill and restart the App you want to monitor. 62 | 63 | ### How to uninstall 64 | 65 | dpkg -r com.isecpartners.introspy 66 | 67 | 68 | Generating HTML Reports 69 | ----------------------- 70 | 71 | The tracer will store data about API calls made by applications in a database 72 | stored on the device (actually one in each application's folder). This database 73 | can be fed to a Python script call Introspy-Analyzer in order to generate HTML 74 | reports that make it a lot easier to review the data collected by the tracer. 75 | The script will also analyze and flag dangerous API calls in order to facilitate 76 | the process of identifying vulnerabilities within iOS applications. 77 | 78 | Introspy-Analyzer is hosted on a separate repository: 79 | https://github.com/iSECPartners/Introspy-Analyzer 80 | 81 | 82 | Building Introspy-iOS 83 | --------------------- 84 | 85 | Most users should just download and install the pre-compiled Debian package. 86 | However, if you want to modify the library's functionality you will have to 87 | build the Debian package yourself. 88 | 89 | The build requires the Theos suite, available at https://github.com/theos/theos. 90 | For general instructions on how to install Theos, see 91 | https://github.com/theos/theos/wiki/Installation. 92 | 93 | You must also set the $THEOS variable in your environment, and export it so 94 | make will see its value when you run it 95 | 96 | export THEOS=/absolute/path/to/theos 97 | export PATH=$THEOS/bin:$PATH 98 | 99 | Then, the package can be built using: 100 | 101 | make package 102 | 103 | Once you've successfully created the debian package, you can use Theos to 104 | automatically install the package and re-spring the device by specifying the 105 | device's IP address in the THEOS_DEVICE_IP environment variable: 106 | 107 | export THEOS_DEVICE_IP=192.168.1.127 108 | make install 109 | 110 | 111 | License 112 | ------- 113 | 114 | See ./LICENSE. 115 | 116 | Authors 117 | ------- 118 | 119 | * Tom Daniels 120 | * Alban Diquet 121 | 122 | Maintainers 123 | ------- 124 | 125 | * Herman Duarte 126 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .theos 3 | theos 4 | *.deb 5 | obj/* 6 | _/* 7 | *.swp 8 | .gitconfig 9 | *.sublime-workspace 10 | packages/* 11 | -------------------------------------------------------------------------------- /src/CallStackInspector.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CallStackInspector: NSObject { 3 | 4 | } 5 | 6 | // Some of the functions we hook can be called by both an application and the iOS API 7 | // For example, CCCryptor is used internally by the SSL stack to handle the crypto 8 | // We don't want to hook internal calls, only what the App is directly calling. 9 | // So we use this to figure out who called the function we're hooking. 10 | + (BOOL) wasDirectlyCalledByApp; 11 | + (BOOL) wasCalledByAppAtIndex:(NSUInteger)index; 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /src/CallStackInspector.m: -------------------------------------------------------------------------------- 1 | #import "CallStackInspector.h" 2 | 3 | 4 | @implementation CallStackInspector 5 | 6 | 7 | 8 | + (BOOL) wasCalledByAppAtIndex:(NSUInteger)index { 9 | //NSLog(@"%@",[NSThread callStackSymbols]); 10 | NSString *appProcessName = [[NSProcessInfo processInfo] processName]; 11 | NSArray *callStack = [NSThread callStackSymbols]; 12 | 13 | // Not ideal: Check if the app's process name is close enough in the call stack 14 | NSRange callerAtIndex = [[callStack objectAtIndex:index] rangeOfString: appProcessName]; 15 | 16 | if (callerAtIndex.location == NSNotFound) { 17 | return false; 18 | } 19 | return true; 20 | } 21 | 22 | + (BOOL) wasDirectlyCalledByApp { 23 | return [self wasCalledByAppAtIndex:3]; 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /src/CallTracer.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CallTracer: NSObject { 3 | NSMutableDictionary* args; 4 | NSString* className; 5 | NSString* methodName; 6 | } 7 | 8 | @property (retain) NSDictionary *args; 9 | @property (retain) NSString *className; 10 | @property (retain) NSString *methodName; 11 | @property (retain) NSDictionary *argsAndReturnValue; 12 | 13 | - (CallTracer*)initWithClass:(NSString *)clazz andMethod:(NSString *)meth; 14 | 15 | // Plist objects are string, number, boolean, date, data, dictionary and array. 16 | - (void) addArgFromPlistObject:(id) arg withKey:(NSString *)key; 17 | - (void) addReturnValueFromPlistObject:(id) result; 18 | 19 | - (NSData *) serializeArgsAndReturnValue; 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /src/CallTracer.m: -------------------------------------------------------------------------------- 1 | #import "CallTracer.h" 2 | #import "SQLiteStorage.h" 3 | #import "PlistObjectConverter.h" 4 | 5 | 6 | 7 | @implementation CallTracer 8 | 9 | @synthesize args; 10 | @synthesize className; 11 | @synthesize methodName; 12 | @synthesize argsAndReturnValue; 13 | 14 | - (CallTracer*)initWithClass:(NSString *)clazz andMethod:(NSString *)meth { 15 | /* initialize the call tracer with class and method names */ 16 | self = [super init]; 17 | args = [[NSMutableDictionary alloc] init]; 18 | className = [[NSString alloc] initWithString:clazz]; 19 | methodName = [[NSString alloc] initWithString:meth]; 20 | argsAndReturnValue = [[NSMutableDictionary alloc] init]; 21 | [argsAndReturnValue setValue:args forKey:@"arguments"]; 22 | 23 | return self; 24 | } 25 | 26 | 27 | - (void) addArgFromPlistObject:(id) arg withKey:(NSString *)key { 28 | if(arg == nil) { 29 | [args setValue:[PlistObjectConverter getSerializedNilValue] forKey:key]; 30 | } 31 | else { 32 | [args setValue:arg forKey:key]; 33 | } 34 | } 35 | 36 | 37 | - (void) addReturnValueFromPlistObject:(id) result { 38 | if(result == nil) { 39 | [argsAndReturnValue setValue:[PlistObjectConverter getSerializedNilValue] forKey:@"returnValue"]; 40 | } 41 | else { 42 | [argsAndReturnValue setValue:result forKey:@"returnValue"]; 43 | } 44 | } 45 | 46 | 47 | - (NSData *) serializeArgsAndReturnValue { 48 | NSError *error; 49 | NSData *plist = [NSPropertyListSerialization dataWithPropertyList:(id)argsAndReturnValue 50 | format:NSPropertyListXMLFormat_v1_0 51 | options:0 52 | error:&error]; 53 | return plist; 54 | } 55 | 56 | 57 | - (void)dealloc 58 | { 59 | [args release]; 60 | [argsAndReturnValue release]; 61 | [className release]; 62 | [methodName release]; 63 | [super dealloc]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | TWEAK_NAME = introspy 4 | introspy_FILES = Tweak.xm CallTracer.m hooks/CommonDigestHooks.m hooks/LibCHooks.m hooks/KeychainHooks.m hooks/DelegateProxies.m PlistObjectConverter.m hooks/CommonKeyDerivationHooks.m CallStackInspector.m SQLiteStorage.m hooks/SecurityHooks.m hooks/CommonCryptorHooks.m hooks/CommonHMACHooks.m 5 | introspy_LIBRARIES = sqlite3 6 | 7 | include $(THEOS_MAKE_PATH)/tweak.mk 8 | 9 | after-install:: 10 | install.exec "killall -9 SpringBoard" 11 | -------------------------------------------------------------------------------- /src/PlistObjectConverter.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface PlistObjectConverter: NSObject { 5 | 6 | } 7 | 8 | // Plist objects are string, number, boolean, date, data, dictionary and array. 9 | // We need to convert anything that's not a plist object to a dictionnary so we can store it. 10 | + (NSString *) getSerializedNilValue; 11 | + (id) autoConvertNil: (id) pointer; 12 | + (NSDictionary *) convertURL:(NSURL *)aURL; 13 | + (NSDictionary *) convertNSURLRequest:(NSURLRequest *)request; 14 | + (NSDictionary *) convertNSURLResponse:(NSURLResponse *)response; 15 | + (NSDictionary *) convertNSCachedURLResponse:(NSCachedURLResponse *)response; 16 | + (NSDictionary *) convertNSURLAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 17 | + (NSDictionary *) convertNSURLProtectionSpace:(NSURLProtectionSpace *)pSpace; 18 | + (NSDictionary *) convertNSURLCredential: (NSURLCredential*) credential; 19 | + (NSDictionary *) convertNSHTTPCookie: (NSHTTPCookie*) cookie; 20 | + (NSDictionary *) convertUIPasteboard: (UIPasteboard*) pasteboard; 21 | + (NSArray *) convertDelegate: (id)delegate followingProtocol: (NSString*)protocol; 22 | 23 | // Security framework objects 24 | // TODO : Split PlistObjectConverter in multiple files 25 | //+ (id) convertSecItemResult: (CFTypeRef*) result withQuery: (CFDictionaryRef) query; 26 | + (NSDictionary *) convertSecItemAttributesDict: (CFDictionaryRef) attributes; 27 | + (NSDictionary *) convertSecCertificateRef: (SecCertificateRef) certificate; 28 | + (NSDictionary *) convertSecTrustRef:(SecTrustRef) trust; 29 | + (NSDictionary *) convertSecIdentityRef: (SecIdentityRef) identity; 30 | + (NSDictionary *) convertSecKeyRef:(SecKeyRef) key; 31 | 32 | + (NSData *) convertCBuffer:(const void *) buffer withLength: (size_t) length; 33 | @end 34 | -------------------------------------------------------------------------------- /src/PlistObjectConverter.m: -------------------------------------------------------------------------------- 1 | #import // For convertDelegate() 2 | #import 3 | 4 | #import "PlistObjectConverter.h" 5 | 6 | extern NSString *objectTypeNotSupported; 7 | 8 | 9 | @implementation PlistObjectConverter 10 | 11 | 12 | // What we store in the plist if the object's pointer is nil 13 | static NSString *serializedNilValue = @"nil"; 14 | 15 | 16 | + (NSString *) getSerializedNilValue { 17 | return serializedNilValue; 18 | } 19 | 20 | 21 | // Utility function to automatically convert nil to an empty string 22 | + (id) autoConvertNil:(id) pointer { 23 | if (pointer == nil) { 24 | return serializedNilValue; 25 | } 26 | else { 27 | return pointer; 28 | } 29 | } 30 | 31 | 32 | + (NSDictionary *) convertURL:(NSURL *)aURL { 33 | if (aURL == nil) { 34 | return [NSDictionary dictionary]; 35 | } 36 | 37 | NSDictionary *url_dict = nil; 38 | NSString *scheme = [aURL scheme]; 39 | if (aURL != nil) { 40 | // store specific infoz if its an HTTP URL 41 | if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) { 42 | url_dict = [NSDictionary dictionaryWithObjects: 43 | [NSArray arrayWithObjects: [aURL absoluteString], 44 | [aURL scheme], 45 | [aURL host], 46 | [PlistObjectConverter autoConvertNil: [aURL port]], 47 | [aURL path], 48 | [PlistObjectConverter autoConvertNil: [aURL parameterString]], 49 | [PlistObjectConverter autoConvertNil: [aURL query]], nil] 50 | forKeys: 51 | [NSArray arrayWithObjects: 52 | @"absoluteString", 53 | @"scheme", 54 | @"host", 55 | @"port", 56 | @"path", 57 | @"parameterString", 58 | @"query", nil]]; 59 | // otherwise just store the whole string for now. 60 | } else { 61 | url_dict = [NSDictionary dictionaryWithObjects: 62 | [NSArray arrayWithObjects: 63 | [aURL absoluteString], nil] 64 | forKeys: 65 | [NSArray arrayWithObjects: 66 | @"absoluteString", nil]]; 67 | } 68 | } 69 | return url_dict; 70 | } 71 | 72 | 73 | // Convert an NSURLRequest to an NSDictionary suitable for plist storage. 74 | + (NSDictionary *) convertNSURLRequest:(NSURLRequest *)request { 75 | if (request == nil) 76 | return [NSDictionary dictionary]; 77 | 78 | NSDictionary *url_req = [NSDictionary dictionaryWithObjects: 79 | [NSArray arrayWithObjects: 80 | [PlistObjectConverter convertURL:[request URL]], 81 | [request HTTPMethod], 82 | [PlistObjectConverter autoConvertNil: [request HTTPBody]], 83 | [NSNumber numberWithUnsignedInt:[request cachePolicy]], 84 | nil] 85 | forKeys: [NSArray arrayWithObjects: 86 | @"URL", 87 | @"HTTPMethod", 88 | @"HTTPBody", 89 | @"cachePolicy", 90 | nil]]; 91 | return url_req; 92 | } 93 | 94 | 95 | + (NSDictionary *) convertNSURLResponse:(NSURLResponse *)response { 96 | if (response == nil) 97 | return [NSDictionary dictionary]; 98 | 99 | NSDictionary *responseDict = [NSDictionary dictionaryWithObjects: 100 | [NSArray arrayWithObjects: 101 | [PlistObjectConverter convertURL:[response URL]], 102 | [PlistObjectConverter autoConvertNil: [response MIMEType]], 103 | [response suggestedFilename], 104 | [PlistObjectConverter autoConvertNil: [response textEncodingName]], 105 | nil] 106 | forKeys: [NSArray arrayWithObjects: 107 | @"URL", 108 | @"MIMEType", 109 | @"suggestedFilename", 110 | @"textEncodingName", 111 | nil]]; 112 | return responseDict; 113 | } 114 | 115 | 116 | + (NSDictionary *) convertNSCachedURLResponse:(NSCachedURLResponse *)response { 117 | if (response == nil) 118 | return [NSDictionary dictionary]; 119 | 120 | // Do we want to store the actual data ? 121 | NSDictionary *responseDict = [NSDictionary dictionaryWithObjects: 122 | [NSArray arrayWithObjects: 123 | [PlistObjectConverter convertNSURLResponse:[response response]], 124 | [NSNumber numberWithUnsignedInt:[response storagePolicy]], 125 | [PlistObjectConverter autoConvertNil:[response userInfo]], 126 | nil] 127 | forKeys: [NSArray arrayWithObjects: 128 | @"response", 129 | @"storagePolicy", 130 | @"userInfo", 131 | nil]]; 132 | return responseDict; 133 | } 134 | 135 | 136 | + (NSDictionary *) convertNSURLProtectionSpace:(NSURLProtectionSpace *)pSpace { 137 | if (pSpace == nil) { 138 | return [NSDictionary dictionary]; 139 | } 140 | 141 | NSDictionary *pSpaceDict = [NSDictionary dictionaryWithObjects: 142 | [NSArray arrayWithObjects: 143 | [pSpace authenticationMethod], 144 | [PlistObjectConverter autoConvertNil: [pSpace distinguishedNames]], 145 | [pSpace host], 146 | [NSNumber numberWithBool: [pSpace isProxy]], 147 | [NSNumber numberWithUnsignedInt: [pSpace port]], 148 | [PlistObjectConverter autoConvertNil: [pSpace protocol]], 149 | [PlistObjectConverter autoConvertNil: [pSpace proxyType]], 150 | [PlistObjectConverter autoConvertNil: [pSpace realm]], 151 | [NSNumber numberWithBool: [pSpace receivesCredentialSecurely]], 152 | [PlistObjectConverter convertSecTrustRef: [pSpace serverTrust]], 153 | nil] 154 | forKeys: 155 | [NSArray arrayWithObjects: 156 | @"authenticationMethod", 157 | @"distinguishedNames", 158 | @"host", 159 | @"isProxy", 160 | @"port", 161 | @"protocol", 162 | @"proxyType", 163 | @"realm", 164 | @"receivesCredentialSecurely", 165 | @"serverTrust", 166 | nil]]; 167 | return pSpaceDict; 168 | } 169 | 170 | 171 | // Convert an NSURLAuthenticationChallenge to an NSDictionary suitable for plist storage 172 | + (NSDictionary *) convertNSURLAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 173 | 174 | if (challenge == nil) { 175 | return [NSDictionary dictionary]; 176 | } 177 | 178 | // Parse the protection space 179 | NSURLProtectionSpace *pSpace = [challenge protectionSpace]; 180 | NSDictionary *pSpaceDict = [PlistObjectConverter convertNSURLProtectionSpace:pSpace]; 181 | 182 | 183 | // Parse the proposed credential 184 | NSURLCredential *cred = [challenge proposedCredential]; 185 | NSDictionary *credentialDict = [PlistObjectConverter convertNSURLCredential:cred]; 186 | 187 | // All done 188 | NSDictionary *challengeDict = [NSDictionary dictionaryWithObjects: 189 | [NSArray arrayWithObjects: 190 | pSpaceDict, 191 | credentialDict, nil] 192 | forKeys: 193 | [NSArray arrayWithObjects: 194 | @"protectionSpace", 195 | @"proposedCredential", nil]]; 196 | 197 | return challengeDict; 198 | } 199 | 200 | 201 | + (NSDictionary *) convertSecCertificateRef: (SecCertificateRef) certificate { 202 | if (certificate == nil) 203 | return [NSDictionary dictionary]; 204 | 205 | // Just store the summary of the certificate 206 | NSString *certSummary = (NSString *)SecCertificateCopySubjectSummary(certificate); 207 | NSDictionary *credentialDict = [NSDictionary dictionaryWithObjects: 208 | [NSArray arrayWithObjects: 209 | certSummary, 210 | nil] 211 | forKeys: 212 | [NSArray arrayWithObjects: 213 | @"subjectSummary", 214 | nil]]; 215 | 216 | return credentialDict; 217 | } 218 | 219 | #if 0 220 | + (id) convertSecItemResult: (CFTypeRef*) result withQuery: (CFDictionaryRef) query { 221 | if (result == NULL) { 222 | return [PlistObjectConverter getSerializedNilValue]; 223 | } 224 | int resultNb = 0; 225 | 226 | 227 | // What an awful API 228 | if ((CFDictionaryContainsKey(query, kSecReturnData)) && (CFDictionaryGetValue(query, kSecReturnData) == kCFBooleanTrue)) { 229 | resultNb++; 230 | } 231 | else if ((CFDictionaryContainsKey(query, kSecReturnAttributes)) && (CFDictionaryGetValue(query, kSecReturnAttributes) == kCFBooleanTrue)) { 232 | resultNb++; 233 | } 234 | else if ((CFDictionaryContainsKey(query, kSecReturnRef)) && (CFDictionaryGetValue(query, kSecReturnRef) == kCFBooleanTrue)) { 235 | resultNb++; 236 | } 237 | else if ((CFDictionaryContainsKey(query, kSecReturnPersistentRef)) && (CFDictionaryGetValue(query, kSecReturnPersistentRef) == kCFBooleanTrue)) { 238 | resultNb++; 239 | } 240 | NSLog(@"========================================RESULT NB %d", resultNb); 241 | if (resultNb == 1) { 242 | if ((CFDictionaryContainsKey(query, kSecReturnData)) && (CFDictionaryGetValue(query, kSecReturnData) == kCFBooleanTrue)) { 243 | 244 | NSLog(@"========================================PASSWORD "); 245 | NSLog(@"========================================LENGTH %ld", CFDataGetLength((CFDataRef)result)); 246 | //NSData *theData = (NSData *)result; 247 | //NSLog(@"========================================LOL %@ ", [theData base64EncodedStringWithOptions:0]); 248 | //return (NSData *)result; 249 | return [NSData data]; 250 | } 251 | else if ((CFDictionaryContainsKey(query, kSecReturnAttributes)) && (CFDictionaryGetValue(query, kSecReturnAttributes) == kCFBooleanTrue)) { 252 | NSLog(@"======================================== DICT "); 253 | return (NSDictionary*) result; 254 | } 255 | else if ( ((CFDictionaryContainsKey(query, kSecReturnRef)) && (CFDictionaryGetValue(query, kSecReturnRef) == kCFBooleanTrue)) || ((CFDictionaryContainsKey(query, kSecReturnPersistentRef)) && (CFDictionaryGetValue(query, kSecReturnPersistentRef) == kCFBooleanTrue)) ) { 256 | NSLog(@"======================================== SECITEM "); 257 | 258 | CFTypeRef secClass = CFDictionaryGetValue(query, kSecClass); 259 | if ((secClass == kSecClassGenericPassword) || (secClass == kSecClassGenericPassword)) { 260 | return (NSData *)result; 261 | } 262 | else if (secClass == kSecClassCertificate) { 263 | if (CFDictionaryContainsKey(query, kSecValueRef)) { 264 | return [PlistObjectConverter convertSecCertificateRef:(SecCertificateRef)result]; 265 | } 266 | else if (CFDictionaryContainsKey(query, kSecValuePersistentRef)) { 267 | return [PlistObjectConverter convertSecCertificateRef:(SecCertificateRef)result]; 268 | } 269 | } 270 | else if (secClass == kSecClassIdentity) { 271 | if (CFDictionaryContainsKey(query, kSecValueRef)) { 272 | return [PlistObjectConverter convertSecIdentityRef:(SecIdentityRef) result]; 273 | } 274 | else if (CFDictionaryContainsKey(query, kSecValuePersistentRef)) { 275 | return [PlistObjectConverter convertSecIdentityRef:(SecIdentityRef) result]; 276 | } 277 | } 278 | else if (secClass == kSecClassKey) { 279 | if (CFDictionaryContainsKey(query, kSecValueRef)) { 280 | return [PlistObjectConverter convertSecKeyRef:(SecKeyRef) result]; 281 | } 282 | else if (CFDictionaryContainsKey(query, kSecValuePersistentRef)) { 283 | return [PlistObjectConverter convertSecKeyRef:(SecKeyRef) result]; 284 | } 285 | 286 | } 287 | 288 | } 289 | } 290 | 291 | else if (resultNb > 1) { 292 | //Give up for now 293 | // TODO: support queries for multiple items 294 | } 295 | 296 | return objectTypeNotSupported; 297 | } 298 | #endif 299 | 300 | // attributes dictionnary when calling SecItemAdd() and SecItemUpdate() 301 | + (NSDictionary *) convertSecItemAttributesDict: (CFDictionaryRef) attributes { 302 | 303 | // Need to make the content of attributes serializable 304 | NSMutableDictionary *attributesPlist = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*) attributes]; 305 | 306 | CFTypeRef secClass = CFDictionaryGetValue(attributes, kSecClass); 307 | 308 | if (CFDictionaryContainsKey(attributes, kSecValueData)) { 309 | NSData *theData = (NSData *)CFDictionaryGetValue(attributes, kSecValueData); 310 | [attributesPlist setObject:theData forKey:@"kSecValueData"]; 311 | [attributesPlist removeObjectForKey:(id)kSecValueData]; 312 | } 313 | 314 | 315 | if ((secClass == kSecClassGenericPassword) || (secClass == kSecClassGenericPassword)) { 316 | // Nothing to do for passwords 317 | } 318 | else if (secClass == kSecClassCertificate) { 319 | if (CFDictionaryContainsKey(attributes, kSecValueRef)) { 320 | [attributesPlist setObject:[PlistObjectConverter convertSecCertificateRef:(SecCertificateRef)CFDictionaryGetValue(attributes, kSecValueRef)] 321 | forKey:@"kSecValueRef"]; 322 | [attributesPlist removeObjectForKey:(id)kSecValueRef]; 323 | } 324 | else if (CFDictionaryContainsKey(attributes, kSecValuePersistentRef)) { 325 | [attributesPlist setObject:[PlistObjectConverter convertSecCertificateRef:(SecCertificateRef)CFDictionaryGetValue(attributes, kSecValuePersistentRef)] 326 | forKey:@"kSecValuePersistentRef"]; 327 | [attributesPlist removeObjectForKey:(id)kSecValuePersistentRef]; 328 | } 329 | } 330 | else if (secClass == kSecClassIdentity) { 331 | if (CFDictionaryContainsKey(attributes, kSecValueRef)) { 332 | [attributesPlist setObject:[PlistObjectConverter convertSecIdentityRef:(SecIdentityRef)CFDictionaryGetValue(attributes, kSecValueRef)] 333 | forKey:@"kSecValueRef"]; 334 | [attributesPlist removeObjectForKey:(id)kSecValueRef]; 335 | } 336 | else if (CFDictionaryContainsKey(attributes, kSecValuePersistentRef)) { 337 | [attributesPlist setObject:[PlistObjectConverter convertSecIdentityRef:(SecIdentityRef)CFDictionaryGetValue(attributes, kSecValuePersistentRef)] 338 | forKey:@"kSecValuePersistentRef"]; 339 | [attributesPlist removeObjectForKey:(id)kSecValuePersistentRef]; 340 | } 341 | } 342 | else if (secClass == kSecClassKey) { 343 | if (CFDictionaryContainsKey(attributes, kSecValueRef)) { 344 | [attributesPlist setObject:[PlistObjectConverter convertSecKeyRef:(SecKeyRef)CFDictionaryGetValue(attributes, kSecValueRef)] 345 | forKey:@"kSecValueRef"]; 346 | [attributesPlist removeObjectForKey:(id)kSecValueRef]; 347 | } 348 | else if (CFDictionaryContainsKey(attributes, kSecValuePersistentRef)) { 349 | [attributesPlist setObject:[PlistObjectConverter convertSecKeyRef:(SecKeyRef)CFDictionaryGetValue(attributes, kSecValuePersistentRef)] 350 | forKey:@"kSecValuePersistentRef"]; 351 | [attributesPlist removeObjectForKey:(id)kSecValuePersistentRef]; 352 | } 353 | } 354 | 355 | return attributesPlist; 356 | } 357 | 358 | 359 | + (NSDictionary *) convertSecIdentityRef: (SecIdentityRef) identity { 360 | if (identity == nil) 361 | return [NSDictionary dictionary]; 362 | 363 | // TODO: Dump the client cert and private key 364 | SecCertificateRef certRef; 365 | SecIdentityCopyCertificate(identity, &certRef); 366 | SecKeyRef privateKeyRef; 367 | SecIdentityCopyPrivateKey(identity, &privateKeyRef); 368 | 369 | NSDictionary *identityDict = [NSDictionary dictionaryWithObjects: 370 | [NSArray arrayWithObjects: 371 | [PlistObjectConverter convertSecCertificateRef:certRef], 372 | [PlistObjectConverter convertSecKeyRef:privateKeyRef], 373 | nil] 374 | forKeys: 375 | [NSArray arrayWithObjects: 376 | @"certificate", 377 | @"privateKey", 378 | nil]]; 379 | 380 | if (certRef) 381 | CFRelease(certRef); 382 | return identityDict; 383 | } 384 | 385 | 386 | + (NSDictionary *) convertSecKeyRef:(SecKeyRef) key { 387 | if (key == nil) 388 | return [NSDictionary dictionary]; 389 | 390 | // TODO: Dump private keys 391 | NSDictionary *keyDict = [NSDictionary dictionaryWithObjects: 392 | [NSArray arrayWithObjects: 393 | objectTypeNotSupported, 394 | nil] 395 | forKeys: 396 | [NSArray arrayWithObjects: 397 | @"key", 398 | nil]]; 399 | return keyDict; 400 | } 401 | 402 | 403 | + (NSDictionary *) convertSecTrustRef:(SecTrustRef) trust { 404 | if (trust == nil) 405 | return [NSDictionary dictionary]; 406 | 407 | // This is getting complicated... 408 | // Just store the summary of the each certificate for now 409 | unsigned int certNB = SecTrustGetCertificateCount(trust); 410 | NSMutableDictionary *trustDict = [NSMutableDictionary dictionaryWithCapacity:certNB]; 411 | for(int i=0; i 3 | 4 | @implementation SQLiteStorage 5 | 6 | 7 | // Database settings 8 | static BOOL logToConsole = TRUE; 9 | static NSString *appstoreDBFileFormat = @"~/Library/introspy-%@.db"; // Becomes ~/Library/introspy-.db 10 | static NSString *systemDBFileFormat = @"~/Library/Preferences/introspy-%@.db"; 11 | static const char createTableStmtStr[] = "CREATE TABLE tracedCalls (className TEXT, methodName TEXT, argumentsAndReturnValueDict TEXT)"; 12 | static const char saveTracedCallStmtStr[] = "INSERT INTO tracedCalls VALUES (?1, ?2, ?3)"; 13 | 14 | 15 | // Internal stuff 16 | static sqlite3_stmt *saveTracedCallStmt; 17 | static sqlite3 *dbConnection; 18 | 19 | 20 | - (SQLiteStorage *)initWithDefaultDBFilePathAndLogToConsole: (BOOL) shouldLog { 21 | NSString *DBFilePath = nil; 22 | // Put application name in the DB's filename to avoid confusion 23 | NSString *appId = [[NSBundle mainBundle] bundleIdentifier]; 24 | 25 | // Are we monitoring a System app or an App Store app ? 26 | NSString *appRoot = [@"~/" stringByExpandingTildeInPath]; 27 | if ([appRoot isEqualToString: @"/var/mobile"]) { 28 | DBFilePath = [NSString stringWithFormat:systemDBFileFormat, appId]; 29 | } 30 | else { 31 | DBFilePath = [NSString stringWithFormat:appstoreDBFileFormat, appId]; 32 | } 33 | 34 | return [self initWithDBFilePath: [DBFilePath stringByExpandingTildeInPath] andLogToConsole: shouldLog]; 35 | } 36 | 37 | 38 | - (SQLiteStorage *)initWithDBFilePath:(NSString *) DBFilePath andLogToConsole: (BOOL) shouldLog { 39 | self = [super init]; 40 | sqlite3 *dbConn; 41 | 42 | // Open the DB file if it's already there 43 | if (sqlite3_open_v2([DBFilePath UTF8String], &dbConn, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) { 44 | 45 | // If not, create the DB file 46 | if (sqlite3_open_v2([DBFilePath UTF8String], &dbConn, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { 47 | NSLog(@"IntrospySQLiteStorage - Unable to open database!"); 48 | return nil; 49 | } 50 | else { 51 | // Create the tables in the DB we just created 52 | if (sqlite3_exec(dbConn, createTableStmtStr, NULL, NULL, NULL) != SQLITE_OK) { 53 | NSLog(@"IntrospySQLiteStorage - Unable to create tables!"); 54 | return nil; 55 | } 56 | } 57 | } 58 | 59 | // Prepare the INSERT statement we'll use to store everything 60 | sqlite3_stmt *statement = nil; 61 | if (sqlite3_prepare_v2(dbConn, saveTracedCallStmtStr, -1, &statement, NULL) != SQLITE_OK) { 62 | NSLog(@"IntrospySQLiteStorage - Unable to prepare statement!"); 63 | return nil; 64 | } 65 | 66 | saveTracedCallStmt = statement; 67 | dbConnection = dbConn; 68 | logToConsole = shouldLog; 69 | return self; 70 | } 71 | 72 | 73 | - (BOOL)saveTracedCall: (CallTracer*) tracedCall { 74 | int queryResult = SQLITE_ERROR; 75 | 76 | // Serialize arguments and return value to an XML plist 77 | NSData *argsAndReturnValueData = [tracedCall serializeArgsAndReturnValue]; 78 | if (argsAndReturnValueData == nil) { 79 | NSLog(@"IntrospySQLiteStorage::saveTraceCall: can't serialize args or return value"); 80 | return NO; 81 | } 82 | NSString *argsAndReturnValueStr = [[NSString alloc] initWithData:argsAndReturnValueData encoding:NSUTF8StringEncoding]; 83 | 84 | // Do the query; has to be atomic or we get random SQLITE_PROTOCOL errors 85 | // TODO: this is probably super slow 86 | @synchronized(appstoreDBFileFormat) { 87 | sqlite3_reset(saveTracedCallStmt); 88 | sqlite3_bind_text(saveTracedCallStmt, 1, [ [tracedCall className] UTF8String], -1, nil); 89 | sqlite3_bind_text(saveTracedCallStmt, 2, [ [tracedCall methodName] UTF8String], -1, nil); 90 | sqlite3_bind_text(saveTracedCallStmt, 3, [argsAndReturnValueStr UTF8String], -1, nil); 91 | queryResult = sqlite3_step(saveTracedCallStmt); 92 | } 93 | 94 | if (logToConsole) { 95 | NSLog(@"\n-----INTROSPY-----\nCALLED %@ %@\nWITH:\n%@\n---------------", [tracedCall className], [tracedCall methodName], [tracedCall argsAndReturnValue]); 96 | } 97 | 98 | [argsAndReturnValueStr release]; 99 | 100 | if (queryResult != SQLITE_DONE) { 101 | NSLog(@"IntrospySQLiteStorage - Commit Failed: %x!", queryResult); 102 | return NO; 103 | } 104 | return YES; 105 | } 106 | 107 | 108 | - (void)dealloc 109 | { 110 | sqlite3_finalize(saveTracedCallStmt); 111 | sqlite3_close(dbConnection); 112 | [super dealloc]; 113 | } 114 | 115 | 116 | @end 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/Tweak.xm: -------------------------------------------------------------------------------- 1 | // Utility functions 2 | #import "SQLiteStorage.h" 3 | #import "PlistObjectConverter.h" 4 | #import "CallStackInspector.h" 5 | 6 | // Delegate hooks => We proxy the delegate objects 7 | #import "hooks/DelegateProxies.h" 8 | 9 | // Hooks for C functions directly use MobileSubstrate 10 | #import "hooks/SecurityHooks.h" 11 | #import "hooks/KeychainHooks.h" 12 | #import "hooks/CommonCryptorHooks.h" 13 | #import "hooks/CommonHMACHooks.h" 14 | #import "hooks/CommonKeyDerivationHooks.h" 15 | #import "hooks/CommonDigestHooks.h" 16 | #import "hooks/LibCHooks.h" 17 | 18 | // Hooks for ObjC methods rely on the Logos pre-processor 19 | // H4ck to split Logos hooking code into separate files: we're including actual code, not headers 20 | SQLiteStorage *traceStorage; 21 | NSString *objectTypeNotSupported = @"Introspy - Not supported"; 22 | static NSString *preferenceFilePath = @"/private/var/mobile/Library/Preferences/com.isecpartners.introspy.plist"; 23 | 24 | // Utility function to parse the preference file 25 | static BOOL getBoolFromPreferences(NSMutableDictionary *preferences, NSString *preferenceValue) { 26 | id value = [preferences objectForKey:preferenceValue]; 27 | if (value == nil) { 28 | return YES; // default to YES 29 | } 30 | return [value boolValue]; 31 | } 32 | 33 | // Log all custom URL schemes registered 34 | // TODO: should we refactor this out of the main Tweak? 35 | static void traceURISchemes() { 36 | NSArray *url_schemes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"]; 37 | for (id schemeBundle in url_schemes) { 38 | NSString *name = [schemeBundle objectForKey:@"CFBundleURLName"]; 39 | NSNumber *isPrivate = [schemeBundle objectForKey:@"CFBundleURLIsPrivate"]; 40 | for (id scheme in [schemeBundle objectForKey:@"CFBundleURLSchemes"]) { 41 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"CFBundleURLTypes" andMethod:@"CFBundleURLSchemes"]; 42 | [tracer addArgFromPlistObject:name withKey:@"CFBundleURLName"]; 43 | [tracer addArgFromPlistObject:isPrivate withKey:@"CFBundleURLIsPrivate"]; 44 | [tracer addArgFromPlistObject:scheme withKey:@"CFBundleURLScheme"]; 45 | [traceStorage saveTracedCall:tracer]; 46 | [tracer release]; 47 | } 48 | } 49 | } 50 | 51 | // Regular hooks 52 | %group URLSchemes 53 | //#include "hooks/UIApplicationHooks.xm" 54 | %hook UIApplication 55 | 56 | - (void)setDelegate: (id)delegate { 57 | // Proxy the delegate so we can hook it 58 | UIApplicationDelegateProx *delegateProxy = [[UIApplicationDelegateProx alloc] initWithOriginalDelegate:delegate]; 59 | %orig(delegateProxy); 60 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIApplication" andMethod:@"setDelegate:"]; 61 | [tracer addArgFromPlistObject:[PlistObjectConverter convertDelegate:delegate followingProtocol:@"UIApplicationDelegate"] withKey:@"delegate"]; 62 | [traceStorage saveTracedCall:tracer]; 63 | [tracer release]; 64 | return; 65 | } 66 | %end 67 | %end 68 | 69 | 70 | %group FileSystemHooks 71 | //#include "hooks/NSDataHooks.xm" 72 | //#include "hooks/NSFileHandleHooks.xm" 73 | //#include "hooks/NSFileManagerHooks.xm" 74 | //#include "hooks/NSInputStreamHooks.xm" 75 | //#include "hooks/NSOutputStreamHooks.xm" 76 | %hook NSData 77 | 78 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag { 79 | BOOL origResult = %orig(path, flag); 80 | // NSData methods are called a lot by other iOS APIs and we don't want to log that so we use the CallStackInspector 81 | if ([CallStackInspector wasDirectlyCalledByApp]) { 82 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"writeToFile:atomically:"]; 83 | [tracer addArgFromPlistObject:path withKey:@"path"]; 84 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: flag] withKey:@"flag"]; 85 | [tracer addReturnValueFromPlistObject:[NSNumber numberWithBool: origResult]]; 86 | [traceStorage saveTracedCall: tracer]; 87 | [tracer release]; 88 | } 89 | return origResult; 90 | } 91 | 92 | - (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)mask error:(NSError **)errorPtr { 93 | BOOL origResult = %orig(path, mask, errorPtr); 94 | if ([CallStackInspector wasDirectlyCalledByApp]) { 95 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"writeToFile:options:error:"]; 96 | [tracer addArgFromPlistObject:path withKey:@"path"]; 97 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger: mask] withKey:@"mask"]; 98 | // For now let's just store the pointer value of the errorPtr parameter 99 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 100 | [tracer addReturnValueFromPlistObject:[NSNumber numberWithBool: origResult]]; 101 | [traceStorage saveTracedCall: tracer]; 102 | [tracer release]; 103 | } 104 | return origResult; 105 | } 106 | 107 | - (BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flag { 108 | BOOL origResult = %orig(aURL, flag); 109 | if ([CallStackInspector wasDirectlyCalledByApp]) { 110 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"writeToURL:atomically:"]; 111 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 112 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: flag] withKey:@"flag"]; 113 | [tracer addReturnValueFromPlistObject:[NSNumber numberWithBool: origResult]]; 114 | [traceStorage saveTracedCall: tracer]; 115 | [tracer release]; 116 | } 117 | return origResult; 118 | } 119 | 120 | - (BOOL)writeToURL:(NSURL *)aURL options:(NSDataWritingOptions)mask error:(NSError **)errorPtr { 121 | BOOL origResult = %orig(aURL, mask, errorPtr); 122 | if ([CallStackInspector wasDirectlyCalledByApp]) { 123 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"writeToURL:options:error:"]; 124 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 125 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger:mask] withKey:@"mask"]; 126 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 127 | [tracer addReturnValueFromPlistObject:[NSNumber numberWithBool: origResult]]; 128 | [traceStorage saveTracedCall: tracer]; 129 | [tracer release]; 130 | } 131 | return origResult; 132 | } 133 | 134 | + (id)dataWithContentsOfFile:(NSString *)path { 135 | id origResult = %orig(path); 136 | if ([CallStackInspector wasDirectlyCalledByApp]) { 137 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"dataWithContentsOfFile:"]; 138 | [tracer addArgFromPlistObject:path withKey:@"path"]; 139 | // origResult should be NSData* ? 140 | [tracer addReturnValueFromPlistObject: origResult]; 141 | [traceStorage saveTracedCall: tracer]; 142 | [tracer release]; 143 | } 144 | return origResult; 145 | } 146 | 147 | + (id)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)mask error:(NSError **)errorPtr { 148 | id origResult = %orig(path, mask, errorPtr); 149 | if ([CallStackInspector wasDirectlyCalledByApp]) { 150 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"dataWithContentsOfFile:options:error:"]; 151 | [tracer addArgFromPlistObject:path withKey:@"path"]; 152 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger:mask] withKey:@"mask"]; 153 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 154 | [tracer addReturnValueFromPlistObject: origResult]; 155 | [traceStorage saveTracedCall: tracer]; 156 | [tracer release]; 157 | } 158 | return origResult; 159 | } 160 | 161 | + (id)dataWithContentsOfURL:(NSURL *)aURL { 162 | id origResult = %orig(aURL); 163 | if ([CallStackInspector wasDirectlyCalledByApp]) { 164 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"dataWithContentsOfURL:"]; 165 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 166 | [tracer addReturnValueFromPlistObject: origResult]; 167 | [traceStorage saveTracedCall: tracer]; 168 | [tracer release]; 169 | } 170 | return origResult; 171 | } 172 | 173 | + (id)dataWithContentsOfURL:(NSURL *)aURL options:(NSDataReadingOptions)mask error:(NSError **)errorPtr { 174 | id origResult = %orig(aURL, mask, errorPtr); 175 | if ([CallStackInspector wasDirectlyCalledByApp]) { 176 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"dataWithContentsOfURL:options:error:"]; 177 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 178 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger:mask] withKey:@"mask"]; 179 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 180 | [tracer addReturnValueFromPlistObject: origResult]; 181 | [traceStorage saveTracedCall: tracer]; 182 | [tracer release]; 183 | } 184 | return origResult; 185 | } 186 | 187 | - (id)initWithContentsOfFile:(NSString *)path { 188 | id origResult = %orig(path); 189 | if ([CallStackInspector wasDirectlyCalledByApp]) { 190 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"initWithContentsOfFile:"]; 191 | [tracer addArgFromPlistObject:path withKey:@"path"]; 192 | [tracer addReturnValueFromPlistObject: origResult]; 193 | [traceStorage saveTracedCall: tracer]; 194 | [tracer release]; 195 | } 196 | return origResult; 197 | } 198 | 199 | - (id)initWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)mask error:(NSError **)errorPtr { 200 | id origResult = %orig(path, mask, errorPtr); 201 | if ([CallStackInspector wasDirectlyCalledByApp]) { 202 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"initWithContentsOfFile:options:error:"]; 203 | [tracer addArgFromPlistObject:path withKey:@"path"]; 204 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger:mask] withKey:@"mask"]; 205 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 206 | [tracer addReturnValueFromPlistObject: origResult]; 207 | [traceStorage saveTracedCall: tracer]; 208 | [tracer release]; 209 | } 210 | return origResult; 211 | } 212 | 213 | - (id)initWithContentsOfURL:(NSURL *)aURL { 214 | id origResult = %orig(aURL); 215 | if ([CallStackInspector wasDirectlyCalledByApp]) { 216 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"initWithContentsOfURL:"]; 217 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 218 | [tracer addReturnValueFromPlistObject: origResult]; 219 | [traceStorage saveTracedCall: tracer]; 220 | [tracer release]; 221 | } 222 | return origResult; 223 | } 224 | 225 | - (id)initWithContentsOfURL:(NSURL *)aURL options:(NSDataReadingOptions)mask error:(NSError **)errorPtr { 226 | id origResult = %orig(aURL, mask, errorPtr); 227 | if ([CallStackInspector wasDirectlyCalledByApp]) { 228 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSData" andMethod:@"initWithContentsOfURL:options:error:"]; 229 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: aURL] withKey:@"aURL"]; 230 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger:mask] withKey:@"mask"]; 231 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"errorPtr"]; 232 | [tracer addReturnValueFromPlistObject: origResult]; 233 | [traceStorage saveTracedCall: tracer]; 234 | [tracer release]; 235 | } 236 | return origResult; 237 | } 238 | %end 239 | 240 | 241 | %hook NSFileHandle 242 | 243 | + (id)fileHandleForReadingAtPath:(NSString *)path { 244 | id origResult = %orig(path); 245 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForReadingAtPath:"]; 246 | [tracer addArgFromPlistObject:path withKey:@"path"]; 247 | // Only store the value of the pointer for now. TODO: Convert NSFilehandle to Plist object 248 | // TODO: what do you want to parse what out of it? i.e., do you want to 249 | // actually make instance calls to the filehandle? to e.g., get the 250 | // data? [NSFileHandle readDataToEndOfFile]? 251 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 252 | [traceStorage saveTracedCall: tracer]; 253 | [tracer release]; 254 | return origResult; 255 | } 256 | 257 | + (id)fileHandleForReadingFromURL:(NSURL *)url error:(NSError **)error { 258 | id origResult = %orig(url, error); 259 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForReadingFromURL:error:"]; 260 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 261 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"error"]; 262 | [tracer addReturnValueFromPlistObject:objectTypeNotSupported]; 263 | [traceStorage saveTracedCall: tracer]; 264 | [tracer release]; 265 | return origResult; 266 | } 267 | 268 | + (id)fileHandleForUpdatingAtPath:(NSString *)path { 269 | id origResult = %orig(path); 270 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForUpdatingAtPath:"]; 271 | [tracer addArgFromPlistObject:path withKey:@"path"]; 272 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 273 | [traceStorage saveTracedCall: tracer]; 274 | [tracer release]; 275 | return origResult; 276 | } 277 | 278 | + (id)fileHandleForUpdatingURL:(NSURL *)url error:(NSError **)error { 279 | id origResult = %orig(url, error); 280 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForUpdatingURL:error:"]; 281 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 282 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"error"]; 283 | [tracer addReturnValueFromPlistObject:objectTypeNotSupported]; 284 | [traceStorage saveTracedCall: tracer]; 285 | [tracer release]; 286 | return origResult; 287 | } 288 | 289 | + (id)fileHandleForWritingAtPath:(NSString *)path { 290 | id origResult = %orig(path); 291 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForWritingAtPath:"]; 292 | [tracer addArgFromPlistObject:path withKey:@"path"]; 293 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 294 | [traceStorage saveTracedCall: tracer]; 295 | [tracer release]; 296 | return origResult; 297 | } 298 | 299 | + (id)fileHandleForWritingToURL:(NSURL *)url error:(NSError **)error { 300 | id origResult = %orig(url, error); 301 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileHandle" andMethod:@"fileHandleForWritingToURL:error:"]; 302 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 303 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"error"]; 304 | [tracer addReturnValueFromPlistObject:objectTypeNotSupported]; 305 | [traceStorage saveTracedCall: tracer]; 306 | [tracer release]; 307 | return origResult; 308 | } 309 | %end 310 | 311 | 312 | // NSFileManager ends up calling NSData methods that we hook as well. Is it useful to hook NSFileManager then ? 313 | %hook NSFileManager 314 | 315 | - (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)contents attributes:(NSDictionary *) attributes { 316 | BOOL origResult = %orig(path, contents, attributes); 317 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileManager" andMethod:@"createFileAtPath:contents:attributes:"]; 318 | [tracer addArgFromPlistObject:path withKey:@"path"]; 319 | [tracer addArgFromPlistObject:contents withKey:@"contents"]; 320 | [tracer addArgFromPlistObject:attributes withKey:@"attributes"]; 321 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithBool:origResult]]; 322 | [traceStorage saveTracedCall: tracer]; 323 | [tracer release]; 324 | return origResult; 325 | } 326 | 327 | - (NSData *)contentsAtPath:(NSString *)path { 328 | id origResult = %orig(path); 329 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileManager" andMethod:@"contentsAtPath:"]; 330 | [tracer addArgFromPlistObject:path withKey:@"path"]; 331 | [tracer addReturnValueFromPlistObject: origResult]; 332 | [traceStorage saveTracedCall: tracer]; 333 | [tracer release]; 334 | return origResult; 335 | } 336 | 337 | - (id )ubiquityIdentityToken { 338 | id origResult = %orig; 339 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSFileManager" andMethod:@"ubiquityIdentityToken"]; 340 | // Not sure about the return value, let's just store the pointer for now 341 | [tracer addReturnValueFromPlistObject:objectTypeNotSupported]; 342 | [traceStorage saveTracedCall: tracer]; 343 | [tracer release]; 344 | return origResult; 345 | } 346 | %end 347 | 348 | 349 | // NSInputStream ends up calling NSData methods that we hook as well. Is it useful to hook NSInputStream then ? 350 | %hook NSInputStream 351 | 352 | + (id)inputStreamWithFileAtPath:(NSString *)path { 353 | id origResult = %orig(path); 354 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSInputStream" andMethod:@"inputStreamWithFileAtPath:"]; 355 | [tracer addArgFromPlistObject:path withKey:@"path"]; 356 | // Just store the pointer value for the return value 357 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 358 | [traceStorage saveTracedCall: tracer]; 359 | [tracer release]; 360 | return origResult; 361 | } 362 | 363 | + (id)inputStreamWithURL:(NSURL *)url { 364 | id origResult = %orig(url); 365 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSInputStream" andMethod:@"inputStreamWithURL:"]; 366 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 367 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 368 | [traceStorage saveTracedCall: tracer]; 369 | [tracer release]; 370 | return origResult; 371 | } 372 | 373 | - (id)initWithFileAtPath:(NSString *)path { 374 | id origResult = %orig(path); 375 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSInputStream" andMethod:@"initWithFileAtPath:"]; 376 | [tracer addArgFromPlistObject:path withKey:@"path"]; 377 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 378 | [traceStorage saveTracedCall: tracer]; 379 | [tracer release]; 380 | return origResult; 381 | } 382 | 383 | - (id)initWithURL:(NSURL *)url { 384 | id origResult = %orig(url); 385 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSInputStream" andMethod:@"initWithURL:"]; 386 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 387 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 388 | [traceStorage saveTracedCall: tracer]; 389 | [tracer release]; 390 | return origResult; 391 | } 392 | %end 393 | 394 | 395 | %hook NSOutputStream 396 | 397 | + (id)outputStreamToFileAtPath:(NSString *)path append:(BOOL)shouldAppend { 398 | id origResult = %orig(path, shouldAppend); 399 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSOutputStream" andMethod:@"outputStreamToFileAtPath:append:"]; 400 | [tracer addArgFromPlistObject:path withKey:@"path"]; 401 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: shouldAppend] withKey:@"shouldAppend"]; 402 | // Just store the pointer value for the return value 403 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 404 | [traceStorage saveTracedCall: tracer]; 405 | [tracer release]; 406 | return origResult; 407 | } 408 | 409 | + (id)outputStreamWithURL:(NSURL *)url append:(BOOL)shouldAppend { 410 | id origResult = %orig(url, shouldAppend); 411 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSOutputStream" andMethod:@"outputStreamWithURL:append:"]; 412 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 413 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: shouldAppend] withKey:@"shouldAppend"]; 414 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 415 | [traceStorage saveTracedCall: tracer]; 416 | [tracer release]; 417 | return origResult; 418 | } 419 | 420 | - (id)initToFileAtPath:(NSString *)path append:(BOOL)shouldAppend { 421 | id origResult = %orig(path, shouldAppend); 422 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSOutputStream" andMethod:@"initToFileAtPath:append:"]; 423 | [tracer addArgFromPlistObject:path withKey:@"path"]; 424 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: shouldAppend] withKey:@"shouldAppend"]; 425 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 426 | [traceStorage saveTracedCall: tracer]; 427 | [tracer release]; 428 | return origResult; 429 | } 430 | 431 | - (id)initWithURL:(NSURL *)url append:(BOOL)shouldAppend { 432 | id origResult = %orig(url, shouldAppend); 433 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSOutputStream" andMethod:@"initWithURL:append:"]; 434 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 435 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: shouldAppend] withKey:@"shouldAppend"]; 436 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 437 | [traceStorage saveTracedCall: tracer]; 438 | [tracer release]; 439 | return origResult; 440 | } 441 | %end 442 | %end 443 | 444 | 445 | %group HTTPHooks 446 | //#include "hooks/NSURLConnectionHooks.xm" 447 | //#include "hooks/NSHTTPCookieHooks.xm" 448 | //#include "hooks/NSURLCredentialHooks.xm" 449 | %hook NSURLConnection 450 | 451 | // Not hooking these methods: 452 | // + connectionWithRequest:delegate: ends up calling initWithRequest:delegate: 453 | // + sendAsynchronousRequest:queue:completionHandler: ends up calling sendSynchronousRequest:returningResponse:error: 454 | 455 | + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error { 456 | NSData *origResult = %orig(request, response, error); 457 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnection" andMethod:@"sendSynchronousRequest:returningResponse:error:"]; 458 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLRequest:request] withKey:@"request"]; 459 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"response"]; 460 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"error"]; 461 | [tracer addReturnValueFromPlistObject:origResult]; 462 | [traceStorage saveTracedCall:tracer]; 463 | [tracer release]; 464 | return origResult; 465 | } 466 | 467 | - (id)initWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate { 468 | 469 | // Proxy the delegate so we can hook it 470 | NSURLConnectionDelegateProx *delegateProxy = [[NSURLConnectionDelegateProx alloc] initWithOriginalDelegate:delegate]; 471 | id origResult = %orig(request, delegateProxy); 472 | 473 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnection" andMethod:@"initWithRequest:delegate:"]; 474 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLRequest:request] withKey:@"request"]; 475 | [tracer addArgFromPlistObject:[PlistObjectConverter convertDelegate:delegate followingProtocol:@"NSURLConnectionDelegate"] withKey:@"delegate"]; 476 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 477 | [traceStorage saveTracedCall:tracer]; 478 | [tracer release]; 479 | return origResult; 480 | } 481 | 482 | - (id)initWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate startImmediately:(BOOL)startImmediately { 483 | 484 | // Proxy the delegate so we can hook it 485 | NSURLConnectionDelegateProx *delegateProxy = [[NSURLConnectionDelegateProx alloc] initWithOriginalDelegate:delegate]; 486 | id origResult = %orig(request, delegateProxy, startImmediately); 487 | 488 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnection" andMethod:@"initWithRequest:delegate:startImmediately:"]; 489 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLRequest:request] withKey:@"request"]; 490 | [tracer addArgFromPlistObject:[PlistObjectConverter convertDelegate:delegate followingProtocol:@"NSURLConnectionDelegate"] withKey:@"delegate"]; 491 | [tracer addArgFromPlistObject:[NSNumber numberWithBool:startImmediately] withKey:@"startImmediately"]; 492 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 493 | [traceStorage saveTracedCall:tracer]; 494 | [tracer release]; 495 | return origResult; 496 | } 497 | 498 | // The following methods are not explicitely part of NSURLConnection. 499 | // However, when implementing custom cert validation using the NSURLConnectionDelegate protocol, 500 | // the application sends the result of the validation (server cert was OK/bad) to [challenge sender]. 501 | // The class of [challenge sender] is NSURLConnection because it implements the NSURLAuthenticationChallengeSender 502 | // protocol. So we're hooking this in order to find when the validation might have been disabled. 503 | 504 | // The usual way of disabling SSL cert validation 505 | - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 506 | %orig(challenge); 507 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnection" andMethod:@"continueWithoutCredentialForAuthenticationChallenge:"]; 508 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLAuthenticationChallenge: challenge] withKey:@"challenge"]; 509 | [traceStorage saveTracedCall:tracer]; 510 | [tracer release]; 511 | } 512 | 513 | // Might indicate client certificates or cert pinning. TODO: Investigate 514 | - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 515 | %orig(credential, challenge); 516 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnection" andMethod:@"useCredential:forAuthenticationChallenge:"]; 517 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLCredential:credential] withKey:@"credential"]; 518 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLAuthenticationChallenge: challenge] withKey:@"challenge"]; 519 | [traceStorage saveTracedCall:tracer]; 520 | [tracer release]; 521 | } 522 | %end 523 | 524 | 525 | %hook NSHTTPCookie 526 | 527 | // No need to hook +cookieWithProperties: because it just calls –initWithProperties: 528 | 529 | 530 | // This might be unnecessary. We should see the cookies getting created as we hook the constructor. 531 | // TODO: Double check 532 | #if 0 533 | + (NSArray *)cookiesWithResponseHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)theURL { 534 | NSArray *origResult = %orig(headerFields, theURL); 535 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSHTTPCookie" andMethod:@"cookiesWithResponseHeaderFields:forURL:"]; 536 | [tracer addArgFromPlistObject:headerFields withKey:@"headerFields"]; 537 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL:theURL] withKey:@"theURL"]; 538 | [tracer addReturnValueFromPlistObject:origResult]; 539 | [traceStorage saveTracedCall:tracer]; 540 | [tracer release]; 541 | return origResult; 542 | } 543 | #endif 544 | 545 | 546 | - (id)initWithProperties:(NSDictionary *)properties { 547 | id origResult = %orig(properties); 548 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSHTTPCookie" andMethod:@"initWithProperties:"]; 549 | [tracer addArgFromPlistObject:properties withKey:@"properties"]; 550 | [tracer addReturnValueFromPlistObject:[PlistObjectConverter convertNSHTTPCookie:origResult]]; 551 | [traceStorage saveTracedCall:tracer]; 552 | [tracer release]; 553 | return origResult; 554 | } 555 | %end 556 | 557 | 558 | %hook NSURLCredential 559 | 560 | //credentialWithXXX() all call initWithXXX() so we don't hook them 561 | 562 | - (id)initWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence { 563 | id origResult = %orig(user, password, persistence); 564 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLCredential" andMethod:@"initWithUser:password:persistence:"]; 565 | [tracer addArgFromPlistObject:user withKey:@"user"]; 566 | [tracer addArgFromPlistObject:password withKey:@"password"]; 567 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) persistence] withKey:@"persistence"]; 568 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertNSURLCredential:origResult]]; 569 | [traceStorage saveTracedCall: tracer]; 570 | [tracer release]; 571 | return origResult; 572 | } 573 | 574 | - (id)initWithTrust:(SecTrustRef)trust { 575 | id origResult = %orig(trust); 576 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLCredential" andMethod:@"initWithTrust:"]; 577 | [tracer addArgFromPlistObject:[PlistObjectConverter convertSecTrustRef: trust] withKey:@"trust"]; 578 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertNSURLCredential:origResult]]; 579 | [traceStorage saveTracedCall: tracer]; 580 | [tracer release]; 581 | return origResult; 582 | } 583 | 584 | // We probably don't need this as we can already see client cert stuff by hooking NSURLConnection 585 | #if 0 586 | - (id)initWithIdentity:(SecIdentityRef)identity certificates:(NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence { 587 | id origResult = %orig(identity, certArray, persistence); 588 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLCredential" andMethod:@"initWithIdentity:certificates:persistence:"]; 589 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) identity] withKey:@"identity"]; 590 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) certArray] withKey:@"certArray"]; 591 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) persistence] withKey:@"persistence"]; 592 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertNSURLCredential:origResult]]; 593 | [traceStorage saveTracedCall: tracer]; 594 | [tracer release]; 595 | return origResult; 596 | } 597 | #endif 598 | 599 | %end 600 | %end 601 | 602 | 603 | %group UserPreferencesHooks 604 | //#include "hooks/NSUserDefaultsHooks.xm" 605 | %hook NSUserDefaults 606 | 607 | // setObject:forKey: is called by every other setXXX functions. Let's not hook it 608 | // object:forKey: is called by every other functions. Let's not hook it 609 | 610 | - (void)setBool:(BOOL)value forKey:(NSString *)defaultName { 611 | %orig(value, defaultName); 612 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"setBool:forKey:"]; 613 | [tracer addArgFromPlistObject:[NSNumber numberWithBool: value] withKey:@"value"]; 614 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 615 | [traceStorage saveTracedCall: tracer]; 616 | [tracer release]; 617 | return; 618 | } 619 | 620 | - (void)setFloat:(float)value forKey:(NSString *)defaultName { 621 | %orig(value, defaultName); 622 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"setFloat:forKey:"]; 623 | [tracer addArgFromPlistObject:[NSNumber numberWithFloat: value] withKey:@"value"]; 624 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 625 | [traceStorage saveTracedCall: tracer]; 626 | [tracer release]; 627 | return; 628 | } 629 | 630 | - (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName { 631 | %orig(value, defaultName); 632 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"setInteger:forKey:"]; 633 | [tracer addArgFromPlistObject:[NSNumber numberWithInteger: value] withKey:@"value"]; 634 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 635 | [traceStorage saveTracedCall: tracer]; 636 | [tracer release]; 637 | return; 638 | } 639 | 640 | - (void)setURL:(NSURL *)url forKey:(NSString *)defaultName { 641 | %orig(url, defaultName); 642 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"setURL:forKey:"]; 643 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 644 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 645 | [traceStorage saveTracedCall: tracer]; 646 | [tracer release]; 647 | return; 648 | } 649 | 650 | - (void)setDouble:(double)value forKey:(NSString *)defaultName { 651 | %orig(value, defaultName); 652 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"setDouble:forKey:"]; 653 | [tracer addArgFromPlistObject:[NSNumber numberWithDouble: value] withKey:@"value"]; 654 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 655 | [traceStorage saveTracedCall: tracer]; 656 | [tracer release]; 657 | return; 658 | } 659 | 660 | - (NSArray *)arrayForKey:(NSString *)defaultName { 661 | NSArray *origResult = %orig(defaultName); 662 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"arrayForKey:"]; 663 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 664 | // Dangerous: will crash if the array contains non-plist objects 665 | [tracer addReturnValueFromPlistObject: origResult]; 666 | [traceStorage saveTracedCall: tracer]; 667 | [tracer release]; 668 | return origResult; 669 | } 670 | 671 | - (BOOL)boolForKey:(NSString *)defaultName { 672 | BOOL origResult = %orig(defaultName); 673 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"boolForKey:"]; 674 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 675 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithBool: origResult]]; 676 | [traceStorage saveTracedCall: tracer]; 677 | [tracer release]; 678 | return origResult; 679 | } 680 | 681 | - (NSData *)dataForKey:(NSString *)defaultName { 682 | NSData *origResult = %orig(defaultName); 683 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"dataForKey:"]; 684 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 685 | [tracer addReturnValueFromPlistObject: origResult]; 686 | [traceStorage saveTracedCall: tracer]; 687 | [tracer release]; 688 | return origResult; 689 | } 690 | 691 | - (NSDictionary *)dictionaryForKey:(NSString *)defaultName { 692 | NSDictionary *origResult = %orig(defaultName); 693 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"dictionaryForKey:"]; 694 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 695 | // Dangerous: will crash if the dict contains non-plist objects 696 | [tracer addReturnValueFromPlistObject: origResult]; 697 | [traceStorage saveTracedCall: tracer]; 698 | [tracer release]; 699 | return origResult; 700 | } 701 | 702 | - (float)floatForKey:(NSString *)defaultName { 703 | float origResult = %orig(defaultName); 704 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"floatForKey:"]; 705 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 706 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithFloat: origResult]]; 707 | [traceStorage saveTracedCall: tracer]; 708 | [tracer release]; 709 | return origResult; 710 | } 711 | 712 | - (double)doubleForKey:(NSString *)defaultName { 713 | double origResult = %orig(defaultName); 714 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"doubleForKey:"]; 715 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 716 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithDouble: origResult]]; 717 | [traceStorage saveTracedCall: tracer]; 718 | [tracer release]; 719 | return origResult; 720 | } 721 | 722 | - (NSInteger)integerForKey:(NSString *)defaultName { 723 | double origResult = %orig(defaultName); 724 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"integerForKey:"]; 725 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 726 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInteger: origResult]]; 727 | [traceStorage saveTracedCall: tracer]; 728 | [tracer release]; 729 | return origResult; 730 | } 731 | 732 | - (NSArray *)stringArrayForKey:(NSString *)defaultName { 733 | NSArray *origResult = %orig(defaultName); 734 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"stringArrayForKey:"]; 735 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 736 | [tracer addReturnValueFromPlistObject: origResult]; 737 | [traceStorage saveTracedCall: tracer]; 738 | [tracer release]; 739 | return origResult; 740 | } 741 | 742 | - (NSString *)stringForKey:(NSString *)defaultName { 743 | NSString *origResult = %orig(defaultName); 744 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"stringForKey:"]; 745 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 746 | [tracer addReturnValueFromPlistObject: origResult]; 747 | [traceStorage saveTracedCall: tracer]; 748 | [tracer release]; 749 | return origResult; 750 | } 751 | 752 | - (NSURL *)URLForKey:(NSString *)defaultName { 753 | NSURL *origResult = %orig(defaultName); 754 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"URLForKey:"]; 755 | [tracer addArgFromPlistObject:defaultName withKey:@"defaultName"]; 756 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertURL: origResult]]; 757 | [traceStorage saveTracedCall: tracer]; 758 | [tracer release]; 759 | return origResult; 760 | } 761 | 762 | 763 | - (NSDictionary *)dictionaryRepresentation { 764 | NSDictionary *origResult = %orig(); 765 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSUserDefaults" andMethod:@"dictionaryRepresentation"]; 766 | [tracer addReturnValueFromPlistObject: origResult]; 767 | [traceStorage saveTracedCall: tracer]; 768 | [tracer release]; 769 | return origResult; 770 | } 771 | %end 772 | %end 773 | 774 | 775 | %group PasteboardHooks 776 | //#include "hooks/UIPasteboardHooks.xm" 777 | %hook UIPasteboard 778 | 779 | + (UIPasteboard *)generalPasteboard { 780 | UIPasteboard *origResult = %orig; 781 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"generalPasteboard"]; 782 | [tracer addReturnValueFromPlistObject:[PlistObjectConverter convertUIPasteboard: origResult]]; 783 | [traceStorage saveTracedCall: tracer]; 784 | [tracer release]; 785 | return origResult; 786 | } 787 | 788 | + (UIPasteboard *)pasteboardWithName:(NSString *)pasteboardName create:(BOOL)create { 789 | UIPasteboard *origResult = %orig; 790 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"pasteboardWithName:create:"]; 791 | [tracer addArgFromPlistObject:pasteboardName withKey:@"pasteboardName"]; 792 | [tracer addArgFromPlistObject:[NSNumber numberWithBool:create] withKey:@"create"]; 793 | [tracer addReturnValueFromPlistObject:[PlistObjectConverter convertUIPasteboard: origResult]]; 794 | [traceStorage saveTracedCall: tracer]; 795 | [tracer release]; 796 | return origResult; 797 | } 798 | 799 | + (UIPasteboard *)pasteboardWithUniqueName { 800 | UIPasteboard *origResult = %orig; 801 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"pasteboardWithUniqueName"]; 802 | [tracer addArgFromPlistObject:origResult.name withKey:@"uniqueName"]; 803 | [tracer addReturnValueFromPlistObject:[PlistObjectConverter convertUIPasteboard: origResult]]; 804 | [traceStorage saveTracedCall: tracer]; 805 | [tracer release]; 806 | return origResult; 807 | } 808 | 809 | - (NSData *)dataForPasteboardType:(NSString *)pasteboardType { 810 | NSData *origResult = %orig(pasteboardType); 811 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"dataForPasteboardType:"]; 812 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 813 | [tracer addReturnValueFromPlistObject:origResult]; 814 | [traceStorage saveTracedCall:tracer]; 815 | [tracer release]; 816 | return origResult; 817 | } 818 | 819 | - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet { 820 | NSArray *origResult = %orig(pasteboardType, itemSet); 821 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"dataForPasteboardType:inItemSet:"]; 822 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 823 | //TODO: need to figure out how to store this properly if we want it 824 | [tracer addArgFromPlistObject:@"introspy - not implemented" withKey:@"itemSet"]; 825 | [tracer addReturnValueFromPlistObject:origResult]; 826 | [traceStorage saveTracedCall:tracer]; 827 | [tracer release]; 828 | return origResult; 829 | } 830 | 831 | - (void)setData:(NSData *)data forPasteboardType:(NSString *)pasteboardType { 832 | %orig(data, pasteboardType); 833 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"setData:forPasteboardType:"]; 834 | [tracer addArgFromPlistObject:data withKey:@"data"]; 835 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 836 | [traceStorage saveTracedCall:tracer]; 837 | [tracer release]; 838 | return; 839 | } 840 | 841 | - (void)setValue:(id)value forPasteboardType:(NSString *)pasteboardType { 842 | %orig(value, pasteboardType); 843 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"setValue:forPasteboardType:"]; 844 | [tracer addArgFromPlistObject:value withKey:@"value"]; 845 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 846 | [traceStorage saveTracedCall:tracer]; 847 | [tracer release]; 848 | return; 849 | } 850 | 851 | - (void)addItems:(NSArray *)items { 852 | %orig(items); 853 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"addItems:"]; 854 | [tracer addArgFromPlistObject:items withKey:@"items"]; 855 | [traceStorage saveTracedCall:tracer]; 856 | [tracer release]; 857 | return; 858 | } 859 | 860 | - (id)valueForPasteboardType:(NSString *)pasteboardType { 861 | id origResult = %orig(pasteboardType); 862 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"valueForPasteboardType:"]; 863 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 864 | [tracer addReturnValueFromPlistObject:origResult]; 865 | [traceStorage saveTracedCall:tracer]; 866 | [tracer release]; 867 | return origResult; 868 | } 869 | 870 | - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet { 871 | NSArray *origResult = %orig(pasteboardType, itemSet); 872 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIPasteboard" andMethod:@"valuesForPasteboardType:itemSet:"]; 873 | [tracer addArgFromPlistObject:pasteboardType withKey:@"pasteboardType"]; 874 | [tracer addArgFromPlistObject:@"introspy - not implemented" withKey:@"itemSet"]; 875 | [tracer addReturnValueFromPlistObject:origResult]; 876 | [traceStorage saveTracedCall:tracer]; 877 | [tracer release]; 878 | return origResult; 879 | } 880 | %end 881 | %end 882 | 883 | 884 | %group XMLHooks 885 | //#include "hooks/NSXMLParserHooks.xm" 886 | %hook NSXMLParser 887 | 888 | - (id)initWithContentsOfURL:(NSURL *)url { 889 | id origResult = %orig(url); 890 | // NSXMLParser methods are called a lot by other iOS APIs (for example to parse HTML responses) and we don't want to log that so we use the CallStackInspector 891 | if ([CallStackInspector wasDirectlyCalledByApp]) { 892 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSXMLParser" andMethod:@"initWithContentsOfURL:"]; 893 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL:url] withKey:@"url"]; 894 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 895 | [traceStorage saveTracedCall: tracer]; 896 | [tracer release]; 897 | } 898 | return origResult; 899 | } 900 | 901 | 902 | - (id)initWithData:(NSData *)data { 903 | id origResult = %orig(data); 904 | if ([CallStackInspector wasDirectlyCalledByApp]) { 905 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSXMLParser" andMethod:@"initWithData:"]; 906 | [tracer addArgFromPlistObject:data withKey:@"data"]; 907 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 908 | [traceStorage saveTracedCall: tracer]; 909 | [tracer release]; 910 | } 911 | return origResult; 912 | } 913 | 914 | 915 | - (id)initWithStream:(NSInputStream *)stream { 916 | id origResult = %orig(stream); 917 | if ([CallStackInspector wasDirectlyCalledByApp]) { 918 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSXMLParser" andMethod:@"initWithStream:"]; 919 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"stream"]; 920 | [tracer addReturnValueFromPlistObject: objectTypeNotSupported]; 921 | [traceStorage saveTracedCall: tracer]; 922 | [tracer release]; 923 | } 924 | return origResult; 925 | } 926 | 927 | 928 | - (void)setShouldResolveExternalEntities:(BOOL)shouldResolveExternalEntities { 929 | %orig(shouldResolveExternalEntities); 930 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSXMLParser" andMethod:@"setShouldResolveExternalEntities:"]; 931 | [tracer addArgFromPlistObject:[NSNumber numberWithBool:shouldResolveExternalEntities] withKey:@"shouldResolveExternalEntities"]; 932 | [traceStorage saveTracedCall: tracer]; 933 | [tracer release]; 934 | } 935 | %end 936 | %end 937 | 938 | 939 | 940 | // Tweak starts here 941 | %ctor { 942 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 943 | 944 | // Only hook Apps the user has selected in Introspy's settings panel 945 | NSString *appId = [[NSBundle mainBundle] bundleIdentifier]; 946 | // Load Introspy preferences 947 | NSMutableDictionary *preferences = [[NSMutableDictionary alloc] initWithContentsOfFile:preferenceFilePath]; 948 | id shouldHook = [preferences objectForKey:appId]; 949 | if ( (shouldHook == nil) || (! [shouldHook boolValue]) ) { 950 | NSLog(@"Introspy - Profiling disabled for %@", appId); 951 | [preferences release]; 952 | [pool drain]; 953 | return; 954 | } 955 | 956 | // Initialize DB storage 957 | NSLog(@"Introspy - Profiling enabled for %@", appId); 958 | BOOL shouldLog = getBoolFromPreferences(preferences, @"LogToTheConsole"); 959 | traceStorage = [[SQLiteStorage alloc] initWithDefaultDBFilePathAndLogToConsole: shouldLog]; 960 | if (traceStorage != nil) { 961 | // Initialize hooks 962 | // Data Storage hooks 963 | if (getBoolFromPreferences(preferences, @"FileSystemHooks")) { 964 | %init(FileSystemHooks); 965 | } 966 | if (getBoolFromPreferences(preferences, @"UserPreferencesHooks")) { 967 | %init(UserPreferencesHooks); 968 | } 969 | if (getBoolFromPreferences(preferences, @"KeyChainHooks")) { 970 | [KeychainHooks enableHooks]; 971 | } 972 | // Crypto hooks 973 | if (getBoolFromPreferences(preferences, @"CommonCryptoHooks")) { 974 | [LibCHooks enableHooks]; // Not really part of CommonCrypto 975 | [CommonCryptorHooks enableHooks]; 976 | [CommonHMACHooks enableHooks]; 977 | [CommonKeyDerivationHooks enableHooks]; 978 | [CommonDigestHooks enableHooks]; 979 | } 980 | if (getBoolFromPreferences(preferences, @"SecurityHooks")) { 981 | [SecurityHooks enableHooks]; 982 | } 983 | // Network hooks 984 | if (getBoolFromPreferences(preferences, @"HTTPHooks")) { 985 | %init(HTTPHooks); 986 | } 987 | // IPC hooks 988 | if (getBoolFromPreferences(preferences, @"PasteboardHooks")) { 989 | %init(PasteboardHooks); 990 | } 991 | if (getBoolFromPreferences(preferences, @"URLSchemesHooks")) { 992 | traceURISchemes(); 993 | %init(URLSchemes); 994 | } 995 | // Misc hooks 996 | if (getBoolFromPreferences(preferences, @"XMLHooks")) { 997 | %init(XMLHooks); 998 | } 999 | } 1000 | else { 1001 | NSLog(@"Introspy - DB Initialization error; disabling hooks."); 1002 | } 1003 | 1004 | [preferences release]; 1005 | [pool drain]; 1006 | } 1007 | 1008 | /* vim: set filetype=objc : */ 1009 | -------------------------------------------------------------------------------- /src/control: -------------------------------------------------------------------------------- 1 | Package: com.isecpartners.introspy 2 | Name: introspy 3 | Depends: mobilesubstrate, preferenceloader, applist 4 | Version: 0.5.1 5 | Architecture: iphoneos-arm 6 | Description: Open-source security profiler designed to help penetration testers understand what an application does at runtime. 7 | Maintainer: Tom Daniels & Alban Diquet & Herman Duarte 8 | Author: Tom Daniels & Alban Diquet 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /src/hooks/CommonCryptorHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CommonCryptorHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/CommonCryptorHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #import "CommonCryptorHooks.h" 6 | #import "../SQLiteStorage.h" 7 | #import "../PlistObjectConverter.h" 8 | #import "../CallStackInspector.h" 9 | 10 | // Nice global 11 | extern SQLiteStorage *traceStorage; 12 | 13 | 14 | // Utiity function 15 | static size_t getIVLength(CCAlgorithm alg) { 16 | 17 | switch(alg) { 18 | case kCCAlgorithmAES128: 19 | return kCCBlockSizeAES128; 20 | case kCCAlgorithmDES: 21 | return kCCBlockSizeDES; 22 | case kCCAlgorithm3DES: 23 | return kCCBlockSize3DES; 24 | case kCCAlgorithmCAST: 25 | return kCCBlockSizeCAST; 26 | case kCCAlgorithmRC2: 27 | return kCCBlockSizeRC2; 28 | default: 29 | return 0; 30 | } 31 | } 32 | 33 | 34 | // Hook CCCryptorCreate() 35 | static CCCryptorStatus (*original_CCCryptorCreate)( 36 | CCOperation op, 37 | CCAlgorithm alg, 38 | CCOptions options, 39 | const void *key, 40 | size_t keyLength, 41 | const void *iv, 42 | CCCryptorRef *cryptorRef); 43 | 44 | 45 | static CCCryptorStatus replaced_CCCryptorCreate( 46 | CCOperation op, 47 | CCAlgorithm alg, 48 | CCOptions options, 49 | const void *key, 50 | size_t keyLength, 51 | const void *iv, 52 | CCCryptorRef *cryptorRef) 53 | { 54 | 55 | CCCryptorStatus origResult = original_CCCryptorCreate(op, alg, options, key, keyLength, iv, cryptorRef); 56 | 57 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 58 | if ([CallStackInspector wasDirectlyCalledByApp]) { 59 | 60 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCCryptorCreate"]; 61 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) op] withKey:@"op"]; 62 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) alg] withKey:@"alg"]; 63 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) options] withKey:@"options"]; 64 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: key withLength: keyLength] withKey:@"key"]; 65 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: iv withLength: getIVLength(alg)] withKey:@"iv"]; 66 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) cryptorRef] withKey:@"cryptorRef"]; 67 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 68 | [traceStorage saveTracedCall: tracer]; 69 | [tracer release]; 70 | } 71 | return origResult; 72 | } 73 | 74 | 75 | // Hook CCCryptorCreateFromData() 76 | static CCCryptorStatus (*original_CCCryptorCreateFromData)( 77 | CCOperation op, 78 | CCAlgorithm alg, 79 | CCOptions options, 80 | const void *key, 81 | size_t keyLength, 82 | const void *iv, 83 | const void *data, 84 | size_t dataLength, 85 | CCCryptorRef *cryptorRef, 86 | size_t *dataUsed); 87 | 88 | 89 | static CCCryptorStatus replaced_CCCryptorCreateFromData( 90 | CCOperation op, 91 | CCAlgorithm alg, 92 | CCOptions options, 93 | const void *key, 94 | size_t keyLength, 95 | const void *iv, 96 | const void *data, 97 | size_t dataLength, 98 | CCCryptorRef *cryptorRef, 99 | size_t *dataUsed) 100 | { 101 | 102 | CCCryptorStatus origResult = original_CCCryptorCreateFromData(op, alg, options, key, keyLength, iv, data, dataLength, cryptorRef, dataUsed); 103 | 104 | if ([CallStackInspector wasDirectlyCalledByApp]) { 105 | 106 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCCryptorCreateFromData"]; 107 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) op] withKey:@"op"]; 108 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) alg] withKey:@"alg"]; 109 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) options] withKey:@"options"]; 110 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: key withLength: keyLength] withKey:@"key"]; 111 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: iv withLength: getIVLength(alg)] withKey:@"iv"]; 112 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: data withLength: dataLength] withKey:@"data"]; 113 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) *dataUsed] withKey:@"dataUsed"]; 114 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) cryptorRef] withKey:@"cryptorRef"]; 115 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 116 | [traceStorage saveTracedCall: tracer]; 117 | [tracer release]; 118 | } 119 | return origResult; 120 | } 121 | 122 | 123 | // Hook CCCryptorUpdate() 124 | static CCCryptorStatus (*original_CCCryptorUpdate)( 125 | CCCryptorRef cryptorRef, 126 | const void *dataIn, 127 | size_t dataInLength, 128 | void *dataOut, 129 | size_t dataOutAvailable, 130 | size_t *dataOutMoved); 131 | 132 | 133 | static CCCryptorStatus replaced_CCCryptorUpdate( 134 | CCCryptorRef cryptorRef, 135 | const void *dataIn, 136 | size_t dataInLength, 137 | void *dataOut, 138 | size_t dataOutAvailable, 139 | size_t *dataOutMoved) 140 | { 141 | // dataIn and dataOut may be the same pointer (Encryption and decryption can be performed "in-place") 142 | // Hence we first save dataIn, then call CCCryptorUpdate() and then save dataOut to get both buffers 143 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCCryptorUpdate"]; 144 | if ([CallStackInspector wasDirectlyCalledByApp]) { 145 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) cryptorRef] withKey:@"cryptorRef"]; 146 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: dataIn withLength: dataInLength] withKey:@"dataIn"]; 147 | } 148 | 149 | CCCryptorStatus origResult = original_CCCryptorUpdate(cryptorRef, dataIn, dataInLength, dataOut, dataOutAvailable, dataOutMoved); 150 | 151 | if ([CallStackInspector wasDirectlyCalledByApp]) { 152 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: dataOut withLength: *dataOutMoved] withKey:@"dataOut"]; 153 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 154 | [traceStorage saveTracedCall: tracer]; 155 | } 156 | [tracer release]; 157 | 158 | return origResult; 159 | } 160 | 161 | 162 | // Hook CCCryptorFinal() 163 | static CCCryptorStatus (*original_CCCryptorFinal)( 164 | CCCryptorRef cryptorRef, 165 | void *dataOut, 166 | size_t dataOutAvailable, 167 | size_t *dataOutMoved); 168 | 169 | static CCCryptorStatus replaced_CCCryptorFinal( 170 | CCCryptorRef cryptorRef, 171 | void *dataOut, 172 | size_t dataOutAvailable, 173 | size_t *dataOutMoved) 174 | { 175 | CCCryptorStatus origResult = original_CCCryptorFinal(cryptorRef, dataOut, dataOutAvailable, dataOutMoved); 176 | 177 | if ([CallStackInspector wasDirectlyCalledByApp]) { 178 | 179 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCCryptorFinal"]; 180 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) cryptorRef] withKey:@"cryptorRef"]; 181 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: dataOut withLength: *dataOutMoved] withKey:@"dataOut"]; 182 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) dataOutAvailable] withKey:@"dataOutAvailable"]; 183 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 184 | [traceStorage saveTracedCall: tracer]; 185 | [tracer release]; 186 | } 187 | return origResult; 188 | } 189 | 190 | // Hook CCCrypt() 191 | static CCCryptorStatus (*original_CCCrypt)( 192 | CCOperation op, 193 | CCAlgorithm alg, 194 | CCOptions options, 195 | const void *key, 196 | size_t keyLength, 197 | const void *iv, 198 | const void *dataIn, 199 | size_t dataInLength, 200 | void *dataOut, 201 | size_t dataOutAvailable, 202 | size_t *dataOutMoved); 203 | 204 | static CCCryptorStatus replaced_CCCrypt( 205 | CCOperation op, 206 | CCAlgorithm alg, 207 | CCOptions options, 208 | const void *key, 209 | size_t keyLength, 210 | const void *iv, 211 | const void *dataIn, 212 | size_t dataInLength, 213 | void *dataOut, 214 | size_t dataOutAvailable, 215 | size_t *dataOutMoved) 216 | { 217 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCCrypt"]; 218 | 219 | if ([CallStackInspector wasDirectlyCalledByApp]) { 220 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) op] withKey:@"op"]; 221 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) alg] withKey:@"alg"]; 222 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) options] withKey:@"options"]; 223 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: key withLength: keyLength] withKey:@"key"]; 224 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: iv withLength: getIVLength(alg)] withKey:@"iv"]; 225 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: dataIn withLength: dataInLength] withKey:@"dataIn"]; 226 | } 227 | 228 | CCCryptorStatus origResult = original_CCCrypt(op, alg, options, key, keyLength, iv, dataIn, 229 | dataInLength, dataOut, dataOutAvailable, dataOutMoved); 230 | 231 | if ([CallStackInspector wasDirectlyCalledByApp]) { 232 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: dataOut withLength: *dataOutMoved] withKey:@"dataOut"]; 233 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) dataOutAvailable] withKey:@"dataOutAvailable"]; 234 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 235 | [traceStorage saveTracedCall: tracer]; 236 | } 237 | [tracer release]; 238 | return origResult; 239 | } 240 | 241 | 242 | @implementation CommonCryptorHooks 243 | 244 | + (void)enableHooks { 245 | MSHookFunction((void *) CCCryptorCreate, (void *) replaced_CCCryptorCreate, (void **) &original_CCCryptorCreate); 246 | MSHookFunction((void *) CCCryptorCreateFromData, (void *) replaced_CCCryptorCreateFromData, (void **) &original_CCCryptorCreateFromData); 247 | MSHookFunction((void *) CCCryptorUpdate, (void *) replaced_CCCryptorUpdate, (void **) &original_CCCryptorUpdate); 248 | MSHookFunction((void *) CCCryptorFinal, (void *) replaced_CCCryptorFinal, (void **) &original_CCCryptorFinal); 249 | MSHookFunction((void *) CCCrypt, (void *) replaced_CCCrypt, (void **) &original_CCCrypt); 250 | } 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /src/hooks/CommonDigestHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CommonDigestHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/CommonDigestHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #import "CommonDigestHooks.h" 6 | #import "../SQLiteStorage.h" 7 | #import "../PlistObjectConverter.h" 8 | #import "../CallStackInspector.h" 9 | 10 | // Nice global 11 | extern SQLiteStorage *traceStorage; 12 | 13 | 14 | // No need to hook the CC_MDX_Init() functions as they don't do anything interesting 15 | 16 | 17 | // Generic function to log CC_XXX_Update() calls 18 | static int log_CC_XXX_Update(void *c, const void *data, CC_LONG len, NSString *functionName, int (*functionPointer)(void *, const void *, CC_LONG) ) { 19 | 20 | int origResult = functionPointer(c, data, len); 21 | 22 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 23 | // Index = 3 because this function is called by Introspy first. TODO: Check that 24 | if ([CallStackInspector wasCalledByAppAtIndex:3]) { 25 | 26 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:functionName]; 27 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) c] withKey:@"c"]; 28 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: data withLength: len] withKey:@"data"]; 29 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 30 | [traceStorage saveTracedCall: tracer]; 31 | [tracer release]; 32 | } 33 | return origResult; 34 | } 35 | 36 | 37 | // Hook CC_MD2_Update() 38 | static int (*original_CC_MD2_Update)(CC_MD2_CTX *c, const void *data, CC_LONG len); 39 | static int replaced_CC_MD2_Update(CC_MD2_CTX *c, const void *data, CC_LONG len) { 40 | // We're casting original_CC_MD2_Update() from (int)(CC_MD2_CTX *, const void *, CC_LONG) 41 | // to (int)(void *, const void *, CC_LONG). Specifically, the first argument needs to be 42 | // (void *) instead of (CC_MD2_CTX *) because we want log_CC_XXX_Update() to be generic. 43 | // It compiles but will it work !?? 44 | return log_CC_XXX_Update(c, data, len, @"CC_MD2_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_MD2_Update); 45 | } 46 | 47 | 48 | // Hook CC_MD4_Update() 49 | static int (*original_CC_MD4_Update)(CC_MD4_CTX *c, const void *data, CC_LONG len); 50 | static int replaced_CC_MD4_Update(CC_MD4_CTX *c, const void *data, CC_LONG len) { 51 | return log_CC_XXX_Update(c, data, len, @"CC_MD4_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_MD4_Update); 52 | } 53 | 54 | 55 | // Hook CC_MD5_Update() 56 | static int (*original_CC_MD5_Update)(CC_MD5_CTX *c, const void *data, CC_LONG len); 57 | static int replaced_CC_MD5_Update(CC_MD5_CTX *c, const void *data, CC_LONG len) { 58 | return log_CC_XXX_Update(c, data, len, @"CC_MD5_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_MD5_Update); 59 | } 60 | 61 | 62 | 63 | // Hook CC_SHA1_Update() 64 | static int (*original_CC_SHA1_Update)(CC_SHA1_CTX *c, const void *data, CC_LONG len); 65 | 66 | static int replaced_CC_SHA1_Update(CC_SHA1_CTX *c, const void *data, CC_LONG len) { 67 | return log_CC_XXX_Update(c, data, len, @"CC_SHA1_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_SHA1_Update); 68 | } 69 | 70 | 71 | // Hook CC_SHA512_Update() 72 | static int (*original_CC_SHA512_Update)(CC_SHA512_CTX *c, const void *data, CC_LONG len); 73 | 74 | static int replaced_CC_SHA512_Update(CC_SHA512_CTX *c, const void *data, CC_LONG len) { 75 | return log_CC_XXX_Update(c, data, len, @"CC_SHA512_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_SHA512_Update); 76 | } 77 | 78 | 79 | 80 | // Hook CC_SHA256_Update() 81 | static int (*original_CC_SHA256_Update)(CC_SHA256_CTX *c, const void *data, CC_LONG len); 82 | 83 | static int replaced_CC_SHA256_Update(CC_SHA256_CTX *c, const void *data, CC_LONG len) { 84 | return log_CC_XXX_Update(c, data, len, @"CC_SHA256_Update", (int (*)(void *, const void *, CC_LONG)) original_CC_SHA256_Update); 85 | } 86 | 87 | 88 | 89 | 90 | // Generic function to log CC_XXX_Final() calls 91 | static int log_CC_XXX_Final(unsigned char *md, void *c, NSString *functionName, int (*functionPointer)(unsigned char *, void *), int digestLen) { 92 | 93 | int origResult = functionPointer(md, c); 94 | 95 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 96 | if ([CallStackInspector wasCalledByAppAtIndex:3]) { 97 | 98 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:functionName]; 99 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: md withLength: digestLen] withKey:@"md"]; 100 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) c] withKey:@"c"]; 101 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 102 | [traceStorage saveTracedCall: tracer]; 103 | [tracer release]; 104 | } 105 | return origResult; 106 | } 107 | 108 | 109 | // Hook CC_MD2_Final() 110 | static int (*original_CC_MD2_Final)(unsigned char *md, CC_MD2_CTX *c); 111 | 112 | static int replaced_CC_MD2_Final(unsigned char *md, CC_MD2_CTX *c) { 113 | return log_CC_XXX_Final(md, c, @"CC_MD2_Final", (int (*)(unsigned char *, void *)) original_CC_MD2_Final, CC_MD2_DIGEST_LENGTH); 114 | } 115 | 116 | 117 | // Hook CC_MD4_Final() 118 | static int (*original_CC_MD4_Final)(unsigned char *md, CC_MD4_CTX *c); 119 | 120 | static int replaced_CC_MD4_Final(unsigned char *md, CC_MD4_CTX *c) { 121 | return log_CC_XXX_Final(md, c, @"CC_MD2_Final", (int (*)(unsigned char *, void *)) original_CC_MD4_Final, CC_MD4_DIGEST_LENGTH); 122 | } 123 | 124 | 125 | // Hook CC_MD5_Final() 126 | static int (*original_CC_MD5_Final)(unsigned char *md, CC_MD5_CTX *c); 127 | 128 | static int replaced_CC_MD5_Final(unsigned char *md, CC_MD5_CTX *c) { 129 | return log_CC_XXX_Final(md, c, @"CC_MD5_Final", (int (*)(unsigned char *, void *)) original_CC_MD5_Final, CC_MD5_DIGEST_LENGTH); 130 | } 131 | 132 | 133 | 134 | // Hook CC_SHA1_Final() 135 | static int (*original_CC_SHA1_Final)(unsigned char *md, CC_SHA1_CTX *c); 136 | 137 | static int replaced_CC_SHA1_Final(unsigned char *md, CC_SHA1_CTX *c) { 138 | return log_CC_XXX_Final(md, c, @"CC_SHA1_Final", (int (*)(unsigned char *, void *)) original_CC_SHA1_Final, CC_SHA1_DIGEST_LENGTH); 139 | } 140 | 141 | 142 | // Hook CC_SHA512_Final() 143 | static int (*original_CC_SHA512_Final)(unsigned char *md, CC_SHA512_CTX *c); 144 | 145 | static int replaced_CC_SHA512_Final(unsigned char *md, CC_SHA512_CTX *c) { 146 | return log_CC_XXX_Final(md, c, @"CC_SHA512_Final", (int (*)(unsigned char *, void *)) original_CC_SHA512_Final, CC_SHA512_DIGEST_LENGTH); 147 | } 148 | 149 | 150 | // Hook CC_SHA256_Final() 151 | static int (*original_CC_SHA256_Final)(unsigned char *md, CC_SHA256_CTX *c); 152 | 153 | static int replaced_CC_SHA256_Final(unsigned char *md, CC_SHA256_CTX *c) { 154 | return log_CC_XXX_Final(md, c, @"CC_SHA256_Final", (int (*)(unsigned char *, void *)) original_CC_SHA256_Final, CC_SHA256_DIGEST_LENGTH); 155 | } 156 | 157 | 158 | 159 | // Generic function to log CC_XXX() calls 160 | static unsigned char * log_CC_XXX(const void *data, CC_LONG len, unsigned char *md, NSString *functionName, unsigned char * (*functionPointer)(const void *, CC_LONG, unsigned char *), int digestLen) { 161 | 162 | unsigned char *origResult = functionPointer(data, len, md); 163 | 164 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 165 | if ([CallStackInspector wasDirectlyCalledByApp]) { 166 | 167 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:functionName]; 168 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: data withLength: len] withKey:@"data"]; 169 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: md withLength: digestLen] withKey:@"md"]; 170 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt: (int)origResult]]; 171 | [traceStorage saveTracedCall: tracer]; 172 | [tracer release]; 173 | } 174 | return origResult; 175 | } 176 | 177 | 178 | // Hook CC_MD2() 179 | static unsigned char * (*original_CC_MD2)(const void *data, CC_LONG len, unsigned char *md); 180 | 181 | static unsigned char * replaced_CC_MD2(const void *data, CC_LONG len, unsigned char *md) { 182 | return log_CC_XXX(data, len, md, @"CC_MD2", original_CC_MD2, CC_MD2_DIGEST_LENGTH); 183 | } 184 | 185 | 186 | // Hook CC_MD4() 187 | static unsigned char * (*original_CC_MD4)(const void *data, CC_LONG len, unsigned char *md); 188 | 189 | static unsigned char * replaced_CC_MD4(const void *data, CC_LONG len, unsigned char *md) { 190 | return log_CC_XXX(data, len, md, @"CC_MD4", original_CC_MD4, CC_MD4_DIGEST_LENGTH); 191 | } 192 | 193 | 194 | // Hook CC_MD5() 195 | static unsigned char * (*original_CC_MD5)(const void *data, CC_LONG len, unsigned char *md); 196 | 197 | static unsigned char * replaced_CC_MD5(const void *data, CC_LONG len, unsigned char *md) { 198 | return log_CC_XXX(data, len, md, @"CC_MD5", original_CC_MD5, CC_MD5_DIGEST_LENGTH); 199 | } 200 | 201 | 202 | 203 | // Hook CC_SHA1() 204 | static unsigned char * (*original_CC_SHA1)(const void *data, CC_LONG len, unsigned char *md); 205 | 206 | static unsigned char * replaced_CC_SHA1(const void *data, CC_LONG len, unsigned char *md) { 207 | return log_CC_XXX(data, len, md, @"CC_SHA1", original_CC_SHA1, CC_SHA1_DIGEST_LENGTH); 208 | } 209 | 210 | 211 | // Hook CC_SHA512() 212 | static unsigned char * (*original_CC_SHA512)(const void *data, CC_LONG len, unsigned char *md); 213 | 214 | static unsigned char * replaced_CC_SHA512(const void *data, CC_LONG len, unsigned char *md) { 215 | return log_CC_XXX(data, len, md, @"CC_SHA512", original_CC_SHA512, CC_SHA512_DIGEST_LENGTH); 216 | } 217 | 218 | 219 | // Hook CC_SHA256() 220 | static unsigned char * (*original_CC_SHA256)(const void *data, CC_LONG len, unsigned char *md); 221 | 222 | static unsigned char * replaced_CC_SHA256(const void *data, CC_LONG len, unsigned char *md) { 223 | return log_CC_XXX(data, len, md, @"CC_SHA256", original_CC_SHA256, CC_SHA256_DIGEST_LENGTH); 224 | } 225 | 226 | 227 | 228 | 229 | @implementation CommonDigestHooks 230 | 231 | + (void)enableHooks { 232 | MSHookFunction(CC_MD2_Update, replaced_CC_MD2_Update, (void **) &original_CC_MD2_Update); 233 | MSHookFunction(CC_MD4_Update, replaced_CC_MD4_Update, (void **) &original_CC_MD4_Update); 234 | MSHookFunction(CC_MD5_Update, replaced_CC_MD5_Update, (void **) &original_CC_MD5_Update); 235 | MSHookFunction(CC_SHA1_Update, replaced_CC_SHA1_Update, (void **) &original_CC_SHA1_Update); 236 | MSHookFunction(CC_SHA512_Update, replaced_CC_SHA512_Update, (void **) &original_CC_SHA512_Update); 237 | MSHookFunction(CC_SHA256_Update, replaced_CC_SHA256_Update, (void **) &original_CC_SHA256_Update); 238 | 239 | MSHookFunction(CC_MD2_Final, replaced_CC_MD2_Final, (void **) &original_CC_MD2_Final); 240 | MSHookFunction(CC_MD4_Final, replaced_CC_MD4_Final, (void **) &original_CC_MD4_Final); 241 | MSHookFunction(CC_MD5_Final, replaced_CC_MD5_Final, (void **) &original_CC_MD5_Final); 242 | MSHookFunction(CC_SHA1_Final, replaced_CC_SHA1_Final, (void **) &original_CC_SHA1_Final); 243 | MSHookFunction(CC_SHA512_Final, replaced_CC_SHA512_Final, (void **) &original_CC_SHA512_Final); 244 | MSHookFunction(CC_SHA256_Final, replaced_CC_SHA256_Final, (void **) &original_CC_SHA256_Final); 245 | 246 | MSHookFunction(CC_MD2, replaced_CC_MD2, (void **) &original_CC_MD2); 247 | MSHookFunction(CC_MD4, replaced_CC_MD4, (void **) &original_CC_MD4); 248 | MSHookFunction(CC_MD5, replaced_CC_MD5, (void **) &original_CC_MD5); 249 | MSHookFunction(CC_SHA1, replaced_CC_SHA1, (void **) &original_CC_SHA1); 250 | MSHookFunction(CC_SHA512, replaced_CC_SHA512, (void **) &original_CC_SHA512); 251 | MSHookFunction(CC_SHA256, replaced_CC_SHA256, (void **) &original_CC_SHA256); 252 | } 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /src/hooks/CommonHMACHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CommonHMACHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/CommonHMACHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #import "CommonHMACHooks.h" 8 | #import "../SQLiteStorage.h" 9 | #import "../PlistObjectConverter.h" 10 | #import "../CallStackInspector.h" 11 | 12 | // Nice global 13 | extern SQLiteStorage *traceStorage; 14 | 15 | // Private function 16 | typedef struct CCHmacContext * CCHmacContextRef; 17 | size_t 18 | CCHmacOutputSizeFromRef(CCHmacContextRef ctx) 19 | __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); 20 | 21 | 22 | 23 | // from Apple's CommonHMAC.c implementation 24 | // This is what a CCHmacContext actually points to. 384 bytes to work with 25 | // if iOS >= 6 (otherwise it's a _CCHmacContext) 26 | //typedef struct { 27 | // struct ccdigest_info *di; 28 | //} _NewHmacContext; 29 | /* 30 | * This is what a CCHmacContext actually points to. 31 | */ 32 | //typedef struct { 33 | // uint32_t digestLen; 34 | // uint32_t blockLen; 35 | // union { 36 | // CC_MD5_CTX md5Ctx; 37 | // CC_SHA1_CTX sha1Ctx; 38 | // CC_SHA256_CTX sha256Ctx; 39 | // CC_SHA512_CTX sha512Ctx; 40 | // } digest; 41 | // uint8_t k_opad[HMAC_MAX_BLOCK_SIZE]; /* max block size */ 42 | // ccDigestInit digestInit; 43 | // ccDigestUpdate digestUpdate; 44 | // ccDigestFinal digestFinal; 45 | //} _CCHmacContext; 46 | 47 | static size_t getHmacLength(CCHmacAlgorithm algorithm) { 48 | 49 | switch(algorithm) { 50 | case kCCHmacAlgSHA1: 51 | return CC_SHA1_DIGEST_LENGTH; 52 | case kCCHmacAlgMD5: 53 | return CC_MD5_DIGEST_LENGTH; 54 | case kCCHmacAlgSHA256: 55 | return CC_SHA256_DIGEST_LENGTH; 56 | case kCCHmacAlgSHA384: 57 | return CC_SHA384_DIGEST_LENGTH; 58 | case kCCHmacAlgSHA512: 59 | return CC_SHA512_DIGEST_LENGTH; 60 | case kCCHmacAlgSHA224: 61 | return CC_SHA224_DIGEST_LENGTH; 62 | default: 63 | return 0; 64 | } 65 | } 66 | 67 | 68 | // Hook CCHmacInit() 69 | static void (*original_CCHmacInit)(CCHmacContext *ctx, CCHmacAlgorithm algorithm, const void *key, size_t keyLength); 70 | 71 | static void replaced_CCHmacInit(CCHmacContext *ctx, CCHmacAlgorithm algorithm, const void *key, size_t keyLength) { 72 | 73 | original_CCHmacInit(ctx, algorithm, key, keyLength); 74 | 75 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 76 | if ([CallStackInspector wasDirectlyCalledByApp]) { 77 | 78 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCHmacInit"]; 79 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) ctx] withKey:@"ctx"]; 80 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) algorithm] withKey:@"algorithm"]; 81 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: key withLength: keyLength] withKey:@"key"]; 82 | [traceStorage saveTracedCall: tracer]; 83 | [tracer release]; 84 | } 85 | return; 86 | } 87 | 88 | 89 | // Hook CCHmacUpdate() 90 | static void (*original_CCHmacUpdate)(CCHmacContext *ctx, const void *data, size_t dataLength); 91 | 92 | static void replaced_CCHmacUpdate(CCHmacContext *ctx, const void *data, size_t dataLength){ 93 | 94 | original_CCHmacUpdate(ctx, data, dataLength); 95 | if ([CallStackInspector wasDirectlyCalledByApp]) { 96 | 97 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCHmacUpdate"]; 98 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) ctx] withKey:@"ctx"]; 99 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: data withLength: dataLength] withKey:@"data"]; 100 | [traceStorage saveTracedCall: tracer]; 101 | [tracer release]; 102 | } 103 | return; 104 | } 105 | 106 | 107 | // Hook CCHmacFinal() 108 | // TODO: Figure out how to get the size of macout 109 | static void (*original_CCHmacFinal)(CCHmacContext *ctx, void *macOut); 110 | 111 | static void replaced_CCHmacFinal(CCHmacContext *ctx, void *macOut) { 112 | 113 | original_CCHmacFinal(ctx, macOut); 114 | 115 | if ([CallStackInspector wasDirectlyCalledByApp]) { 116 | //TODO Test this 117 | CCHmacContextRef ctxRef; 118 | ctxRef = (void *) ctx; 119 | size_t macOutLen = CCHmacOutputSizeFromRef(ctxRef); 120 | 121 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCHmacFinal"]; 122 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) ctx] withKey:@"ctx"]; 123 | // TODO: cast the CCHmacContext to a _NewHmacContext(defined above) and 124 | // grab the output_size. based on apples implementation: 125 | // http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-60026/Source/API/CommonHMAC.c 126 | // note this changed in iOS 6 so wont work in previous versions. we cld 127 | // preprocessor for version and cast to _CCHmacContext if we wanted... 128 | //_NewHmacContext *hmacCtx = (_NewHmacContext *)ctx; 129 | //_CCHmacContext *hmacCtx = (_CCHmacContext *)ctx; 130 | //uint32_t ui_len = hmacCtx->digestLen; 131 | //NSLog(@"HMAC has digest length: %i", ui_len); 132 | //[tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer:macOut withLength:ui_len] withKey:@"macOut"]; 133 | 134 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: macOut withLength: macOutLen] withKey:@"macOut"]; 135 | //[tracer addArgFromPlistObject:@"Introspy - not implemented" withKey:@"macOut"]; 136 | [traceStorage saveTracedCall: tracer]; 137 | [tracer release]; 138 | } 139 | return; 140 | } 141 | 142 | 143 | // Hook CCHmac() 144 | static void (*original_CCHmac)(CCHmacAlgorithm algorithm, const void *key, size_t keyLength, const void *data, size_t dataLength, void *macOut); 145 | 146 | static void replaced_CCHmac(CCHmacAlgorithm algorithm, const void *key, size_t keyLength, const void *data, size_t dataLength, void *macOut) { 147 | 148 | original_CCHmac(algorithm, key, keyLength, data, dataLength, macOut); 149 | if ([CallStackInspector wasDirectlyCalledByApp]) { 150 | 151 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCHmac"]; 152 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) algorithm] withKey:@"algorithm"]; 153 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: key withLength: keyLength] withKey:@"key"]; 154 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: data withLength: dataLength] withKey:@"data"]; 155 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: macOut withLength: getHmacLength(algorithm)] withKey:@"macOut"]; 156 | [traceStorage saveTracedCall: tracer]; 157 | [tracer release]; 158 | } 159 | return; 160 | } 161 | 162 | @implementation CommonHMACHooks 163 | 164 | + (void)enableHooks { 165 | MSHookFunction(CCHmacInit, replaced_CCHmacInit, (void **) &original_CCHmacInit); 166 | MSHookFunction(CCHmacUpdate, replaced_CCHmacUpdate, (void **) &original_CCHmacUpdate); 167 | MSHookFunction(CCHmacFinal, replaced_CCHmacFinal, (void **) &original_CCHmacFinal); 168 | MSHookFunction(CCHmac, replaced_CCHmac, (void **) &original_CCHmac); 169 | } 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /src/hooks/CommonKeyDerivationHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface CommonKeyDerivationHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/CommonKeyDerivationHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #import "CommonKeyDerivationHooks.h" 6 | #import "../SQLiteStorage.h" 7 | #import "../PlistObjectConverter.h" 8 | #import "../CallStackInspector.h" 9 | 10 | // Nice global 11 | extern SQLiteStorage *traceStorage; 12 | 13 | 14 | 15 | static int (*original_CCKeyDerivationPBKDF)( 16 | CCPBKDFAlgorithm algorithm, 17 | const char *password, 18 | size_t passwordLen, 19 | const uint8_t *salt, 20 | size_t saltLen, 21 | CCPseudoRandomAlgorithm prf, 22 | uint rounds, 23 | uint8_t *derivedKey, 24 | size_t derivedKeyLen); 25 | 26 | 27 | 28 | static int replaced_CCKeyDerivationPBKDF( 29 | CCPBKDFAlgorithm algorithm, 30 | const char *password, 31 | size_t passwordLen, 32 | const uint8_t *salt, 33 | size_t saltLen, 34 | CCPseudoRandomAlgorithm prf, 35 | uint rounds, 36 | uint8_t *derivedKey, 37 | size_t derivedKeyLen) { 38 | 39 | int origResult = original_CCKeyDerivationPBKDF(algorithm, password, passwordLen, salt, saltLen, prf, rounds, derivedKey, derivedKeyLen); 40 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 41 | if ([CallStackInspector wasDirectlyCalledByApp]) { 42 | 43 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"CCKeyDerivationPBKDF"]; 44 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) algorithm] withKey:@"algorithm"]; 45 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: password withLength: passwordLen] withKey:@"password"]; 46 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: salt withLength: saltLen] withKey:@"salt"]; 47 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) prf] withKey:@"prf"]; 48 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) rounds] withKey:@"rounds"]; 49 | [tracer addArgFromPlistObject:[PlistObjectConverter convertCBuffer: derivedKey withLength: derivedKeyLen] withKey:@"derivedKey"]; 50 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 51 | [traceStorage saveTracedCall: tracer]; 52 | [tracer release]; 53 | } 54 | return origResult;} 55 | 56 | 57 | @implementation CommonKeyDerivationHooks 58 | 59 | + (void)enableHooks { 60 | MSHookFunction((void *) CCKeyDerivationPBKDF, (void *) replaced_CCKeyDerivationPBKDF, (void **) &original_CCKeyDerivationPBKDF); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /src/hooks/DelegateProxies.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface GenericDelegateProx : NSObject { 4 | id originalDelegate; // The delegate object we're going to proxy 5 | } 6 | 7 | @property (retain) id originalDelegate; // Need retain or the delegate gets freed before we're done using it. 8 | 9 | 10 | - (id) initWithOriginalDelegate:(id)origDeleg; 11 | 12 | // Mirror the original delegate's list of implemented methods 13 | - (BOOL)respondsToSelector:(SEL)aSelector; 14 | - (id)forwardingTargetForSelector:(SEL)sel; 15 | - (void)dealloc; 16 | 17 | @end 18 | 19 | 20 | @interface UIApplicationDelegateProx : GenericDelegateProx 21 | 22 | // What we actually hook within UIApplicationDelegate 23 | - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url; 24 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; 25 | 26 | @end 27 | 28 | 29 | @interface NSURLConnectionDelegateProx : GenericDelegateProx 30 | 31 | // What we actually hook within NSURLConnectionDelegate 32 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse; 33 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse; 34 | 35 | @end 36 | 37 | -------------------------------------------------------------------------------- /src/hooks/DelegateProxies.m: -------------------------------------------------------------------------------- 1 | 2 | #import "DelegateProxies.h" 3 | #import "../SQLiteStorage.h" 4 | #import "../PlistObjectConverter.h" 5 | 6 | 7 | // Nice global 8 | extern SQLiteStorage *traceStorage; 9 | 10 | 11 | @implementation GenericDelegateProx 12 | 13 | 14 | @synthesize originalDelegate; 15 | 16 | 17 | - (id) initWithOriginalDelegate:(id)origDeleg { 18 | self = [super init]; 19 | 20 | if (self) { // Store original delegate 21 | [self setOriginalDelegate:(origDeleg)]; 22 | } 23 | return self; 24 | } 25 | 26 | 27 | - (BOOL)respondsToSelector:(SEL)aSelector { 28 | return [originalDelegate respondsToSelector:aSelector]; 29 | } 30 | 31 | 32 | - (id)forwardingTargetForSelector:(SEL)sel { 33 | return originalDelegate; 34 | } 35 | 36 | 37 | - (void)dealloc { 38 | [originalDelegate release]; 39 | [super dealloc]; 40 | } 41 | @end 42 | 43 | 44 | @implementation UIApplicationDelegateProx 45 | 46 | - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { 47 | BOOL origResult = [originalDelegate application:application handleOpenURL:url]; 48 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIApplicationDelegate" andMethod:@"application:handleOpenURL:"]; 49 | [tracer addArgFromPlistObject:@"Introspy - not implemented" withKey:@"application"]; 50 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 51 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithBool:origResult]]; 52 | [traceStorage saveTracedCall:tracer]; 53 | [tracer release]; 54 | return origResult; 55 | } 56 | 57 | 58 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { 59 | BOOL origResult = [originalDelegate application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; 60 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"UIApplicationDelegate" andMethod:@"application:openURL:sourceApplication:annotation:"]; 61 | [tracer addArgFromPlistObject:@"Introspy - not implemented" withKey:@"application"]; 62 | [tracer addArgFromPlistObject:[PlistObjectConverter convertURL: url] withKey:@"url"]; 63 | [tracer addArgFromPlistObject:sourceApplication withKey:@"sourceApplication"]; 64 | [tracer addArgFromPlistObject:annotation withKey:@"annotation"]; 65 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithBool:origResult]]; 66 | [traceStorage saveTracedCall:tracer]; 67 | [tracer release]; 68 | return origResult; 69 | } 70 | 71 | @end 72 | 73 | 74 | @implementation NSURLConnectionDelegateProx 75 | 76 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { 77 | id origResult = [originalDelegate connection:connection willCacheResponse:cachedResponse]; 78 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnectionDelegate" andMethod:@"connection:willCacheResponse:"]; 79 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) connection] withKey:@"connection"]; 80 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSCachedURLResponse: cachedResponse] withKey:@"cachedResponse"]; 81 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertNSCachedURLResponse:origResult]]; 82 | [traceStorage saveTracedCall:tracer]; 83 | [tracer release]; 84 | return origResult; 85 | } 86 | 87 | 88 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { 89 | id origResult = [originalDelegate connection:connection willSendRequest:request redirectResponse:redirectResponse]; 90 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"NSURLConnectionDelegate" andMethod:@"connection:willSendRequest:redirectResponse:"]; 91 | [tracer addArgFromPlistObject:[NSNumber numberWithUnsignedInt: (unsigned int) connection] withKey:@"connection"]; 92 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLRequest:request] withKey:@"request"]; 93 | [tracer addArgFromPlistObject:[PlistObjectConverter convertNSURLResponse:redirectResponse] withKey:@"redirectResponse"]; 94 | [tracer addReturnValueFromPlistObject: [PlistObjectConverter convertNSURLRequest:origResult]]; 95 | [traceStorage saveTracedCall:tracer]; 96 | [tracer release]; 97 | return origResult; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /src/hooks/KeychainHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface KeychainHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/KeychainHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #import "KeychainHooks.h" 6 | #import "../SQLiteStorage.h" 7 | #import "../PlistObjectConverter.h" 8 | #import "../CallStackInspector.h" 9 | 10 | 11 | // Nice global 12 | extern SQLiteStorage *traceStorage; 13 | extern NSString *objectTypeNotSupported; 14 | 15 | // Hook SecItemAdd() 16 | static OSStatus (*original_SecItemAdd)(CFDictionaryRef attributes, CFTypeRef *result); 17 | 18 | static OSStatus replaced_SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result){ 19 | 20 | OSStatus origResult = original_SecItemAdd(attributes, result); 21 | 22 | // Need the call stack inspector or we get into a weird infinite loop of SecItemAdd() calls 23 | // because SecIdentityCopyCertificate() seems to call SecItemAdd(), or something... 24 | if ([CallStackInspector wasDirectlyCalledByApp]) { 25 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"SecItemAdd"]; 26 | [tracer addArgFromPlistObject:[PlistObjectConverter convertSecItemAttributesDict:attributes] withKey:@"attributes"]; 27 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"result"]; 28 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInt:origResult]]; 29 | [traceStorage saveTracedCall: tracer]; 30 | [tracer release]; 31 | } 32 | 33 | return origResult; 34 | } 35 | 36 | 37 | // Hook SecItemCopyMatching() 38 | static OSStatus (*original_SecItemCopyMatching)(CFDictionaryRef query, CFTypeRef *result); 39 | 40 | static OSStatus replaced_SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result){ 41 | OSStatus origResult = original_SecItemCopyMatching(query, result); 42 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"SecItemCopyMatching"]; 43 | [tracer addArgFromPlistObject:(NSDictionary*)query withKey:@"query"]; 44 | [tracer addArgFromPlistObject:objectTypeNotSupported withKey:@"result"]; 45 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInt:origResult]]; 46 | [traceStorage saveTracedCall: tracer]; 47 | [tracer release]; 48 | return origResult; 49 | } 50 | 51 | 52 | // Hook SecItemDelete() 53 | static OSStatus (*original_SecItemDelete)(CFDictionaryRef query); 54 | 55 | static OSStatus replaced_SecItemDelete(CFDictionaryRef query){ 56 | OSStatus origResult = original_SecItemDelete(query); 57 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"SecItemDelete"]; 58 | [tracer addArgFromPlistObject:(NSDictionary*)query withKey:@"query"]; 59 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInt:origResult]]; 60 | [traceStorage saveTracedCall: tracer]; 61 | [tracer release]; 62 | return origResult; 63 | } 64 | 65 | 66 | // Hook SecItemUpdate() 67 | static OSStatus (*original_SecItemUpdate)(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); 68 | 69 | static OSStatus replaced_SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate){ 70 | OSStatus origResult = original_SecItemUpdate(query, attributesToUpdate); 71 | 72 | if ([CallStackInspector wasDirectlyCalledByApp]) { 73 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"SecItemUpdate"]; 74 | [tracer addArgFromPlistObject:(NSDictionary*)query withKey:@"query"]; 75 | [tracer addArgFromPlistObject:[PlistObjectConverter convertSecItemAttributesDict:attributesToUpdate] withKey:@"attributesToUpdate"]; 76 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInt:origResult]]; 77 | [traceStorage saveTracedCall: tracer]; 78 | [tracer release]; 79 | } 80 | return origResult; 81 | } 82 | 83 | 84 | @implementation KeychainHooks 85 | 86 | + (void)enableHooks { 87 | MSHookFunction(SecItemAdd, replaced_SecItemAdd, (void **) &original_SecItemAdd); 88 | MSHookFunction(SecItemCopyMatching, replaced_SecItemCopyMatching, (void **) &original_SecItemCopyMatching); 89 | MSHookFunction(SecItemDelete, replaced_SecItemDelete, (void **) &original_SecItemDelete); 90 | MSHookFunction(SecItemUpdate, replaced_SecItemUpdate, (void **) &original_SecItemUpdate); 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /src/hooks/LibCHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface LibCHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/LibCHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #import "LibCHooks.h" 5 | #import "../SQLiteStorage.h" 6 | #import "../PlistObjectConverter.h" 7 | #import "../CallStackInspector.h" 8 | 9 | // Nice global 10 | extern SQLiteStorage *traceStorage; 11 | 12 | 13 | // Hook rand() 14 | static int (*original_rand)(); 15 | 16 | static int replaced_rand() { 17 | 18 | int origResult = original_rand(); 19 | 20 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 21 | if ([CallStackInspector wasDirectlyCalledByApp]) { 22 | 23 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"rand"]; 24 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedInt:origResult]]; 25 | [traceStorage saveTracedCall: tracer]; 26 | [tracer release]; 27 | } 28 | return origResult; 29 | } 30 | 31 | 32 | // Hook random() 33 | static long (*original_random)(); 34 | 35 | static long replaced_random() { 36 | 37 | long origResult = original_random(); 38 | 39 | // Only log what the application directly calls. For example we don't want to log internal SSL crypto calls 40 | if ([CallStackInspector wasDirectlyCalledByApp]) { 41 | 42 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"random"]; 43 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithUnsignedLong:origResult]]; 44 | [traceStorage saveTracedCall: tracer]; 45 | [tracer release]; 46 | } 47 | return origResult; 48 | } 49 | 50 | 51 | 52 | @implementation LibCHooks 53 | 54 | + (void)enableHooks { 55 | MSHookFunction(random, replaced_random, (void **) &original_random); 56 | MSHookFunction(rand, replaced_rand, (void **) &original_rand); 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /src/hooks/SecurityHooks.h: -------------------------------------------------------------------------------- 1 | 2 | @interface SecurityHooks : NSObject { 3 | 4 | } 5 | 6 | + (void)enableHooks; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/SecurityHooks.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #import "SecurityHooks.h" 6 | #import "../SQLiteStorage.h" 7 | #import "../PlistObjectConverter.h" 8 | 9 | 10 | // Nice global 11 | extern SQLiteStorage *traceStorage; 12 | 13 | 14 | // Public Crypto Hook 15 | // Hook SecPKCS12Import() - If the app uses a client cert 16 | static OSStatus (*original_SecPKCS12Import)(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items); 17 | 18 | static OSStatus replaced_SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items) { 19 | OSStatus origResult = original_SecPKCS12Import(pkcs12_data, options, items); 20 | CallTracer *tracer = [[CallTracer alloc] initWithClass:@"C" andMethod:@"SecPKCS12Import"]; 21 | [tracer addArgFromPlistObject:(NSData*)pkcs12_data withKey:@"pkcs12_data"]; 22 | [tracer addArgFromPlistObject:(NSDictionary*)options withKey:@"options"]; 23 | [tracer addReturnValueFromPlistObject: [NSNumber numberWithInt:origResult]]; 24 | [traceStorage saveTracedCall: tracer]; 25 | [tracer release]; 26 | return original_SecPKCS12Import(pkcs12_data, options, items); 27 | } 28 | 29 | @implementation SecurityHooks 30 | 31 | + (void)enableHooks { 32 | MSHookFunction((void *) SecPKCS12Import,(void *) replaced_SecPKCS12Import, (void **) &original_SecPKCS12Import); 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /src/introspy.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.UIKit" ); }; } 2 | -------------------------------------------------------------------------------- /src/layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.isecpartners.introspy 2 | Name: introspy 3 | Depends: mobilesubstrate, preferenceloader, applist 4 | Version: 0.5.1 5 | Architecture: iphoneos-arm 6 | Description: Open-source security profiler designed to help penetration testers understand what an application does at runtime. 7 | Maintainer: Tom Daniels & Alban Diquet & Herman Duarte 8 | Author: Tom Daniels & Alban Diquet 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /src/layout/Library/PreferenceLoader/Preferences/Introspy.plist: -------------------------------------------------------------------------------- 1 | entry = { 2 | bundle = AppList; 3 | cell = PSLinkCell; 4 | icon = "/Library/PreferenceLoader/Preferences/introspy.png"; 5 | isController = 1; 6 | label = "Introspy - Apps"; 7 | ALSettingsPath = "/var/mobile/Library/Preferences/com.isecpartners.introspy.plist"; 8 | ALSettingsKeyPrefix = ""; 9 | ALChangeNotification = "com.isecpartners.introspy"; 10 | ALAllowsSelection = 1; 11 | ALSectionDescriptors = ( 12 | { 13 | title = "System Applications Profiling"; 14 | predicate = "isSystemApplication = TRUE"; 15 | "cell-class-name" = "ALSwitchCell"; 16 | "icon-size" = 29; 17 | "suppress-hidden-apps" = 1; 18 | }, 19 | { 20 | title = "User Applications Profiling"; 21 | predicate = "isSystemApplication = FALSE"; 22 | "cell-class-name" = "ALSwitchCell"; 23 | "icon-size" = 29; 24 | "suppress-hidden-apps" = 1; 25 | } 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/layout/Library/PreferenceLoader/Preferences/Introspy2.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | entry 9 | 10 | cell 11 | PSLinkCell 12 | icon 13 | introspy.png 14 | label 15 | Introspy - Settings 16 | 17 | items 18 | 19 | 20 | cell 21 | PSGroupCell 22 | label 23 | General Settings 24 | 25 | 26 | cell 27 | PSSwitchCell 28 | default 29 | 30 | defaults 31 | com.isecpartners.introspy 32 | key 33 | LogToTheConsole 34 | label 35 | Log To The Console 36 | 37 | 38 | cell 39 | PSGroupCell 40 | label 41 | Data Storage Profiling 42 | 43 | 44 | cell 45 | PSSwitchCell 46 | default 47 | 48 | defaults 49 | com.isecpartners.introspy 50 | key 51 | FileSystemHooks 52 | label 53 | File System 54 | 55 | 56 | cell 57 | PSSwitchCell 58 | default 59 | 60 | defaults 61 | com.isecpartners.introspy 62 | key 63 | UserPreferencesHooks 64 | label 65 | User Preferences 66 | 67 | 68 | cell 69 | PSSwitchCell 70 | default 71 | 72 | defaults 73 | com.isecpartners.introspy 74 | key 75 | KeyChainHooks 76 | label 77 | KeyChain 78 | 79 | 80 | cell 81 | PSGroupCell 82 | label 83 | Crypto Profiling 84 | 85 | 86 | cell 87 | PSSwitchCell 88 | default 89 | 90 | defaults 91 | com.isecpartners.introspy 92 | key 93 | CommonCryptoHooks 94 | label 95 | Common Crypto 96 | 97 | 98 | cell 99 | PSSwitchCell 100 | default 101 | 102 | defaults 103 | com.isecpartners.introspy 104 | key 105 | SecurityHooks 106 | label 107 | Security Framework 108 | 109 | 110 | cell 111 | PSGroupCell 112 | label 113 | Network Profiling 114 | 115 | 116 | cell 117 | PSSwitchCell 118 | default 119 | 120 | defaults 121 | com.isecpartners.introspy 122 | key 123 | HTTPHooks 124 | label 125 | HTTP 126 | 127 | 128 | cell 129 | PSGroupCell 130 | label 131 | IPC Profiling 132 | 133 | 134 | cell 135 | PSSwitchCell 136 | default 137 | 138 | defaults 139 | com.isecpartners.introspy 140 | key 141 | PasteboardHooks 142 | label 143 | Pasteboard 144 | 145 | 146 | cell 147 | PSSwitchCell 148 | default 149 | 150 | defaults 151 | com.isecpartners.introspy 152 | key 153 | URLSchemesHooks 154 | label 155 | URL Schemes 156 | 157 | 158 | cell 159 | PSGroupCell 160 | label 161 | Misc APIs Profiling 162 | 163 | 164 | cell 165 | PSSwitchCell 166 | default 167 | 168 | defaults 169 | com.isecpartners.introspy 170 | key 171 | XMLHooks 172 | label 173 | XML 174 | 175 | 176 | title 177 | Introspy 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/layout/Library/PreferenceLoader/Preferences/introspy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/Introspy-iOS/64291d947cc336c7fc21a04a4ebb4e5ae752cfdb/src/layout/Library/PreferenceLoader/Preferences/introspy.png -------------------------------------------------------------------------------- /testapp/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .theos 3 | theos 4 | *.deb 5 | obj/* 6 | _/* 7 | *.swp 8 | .gitconfig 9 | *.sublime-workspace 10 | packages/* 11 | -------------------------------------------------------------------------------- /testapp/CryptoTester.h: -------------------------------------------------------------------------------- 1 | @interface CryptoTester : NSObject { 2 | 3 | } 4 | 5 | + (void)runAllTests; 6 | 7 | + (void) testCommonHmac; 8 | + (void) testRand; 9 | + (void) testCommonDigest; 10 | + (void) testCommonCryptor; 11 | + (void) testCommonKeyDerivation; 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /testapp/CryptoTester.m: -------------------------------------------------------------------------------- 1 | #import "CryptoTester.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | @implementation CryptoTester 9 | 10 | 11 | static char testKey[16] = "Key 123"; 12 | static char testIv[16] = "IVIVIVIV"; 13 | static char testData[16] = "s3cret 123"; 14 | 15 | 16 | + (void)runAllTests { 17 | 18 | [self testCommonHmac]; 19 | [self testRand]; 20 | [self testCommonDigest]; 21 | [self testCommonCryptor]; 22 | [self testCommonKeyDerivation]; 23 | } 24 | 25 | 26 | + (void) testCommonHmac { 27 | char dataOut[16]; 28 | CCHmacContext ctx; 29 | 30 | CCHmacInit(&ctx, kCCHmacAlgSHA1, testKey, 16); 31 | CCHmacUpdate(&ctx, testData, 16); 32 | CCHmacFinal(&ctx, dataOut); 33 | 34 | CCHmac(kCCHmacAlgSHA1, testKey, 16, testData, 16, dataOut); 35 | } 36 | 37 | 38 | + (void) testRand { 39 | rand(); 40 | random(); 41 | } 42 | 43 | 44 | + (void) testCommonDigest { 45 | CC_MD5_CTX ctx; 46 | unsigned char dataOut[CC_MD5_DIGEST_LENGTH]; 47 | 48 | CC_MD5_Init(&ctx); 49 | CC_MD5_Update(&ctx, testData, 16); 50 | CC_MD5_Final(dataOut, &ctx); 51 | 52 | CC_MD5(testData, 16, dataOut); 53 | } 54 | 55 | 56 | + (void) testCommonCryptor { 57 | CCCryptorRef cryptorRef; 58 | char dataOut[16]; 59 | size_t dataOutMoved; 60 | 61 | CCCryptorCreateFromData(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 62 | testKey, 16, testIv, testData, 16, &cryptorRef, &dataOutMoved); 63 | 64 | CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 65 | testKey, 16, testIv, &cryptorRef); 66 | CCCryptorUpdate(cryptorRef, testData, 16, dataOut, 0, &dataOutMoved); 67 | CCCryptorFinal(cryptorRef, dataOut, 16, &dataOutMoved); 68 | 69 | 70 | CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, testKey, 16, 71 | testIv, testData, 16, dataOut, 16, &dataOutMoved); 72 | } 73 | 74 | 75 | + (void) testCommonKeyDerivation { 76 | CCPBKDFAlgorithm algorithm = kCCPBKDF2; 77 | const char password[9] = "s3cretPW"; 78 | size_t passwordLen = 9; 79 | const uint8_t salt[4] = "abc"; 80 | size_t saltLen = 4; 81 | CCPseudoRandomAlgorithm prf = kCCPRFHmacAlgSHA384; 82 | uint rounds = 123; 83 | uint8_t derivedKey[33]; 84 | size_t derivedKeyLen = 33; 85 | 86 | CCKeyDerivationPBKDF(algorithm, password, passwordLen, salt, saltLen, prf, rounds, derivedKey, derivedKeyLen); 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /testapp/FileSystemTester.h: -------------------------------------------------------------------------------- 1 | @interface FileSystemTester : NSObject { 2 | 3 | } 4 | 5 | - (FileSystemTester *)init; 6 | - (void)runAllTests; 7 | 8 | - (void)testNSFileHandle; 9 | - (void)testNSFileManager; 10 | - (void)testNSData; 11 | - (void)testNSInputStream; 12 | - (void)testNSOutputStream; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /testapp/FileSystemTester.m: -------------------------------------------------------------------------------- 1 | #import "FileSystemTester.h" 2 | #include 3 | 4 | @implementation FileSystemTester : NSObject 5 | 6 | 7 | // Testing settings 8 | static NSString *testFilePath = @"~/introspytest.file"; 9 | static NSString *testContentStr = @"introspy testing 12345"; 10 | 11 | 12 | // Internal stuff 13 | static NSURL *testURL; 14 | static NSData *testContent; 15 | 16 | 17 | - (FileSystemTester *)init { 18 | self = [super init]; 19 | testFilePath = [testFilePath stringByExpandingTildeInPath]; 20 | testURL = [NSURL fileURLWithPath:testFilePath]; 21 | testContent = [testContentStr dataUsingEncoding: [NSString defaultCStringEncoding]]; 22 | return self; 23 | } 24 | 25 | 26 | - (void)runAllTests { 27 | 28 | [self testNSFileManager]; 29 | [self testNSData]; 30 | [self testNSFileHandle]; 31 | [self testNSInputStream]; 32 | [self testNSOutputStream]; 33 | } 34 | 35 | 36 | - (void)testNSFileHandle { 37 | 38 | [NSFileHandle fileHandleForReadingAtPath:testFilePath]; 39 | [NSFileHandle fileHandleForUpdatingAtPath:testFilePath]; 40 | [NSFileHandle fileHandleForWritingAtPath:testFilePath]; 41 | 42 | [NSFileHandle fileHandleForReadingFromURL:testURL error:nil]; 43 | [NSFileHandle fileHandleForUpdatingURL:testURL error:nil]; 44 | [NSFileHandle fileHandleForWritingToURL:testURL error:nil]; 45 | } 46 | 47 | 48 | - (void)testNSOutputStream { 49 | NSOutputStream *testStream = [[NSOutputStream alloc] initToFileAtPath:testFilePath append:NO]; 50 | [testStream release]; 51 | 52 | NSOutputStream *testStream2 = [[NSOutputStream alloc] initWithURL:testURL append:NO]; 53 | [testStream2 release]; 54 | 55 | [NSOutputStream outputStreamToFileAtPath:testFilePath append:NO]; 56 | [NSOutputStream outputStreamWithURL:testURL append:NO]; 57 | } 58 | 59 | - (void)testNSInputStream { 60 | NSInputStream *testStream = [[NSInputStream alloc] initWithFileAtPath:testFilePath]; 61 | [testStream release]; 62 | 63 | NSInputStream *testStream2 = [[NSInputStream alloc] initWithURL:testURL]; 64 | [testStream2 release]; 65 | 66 | [NSInputStream inputStreamWithFileAtPath:testFilePath]; 67 | [NSInputStream inputStreamWithURL:testURL]; 68 | } 69 | 70 | 71 | - (void)testNSFileManager { 72 | 73 | NSFileManager * fileManager = [NSFileManager defaultManager]; 74 | 75 | // Test createFileAtPath:contents:attributes: with NSFileProtectionCompleteUntilFirstUserAuthentication 76 | NSDictionary *testAttr = [NSDictionary 77 | dictionaryWithObjects: 78 | [NSArray arrayWithObjects: NSFileProtectionCompleteUntilFirstUserAuthentication, nil] 79 | forKeys: 80 | [NSArray arrayWithObjects: NSFileProtectionKey, nil]]; 81 | 82 | [fileManager createFileAtPath:testFilePath contents:testContent attributes:testAttr]; 83 | 84 | // Test createFileAtPath:contents:attributes: with no protection attribute 85 | [fileManager createFileAtPath:testFilePath contents:testContent attributes:nil]; 86 | 87 | // Test ubiquityIdentityToken - iOS 6 only 88 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 89 | [fileManager ubiquityIdentityToken]; 90 | #endif 91 | 92 | // Test contentsAtPath: 93 | NSData* readContent = [fileManager contentsAtPath:testFilePath]; 94 | [readContent length]; 95 | 96 | // Cleanup 97 | [fileManager removeItemAtPath:testFilePath error:nil]; 98 | } 99 | 100 | 101 | - (void)testNSData { 102 | 103 | // Test writeToFile:atomically: 104 | [testContent writeToFile:testFilePath atomically:YES]; 105 | 106 | // Test writeToFile:options:error: 107 | [testContent writeToFile:testFilePath options:NSDataWritingFileProtectionNone error:nil]; 108 | 109 | // Test writeToURL:atomically: 110 | [testContent writeToURL:testURL atomically:YES]; 111 | 112 | // Test writeToURL:options:error: 113 | [testContent writeToURL:testURL options:NSDataWritingFileProtectionNone error:nil]; 114 | 115 | // Test writeToURL:options:error: with no options 116 | [testContent writeToURL:testURL options:0 error:nil]; 117 | 118 | 119 | // Test dataWithContentsOfFile: 120 | NSData* readContent = [NSData dataWithContentsOfFile:testFilePath]; 121 | 122 | // Test dataWithContentsOfFile:options:error: 123 | readContent = [NSData dataWithContentsOfFile: testFilePath options:NSDataReadingUncached error:nil]; 124 | 125 | // Test dataWithContentsOfURL: 126 | readContent = [NSData dataWithContentsOfURL: testURL]; 127 | 128 | // Test dataWithContentsOfURL:options:error: 129 | readContent = [NSData dataWithContentsOfURL:testURL options:NSDataReadingUncached error:nil]; 130 | 131 | // Test initWithContentsOfFile: 132 | readContent = [[NSData alloc] initWithContentsOfFile:testFilePath]; 133 | [readContent release]; 134 | 135 | // Test initWithContentsOfFile:options:error: 136 | readContent = [[NSData alloc] initWithContentsOfFile:testFilePath options:NSDataReadingUncached error:nil]; 137 | [readContent release]; 138 | 139 | // Test initWithContentsOfURL: 140 | readContent = [[NSData alloc] initWithContentsOfURL:testURL]; 141 | [readContent release]; 142 | 143 | // Test initWithContentsOfURL:options:error: 144 | readContent = [[NSData alloc] initWithContentsOfURL:testURL options:NSDataReadingUncached error:nil]; 145 | [readContent release]; 146 | 147 | // Test dataWithContentsOfMappedFile 148 | readContent = [NSData dataWithContentsOfMappedFile:testFilePath]; 149 | 150 | // Test initWithContentsOfMappedFile: 151 | readContent = [[NSData alloc] initWithContentsOfMappedFile:testFilePath]; 152 | [readContent release]; 153 | 154 | // Cleanup 155 | NSFileManager * fileManager = [NSFileManager defaultManager]; 156 | [fileManager removeItemAtPath:testFilePath error:nil]; 157 | } 158 | 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /testapp/HTTPTester.h: -------------------------------------------------------------------------------- 1 | @interface HTTPTester: NSObject { 2 | 3 | } 4 | 5 | + (void)runAllTests; 6 | + (void)testNSURLConnectionClassMethods; 7 | + (void)testNSURLConnectionInstanceMethods; 8 | + (void) testNSHTTPCookie; 9 | + (void)testNSURLCredential; 10 | 11 | @end 12 | 13 | 14 | @interface NSURLConnectionDelegateTester: NSObject { 15 | } 16 | 17 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection; 18 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; 19 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse; 20 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; 21 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse; 22 | @end 23 | 24 | @interface NSURLConnectionDelegateTester1: NSURLConnectionDelegateTester { 25 | } 26 | - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 27 | @end 28 | 29 | 30 | @interface NSURLConnectionDelegateTester2: NSURLConnectionDelegateTester { 31 | } 32 | - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; 33 | @end -------------------------------------------------------------------------------- /testapp/HTTPTester.m: -------------------------------------------------------------------------------- 1 | #import "HTTPTester.h" 2 | 3 | @implementation HTTPTester : NSObject 4 | 5 | + (void)runAllTests { 6 | [self testNSURLConnectionInstanceMethods]; 7 | [self testNSURLConnectionClassMethods]; 8 | [self testNSHTTPCookie]; 9 | [self testNSURLCredential]; 10 | } 11 | 12 | 13 | + (void)testNSURLCredential { 14 | 15 | [[NSURLCredential alloc] initWithUser:@"User" password:@"S3cr3t" persistence:NSURLCredentialPersistencePermanent]; 16 | [NSURLCredential credentialWithUser:@"User" password:@"S3cr3t" persistence:NSURLCredentialPersistencePermanent]; 17 | 18 | } 19 | 20 | 21 | + (void)testNSURLConnectionClassMethods { 22 | [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL: 23 | [NSURL URLWithString:@"https://www.google.com/?method=connectionWithRequest"]] 24 | delegate:nil]; 25 | 26 | NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 27 | [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL: 28 | [NSURL URLWithString:@"https://www.google.com/?method=sendAsynchronousRequest"]] 29 | queue:queue 30 | completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { }]; 31 | 32 | NSError *error = nil; 33 | NSURLResponse *response = nil; 34 | [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL: 35 | [NSURL URLWithString:@"https://www.google.com/?method=sendSynchronousRequest"]] 36 | returningResponse:&response 37 | error:&error]; 38 | } 39 | 40 | 41 | + (void)testNSURLConnectionInstanceMethods { 42 | NSURLConnectionDelegateTester1* deleg1 = [[NSURLConnectionDelegateTester1 alloc] init]; 43 | // Connect over HTTP to trigger a redirection 44 | NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL: 45 | [NSURL URLWithString:@"http://www.isecpartners.com/?method=initWithRequest:delegate:"]] 46 | delegate:deleg1]; 47 | [deleg1 release]; // Give ownership to the connection 48 | [conn start]; 49 | 50 | NSURLConnectionDelegateTester2* deleg2 = [[NSURLConnectionDelegateTester2 alloc] init]; 51 | NSURLConnection *conn2 = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL: 52 | [NSURL URLWithString:@"https://www.isecpartners.com/?method=initWithRequest:delegate:startImmediately:"]] 53 | delegate:deleg2 54 | startImmediately:NO]; 55 | [conn2 start]; 56 | [deleg2 release]; // Give ownership to the connection 57 | } 58 | 59 | 60 | + (void)testNSHTTPCookie { 61 | 62 | NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys: 63 | @"www.isecpartners.com", NSHTTPCookieDomain, 64 | @"\test", NSHTTPCookiePath, 65 | @"testCookies", NSHTTPCookieName, 66 | @"1", NSHTTPCookieValue, 67 | nil]; 68 | [NSHTTPCookie cookieWithProperties:properties]; 69 | NSHTTPCookie *cookie = [[NSHTTPCookie alloc] initWithProperties:properties]; 70 | [cookie release]; 71 | } 72 | 73 | @end 74 | 75 | 76 | 77 | // Helpers classes for NSURLConnection and NSURLConnectionDelegate testing 78 | @implementation NSURLConnectionDelegateTester 79 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 80 | [connection release]; 81 | } 82 | 83 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 84 | [connection release]; 85 | } 86 | 87 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { 88 | return cachedResponse; 89 | } 90 | 91 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 92 | } 93 | 94 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { 95 | return request; 96 | } 97 | @end 98 | 99 | 100 | @implementation NSURLConnectionDelegateTester1 101 | - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 102 | // Disable cert validation #1 103 | if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 104 | { 105 | [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 106 | } 107 | } 108 | @end 109 | 110 | 111 | @implementation NSURLConnectionDelegateTester2 112 | - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 113 | // Disable cert validation #2 114 | if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 115 | { 116 | // Accept the certificate 117 | [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] 118 | forAuthenticationChallenge:challenge]; 119 | } 120 | } 121 | @end 122 | -------------------------------------------------------------------------------- /testapp/IntrospyTestAppApplication.mm: -------------------------------------------------------------------------------- 1 | #import "RootViewController.h" 2 | 3 | @interface IntrospyTestAppApplication: UIApplication { 4 | UIWindow *_window; 5 | RootViewController *_viewController; 6 | } 7 | @property (nonatomic, retain) UIWindow *window; 8 | @end 9 | 10 | @implementation IntrospyTestAppApplication 11 | @synthesize window = _window; 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 14 | _viewController = [[RootViewController alloc] init]; 15 | [_window addSubview:_viewController.view]; 16 | [_window makeKeyAndVisible]; 17 | return YES; 18 | } 19 | 20 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { 21 | return YES; 22 | } 23 | 24 | //- (void)applicationDidFinishLaunching:(UIApplication *)application { 25 | // _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 26 | // _viewController = [[RootViewController alloc] init]; 27 | // [_window addSubview:_viewController.view]; 28 | // [_window makeKeyAndVisible]; 29 | //} 30 | 31 | - (void)dealloc { 32 | [_viewController release]; 33 | [_window release]; 34 | [super dealloc]; 35 | } 36 | @end 37 | 38 | // vim:ft=objc 39 | -------------------------------------------------------------------------------- /testapp/KeyChainTester.h: -------------------------------------------------------------------------------- 1 | @interface KeyChainTester : NSObject { 2 | 3 | } 4 | // TODO: Rename this class to SecurityTester as it tests the whole Security framework 5 | + (void)runAllTests; 6 | + (NSMutableDictionary *)newKeyChainSearchDict; 7 | + (void)testKeyChain; 8 | + (void)testSecPKCS12Import; 9 | 10 | 11 | @end 12 | 13 | -------------------------------------------------------------------------------- /testapp/KeyChainTester.m: -------------------------------------------------------------------------------- 1 | #import "KeyChainTester.h" 2 | #include 3 | 4 | @implementation KeyChainTester : NSObject 5 | 6 | 7 | // Testing settings 8 | static NSString *keyChainTestKey = @"IntrospyPassword"; 9 | static NSString *keyChainTestValue1 = @"s3cr3t"; 10 | static NSString *keyChainTestValue2 = @"p@ssw0rd"; 11 | 12 | 13 | 14 | + (void)runAllTests { 15 | 16 | [self testKeyChain]; 17 | [self testSecPKCS12Import]; 18 | } 19 | 20 | 21 | // Utility function for the keyChain tests 22 | + (NSMutableDictionary *)newKeyChainSearchDict { 23 | 24 | NSString *appId = [[NSBundle mainBundle] bundleIdentifier]; 25 | NSData *testKey = [keyChainTestKey dataUsingEncoding:NSUTF8StringEncoding]; 26 | NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init]; 27 | 28 | [searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 29 | [searchDictionary setObject:testKey forKey:(id)kSecAttrGeneric]; 30 | [searchDictionary setObject:testKey forKey:(id)kSecAttrAccount]; 31 | [searchDictionary setObject:appId forKey:(id)kSecAttrService]; 32 | 33 | return searchDictionary; 34 | } 35 | 36 | 37 | + (void)testKeyChain { 38 | 39 | NSData *testValue1 = [keyChainTestValue1 dataUsingEncoding:NSUTF8StringEncoding]; 40 | NSData *testValue2 = [keyChainTestValue2 dataUsingEncoding:NSUTF8StringEncoding]; 41 | 42 | // Test SecItemAdd() 43 | NSMutableDictionary *itemAddDict = [self newKeyChainSearchDict]; 44 | [itemAddDict setObject:testValue1 forKey:(id)kSecValueData]; 45 | [itemAddDict setObject:(id)kSecAttrAccessibleWhenUnlocked forKey:(id)kSecAttrAccessible]; 46 | SecItemAdd((CFDictionaryRef)itemAddDict, NULL); 47 | [itemAddDict release]; 48 | 49 | // Test SecItemAdd() with default kSecAttrAccessible 50 | NSMutableDictionary *itemAddDict2 = [self newKeyChainSearchDict]; 51 | [itemAddDict2 setObject:testValue1 forKey:(id)kSecValueData]; 52 | //[itemAddDict setObject:(id)kSecAttrAccessibleWhenUnlocked forKey:(id)kSecAttrAccessible]; 53 | SecItemAdd((CFDictionaryRef)itemAddDict2, NULL); 54 | [itemAddDict2 release]; 55 | 56 | // Test SecItemCopyMatching() 57 | NSMutableDictionary *itemMatchDict = [self newKeyChainSearchDict]; 58 | CFTypeRef *result=NULL; 59 | [itemMatchDict setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; 60 | [itemMatchDict setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; 61 | SecItemCopyMatching((CFDictionaryRef)itemMatchDict, result); 62 | [itemMatchDict release]; 63 | 64 | // Test SecItemUpdate() 65 | NSMutableDictionary *itemSearchDict = [self newKeyChainSearchDict]; 66 | NSMutableDictionary *itemUpdateDict = [[NSMutableDictionary alloc] init]; 67 | [itemUpdateDict setObject:testValue2 forKey:(id)kSecValueData]; 68 | SecItemUpdate((CFDictionaryRef) itemSearchDict, (CFDictionaryRef) itemUpdateDict); 69 | [itemUpdateDict release]; 70 | 71 | // Test SecItemDelete() 72 | SecItemDelete((CFDictionaryRef) itemSearchDict); 73 | [itemSearchDict release]; 74 | } 75 | 76 | 77 | + (void)testSecPKCS12Import { 78 | 79 | // Open the PKCS12 file 80 | NSString *clientCertPath = [[NSString alloc] initWithFormat:@"%@/introspy.p12", [[NSBundle mainBundle] bundlePath]]; 81 | NSData *clientCertData = [[NSData alloc] initWithContentsOfFile:clientCertPath]; 82 | 83 | if ([clientCertData length] == 0) { 84 | NSLog(@"CLIENT CERT NOT FOUND, PATH:%@", clientCertPath); 85 | } 86 | else { 87 | const void *keys[] = { kSecImportExportPassphrase }; 88 | const void *values[] = { (CFStringRef) @"test" }; 89 | CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); 90 | CFArrayRef items = NULL; 91 | 92 | // Load the client cert and private key 93 | if (SecPKCS12Import((CFDataRef) clientCertData, optionsDictionary, &items) == 0) { 94 | 95 | SecIdentityRef clientIdentity; 96 | CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0); 97 | clientIdentity = (SecIdentityRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity); 98 | 99 | // Print the cert's summary 100 | SecCertificateRef clientCertRef; 101 | SecIdentityCopyCertificate(clientIdentity, &clientCertRef); 102 | NSString *clientCertSummary = (NSString*) SecCertificateCopySubjectSummary(clientCertRef); 103 | NSLog(@"LOADED CERT: %@", clientCertSummary); 104 | CFRelease(clientCertRef); 105 | [clientCertSummary release]; 106 | 107 | // Store the cert and private key (twice) in the keychain with kSecAttrAccessibleAlways 108 | NSMutableDictionary *certAddDict = [self newKeyChainSearchDict]; 109 | [certAddDict setObject:(id)kSecClassIdentity forKey:(id)kSecClass]; 110 | [certAddDict setObject:(id)clientIdentity forKey:(id)kSecValueRef]; 111 | [certAddDict setObject:(id)kSecAttrAccessibleAlways forKey:(id)kSecAttrAccessible]; 112 | SecItemAdd((CFDictionaryRef)certAddDict, NULL); 113 | 114 | NSMutableDictionary *certAddDict2 = [self newKeyChainSearchDict]; 115 | [certAddDict2 setObject:(id)kSecClassIdentity forKey:(id)kSecClass]; 116 | [certAddDict2 setObject:(id)kSecAttrAccessibleAlways forKey:(id)kSecAttrAccessible]; 117 | [certAddDict2 setObject:(id)clientIdentity forKey:(id)kSecValuePersistentRef]; 118 | SecItemAdd((CFDictionaryRef)certAddDict, NULL); 119 | 120 | // Update the cert and private key - code will not work but we're only testing the hook 121 | SecItemUpdate((CFDictionaryRef) [self newKeyChainSearchDict], (CFDictionaryRef) certAddDict); 122 | 123 | 124 | [certAddDict release]; 125 | } 126 | 127 | if (optionsDictionary) 128 | CFRelease(optionsDictionary); 129 | 130 | if (items) 131 | CFRelease(items); 132 | } 133 | 134 | [clientCertData release]; 135 | [clientCertPath release]; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /testapp/Makefile: -------------------------------------------------------------------------------- 1 | #TARGET = iphone:6.1 2 | #ARCHS = armv7 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | APPLICATION_NAME = IntrospyTestApp 6 | IntrospyTestApp_FILES = main.m IntrospyTestAppApplication.mm RootViewController.mm XMLTester.m FileSystemTester.m KeyChainTester.m UserPreferencesTester.m CryptoTester.m PasteboardTester.m HTTPTester.m SchemeTester.m 7 | IntrospyTestApp_FRAMEWORKS = UIKit CoreGraphics Security 8 | 9 | include $(THEOS_MAKE_PATH)/application.mk 10 | -------------------------------------------------------------------------------- /testapp/PasteboardTester.h: -------------------------------------------------------------------------------- 1 | @interface PasteboardTester : NSObject { 2 | 3 | } 4 | 5 | + (void)runAllTests; 6 | + (void)testUIPasteboardClassMethods; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /testapp/PasteboardTester.m: -------------------------------------------------------------------------------- 1 | #import "PasteboardTester.h" 2 | 3 | @implementation PasteboardTester : NSObject 4 | 5 | + (void)runAllTests { 6 | [self testUIPasteboardClassMethods]; 7 | } 8 | 9 | + (void) testUIPasteboardClassMethods { 10 | UIPasteboard *pb = [UIPasteboard generalPasteboard]; 11 | NSString *testString = @"testing general pasteboard hooks"; 12 | [pb setValue:testString forPasteboardType:@"public.text"]; 13 | [pb release]; 14 | 15 | UIPasteboard *pb_named = [UIPasteboard pasteboardWithName:@"introspy_pb" create:YES]; 16 | NSString *testString2 = @"testing named pasteboard hooks"; 17 | [pb_named setValue:testString2 forPasteboardType:@"public.text"]; 18 | [pb_named release]; 19 | 20 | UIPasteboard *pb_unique = [UIPasteboard pasteboardWithUniqueName]; 21 | NSString *testString3 = @"testing unique pasteboard hooks"; 22 | [pb_unique setValue:testString3 forPasteboardType:@"public.text"]; 23 | [pb_unique release]; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /testapp/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | { 2 | CFBundleExecutable = "IntrospyTestApp"; 3 | CFBundleIconFile = icon.png; 4 | CFBundleIdentifier = "com.isecpartners.introspytestapp"; 5 | CFBundleInfoDictionaryVersion = 6.0; 6 | CFBundlePackageType = APPL; 7 | CFBundleSignature = "????"; 8 | CFBundleSupportedPlatforms = ( 9 | iPhoneOS 10 | ); 11 | CFBundleVersion = 1.0; 12 | DTPlatformName = iphoneos; 13 | DTSDKName = iphoneos3.0; 14 | LSRequiresIPhoneOS = 1; 15 | MinimumOSVersion = 3.0; 16 | CFBundleURLTypes = ( 17 | { 18 | CFBundleURLName = "com.isecpartners.introspytestapp.scheme1"; 19 | CFBundleURLSchemes = ( 20 | scheme1 21 | ); 22 | }, 23 | { 24 | CFBundleURLIsPrivate = 0; 25 | CFBundleURLName = "com.isecpartners.introspytestapp.scheme2"; 26 | CFBundleURLSchemes = ( 27 | "scheme2-1", 28 | "scheme2-2", 29 | ); 30 | }, 31 | { 32 | CFBundleURLIsPrivate = 1; 33 | CFBundleURLName = "com.isecpartners.introspytestapp.scheme3"; 34 | CFBundleURLSchemes = ( 35 | scheme3 36 | ); 37 | } 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /testapp/Resources/introspy.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/Introspy-iOS/64291d947cc336c7fc21a04a4ebb4e5ae752cfdb/testapp/Resources/introspy.p12 -------------------------------------------------------------------------------- /testapp/RootViewController.h: -------------------------------------------------------------------------------- 1 | @interface RootViewController: UIViewController { 2 | } 3 | @end 4 | -------------------------------------------------------------------------------- /testapp/RootViewController.mm: -------------------------------------------------------------------------------- 1 | #import "RootViewController.h" 2 | 3 | #import "FileSystemTester.h" 4 | #import "PasteboardTester.h" 5 | #import "UserPreferencesTester.h" 6 | #import "HTTPTester.h" 7 | #import "SchemeTester.h" 8 | #import "KeyChainTester.h" 9 | #import "CryptoTester.h" 10 | #import "XMLTester.h" 11 | 12 | 13 | @implementation RootViewController 14 | - (void)loadView { 15 | self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]; 16 | self.view.backgroundColor = [UIColor redColor]; 17 | 18 | 19 | 20 | UIAlertView *alert = 21 | [[UIAlertView alloc] initWithTitle: @"Introspy Test App" 22 | message: @"Introspy Test App Started" 23 | delegate: self 24 | cancelButtonTitle: @"OK" 25 | otherButtonTitles: nil]; 26 | [alert show]; 27 | [alert release]; 28 | 29 | // Data Storage 30 | FileSystemTester *fileSystemTests = [[FileSystemTester alloc] init]; 31 | [fileSystemTests runAllTests]; 32 | [fileSystemTests release]; 33 | 34 | // Pasteboard 35 | [PasteboardTester runAllTests]; 36 | 37 | 38 | // User Preferences 39 | [UserPreferencesTester runAllTests]; 40 | 41 | // XML 42 | [XMLTester runAllTests]; 43 | 44 | // Crypto 45 | [CryptoTester runAllTests]; 46 | 47 | // HTTP 48 | [HTTPTester runAllTests]; 49 | 50 | // URL Schemes 51 | [SchemeTester runAllTests]; 52 | 53 | // Security Framework 54 | [KeyChainTester runAllTests]; 55 | 56 | } 57 | @end 58 | 59 | /* vim: set filetype=objc : */ 60 | -------------------------------------------------------------------------------- /testapp/SchemeTester.h: -------------------------------------------------------------------------------- 1 | @interface SchemeTester: NSObject { 2 | } 3 | 4 | + (void)runAllTests; 5 | + (void)testUIApplicationDelegateInstanceMethods; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /testapp/SchemeTester.m: -------------------------------------------------------------------------------- 1 | #import "SchemeTester.h" 2 | 3 | @implementation SchemeTester: NSObject 4 | 5 | + (void)runAllTests { 6 | [self testUIApplicationDelegateInstanceMethods]; 7 | } 8 | 9 | + (void)testUIApplicationDelegateInstanceMethods { 10 | // test the current (>= iOS 4.2) workflow 11 | UIApplication *introspy_test = [UIApplication sharedApplication]; 12 | NSURL *scheme_path = [NSURL URLWithString:@"scheme1://public/url/scheme?method=application:openURL:sourceApplication:annotation:"]; 13 | [introspy_test openURL:scheme_path]; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /testapp/UserPreferencesTester.h: -------------------------------------------------------------------------------- 1 | @interface UserPreferencesTester : NSObject { 2 | 3 | } 4 | 5 | + (void)runAllTests; 6 | + (void)testNSUserDefaults; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /testapp/UserPreferencesTester.m: -------------------------------------------------------------------------------- 1 | #import "UserPreferencesTester.h" 2 | 3 | @implementation UserPreferencesTester : NSObject 4 | 5 | 6 | + (void)runAllTests { 7 | 8 | [self testNSUserDefaults]; 9 | } 10 | 11 | 12 | + (void)testNSUserDefaults { 13 | 14 | NSMutableString *testKey = [NSMutableString stringWithCapacity:15]; 15 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 16 | NSData* testContent = [@"testContent" dataUsingEncoding: [NSString defaultCStringEncoding]]; 17 | NSURL * testURL = [NSURL fileURLWithPath:@"~/introspytest.file"]; 18 | 19 | [testKey setString:@"testBool"]; 20 | [defaults setBool:YES forKey:testKey]; 21 | [defaults boolForKey:testKey]; 22 | 23 | [testKey setString:@"testFloat"]; 24 | [defaults setFloat:5.5 forKey:testKey]; 25 | [defaults floatForKey:testKey]; 26 | 27 | [testKey setString:@"testInt"]; 28 | [defaults setInteger:1 forKey:testKey]; 29 | [defaults integerForKey:testKey]; 30 | 31 | [testKey setString:@"testDouble"]; 32 | [defaults setDouble:1 forKey:testKey]; 33 | [defaults doubleForKey:testKey]; 34 | 35 | [testKey setString:@"testObject"]; 36 | [defaults setObject:@"testString" forKey:testKey]; 37 | [defaults objectForKey:testKey]; 38 | [defaults stringForKey:testKey]; 39 | 40 | NSArray *testArray; 41 | [testKey setString:@"testArray"]; 42 | testArray = [NSArray arrayWithObjects:@"test1", @"test2", nil]; 43 | [defaults setObject:testArray forKey:testKey]; 44 | [defaults stringArrayForKey:testKey]; 45 | [defaults arrayForKey:testKey]; 46 | [defaults dictionaryForKey:testKey]; 47 | 48 | [testKey setString:@"testData"]; 49 | [defaults setObject:testContent forKey:testKey]; 50 | [defaults dataForKey:testKey]; 51 | 52 | [testKey setString:@"testURL"]; 53 | [defaults setURL:testURL forKey:testKey]; 54 | [defaults URLForKey:testKey]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /testapp/XMLTester.h: -------------------------------------------------------------------------------- 1 | 2 | @interface XMLTester : NSObject { 3 | 4 | } 5 | 6 | + (void)runAllTests; 7 | 8 | + (void) testNSXMLParser; 9 | 10 | @end 11 | 12 | -------------------------------------------------------------------------------- /testapp/XMLTester.m: -------------------------------------------------------------------------------- 1 | #import "XMLTester.h" 2 | 3 | @implementation XMLTester { 4 | 5 | } 6 | 7 | + (void)runAllTests { 8 | [self testNSXMLParser]; 9 | } 10 | 11 | + (void) testNSXMLParser { 12 | NSURL *testURL = [NSURL URLWithString:@"http://www.isecpartners.com"]; 13 | NSXMLParser *testParser = [[NSXMLParser alloc] initWithContentsOfURL: testURL]; 14 | [testParser setShouldResolveExternalEntities: YES]; 15 | [testParser release]; 16 | 17 | NSInputStream *testStream = [NSInputStream inputStreamWithURL:testURL]; 18 | testParser = [[NSXMLParser alloc] initWithStream: testStream]; 19 | [testParser release]; 20 | 21 | NSData *testData = [NSData dataWithContentsOfURL:testURL]; 22 | testParser = [[NSXMLParser alloc] initWithData: testData]; 23 | } 24 | 25 | @end 26 | 27 | -------------------------------------------------------------------------------- /testapp/control: -------------------------------------------------------------------------------- 1 | Package: com.isecpartners.introspytestapp 2 | Name: Introspy Test App 3 | Depends: 4 | Version: 0.0.1 5 | Architecture: iphoneos-arm 6 | Description: An awesome application! 7 | Maintainer: Alban Diquet 8 | Author: Alban Diquet 9 | Section: Utilities 10 | -------------------------------------------------------------------------------- /testapp/main.m: -------------------------------------------------------------------------------- 1 | int main(int argc, char **argv) { 2 | NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; 3 | int ret = UIApplicationMain(argc, argv, @"IntrospyTestAppApplication", @"IntrospyTestAppApplication"); 4 | [p drain]; 5 | return ret; 6 | } 7 | 8 | // vim:ft=objc 9 | --------------------------------------------------------------------------------