├── .gitmodules
├── picture
└── after_restore.jpeg
├── restore-symbol.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── makefile
├── source
├── RSSymbol.h
├── RSScanMethodVisitor.h
├── restore-symbol.pch
├── RSSymbolCollector.h
├── RSSymbol.m
├── RSScanMethodVisitor.m
├── RSSymbolCollector.m
├── main.m
└── restore-symbol.m
├── .gitignore
├── README.md
└── search_oc_block
└── ida_search_block.py
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "class-dump"]
2 | path = class-dump
3 | url = https://github.com/0xced/class-dump
4 |
--------------------------------------------------------------------------------
/picture/after_restore.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobefuturer/restore-symbol/HEAD/picture/after_restore.jpeg
--------------------------------------------------------------------------------
/restore-symbol.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 |
2 | .PHONY:restore-symbol
3 |
4 | TMP_FILE := libMachObjC.a restore-symbol.dSYM/ build/ class-dump/build/
5 |
6 | restore-symbol:
7 | rm -rf $(TMP_FILE)
8 | rm -f restore-symbol
9 | git submodule update --init --recursive
10 | xcodebuild -project "restore-symbol.xcodeproj" -target "restore-symbol" -configuration "Release" CONFIGURATION_BUILD_DIR="$(shell pwd)" -jobs 4 build
11 | rm -rf $(TMP_FILE)
12 |
13 |
14 | clean:
15 | rm -rf restore-symbol $(TMP_FILE)
16 |
17 |
--------------------------------------------------------------------------------
/source/RSSymbol.h:
--------------------------------------------------------------------------------
1 | //
2 | // RSSymbol.h
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/19.
6 | //
7 | //
8 |
9 | #import
10 |
11 |
12 | #define RS_JSON_KEY_ADDRESS @"address"
13 | #define RS_JSON_KEY_SYMBOL_NAME @"name"
14 |
15 | @interface RSSymbol : NSObject
16 |
17 |
18 | @property (nonatomic, strong) NSString * name;
19 | @property (nonatomic) uint64 address;
20 |
21 |
22 | + (NSArray *)symbolsWithJson:(NSData *)json;
23 |
24 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr;
25 |
26 |
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/source/RSScanMethodVisitor.h:
--------------------------------------------------------------------------------
1 | // -*- mode: ObjC -*-
2 |
3 | // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
4 | // Copyright (C) 1997-1998, 2000-2001, 2004-2015 Steve Nygard.
5 |
6 | #import "CDVisitor.h"
7 | #import "RSSymbolCollector.h"
8 |
9 | // This limits the output to methods matching the search string. Some context is included, so that you can see which class, category, or protocol
10 | // contains the method.
11 |
12 | @interface RSScanMethodVisitor : CDVisitor
13 |
14 | - (instancetype)initWithSymbolCollector:(RSSymbolCollector*)collector;
15 |
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/source/restore-symbol.pch:
--------------------------------------------------------------------------------
1 | //
2 | // restore-symbol.pch
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/23.
6 | // Copyright © 2016年 Jun. All rights reserved.
7 | //
8 |
9 | #ifndef restore_symbol_pch
10 | #define restore_symbol_pch
11 |
12 | // Include any system framework and library headers here that should be included in all compilation units.
13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
14 | #include
15 | #import "CDExtensions.h"
16 | #define __cmd __PRETTY_FUNCTION__
17 | #endif /* restore_symbol_pch */
18 |
--------------------------------------------------------------------------------
/source/RSSymbolCollector.h:
--------------------------------------------------------------------------------
1 | //
2 | // RSSymbolCollector.h
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/19.
6 | //
7 | //
8 |
9 | #import
10 | #import "RSSymbol.h"
11 | #import "CDMachOFile.h"
12 |
13 | @interface RSSymbolCollector : NSObject
14 |
15 | @property (nonatomic, weak) CDMachOFile * machOFile;
16 | @property (nonatomic, strong) NSMutableArray *symbols;
17 |
18 |
19 | - (void)addSymbol:(RSSymbol *)symbol;
20 | - (void)addSymbols:(NSArray *)symbols;
21 |
22 |
23 | - (void)generateAppendStringTable:(NSData **)stringTable appendSymbolTable:(NSData **)nlist;
24 | @end
25 |
--------------------------------------------------------------------------------
/source/RSSymbol.m:
--------------------------------------------------------------------------------
1 | //
2 | // RSSymbol.m
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/19.
6 | //
7 | //
8 |
9 | #import "RSSymbol.h"
10 |
11 | @implementation RSSymbol
12 |
13 |
14 | + (NSArray *)symbolsWithJson:(NSData *)json{
15 | NSError * e = nil;
16 |
17 | NSArray *symbols = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingMutableContainers error:&e];
18 |
19 | if (!symbols) {
20 | fprintf(stderr,"Parse json error!\n");
21 | fprintf(stderr,"%s\n", e.description.UTF8String);
22 | return nil;
23 | }
24 |
25 | NSMutableArray * rt = [NSMutableArray array];
26 | for (NSDictionary *dict in symbols) {
27 | NSString *addressString = dict[RS_JSON_KEY_ADDRESS];
28 | unsigned long long address;
29 | NSScanner* scanner = [NSScanner scannerWithString:addressString];
30 | [scanner scanHexLongLong:&address];
31 | RSSymbol * symbol = [self symbolWithName:dict[RS_JSON_KEY_SYMBOL_NAME] address:address];
32 | [rt addObject:symbol];
33 | }
34 |
35 | return rt;
36 | }
37 |
38 |
39 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr{
40 | RSSymbol * s = [RSSymbol new];
41 | s.name = name;
42 | s.address = addr;
43 | return s;
44 | }
45 | @end
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | restore-symbol
2 |
3 |
4 | # svn
5 | *.svn*
6 |
7 | # mac
8 | __MACOSX
9 | *.DS_Store*
10 | *._*
11 | *.lock
12 | *.Spotlight-V100
13 | *.Trashes
14 | *ehthumbs.db
15 | *Thumbs.db
16 |
17 | # Python
18 | *.pyc
19 | __pycache__/
20 |
21 |
22 | # Vim
23 | *.swp
24 | *.swo
25 | *.swn
26 |
27 | # Xcode
28 | *~.nib
29 | build/
30 | *.pbxuser
31 | !default.pbxuser
32 | *.mode1v3
33 | !default.mode1v3
34 | *.mode2v3
35 | !default.mode2v3
36 | *.perspectivev3
37 | !default.perspectivev3
38 | xcuserdata
39 | *.xccheckout
40 | *.xcscmblueprint
41 | *.moved-aside
42 | DerivedData
43 | *.hmap
44 | *.ipa
45 | *.xcuserstate
46 |
47 | # generated files
48 | bin/
49 | gen/
50 |
51 | # built application files
52 | *.apk
53 | *.ap_
54 |
55 | # files for the dex VM
56 | *.dex
57 |
58 | # Java class files
59 | *.class
60 |
61 | # Local configuration file (sdk path, etc)
62 | local.properties
63 |
64 | # Eclipse project files
65 | .classpath
66 | .project
67 | .settings/
68 |
69 | # Proguard folder generated by Eclipse
70 | proguard/
71 |
72 | # Intellij project files
73 | *.iml
74 | *.ipr
75 | *.iws
76 | .idea/
77 |
78 | #Pod
79 | Pods/
80 | !podfile
81 | !podfile.lock
82 |
83 | # Gradle files
84 | .gradle
85 | build/
86 |
87 | # Ignore Gradle GUI config
88 | gradle-app.setting
89 |
90 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
91 | !gradle-wrapper.jar
92 |
93 | #Android studio For eclipse
94 | #build.gradle
95 | #gradle/
96 | #gradlew
97 | #gradlew.bat
98 | #.gradle/
99 | #
100 |
101 |
102 |
103 | #tweak
104 | _/
105 | .theos/
106 | obj/
107 | *.deb
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/source/RSScanMethodVisitor.m:
--------------------------------------------------------------------------------
1 | // -*- mode: ObjC -*-
2 |
3 | // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
4 | // Copyright (C) 1997-1998, 2000-2001, 2004-2015 Steve Nygard.
5 |
6 | #import "RSScanMethodVisitor.h"
7 |
8 | #import "CDClassDump.h"
9 | #import "CDObjectiveC1Processor.h"
10 | #import "CDMachOFile.h"
11 | #import "CDOCProtocol.h"
12 | #import "CDLCDylib.h"
13 | #import "CDOCClass.h"
14 | #import "CDOCCategory.h"
15 | #import "CDOCClassReference.h"
16 | #import "CDOCMethod.h"
17 | //#import "CDTypeController.h"
18 |
19 | @interface RSScanMethodVisitor ()
20 |
21 | @property (nonatomic, strong) CDOCProtocol *context;
22 |
23 | @property (nonatomic, weak) RSSymbolCollector * collector;
24 |
25 | @end
26 |
27 | #pragma mark -
28 |
29 | @implementation RSScanMethodVisitor
30 | {
31 | CDOCProtocol *_context;
32 |
33 | }
34 |
35 | - (id)initWithSymbolCollector:(RSSymbolCollector *)collector
36 | {
37 | if ((self = [super init])) {
38 | _context = nil;
39 | _collector = collector;
40 | }
41 |
42 | return self;
43 | }
44 |
45 | #pragma mark -
46 |
47 | - (void)willVisitProtocol:(CDOCProtocol *)protocol;
48 | {
49 | [self setContext:protocol];
50 | }
51 |
52 | - (void)willVisitClass:(CDOCClass *)aClass;
53 | {
54 | [self setContext:aClass];
55 | }
56 |
57 |
58 | - (void)willVisitCategory:(CDOCCategory *)category;
59 | {
60 | [self setContext:category];
61 | }
62 |
63 |
64 | - (NSString *)getCurrentClassName{
65 | if ([_context isKindOfClass:[CDOCClass class]]) {
66 | return _context.name;
67 | } else if([_context isKindOfClass:[CDOCCategory class]]) {
68 | NSString * className = [[(CDOCCategory *)_context classRef] className];
69 | if (!className) className = @"";
70 | return [NSString stringWithFormat:@"%@(%@)", className ,_context.name];
71 | }
72 | return _context.name;
73 | }
74 |
75 | - (void)visitClassMethod:(CDOCMethod *)method;
76 | {
77 | if (method.address == 0 ) {
78 | return;
79 | }
80 |
81 | NSString *name = [NSString stringWithFormat:@"+[%@ %@]", [self getCurrentClassName], method.name];
82 |
83 | RSSymbol *s = [RSSymbol symbolWithName:name address:method.address];
84 |
85 | [self.collector addSymbol:s];
86 |
87 | }
88 |
89 | - (void)visitInstanceMethod:(CDOCMethod *)method propertyState:(CDVisitorPropertyState *)propertyState;
90 | {
91 | if (method.address == 0 ) {
92 | return;
93 | }
94 | NSString *name = [NSString stringWithFormat:@"-[%@ %@]", [self getCurrentClassName], method.name];
95 |
96 | RSSymbol *s = [RSSymbol symbolWithName:name address:method.address];
97 |
98 | [self.collector addSymbol:s];
99 |
100 | }
101 |
102 |
103 | #pragma mark -
104 |
105 | - (void)setContext:(CDOCProtocol *)newContext;
106 | {
107 | if (newContext != _context) {
108 | _context = newContext;
109 | }
110 | }
111 |
112 |
113 |
114 | @end
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # restore-symbol
2 |
3 | A reverse engineering tool to restore stripped symbol table for iOS app.
4 |
5 | Example: restore symbol for Alipay
6 | 
7 |
8 |
9 | ## How to use
10 |
11 | ### Just restore symbol of oc method
12 |
13 | - 1. Download source code and compile.
14 |
15 | ```
16 |
17 | git clone --recursive https://github.com/tobefuturer/restore-symbol.git
18 | cd restore-symbol && make
19 | ./restore-symbol
20 |
21 | ```
22 |
23 | - 2. Restore symbol using this command. It will output a new mach-o file with symbol.
24 |
25 | ```
26 |
27 | ./restore-symbol /pathto/origin_mach_o_file -o /pathto/mach_o_with_symbol
28 |
29 | ```
30 |
31 | - 3. Copy the new mach-o file (with symbol) to app bundle, replace the origin mach-o file with new mach-o file. Resign app bundle.
32 |
33 | ```
34 |
35 | codesign -f -s "iPhone Developer: XXXXXXX" --signing-time none --entitlement ./xxxx.app.xcent ./xxxx.app
36 |
37 | ```
38 |
39 | - 4. Install the app bundle to iOS device, and use lldb to debug the app. Maybe you can use the ```ios-deploy```, or other way you like. If you use ```ios-deploy``` , you can execute this command.
40 |
41 | ```
42 |
43 | brew install ios-deploy
44 | ios-deploy -d -b xxxx.app
45 |
46 | ```
47 | - 5. Now you can use ```b -[class method]``` to set breakpoint.
48 |
49 | ### Restore symbol of oc block
50 |
51 | - 1. Search block symbol in IDA to get json symbol file, using script([`search_oc_block/ida_search_block.py`](https://github.com/tobefuturer/restore-symbol/blob/master/search_oc_block/ida_search_block.py)) .
52 |
53 | 
54 |
55 | 
56 |
57 | - 2. Use command line tool(restore-symbol) to inject oc method symbols and block symbols into mach o file.
58 |
59 | ```
60 |
61 | ./restore-symbol /pathto/origin_mach_o_file -o /pathto/mach_o_with_symbol -j /pathto/block_symbol.json
62 |
63 | ```
64 |
65 | - 3. Other steps(resign, install, debug) are samen as above.
66 |
67 | ## Command Line Usage
68 | ```
69 | Usage: restore-symbol -o [-j ]
70 |
71 | where options are:
72 | -o New mach-o-file path
73 | --disable-oc-detect Disable auto detect and add oc method into symbol table,
74 | only add symbol in json file
75 | --replace-restrict New mach-o-file will replace the LC_SEGMENT(__RESTRICT,__restrict)
76 | with LC_SEGMENT(__restrict,__restrict) to close dylib inject protection
77 | -j Json file containing extra symbol info, the key is "name","address"
78 | like this:
79 |
80 | [
81 | {
82 | "name": "main",
83 | "address": "0xXXXXXX"
84 | },
85 | {
86 | "name": "-[XXXX XXXXX]",
87 | "address": "0xXXXXXX"
88 | },
89 | ....
90 | ]
91 |
92 | ```
93 |
--------------------------------------------------------------------------------
/source/RSSymbolCollector.m:
--------------------------------------------------------------------------------
1 | //
2 | // RSSymbolCollector.m
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/19.
6 | //
7 | //
8 |
9 | #import "RSSymbolCollector.h"
10 |
11 | #import
12 | #import "CDLCSymbolTable.h"
13 | #import "CDLCSegment.h"
14 | #import "CDSection.h"
15 |
16 | @implementation RSSymbolCollector
17 |
18 |
19 |
20 | - (instancetype)init
21 | {
22 | self = [super init];
23 | if (self) {
24 | _symbols = [NSMutableArray array];
25 | }
26 | return self;
27 | }
28 |
29 |
30 | - (void)addSymbol:(RSSymbol *)symbol{
31 | if (symbol == nil) {
32 | return ;
33 | }
34 | [_symbols addObject:symbol];
35 | }
36 |
37 |
38 | - (void)addSymbols:(NSArray *)symbols{
39 | if (symbols == nil)
40 | return ;
41 | [_symbols addObjectsFromArray:symbols];
42 | }
43 |
44 |
45 | - (void)generateAppendStringTable:(NSData **)stringTable appendSymbolTable:(NSData **)symbolTable{
46 |
47 | const bool is32Bit = ! _machOFile.uses64BitABI;
48 |
49 | NSMutableData * symbolNames = [NSMutableData new];
50 |
51 | NSMutableData * nlistsData = [NSMutableData dataWithLength:_symbols.count * ( is32Bit ? sizeof(struct nlist) : sizeof(struct nlist_64))];
52 |
53 | memset(nlistsData.mutableBytes, 0, nlistsData.length);
54 |
55 | uint32 origin_string_table_size = _machOFile.symbolTable.strsize;
56 |
57 |
58 | for (int i = 0; i < _symbols.count; i ++) {
59 |
60 |
61 | RSSymbol * symbol = _symbols[i];
62 | if (symbol.address == 0) {
63 | continue;
64 | }
65 |
66 |
67 | if (is32Bit) {
68 | struct nlist * list = nlistsData.mutableBytes;
69 | bool isThumb = symbol.address & 1;
70 | list[i].n_desc = isThumb ? N_ARM_THUMB_DEF : 0;
71 | list[i].n_type = N_PEXT | N_SECT;
72 | list[i].n_sect = [self n_sectForAddress:symbol.address];
73 | list[i].n_value = (uint32_t)symbol.address & ~ 1;
74 | list[i].n_un.n_strx = origin_string_table_size + (uint32)symbolNames.length;
75 |
76 | } else {
77 | struct nlist_64 * list = nlistsData.mutableBytes;
78 | list[i].n_desc = 0;
79 | list[i].n_type = N_PEXT | N_SECT;
80 | list[i].n_sect = [self n_sectForAddress:symbol.address];
81 | list[i].n_value = symbol.address;
82 | list[i].n_un.n_strx = origin_string_table_size + (uint32)symbolNames.length;
83 | }
84 |
85 | [symbolNames appendBytes:symbol.name.UTF8String length:symbol.name.length];
86 | [symbolNames appendBytes:"\0" length:1];
87 | }
88 |
89 |
90 | *stringTable = symbolNames;
91 | *symbolTable = nlistsData;
92 |
93 |
94 | }
95 |
96 | - (uint8)n_sectForAddress:(uint64)address{
97 |
98 |
99 | uint8 n_sect = 0;
100 |
101 | for (id loadCommand in _machOFile.loadCommands) {
102 | if ([loadCommand isKindOfClass:[CDLCSegment class]]){
103 | CDLCSegment * seg = (CDLCSegment *)loadCommand;
104 | if(![loadCommand containsAddress:address]) {
105 | n_sect += [[seg sections] count];
106 | } else {
107 | for (CDSection * section in [seg sections]){
108 | n_sect ++;
109 | if ([section containsAddress:address]) {
110 | return n_sect;
111 | }
112 |
113 | }
114 |
115 | }
116 | }
117 | }
118 |
119 | NSLog(@"Address(%llx) not found in the image", address);
120 | exit(1);
121 | return 1;
122 | }
123 |
124 |
125 | @end
126 |
--------------------------------------------------------------------------------
/source/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // class-dump
4 | //
5 | // Created by EugeneYang on 16/8/22.
6 | //
7 | //
8 |
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 |
17 |
18 | #define RESTORE_SYMBOL_BASE_VERSION "1.0 (64 bit)"
19 |
20 | #ifdef DEBUG
21 | #define RESTORE_SYMBOL_VERSION RESTORE_SYMBOL_BASE_VERSION //" (Debug version compiled " __DATE__ " " __TIME__ ")"
22 | #else
23 | #define RESTORE_SYMBOL_VERSION RESTORE_SYMBOL_BASE_VERSION
24 | #endif
25 |
26 | #define RS_OPT_DISABLE_OC_DETECT 1
27 | #define RS_OPT_VERSION 2
28 | #define RS_OPT_REPLACE_RESTRICT 3
29 |
30 |
31 |
32 | void print_usage(void)
33 | {
34 | fprintf(stderr,
35 | "\n"
36 | "restore-symbol %s\n"
37 | "\n"
38 | "Usage: restore-symbol -o [-j ] \n"
39 | "\n"
40 | " where options are:\n"
41 | " -o New mach-o-file path\n"
42 | " --disable-oc-detect Disable auto detect and add oc method into symbol table,\n"
43 | " only add symbol in json file\n"
44 | " --replace-restrict New mach-o-file will replace the LC_SEGMENT(__RESTRICT,__restrict)\n"
45 | " with LC_SEGMENT(__restrict,__restrict) to close dylib inject protection\n"
46 | " -j Json file containing extra symbol info, the key is \"name\",\"address\"\n like this:\n \n"
47 | " [\n {\n \"name\": \"main\", \n \"address\": \"0xXXXXXX\"\n }, \n {\n \"name\": \"-[XXXX XXXXX]\", \n \"address\": \"0xXXXXXX\"\n },\n .... \n ]\n"
48 |
49 | ,
50 | RESTORE_SYMBOL_VERSION
51 | );
52 | }
53 |
54 |
55 |
56 | void restore_symbol(NSString * inpath, NSString *outpath, NSString *jsonPath, bool oc_detect_enable, bool replace_restrict);
57 |
58 | int main(int argc, char * argv[]) {
59 |
60 |
61 |
62 |
63 | bool oc_detect_enable = true;
64 | bool replace_restrict = false;
65 | NSString *inpath = nil;
66 | NSString * outpath = nil;
67 | NSString *jsonPath = nil;
68 |
69 | BOOL shouldPrintVersion = NO;
70 |
71 | int ch;
72 |
73 | struct option longopts[] = {
74 | { "disable-oc-detect", no_argument, NULL, RS_OPT_DISABLE_OC_DETECT },
75 | { "output", required_argument, NULL, 'o' },
76 | { "json", required_argument, NULL, 'j' },
77 | { "version", no_argument, NULL, RS_OPT_VERSION },
78 | { "replace-restrict", no_argument, NULL, RS_OPT_REPLACE_RESTRICT },
79 |
80 | { NULL, 0, NULL, 0 },
81 |
82 | };
83 |
84 |
85 | if (argc == 1) {
86 | print_usage();
87 | exit(0);
88 | }
89 |
90 |
91 |
92 | while ( (ch = getopt_long(argc, argv, "o:j:", longopts, NULL)) != -1) {
93 | switch (ch) {
94 | case 'o':
95 | outpath = [NSString stringWithUTF8String:optarg];
96 | break;
97 | case 'j':
98 | jsonPath = [NSString stringWithUTF8String:optarg];
99 | break;
100 |
101 | case RS_OPT_VERSION:
102 | shouldPrintVersion = YES;
103 | break;
104 |
105 | case RS_OPT_DISABLE_OC_DETECT:
106 | oc_detect_enable = false;
107 | break;
108 |
109 | case RS_OPT_REPLACE_RESTRICT:
110 | replace_restrict = true;
111 | break;
112 | default:
113 | break;
114 | }
115 | }
116 |
117 | if (shouldPrintVersion) {
118 | printf("restore-symbol %s compiled %s\n", RESTORE_SYMBOL_VERSION, __DATE__ " " __TIME__);
119 | exit(0);
120 | }
121 |
122 | if (optind < argc) {
123 | inpath = [NSString stringWithUTF8String:argv[optind]];
124 | }
125 |
126 |
127 | restore_symbol(inpath, outpath, jsonPath, oc_detect_enable, replace_restrict);
128 |
129 | }
--------------------------------------------------------------------------------
/search_oc_block/ida_search_block.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import idautils
4 | import idc
5 | from idaapi import PluginForm
6 | import operator
7 | import csv
8 | import sys
9 | import json
10 |
11 |
12 | IS32BIT = not idaapi.get_inf_structure().is_64bit()
13 |
14 | IS_MAC = 'X86_64' in idaapi.get_file_type_name()
15 |
16 | print "Start analyze binary for " + ("Mac" if IS_MAC else "iOS")
17 |
18 |
19 | def isInText(x):
20 | return SegName(x) == '__text'
21 |
22 |
23 | GlobalBlockAddr = LocByName("__NSConcreteGlobalBlock")
24 |
25 | class GlobalBlockInfo:
26 | pass
27 |
28 | AllGlobalBlockMap = {}
29 | for struct in list(DataRefsTo(GlobalBlockAddr)):
30 | func = 0L
31 | FUNC_OFFSET_IN_BLOCK = 12 if IS32BIT else 16
32 | if IS32BIT:
33 | func = Dword(struct + FUNC_OFFSET_IN_BLOCK)
34 | else:
35 | func = Qword(struct + FUNC_OFFSET_IN_BLOCK)
36 |
37 |
38 | info = GlobalBlockInfo()
39 | info.func = func
40 | info.struct = struct
41 | if len(list(DataRefsTo(struct))) == 0:
42 | continue
43 | refTo = list(DataRefsTo(struct))[0]
44 | info.superFuncName = GetFunctionName(refTo)
45 | info.superFunc = LocByName(info.superFuncName)
46 |
47 | AllGlobalBlockMap[func] = info
48 |
49 | def funcIsGlobalBlockFunc(block_func):
50 | return block_func in AllGlobalBlockMap
51 |
52 |
53 | def isPossibleStackBlockForFunc(block_func):
54 | # def superFuncForStackBlock(block_func):
55 |
56 | if not isInText(block_func):
57 | return False
58 |
59 | if GetFunctionAttr(block_func,FUNCATTR_START) != (block_func & ~ 1):
60 | return False
61 |
62 | #block addr cannot be called directly
63 | if len(list(CodeRefsTo(block_func, 0))) !=0 :
64 | # print '%x is not block because be call by %x' % (block_func ,list(CodeRefsTo(block_func, 0))[0])
65 | return False
66 |
67 | # ref to block should be in text section
68 | refsTo = list(DataRefsTo(block_func))
69 | for addr in refsTo:
70 | if not isInText(addr):
71 | # print '%x is not block because be ref from %x' % (block_func, addr)
72 | return False
73 |
74 | # block func should be ref in only 1 function
75 | superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo]
76 | superFuncs = list (set (superFuncs))
77 | if len(superFuncs) != 1:
78 | # print '%x is not block because be not ref from 1 function' % block_func
79 | return False
80 |
81 | return True
82 |
83 | def superFuncForStackBlock(block_func):
84 | refsTo = list(DataRefsTo(block_func))
85 | superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo]
86 | superFuncs = list (set (superFuncs))
87 | if len(superFuncs) != 1:
88 | return None
89 | super_func_addr = superFuncs[0]
90 | if IS_MAC:
91 | return super_func_addr
92 | else:
93 | return super_func_addr | GetReg(super_func_addr, "T") # thumb
94 |
95 |
96 | def superFuncForBlockFunc(block_func):
97 | if funcIsGlobalBlockFunc(block_func):
98 | return AllGlobalBlockMap[block_func].superFunc
99 |
100 | superStackFunc = superFuncForStackBlock(block_func)
101 | return superStackFunc # maybe None
102 |
103 |
104 |
105 | resultDict = {}
106 |
107 |
108 | def findBlockName(block_func):
109 | # print "find block name %X" % block_func
110 | funcName = GetFunctionName(block_func)
111 |
112 | if len(funcName) != 0 and funcName[0] in ('-', '+'):
113 | return funcName
114 |
115 | # maybe nested block
116 | superBlockFuncAddr = superFuncForBlockFunc(block_func)
117 | if superBlockFuncAddr == None:
118 | return "";
119 | if not IS_MAC:
120 | superBlockFuncAddr = superBlockFuncAddr | GetReg(superBlockFuncAddr, "T") # thumb
121 |
122 | superBlockName = findBlockName(superBlockFuncAddr)
123 |
124 | if len(superBlockName) == 0:
125 | return ""
126 | else:
127 | return superBlockName + "_block"
128 |
129 |
130 |
131 | #find all possible Stack Block
132 | allPossibleStackBlockFunc = []
133 | allRefToBlock=[]
134 | if IS32BIT:
135 | allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock")))
136 | else:
137 | allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock_ptr")))
138 | allRefToBlock.sort()
139 |
140 | '''
141 | 2 ref (@PAGE , @PAGEOFF) to __NSConcreteStackBlock_ptr ,
142 | but once actual
143 | filter the list
144 | __text:0000000102D9979C ADRP X8, #__NSConcreteStackBlock_ptr@PAGE
145 | __text:0000000102D997A0 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF]
146 | '''
147 | tmp_array = allRefToBlock[:1]
148 | for i in range(1, len(allRefToBlock)):
149 | if allRefToBlock[i] - allRefToBlock[i - 1] <= 8:
150 | pass
151 | else:
152 | tmp_array.append(allRefToBlock[i])
153 | allRefToBlock = tmp_array
154 |
155 | allRefToBlock = filter(lambda x:isInText(x), allRefToBlock)
156 |
157 | for addr in allRefToBlock:
158 | LineNumAround = 30 #Around 30 arm instruction
159 | scan_addr_min= max (addr - LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_START))
160 | scan_addr_max= min (addr + LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_END))
161 | for scan_addr in range(scan_addr_min, scan_addr_max):
162 | allPossibleStackBlockFunc += list(DataRefsFrom(scan_addr)) # all function pointer used around __NSConcreteStackBlock
163 |
164 | allPossibleStackBlockFunc = list (set (allPossibleStackBlockFunc))
165 |
166 | allPossibleStackBlockFunc = filter(lambda x:isPossibleStackBlockForFunc(x) , allPossibleStackBlockFunc )
167 |
168 |
169 |
170 |
171 | #process all Global Block
172 | for block_func in AllGlobalBlockMap:
173 | block_name = findBlockName(block_func)
174 | resultDict[block_func] = block_name
175 |
176 | for block_func in allPossibleStackBlockFunc:
177 | block_name = findBlockName(block_func)
178 | resultDict[block_func] = block_name
179 |
180 |
181 | output_file = './block_symbol.json'
182 | list_output = []
183 | error_num = 0
184 | for addr in resultDict:
185 | name = resultDict[addr]
186 | if len(name) == 0 or name[0] not in ('-', '+'):
187 | error_num += 1
188 | continue
189 |
190 | list_output += [{"address":("0x%X" % addr), "name":name}]
191 |
192 |
193 | encodeJson = json.dumps(list_output, indent=1)
194 | f = open(output_file, "w")
195 | f.write(encodeJson)
196 | f.close()
197 |
198 | print 'restore block num %d ' % len(list_output)
199 | print 'origin block num: %d(GlobalBlock: %d, StackBlock: %d)' % (len(allRefToBlock) + len(AllGlobalBlockMap), len(AllGlobalBlockMap), len(allRefToBlock))
200 |
--------------------------------------------------------------------------------
/source/restore-symbol.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // restore-symbol
4 | //
5 | // Created by EugeneYang on 16/8/16.
6 | //
7 | //
8 |
9 | #import
10 | #include
11 | #include
12 | #import "CDFile.h"
13 | #import "CDMachOFile.h"
14 | #import "CDLCSymbolTable.h"
15 | #import "CDLCSegment.h"
16 | #import "CDSymbol.h"
17 | #import "CDLCDynamicSymbolTable.h"
18 | #import "CDLCLinkeditData.h"
19 | #import "CDClassDump.h"
20 | #import "CDFindMethodVisitor.h"
21 | #import "RSScanMethodVisitor.h"
22 | #import "CDFatFile.h"
23 |
24 |
25 |
26 | #define IntSize (Is32Bit? sizeof(uint32_t) : sizeof(uint64_t) )
27 | #define NListSize (Is32Bit? sizeof(struct nlist) : sizeof(struct nlist_64) )
28 |
29 |
30 | #define vm_addr_round(v,r) ( (v + (r-1) ) & (-r) )
31 |
32 |
33 |
34 |
35 |
36 | void restore_symbol(NSString * inpath, NSString *outpath, NSString *jsonPath, bool oc_detect_enable, bool replace_restrict){
37 |
38 |
39 |
40 |
41 | if (![[NSFileManager defaultManager] fileExistsAtPath:inpath]) {
42 | fprintf(stderr, "Error: Input file doesn't exist!\n");
43 | exit(1);
44 | }
45 |
46 | if (jsonPath.length != 0 && ![[NSFileManager defaultManager] fileExistsAtPath:jsonPath]) {
47 | fprintf(stderr, "Error: Json file doesn't exist!\n");
48 | exit(1);
49 | }
50 |
51 |
52 | if ([outpath length] == 0) {
53 | fprintf(stderr, "Error: No output file path!\n");
54 | exit(1);
55 | }
56 |
57 | if ([[NSFileManager defaultManager] fileExistsAtPath:outpath]) {
58 | fprintf(stderr, "Error: Output file has exist!\n");
59 | exit(1);
60 | }
61 |
62 | fprintf(stderr, "=========== Start =============\n");
63 |
64 |
65 | NSMutableData * outData = [[NSMutableData alloc] initWithContentsOfFile:inpath];
66 |
67 | CDFile * ofile = [CDFile fileWithContentsOfFile:inpath searchPathState:nil];
68 |
69 | if ([ofile isKindOfClass:[CDFatFile class]] ) {
70 | fprintf(stderr,"Restore-symbol supports armv7 and arm64 archtecture, but not support fat file. Please use lipo to thin the image file first.");
71 | exit(1);
72 | }
73 |
74 |
75 | CDMachOFile * machOFile = (CDMachOFile *)ofile;
76 | const bool Is32Bit = ! machOFile.uses64BitABI;
77 |
78 |
79 | RSSymbolCollector *collector = [RSSymbolCollector new];
80 | collector.machOFile = machOFile;
81 |
82 | if (oc_detect_enable) {
83 | fprintf(stderr, "Scan OC method in mach-o-file.\n");
84 |
85 | CDClassDump *classDump = [[CDClassDump alloc] init];
86 | CDArch targetArch;
87 | if ([machOFile bestMatchForLocalArch:&targetArch] == NO) {
88 | fprintf(stderr, "Error: Couldn't get local architecture!\n");
89 | exit(1);
90 | }
91 | classDump.targetArch = targetArch;
92 | [classDump processObjectiveCData];
93 | [classDump registerTypes];
94 |
95 | NSError *error;
96 | if (![classDump loadFile:machOFile error:&error]) {
97 | fprintf(stderr, "Error: %s\n", [[error localizedFailureReason] UTF8String]);
98 | exit(1);
99 | } else {
100 | [classDump processObjectiveCData];
101 | [classDump registerTypes];
102 |
103 | RSScanMethodVisitor *visitor = [[RSScanMethodVisitor alloc] initWithSymbolCollector:collector];
104 | visitor.classDump = classDump;
105 | [classDump recursivelyVisit:visitor];
106 |
107 | }
108 |
109 | fprintf(stderr, "Scan OC method finish.\n");
110 | }
111 |
112 |
113 | if (jsonPath != nil && jsonPath.length != 0) {
114 | fprintf(stderr, "Parse symbols in json file.\n");
115 | NSData * jsonData = [NSData dataWithContentsOfFile:jsonPath];
116 | if (jsonData == nil) {
117 | fprintf(stderr, "Can't load json data.\n");
118 | exit(1);
119 | }
120 | NSArray *jsonSymbols = [RSSymbol symbolsWithJson:jsonData];
121 | if (jsonSymbols == nil) {
122 | fprintf(stderr,"Error: Json file cann't parse!");
123 | exit(1);
124 | } else {
125 | [collector addSymbols:jsonSymbols];
126 | }
127 | fprintf(stderr, "Parse finish.\n");
128 | }
129 |
130 |
131 | NSData *string_table_append_data = nil;
132 | NSData *symbol_table_append_data = nil;
133 | [collector generateAppendStringTable:&string_table_append_data appendSymbolTable:&symbol_table_append_data];
134 |
135 |
136 | uint32 increase_symbol_num = (uint32)collector.symbols.count;
137 | uint32 increase_size_string_tab = (uint32)string_table_append_data.length;
138 | uint32 increase_size_symtab = (uint32)symbol_table_append_data.length;
139 | uint32 increase_size_all_without_padding = increase_size_symtab + increase_size_string_tab;
140 |
141 |
142 |
143 | uint32 origin_string_table_offset = machOFile.symbolTable.stroff;
144 | uint32 origin_string_table_size = machOFile.symbolTable.strsize;
145 | uint32 origin_symbol_table_offset = machOFile.symbolTable.symoff;
146 | uint32 origin_symbol_table_num = machOFile.symbolTable.nsyms;
147 |
148 |
149 | if (replace_restrict){
150 | CDLCSegment * restrict_seg = [machOFile segmentWithName:@"__RESTRICT"];
151 |
152 | struct segment_command *restrict_seg_cmd = (struct segment_command *)((char *)outData.mutableBytes + restrict_seg.commandOffset);
153 | struct section *restrict_section = NULL;
154 |
155 | int cmd_size = (Is32Bit? sizeof(struct segment_command) : sizeof(struct segment_command_64));
156 | if (restrict_seg.cmdsize > cmd_size) {
157 | restrict_section = (struct section *)((char *)outData.mutableBytes + restrict_seg.commandOffset + cmd_size);
158 | }
159 |
160 | if (restrict_seg && restrict_section) {
161 | fprintf(stderr, "rename segment __RESTRICT in mach-o header.\n");
162 | strncpy(restrict_seg_cmd -> segname, "__restrict", 16);
163 | strncpy(restrict_section -> segname, "__restrict", 16);
164 | } else {
165 | fprintf(stderr, "No section (__RESTRICT,__restrict) in mach-o header.\n");
166 | }
167 |
168 | }
169 |
170 | //LC_CODE_SIGNATURE need align 16 byte, so add padding at end of string table.
171 | uint32 string_table_padding = 0;
172 | {
173 | CDLCLinkeditData * codesignature = nil;
174 | for (CDLoadCommand *command in machOFile.loadCommands) {
175 | if (command.cmd == LC_CODE_SIGNATURE){
176 | codesignature = (CDLCLinkeditData *)command;
177 | }
178 | }
179 |
180 | if (codesignature) {
181 | struct linkedit_data_command *command = (struct linkedit_data_command *)((char *)outData.mutableBytes + codesignature.commandOffset);
182 | uint32_t tmp_offset = command -> dataoff + increase_size_all_without_padding;
183 | uint32_t final_offset = vm_addr_round(tmp_offset, 16);
184 |
185 | string_table_padding = final_offset - tmp_offset;
186 | command -> dataoff = final_offset;
187 | }
188 | }
189 |
190 |
191 | {
192 | CDLCSymbolTable *symtab = [machOFile symbolTable];
193 | struct symtab_command *symtab_out = (struct symtab_command *)((char *)outData.mutableBytes + symtab.commandOffset);
194 | symtab_out -> nsyms += increase_symbol_num;
195 | symtab_out -> stroff += increase_size_symtab;
196 | symtab_out -> strsize += increase_size_string_tab + string_table_padding;
197 | }
198 |
199 | {
200 | CDLCDynamicSymbolTable *dysymtabCommand = [machOFile dynamicSymbolTable];
201 | struct dysymtab_command *command = (struct dysymtab_command *)((char *)outData.mutableBytes + dysymtabCommand.commandOffset);
202 | command -> indirectsymoff += increase_size_symtab;
203 | }
204 |
205 |
206 |
207 | {
208 | CDLCSegment * linkeditSegment = [machOFile segmentWithName:@"__LINKEDIT"];
209 | if (Is32Bit) {
210 | struct segment_command *linkedit_segment_command = (struct segment_command *)((char *)outData.mutableBytes + linkeditSegment.commandOffset);
211 | linkedit_segment_command -> filesize += increase_size_all_without_padding + string_table_padding;
212 | linkedit_segment_command -> vmsize = (uint32) vm_addr_round((linkedit_segment_command -> filesize), 0x4000);
213 |
214 |
215 | } else {
216 |
217 | struct segment_command_64 *linkedit_segment_command = (struct segment_command_64 *)((char *)outData.mutableBytes + linkeditSegment.commandOffset);
218 | linkedit_segment_command -> filesize += increase_size_all_without_padding + string_table_padding;
219 | linkedit_segment_command -> vmsize = vm_addr_round((linkedit_segment_command -> filesize), 0x4000);
220 | }
221 | }
222 |
223 |
224 |
225 |
226 | // must first insert string
227 | [outData replaceBytesInRange:NSMakeRange(origin_string_table_offset + origin_string_table_size , 0) withBytes:(const void *)string_table_append_data.bytes length:increase_size_string_tab + string_table_padding];
228 |
229 | [outData replaceBytesInRange:NSMakeRange(origin_symbol_table_offset + origin_symbol_table_num * NListSize , 0) withBytes:(const void *)symbol_table_append_data.bytes length:increase_size_symtab];
230 |
231 | NSError * err = nil;
232 | [outData writeToFile:outpath options:NSDataWritingWithoutOverwriting error:&err];
233 |
234 | if (!err) {
235 | chmod(outpath.UTF8String, 0755);
236 | }else{
237 | fprintf(stderr,"Write file error : %s", [err localizedDescription].UTF8String);
238 | return;
239 | }
240 |
241 |
242 | fprintf(stderr,"=========== Finish ============\n");
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/restore-symbol.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8F92BBCA1D6B7085008EA4C4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBC91D6B7085008EA4C4 /* main.m */; };
11 | 8F92BBE31D6B70C7008EA4C4 /* libMachObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */; };
12 | 8F92BBEF1D6B7265008EA4C4 /* restore-symbol.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */; };
13 | 8F92BBF01D6B7265008EA4C4 /* RSScanMethodVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */; };
14 | 8F92BBF11D6B7265008EA4C4 /* RSSymbol.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */; };
15 | 8F92BBF21D6B7265008EA4C4 /* RSSymbolCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */; };
16 | 8F92BBF51D6B72E1008EA4C4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */; };
17 | 8F92BBF91D6B7445008EA4C4 /* CDClassDump.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */; };
18 | 8F92BBFD1D6B750E008EA4C4 /* CDVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */; };
19 | 8F92BBFF1D6B758A008EA4C4 /* CDTypeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */; };
20 | 8F92BC021D6B78FD008EA4C4 /* CDSearchPathState.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */; };
21 | 8F92BC041D6B790C008EA4C4 /* CDStructureTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */; };
22 | 8F92BC061D6B791D008EA4C4 /* CDStructureInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXContainerItemProxy section */
26 | 8F92BBD91D6B70A3008EA4C4 /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
29 | proxyType = 2;
30 | remoteGlobalIDString = 013D1F1113A5AE5A00BF0A67;
31 | remoteInfo = MachObjC;
32 | };
33 | 8F92BBDB1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = {
34 | isa = PBXContainerItemProxy;
35 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
36 | proxyType = 2;
37 | remoteGlobalIDString = 01EB825F13A590D9003EDE60;
38 | remoteInfo = "class-dump";
39 | };
40 | 8F92BBDD1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = {
41 | isa = PBXContainerItemProxy;
42 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
43 | proxyType = 2;
44 | remoteGlobalIDString = 013D1EFB13A5A0F100BF0A67;
45 | remoteInfo = deprotect;
46 | };
47 | 8F92BBDF1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = {
48 | isa = PBXContainerItemProxy;
49 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
50 | proxyType = 2;
51 | remoteGlobalIDString = 01B02CFF13A5B0DC0047BC53;
52 | remoteInfo = formatType;
53 | };
54 | 8F92BBE11D6B70A3008EA4C4 /* PBXContainerItemProxy */ = {
55 | isa = PBXContainerItemProxy;
56 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
57 | proxyType = 2;
58 | remoteGlobalIDString = 0165B8B11827137D00CC647F;
59 | remoteInfo = UnitTests;
60 | };
61 | 8F92BBF61D6B73BD008EA4C4 /* PBXContainerItemProxy */ = {
62 | isa = PBXContainerItemProxy;
63 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
64 | proxyType = 1;
65 | remoteGlobalIDString = 013D1F1013A5AE5A00BF0A67;
66 | remoteInfo = MachObjC;
67 | };
68 | /* End PBXContainerItemProxy section */
69 |
70 | /* Begin PBXCopyFilesBuildPhase section */
71 | 8F92BBC41D6B7085008EA4C4 /* CopyFiles */ = {
72 | isa = PBXCopyFilesBuildPhase;
73 | buildActionMask = 2147483647;
74 | dstPath = /usr/share/man/man1/;
75 | dstSubfolderSpec = 0;
76 | files = (
77 | );
78 | runOnlyForDeploymentPostprocessing = 1;
79 | };
80 | /* End PBXCopyFilesBuildPhase section */
81 |
82 | /* Begin PBXFileReference section */
83 | 8F92BBC61D6B7085008EA4C4 /* restore-symbol */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "restore-symbol"; sourceTree = BUILT_PRODUCTS_DIR; };
84 | 8F92BBC91D6B7085008EA4C4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
85 | 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "class-dump.xcodeproj"; path = "class-dump/class-dump.xcodeproj"; sourceTree = ""; };
86 | 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "restore-symbol.m"; sourceTree = ""; };
87 | 8F92BBE81D6B7265008EA4C4 /* RSScanMethodVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSScanMethodVisitor.h; sourceTree = ""; };
88 | 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSScanMethodVisitor.m; sourceTree = ""; };
89 | 8F92BBEA1D6B7265008EA4C4 /* RSSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSymbol.h; sourceTree = ""; };
90 | 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSymbol.m; sourceTree = ""; };
91 | 8F92BBEC1D6B7265008EA4C4 /* RSSymbolCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSymbolCollector.h; sourceTree = ""; };
92 | 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSymbolCollector.m; sourceTree = ""; };
93 | 8F92BBF31D6B72C6008EA4C4 /* restore-symbol.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "restore-symbol.pch"; sourceTree = ""; };
94 | 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
95 | 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDClassDump.m; path = "../class-dump/Source/CDClassDump.m"; sourceTree = ""; };
96 | 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVisitor.m; path = "../class-dump/Source/CDVisitor.m"; sourceTree = ""; };
97 | 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDTypeController.m; path = "../class-dump/Source/CDTypeController.m"; sourceTree = ""; };
98 | 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDSearchPathState.m; path = "../class-dump/Source/CDSearchPathState.m"; sourceTree = ""; };
99 | 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDStructureTable.m; path = "../class-dump/Source/CDStructureTable.m"; sourceTree = ""; };
100 | 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDStructureInfo.m; path = "../class-dump/Source/CDStructureInfo.m"; sourceTree = ""; };
101 | /* End PBXFileReference section */
102 |
103 | /* Begin PBXFrameworksBuildPhase section */
104 | 8F92BBC31D6B7085008EA4C4 /* Frameworks */ = {
105 | isa = PBXFrameworksBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | 8F92BBF51D6B72E1008EA4C4 /* Foundation.framework in Frameworks */,
109 | 8F92BBE31D6B70C7008EA4C4 /* libMachObjC.a in Frameworks */,
110 | );
111 | runOnlyForDeploymentPostprocessing = 0;
112 | };
113 | /* End PBXFrameworksBuildPhase section */
114 |
115 | /* Begin PBXGroup section */
116 | 8F92BBBD1D6B7085008EA4C4 = {
117 | isa = PBXGroup;
118 | children = (
119 | 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */,
120 | 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */,
121 | 8F92BBC81D6B7085008EA4C4 /* source */,
122 | 8F92BBC71D6B7085008EA4C4 /* Products */,
123 | );
124 | sourceTree = "";
125 | };
126 | 8F92BBC71D6B7085008EA4C4 /* Products */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 8F92BBC61D6B7085008EA4C4 /* restore-symbol */,
130 | );
131 | name = Products;
132 | sourceTree = "";
133 | };
134 | 8F92BBC81D6B7085008EA4C4 /* source */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 8F92BC001D6B789F008EA4C4 /* ref-to-class-dump */,
138 | 8F92BBE81D6B7265008EA4C4 /* RSScanMethodVisitor.h */,
139 | 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */,
140 | 8F92BBEA1D6B7265008EA4C4 /* RSSymbol.h */,
141 | 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */,
142 | 8F92BBEC1D6B7265008EA4C4 /* RSSymbolCollector.h */,
143 | 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */,
144 | 8F92BBC91D6B7085008EA4C4 /* main.m */,
145 | 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */,
146 | 8F92BBF31D6B72C6008EA4C4 /* restore-symbol.pch */,
147 | );
148 | path = source;
149 | sourceTree = "";
150 | };
151 | 8F92BBD11D6B70A3008EA4C4 /* Products */ = {
152 | isa = PBXGroup;
153 | children = (
154 | 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */,
155 | 8F92BBDC1D6B70A3008EA4C4 /* class-dump */,
156 | 8F92BBDE1D6B70A3008EA4C4 /* deprotect */,
157 | 8F92BBE01D6B70A3008EA4C4 /* formatType */,
158 | 8F92BBE21D6B70A3008EA4C4 /* UnitTests.xctest */,
159 | );
160 | name = Products;
161 | sourceTree = "";
162 | };
163 | 8F92BC001D6B789F008EA4C4 /* ref-to-class-dump */ = {
164 | isa = PBXGroup;
165 | children = (
166 | 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */,
167 | 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */,
168 | 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */,
169 | 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */,
170 | 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */,
171 | 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */,
172 | );
173 | name = "ref-to-class-dump";
174 | sourceTree = "";
175 | };
176 | /* End PBXGroup section */
177 |
178 | /* Begin PBXNativeTarget section */
179 | 8F92BBC51D6B7085008EA4C4 /* restore-symbol */ = {
180 | isa = PBXNativeTarget;
181 | buildConfigurationList = 8F92BBCD1D6B7085008EA4C4 /* Build configuration list for PBXNativeTarget "restore-symbol" */;
182 | buildPhases = (
183 | 8F92BBC21D6B7085008EA4C4 /* Sources */,
184 | 8F92BBC31D6B7085008EA4C4 /* Frameworks */,
185 | 8F92BBC41D6B7085008EA4C4 /* CopyFiles */,
186 | );
187 | buildRules = (
188 | );
189 | dependencies = (
190 | 8F92BBF71D6B73BD008EA4C4 /* PBXTargetDependency */,
191 | );
192 | name = "restore-symbol";
193 | productName = "restore-symbol";
194 | productReference = 8F92BBC61D6B7085008EA4C4 /* restore-symbol */;
195 | productType = "com.apple.product-type.tool";
196 | };
197 | /* End PBXNativeTarget section */
198 |
199 | /* Begin PBXProject section */
200 | 8F92BBBE1D6B7085008EA4C4 /* Project object */ = {
201 | isa = PBXProject;
202 | attributes = {
203 | LastUpgradeCheck = 0730;
204 | ORGANIZATIONNAME = Jun;
205 | TargetAttributes = {
206 | 8F92BBC51D6B7085008EA4C4 = {
207 | CreatedOnToolsVersion = 7.3.1;
208 | };
209 | };
210 | };
211 | buildConfigurationList = 8F92BBC11D6B7085008EA4C4 /* Build configuration list for PBXProject "restore-symbol" */;
212 | compatibilityVersion = "Xcode 3.2";
213 | developmentRegion = English;
214 | hasScannedForEncodings = 0;
215 | knownRegions = (
216 | en,
217 | );
218 | mainGroup = 8F92BBBD1D6B7085008EA4C4;
219 | productRefGroup = 8F92BBC71D6B7085008EA4C4 /* Products */;
220 | projectDirPath = "";
221 | projectReferences = (
222 | {
223 | ProductGroup = 8F92BBD11D6B70A3008EA4C4 /* Products */;
224 | ProjectRef = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */;
225 | },
226 | );
227 | projectRoot = "";
228 | targets = (
229 | 8F92BBC51D6B7085008EA4C4 /* restore-symbol */,
230 | );
231 | };
232 | /* End PBXProject section */
233 |
234 | /* Begin PBXReferenceProxy section */
235 | 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */ = {
236 | isa = PBXReferenceProxy;
237 | fileType = archive.ar;
238 | path = libMachObjC.a;
239 | remoteRef = 8F92BBD91D6B70A3008EA4C4 /* PBXContainerItemProxy */;
240 | sourceTree = BUILT_PRODUCTS_DIR;
241 | };
242 | 8F92BBDC1D6B70A3008EA4C4 /* class-dump */ = {
243 | isa = PBXReferenceProxy;
244 | fileType = "compiled.mach-o.executable";
245 | path = "class-dump";
246 | remoteRef = 8F92BBDB1D6B70A3008EA4C4 /* PBXContainerItemProxy */;
247 | sourceTree = BUILT_PRODUCTS_DIR;
248 | };
249 | 8F92BBDE1D6B70A3008EA4C4 /* deprotect */ = {
250 | isa = PBXReferenceProxy;
251 | fileType = "compiled.mach-o.executable";
252 | path = deprotect;
253 | remoteRef = 8F92BBDD1D6B70A3008EA4C4 /* PBXContainerItemProxy */;
254 | sourceTree = BUILT_PRODUCTS_DIR;
255 | };
256 | 8F92BBE01D6B70A3008EA4C4 /* formatType */ = {
257 | isa = PBXReferenceProxy;
258 | fileType = "compiled.mach-o.executable";
259 | path = formatType;
260 | remoteRef = 8F92BBDF1D6B70A3008EA4C4 /* PBXContainerItemProxy */;
261 | sourceTree = BUILT_PRODUCTS_DIR;
262 | };
263 | 8F92BBE21D6B70A3008EA4C4 /* UnitTests.xctest */ = {
264 | isa = PBXReferenceProxy;
265 | fileType = wrapper.cfbundle;
266 | path = UnitTests.xctest;
267 | remoteRef = 8F92BBE11D6B70A3008EA4C4 /* PBXContainerItemProxy */;
268 | sourceTree = BUILT_PRODUCTS_DIR;
269 | };
270 | /* End PBXReferenceProxy section */
271 |
272 | /* Begin PBXSourcesBuildPhase section */
273 | 8F92BBC21D6B7085008EA4C4 /* Sources */ = {
274 | isa = PBXSourcesBuildPhase;
275 | buildActionMask = 2147483647;
276 | files = (
277 | 8F92BBEF1D6B7265008EA4C4 /* restore-symbol.m in Sources */,
278 | 8F92BC041D6B790C008EA4C4 /* CDStructureTable.m in Sources */,
279 | 8F92BBCA1D6B7085008EA4C4 /* main.m in Sources */,
280 | 8F92BBF01D6B7265008EA4C4 /* RSScanMethodVisitor.m in Sources */,
281 | 8F92BC061D6B791D008EA4C4 /* CDStructureInfo.m in Sources */,
282 | 8F92BBF21D6B7265008EA4C4 /* RSSymbolCollector.m in Sources */,
283 | 8F92BBF11D6B7265008EA4C4 /* RSSymbol.m in Sources */,
284 | 8F92BBF91D6B7445008EA4C4 /* CDClassDump.m in Sources */,
285 | 8F92BC021D6B78FD008EA4C4 /* CDSearchPathState.m in Sources */,
286 | 8F92BBFF1D6B758A008EA4C4 /* CDTypeController.m in Sources */,
287 | 8F92BBFD1D6B750E008EA4C4 /* CDVisitor.m in Sources */,
288 | );
289 | runOnlyForDeploymentPostprocessing = 0;
290 | };
291 | /* End PBXSourcesBuildPhase section */
292 |
293 | /* Begin PBXTargetDependency section */
294 | 8F92BBF71D6B73BD008EA4C4 /* PBXTargetDependency */ = {
295 | isa = PBXTargetDependency;
296 | name = MachObjC;
297 | targetProxy = 8F92BBF61D6B73BD008EA4C4 /* PBXContainerItemProxy */;
298 | };
299 | /* End PBXTargetDependency section */
300 |
301 | /* Begin XCBuildConfiguration section */
302 | 8F92BBCB1D6B7085008EA4C4 /* Debug */ = {
303 | isa = XCBuildConfiguration;
304 | buildSettings = {
305 | ALWAYS_SEARCH_USER_PATHS = NO;
306 | CLANG_ANALYZER_NONNULL = YES;
307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
308 | CLANG_CXX_LIBRARY = "libc++";
309 | CLANG_ENABLE_MODULES = YES;
310 | CLANG_ENABLE_OBJC_ARC = YES;
311 | CLANG_WARN_BOOL_CONVERSION = YES;
312 | CLANG_WARN_CONSTANT_CONVERSION = YES;
313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
314 | CLANG_WARN_EMPTY_BODY = YES;
315 | CLANG_WARN_ENUM_CONVERSION = YES;
316 | CLANG_WARN_INT_CONVERSION = YES;
317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
318 | CLANG_WARN_UNREACHABLE_CODE = YES;
319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
320 | CODE_SIGN_IDENTITY = "-";
321 | COPY_PHASE_STRIP = NO;
322 | DEBUG_INFORMATION_FORMAT = dwarf;
323 | ENABLE_STRICT_OBJC_MSGSEND = YES;
324 | ENABLE_TESTABILITY = YES;
325 | GCC_C_LANGUAGE_STANDARD = gnu99;
326 | GCC_DYNAMIC_NO_PIC = NO;
327 | GCC_NO_COMMON_BLOCKS = YES;
328 | GCC_OPTIMIZATION_LEVEL = 0;
329 | GCC_PREPROCESSOR_DEFINITIONS = (
330 | "DEBUG=1",
331 | "$(inherited)",
332 | );
333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
335 | GCC_WARN_UNDECLARED_SELECTOR = YES;
336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
337 | GCC_WARN_UNUSED_FUNCTION = YES;
338 | GCC_WARN_UNUSED_VARIABLE = YES;
339 | MACOSX_DEPLOYMENT_TARGET = 10.11;
340 | MTL_ENABLE_DEBUG_INFO = YES;
341 | ONLY_ACTIVE_ARCH = YES;
342 | SDKROOT = macosx;
343 | };
344 | name = Debug;
345 | };
346 | 8F92BBCC1D6B7085008EA4C4 /* Release */ = {
347 | isa = XCBuildConfiguration;
348 | buildSettings = {
349 | ALWAYS_SEARCH_USER_PATHS = NO;
350 | CLANG_ANALYZER_NONNULL = YES;
351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
352 | CLANG_CXX_LIBRARY = "libc++";
353 | CLANG_ENABLE_MODULES = YES;
354 | CLANG_ENABLE_OBJC_ARC = YES;
355 | CLANG_WARN_BOOL_CONVERSION = YES;
356 | CLANG_WARN_CONSTANT_CONVERSION = YES;
357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
358 | CLANG_WARN_EMPTY_BODY = YES;
359 | CLANG_WARN_ENUM_CONVERSION = YES;
360 | CLANG_WARN_INT_CONVERSION = YES;
361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
362 | CLANG_WARN_UNREACHABLE_CODE = YES;
363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
364 | CODE_SIGN_IDENTITY = "-";
365 | COPY_PHASE_STRIP = NO;
366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
367 | ENABLE_NS_ASSERTIONS = NO;
368 | ENABLE_STRICT_OBJC_MSGSEND = YES;
369 | GCC_C_LANGUAGE_STANDARD = gnu99;
370 | GCC_NO_COMMON_BLOCKS = YES;
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | MACOSX_DEPLOYMENT_TARGET = 10.11;
378 | MTL_ENABLE_DEBUG_INFO = NO;
379 | SDKROOT = macosx;
380 | };
381 | name = Release;
382 | };
383 | 8F92BBCE1D6B7085008EA4C4 /* Debug */ = {
384 | isa = XCBuildConfiguration;
385 | buildSettings = {
386 | GCC_PREFIX_HEADER = "$(SRCROOT)/source/restore-symbol.pch";
387 | OTHER_LDFLAGS = "-all_load";
388 | PRODUCT_NAME = "$(TARGET_NAME)";
389 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/class-dump/Source/**";
390 | };
391 | name = Debug;
392 | };
393 | 8F92BBCF1D6B7085008EA4C4 /* Release */ = {
394 | isa = XCBuildConfiguration;
395 | buildSettings = {
396 | GCC_PREFIX_HEADER = "$(SRCROOT)/source/restore-symbol.pch";
397 | OTHER_LDFLAGS = "-all_load";
398 | PRODUCT_NAME = "$(TARGET_NAME)";
399 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/class-dump/Source/**";
400 | };
401 | name = Release;
402 | };
403 | /* End XCBuildConfiguration section */
404 |
405 | /* Begin XCConfigurationList section */
406 | 8F92BBC11D6B7085008EA4C4 /* Build configuration list for PBXProject "restore-symbol" */ = {
407 | isa = XCConfigurationList;
408 | buildConfigurations = (
409 | 8F92BBCB1D6B7085008EA4C4 /* Debug */,
410 | 8F92BBCC1D6B7085008EA4C4 /* Release */,
411 | );
412 | defaultConfigurationIsVisible = 0;
413 | defaultConfigurationName = Release;
414 | };
415 | 8F92BBCD1D6B7085008EA4C4 /* Build configuration list for PBXNativeTarget "restore-symbol" */ = {
416 | isa = XCConfigurationList;
417 | buildConfigurations = (
418 | 8F92BBCE1D6B7085008EA4C4 /* Debug */,
419 | 8F92BBCF1D6B7085008EA4C4 /* Release */,
420 | );
421 | defaultConfigurationIsVisible = 0;
422 | defaultConfigurationName = Release;
423 | };
424 | /* End XCConfigurationList section */
425 | };
426 | rootObject = 8F92BBBE1D6B7085008EA4C4 /* Project object */;
427 | }
428 |
--------------------------------------------------------------------------------