├── LICENSE ├── README.md ├── fishhook.c ├── fishhook.h └── fishhook.podspec /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fishhook 2 | # 相关分析文章 3 | [fishhook源码分析](http://turingh.github.io/2016/03/22/fishhook%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 4 | 5 | # 更多细节,可以阅读这两篇文章 6 | [mach-o格式介绍](http://turingh.github.io/2016/03/07/mach-o%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%88%86%E6%9E%90/) 7 | [mach-o延时绑定](http://turingh.github.io/2016/03/10/Mach-O%E7%9A%84%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5/) 8 | 9 | 以下是官方内容 10 | === 11 | __fishhook__ is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device. This provides functionality that is similar to using [`DYLD_INTERPOSE`][interpose] on OS X. At Facebook, we've found it useful as a way to hook calls in libSystem for debugging/tracing purposes (for example, auditing for double-close issues with file descriptors). 12 | 13 | [interpose]: http://opensource.apple.com/source/dyld/dyld-210.2.3/include/mach-o/dyld-interposing.h "" 14 | 15 | ## Usage 16 | 17 | Once you add `fishhook.h`/`fishhook.c` to your project, you can rebind symbols as follows: 18 | ```Objective-C 19 | #import 20 | 21 | #import 22 | 23 | #import "AppDelegate.h" 24 | #import "fishhook.h" 25 | 26 | static int (*orig_close)(int); 27 | static int (*orig_open)(const char *, int, ...); 28 | 29 | int my_close(int fd) { 30 | printf("Calling real close(%d)\n", fd); 31 | return orig_close(fd); 32 | } 33 | 34 | int my_open(const char *path, int oflag, ...) { 35 | va_list ap = {0}; 36 | mode_t mode = 0; 37 | 38 | if ((oflag & O_CREAT) != 0) { 39 | // mode only applies to O_CREAT 40 | va_start(ap, oflag); 41 | mode = va_arg(ap, int); 42 | va_end(ap); 43 | printf("Calling real open('%s', %d, %d)\n", path, oflag, mode); 44 | return orig_open(path, oflag, mode); 45 | } else { 46 | printf("Calling real open('%s', %d)\n", path, oflag); 47 | return orig_open(path, oflag, mode); 48 | } 49 | } 50 | 51 | int main(int argc, char * argv[]) 52 | { 53 | @autoreleasepool { 54 | rebind_symbols((struct rebinding[2]){{"close", my_close, (void *)&orig_close}, {"open", my_open, (void *)&orig_open}}, 2); 55 | 56 | // Open our own binary and print out first 4 bytes (which is the same 57 | // for all Mach-O binaries on a given architecture) 58 | int fd = open(argv[0], O_RDONLY); 59 | uint32_t magic_number = 0; 60 | read(fd, &magic_number, 4); 61 | printf("Mach-O Magic Number: %x \n", magic_number); 62 | close(fd); 63 | 64 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 65 | } 66 | } 67 | ``` 68 | ### Sample output 69 | ``` 70 | Calling real open('/var/mobile/Applications/161DA598-5B83-41F5-8A44-675491AF6A2C/Test.app/Test', 0) 71 | Mach-O Magic Number: feedface 72 | Calling real close(3) 73 | ... 74 | ``` 75 | 76 | ## How it works 77 | 78 | `dyld` binds lazy and non-lazy symbols by updating pointers in particular sections of the `__DATA` segment of a Mach-O binary. __fishhook__ re-binds these symbols by determining the locations to update for each of the symbol names passed to `rebind_symbols` and then writing out the corresponding replacements. 79 | 80 | For a given image, the `__DATA` segment may contain two sections that are relevant for dynamic symbol bindings: `__nl_symbol_ptr` and `__la_symbol_ptr`. `__nl_symbol_ptr` is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and `__la_symbol_ptr` is an array of pointers to imported functions that is generally filled by a routine called `dyld_stub_binder` during the first call to that symbol (it's also possible to tell `dyld` to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (`struct section`s from ``) provide an offset (in the `reserved1` field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the `__LINKEDIT` segment of the binary, is just an array of indexes into the symbol table (also in `__LINKEDIT`) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given `struct section nl_symbol_ptr`, the corresponding index in the symbol table of the first address in that section is `indirect_symbol_table[nl_symbol_ptr->reserved1]`. The symbol table itself is an array of `struct nlist`s (see ``), and each `nlist` contains an index into the string table in `__LINKEDIT` which where the actual symbol names are stored. So, for each pointer `__nl_symbol_ptr` and `__la_symbol_ptr`, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement. 81 | 82 | The process of looking up the name of a given entry in the lazy or non-lazy pointer tables looks like this: 83 | ![Visual explanation](http://i.imgur.com/HVXqHCz.png) 84 | -------------------------------------------------------------------------------- /fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #import "fishhook.h" 25 | 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | 34 | #ifdef __LP64__ 35 | typedef struct mach_header_64 mach_header_t; 36 | typedef struct segment_command_64 segment_command_t; 37 | typedef struct section_64 section_t; 38 | typedef struct nlist_64 nlist_t; 39 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 40 | #else 41 | typedef struct mach_header mach_header_t; 42 | typedef struct segment_command segment_command_t; 43 | typedef struct section section_t; 44 | typedef struct nlist nlist_t; 45 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 46 | #endif 47 | 48 | #ifndef SEG_DATA_CONST 49 | #define SEG_DATA_CONST "__DATA_CONST" 50 | #endif 51 | 52 | struct rebindings_entry { 53 | struct rebinding *rebindings; 54 | size_t rebindings_nel; 55 | struct rebindings_entry *next; 56 | }; 57 | 58 | static struct rebindings_entry *_rebindings_head; 59 | 60 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 61 | struct rebinding rebindings[], 62 | size_t nel) { 63 | struct rebindings_entry *new_entry = malloc(sizeof(struct rebindings_entry)); 64 | if (!new_entry) { 65 | return -1; 66 | } 67 | new_entry->rebindings = malloc(sizeof(struct rebinding) * nel); 68 | if (!new_entry->rebindings) { 69 | free(new_entry); 70 | return -1; 71 | } 72 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 73 | new_entry->rebindings_nel = nel; 74 | new_entry->next = *rebindings_head; 75 | *rebindings_head = new_entry; 76 | return 0; 77 | } 78 | 79 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 80 | section_t *section, 81 | intptr_t slide, 82 | nlist_t *symtab, 83 | char *strtab, 84 | uint32_t *indirect_symtab) { 85 | //__nl_symbol_ptr和__la_symbol_ptr的reserved1字段标识了section描述的符号在符号表中开始的index 86 | //动态符号表中第一个需要解析的符号 开始地址 87 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 88 | 89 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 90 | 91 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 92 | uint32_t symtab_index = indirect_symbol_indices[i]; 93 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 94 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 95 | continue; 96 | } 97 | //获取每一个需要动态解析的符号在符号表中的地址 98 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 99 | 100 | //通过符号表中每一个导入符号的字符串表偏移量获取符号对应的字符串(符号的名字) 101 | char *symbol_name = strtab + strtab_offset; 102 | struct rebindings_entry *cur = rebindings; 103 | while (cur) { 104 | for (uint j = 0; j < cur->rebindings_nel; j++) { 105 | if (strlen(symbol_name) > 1 && 106 | strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 107 | //找到相同的函数替换指针 108 | if (cur->rebindings[j].replaced != NULL && 109 | indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { 110 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 111 | } 112 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 113 | goto symbol_loop; 114 | } 115 | } 116 | cur = cur->next; 117 | } 118 | symbol_loop:; 119 | } 120 | } 121 | 122 | 123 | //typedef struct { 124 | // const char *dli_fname; /* Pathname of shared object that 125 | // contains address */ 126 | // void *dli_fbase; /* Address at which shared object 127 | // is loaded */ 128 | // const char *dli_sname; /* Name of nearest symbol with address 129 | // lower than addr */ 130 | // void *dli_saddr; /* Exact address of symbol named 131 | // in dli_sname */ 132 | //} Dl_info; 133 | 134 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 135 | const struct mach_header *header, 136 | intptr_t slide) { 137 | //获取Dl_info 138 | Dl_info info; 139 | if (dladdr(header, &info) == 0) { 140 | return; 141 | } 142 | 143 | 144 | segment_command_t *cur_seg_cmd; 145 | segment_command_t *linkedit_segment = NULL; 146 | struct symtab_command* symtab_cmd = NULL; 147 | struct dysymtab_command* dysymtab_cmd = NULL; 148 | 149 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 150 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 151 | cur_seg_cmd = (segment_command_t *)cur; 152 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 153 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 154 | //在lc_segment中遍历寻找__LINKEDIT的section 155 | linkedit_segment = cur_seg_cmd; 156 | } 157 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 158 | //遍历寻找lc_symtab 159 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 160 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 161 | //遍历寻找lc_dysymtab 162 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 163 | } 164 | } 165 | 166 | //检测必要的数据结构是否都存在 167 | /* 168 | LC_SYMTAB这个LoadCommand主要提供了两个信息 169 | Symbol Table的偏移量与Symbol Table中元素的个数 170 | String Table的偏移量与String Table的长度 171 | LC_DYSYMTAB 172 | 提供了动态符号表的位移和元素个数,还有一些其他的表格索引 173 | LC_SEGMENT.__LINKEDIT 174 | 含有为动态链接库使用的原始数据 175 | */ 176 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 177 | !dysymtab_cmd->nindirectsyms) { 178 | return; 179 | } 180 | 181 | // Find base symbol/string table addresses 182 | // 获取链接时程序的基址 183 | // 基址 = __LINKEDIT.VM_Address - __LINK.File_Offset + silde的改变值 184 | // machoview随便看一个程序的__LINKEDIT 185 | // offset | data | Description | value 186 | // ... 187 | // 0x0000390 0x0000000100002000 VM Address 4294975488 188 | // 0x0000398 0x0000000000003000 VM Size 12288 189 | // 0x00003A0 0x0000000000002000 File Offset 8192 190 | // 0x00003A8 0x0000000000002690 File Size 9872 191 | 192 | // base = 100002000-2000 + slide = 0x0000000100000000 + slide 193 | // 这应该是一个公式 194 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 195 | 196 | // 符号表的地址 = 基址 + 符号表偏移量 197 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 198 | // 字符串表的地址 = 基址 + 字符串表偏移量 199 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 200 | 201 | // Get indirect symbol table (array of uint32_t indices into symbol table) 202 | // 动态符号表地址 = 基址 + 动态符号表偏移量 203 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 204 | 205 | //再一次遍历loadcommands 206 | cur = (uintptr_t)header + sizeof(mach_header_t); 207 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 208 | cur_seg_cmd = (segment_command_t *)cur; 209 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 210 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 211 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 212 | continue; 213 | } 214 | //找到__DATA和__DATA_CONST的section 215 | //__DATA section里面保存的是符号跳转的函数指针表 216 | // 对__nl_symbol_ptr以及__la_symbol_ptr进行rebind 217 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 218 | section_t *sect = 219 | (section_t *)(cur + sizeof(segment_command_t)) + j; 220 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 221 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 222 | } 223 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 224 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 225 | } 226 | } 227 | } 228 | } 229 | } 230 | 231 | static void _rebind_symbols_for_image(const struct mach_header *header, 232 | intptr_t slide) { 233 | rebind_symbols_for_image(_rebindings_head, header, slide); 234 | } 235 | 236 | int rebind_symbols_image(void *header, 237 | intptr_t slide, 238 | struct rebinding rebindings[], 239 | size_t rebindings_nel) { 240 | struct rebindings_entry *rebindings_head = NULL; 241 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 242 | rebind_symbols_for_image(rebindings_head, header, slide); 243 | free(rebindings_head); 244 | return retval; 245 | } 246 | 247 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { 248 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 249 | if (retval < 0) { 250 | return retval; 251 | } 252 | // If this was the first call, register callback for image additions (which is also invoked for 253 | // existing images, otherwise, just run on existing images 254 | if (!_rebindings_head->next) { 255 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 256 | } else { 257 | uint32_t c = _dyld_image_count(); 258 | for (uint32_t i = 0; i < c; i++) { 259 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 260 | } 261 | } 262 | return retval; 263 | } 264 | -------------------------------------------------------------------------------- /fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif //__cplusplus 33 | 34 | /* 35 | * A structure representing a particular intended rebinding from a symbol 36 | * name to its replacement 37 | */ 38 | struct rebinding { 39 | const char *name; 40 | void *replacement; 41 | void **replaced; 42 | }; 43 | 44 | /* 45 | * For each rebinding in rebindings, rebinds references to external, indirect 46 | * symbols with the specified name to instead point at replacement for each 47 | * image in the calling process as well as for all future images that are loaded 48 | * by the process. If rebind_functions is called more than once, the symbols to 49 | * rebind are added to the existing list of rebindings, and if a given symbol 50 | * is rebound more than once, the later rebinding will take precedence. 51 | */ 52 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 53 | 54 | /* 55 | * Rebinds as above, but only in the specified image. The header should point 56 | * to the mach-o header, the slide should be the slide offset. Others as above. 57 | */ 58 | int rebind_symbols_image(void *header, 59 | intptr_t slide, 60 | struct rebinding rebindings[], 61 | size_t rebindings_nel); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif //__cplusplus 66 | 67 | #endif //fishhook_h 68 | 69 | -------------------------------------------------------------------------------- /fishhook.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "fishhook" 3 | spec.version = "0.2" 4 | spec.license = { :type => "BSD", :file => "LICENSE" } 5 | spec.homepage = 'https://github.com/facebook/fishhook' 6 | spec.author = { "Facebook, Inc." => "https://github.com/facebook" } 7 | spec.summary = "A library that enables dynamically rebinding symbols in Mach-O binaries running on iOS." 8 | spec.source = { :git => "https://github.com/facebook/fishhook.git", :tag => '0.2'} 9 | spec.source_files = "fishhook.{h,c}" 10 | spec.social_media_url = 'https://twitter.com/fbOpenSource' 11 | 12 | spec.ios.deployment_target = '6.0' 13 | end 14 | --------------------------------------------------------------------------------