Cocoa反向工程实战SvnX添加历史消息

http://www.cocoachina.com/bbs/read.php?tid-12706.html 
这个帖子中给的附件在10.5上直接解压考过去就可以了,10.6还需改动一个文件以让XCode识别它。见附件。 
解压附件并替换图片中的TemplateChooser.plist. 再重启XCode它就能找到了。 
 
好了,我们一步步开始来进行破解工作。首先你得先准备好F-script, F-Script Anywhere, SvnX, XCode和理论教程中的附件。 
目的:在SvnX的提交信息窗口中添加一个button,点击button会弹出一个窗口,在此窗口中可以选择一条历史消息插入到提交消息窗口中的textview中去。并且没有填写消息就不能提交。 
 
1. 肯定是class-dump出svnx的头文件拉,先放在一旁不管,这个一般是用来查看某个类的变量用,方法呢可以用FScript来查看。 
 
2. 运行SvnX并把FScript载入进去 
10.5直接运行FScriptAnywhere,选择SvnX并点击install。10.6上按照指示用GDB把FScript载入到SvnX中。 
10.6载入成功后SvnX的menu如图二所示,选择Open Project Browser. 
10.5上需点击FScript C*****ole上的Project Browser按钮来打开Project Browser. 
 
3. 打开SvnX的commit窗口 
因为我们想为SvnX的提交功能添加历史提交消息,那肯定是从commit Panel这里入手。 
这里需要讲一下,对于程序破解来说,最难的是要获取到你所感兴趣的那个类的实例,objective-c runtime并没有提供直接获取的功能,我能想到的只有两个办法来获取运行时某个类的实例。 
1) 代码中通过[NSApplication keyWindow]拿到当前窗口,然后遍历contentView,找到感兴趣的控件,难后拿到控件的target或者其他属性。一般来说控件 target都是你感兴趣的类。比如Button,它的action肯定是连接到某个类的,那么它的target就是这个类了~然后就获取到这个类的实例 了~不过需要注意的是当有多个相同类型的控件时需要通过别的条件来拿到你感兴趣的控件噢。 
2)扩展你所感兴趣的类,并重定向其某个方法,然后在这个方法中就可以用self来得到当前类的实例了。 
一般来说第二种方法用得比较多噢~ 
好了,我们目标首先得添加一个button到界面上去。可以看出,提交信息的窗口是一个sheet,所以用第一中方法比较麻烦,主要是你要在这个sheet弹出来之前把添加button的代码触发,所以这里选择第二种方法。 
肯定又一个IBAction来弹出这个sheet,我们就选择重写这个方法。 
这 个sheet肯定是commit button打开的嘛,所以祭出FScript,打开ObjectBrowser,选择commit button,发现他被包含在一个NSMatrix中,如图三,那么就选择这个matrix,倒数第二个嘛,所以选择column7,再选择 element,可以看到这个button的enable使用了绑定技术,如图四。 
查找它的target,发现NSMatrix的target是一个叫做MyWorkingCopyController的东东,那么选择它,经过测试发现有以下三个方法有用 
commitPanelCancel: //commit界面上点击cancel按钮触发 
commitPanelValidate: //commit界面上点击OK按钮触发 
startCommitMessage: // 点击commit按钮以sheet方式触发弹出commit窗口 
 
呵呵,简单吧,一下子就找到了破解入口。 
我只需重定向这三个函数,并在startCommitMessage:中拿到commitPanel,添加一个按钮即可。 
重定向commitPanelCancel: & commitPanelValidate:,在其中保存消息即可,并可以检查消息是否为空。 
 
4)开始编码 
打开XCode,创建一个新工程,10.6选择如图四所示,10.5可以直接选的了~ 
(汗一个,那个模板只在10.5上测试过,家里是10.6的机子,创建后发现有红色文件,大家选择那些文件,重定向到磁盘class文件夹中具体文件即可) 
建好后会有5个文件替你创建好了,打开Info.plist,填入svnx的identifier(打开svnx的info.plist),如图五所示。 
首先得在HostAppClasses中假定义一个MyWorkingCopyController,再扩展它的方法。如一下代码所示 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// HostAppClasses.h
@interface MyWorkingCopyController : NSObject {
}
@end
 
// HostAppClasses.m
@interface MyWorkingCopyController (Private)
// 这三个方法其实是原始方法重定向之后的真实方法,只有在运行时才会存在
- (void) _startCommitMessage:(id) arg1;
- (void) _commitPanelValidate:(id)arg1;
- (void) _commitPanelCancel:(id)arg1;
@end
 
 
SVNXRecentMessage *recentMessageController; // 负责插入的按钮的响应和显示界面的类,具体请查看此贴附件中的代码
// [url=http://www.cocoachina.com/bbs/read.php?tid-13283.html]http://www.cocoachina.com/bbs/read.php?tid-13283.html[/url]
 
@implementation MyWorkingCopyController (Private)
 
// 控制全局变浪
- (void) initMessageController
{
    if(recentMessageController)
        [recentMessageController release];
    recentMessageController = nil;
    recentMessageController = [[SVNXRecentMessage alloc] init];
}
 
// 我们自己的方法来截取默认的所有对此原始方法的调用
- (void) kastartCommitMessage:(id) arg1
{
        // 首先调用默认实现
    [self _startCommitMessage:arg1];
     
        // 这边就用到class-dump出来的头文件拉,查看MyWorkingCopyController的定义可以找到似有类变量,通过
        // objective-c runtime来获取实例
    NSPanel *commitPanel;
    object_getInstanceVariable(self, "commitPanel", &commitPanel);
    if(commitPanel)
    {
        [self initMessageController];
        [recentMessageController setBaseWindow:commitPanel];
        [[recentMessageController recentMessageButton] setFrameOrigin:NSMakePoint(20, 20)];
                // 强势插入我们的按钮
        [[commitPanel contentView] addSubview:[recentMessageController recentMessageButton]];
    }
     
    NSTextView *commitPanelText;
    object_getInstanceVariable(self, "commitPanelText", &commitPanelText);
    if(commitPanelText)
    {
        [recentMessageController setCommitTextView:commitPanelText];
    }
}
 
// 保存填写的消息
- (void) saveCommitMessage
{
    NSTextView *commitPanelText;
    object_getInstanceVariable(self, "commitPanelText", &commitPanelText);
    if(commitPanelText)
    {
        NSString *commitMessage = [[commitPanelText textStorage] string];
        if(![commitMessage isEqualToString:@""] && commitMessage != nil)
        {
            NSData *recentMessageData = [[NSUserDefaults standardUserDefaults] valueForKey:@"RecentMessages"];
            NSMutableDictionary *message = [[NSKeyedUnarchiver unarchiveObjectWithData:recentMessageData] mutableCopy];
            if(!message)
            {
                message = [[NSMutableDictionary alloc] init];
            }
            int count = [message count];
            [message setValue:commitMessage forKey:[NSNumber numberWithInt:count]];
             
            recentMessageData = [[NSUserDefaults standardUserDefaults] valueForKey:@"RecentMessagesTime"];
            NSMutableDictionary *messageTime = [[NSKeyedUnarchiver unarchiveObjectWithData:recentMessageData] mutableCopy];
            if(!messageTime)
            {
                messageTime = [[NSMutableDictionary alloc] init];
            }
            //count = [messageTime count];
            [messageTime setValue:[NSDate date] forKey:[NSNumber numberWithInt:count]];
             
             
            [[NSUserDefaults standardUserDefaults] setValue:[NSKeyedArchiver archivedDataWithRootObject:message]  forKey:@"RecentMessages"];
            [[NSUserDefaults standardUserDefaults] setValue:[NSKeyedArchiver archivedDataWithRootObject:messageTime] forKey:@"RecentMessagesTime"];
            [[NSUserDefaults standardUserDefaults] synchronize];
             
            [message release];
            [messageTime release];
        }
    }
}
// 我们自己的方法来截取默认的所有对此原始方法的调用
- (void) kacommitPanelValidate:(id)arg1
{
        // 首先确定填写了消息,否则返回
    NSTextView *commitPanelText;
    object_getInstanceVariable(self, "commitPanelText", &commitPanelText);
    if(commitPanelText)
    {
        NSString *commitMessage = [[commitPanelText textStorage] string];
        if([commitMessage isEqualToString:@""] || commitMessage == nil)
        {
            NSRunAlertPanel(@"Commit message can't be nil.", @"You should fill in the commit message.", @"OK, I got it.", @"", @"");
            NSString *defaultCommitmessage = @"Fix bugs:\nChanges:\nReviewer:";
            [commitPanelText setString:defaultCommitmessage];
            return;
        }
    }
    [self saveCommitMessage];
        // 然后再调用默认实现
    [self _commitPanelValidate:arg1];
}
// 我们自己的方法来截取默认的所有对此原始方法的调用
- (void) kacommitPanelCancel:(id)arg1
{
    [self saveCommitMessage];
    [self _commitPanelCancel:arg1];
}
 
@end
 
 
其实核心代码都在上面了,剩下的就是在PluginLoader中重定向这三个函数而已,查看工程代码即可。 
SVNXRecentMessage类也很简单,这里就不贴出来了,查看工程代码即可。 
 
编译拷贝到/Library/Application Support/SIMBL/Plugins,运行SvnX,哈哈,成功了吧?没有?那继续看看工程代码吧~ 
 
到此,一个程序的破解就结束了,很简单吧~其实最难得就是寻造漏洞那一步了,svnx还是小程序,大家可以试试iPhoto看看,类面的类和数据结构会搞死你~找它可利用的漏洞是在很耗时间的呀~ 
 
另:我也曾写过一个RDC的SIMBL插件,http://www.cocoachina.com/bbs/read.php?tid-13036.html,大家有兴趣也可以自己尝试破解看看,代码那个帖子中也有噢(我第一次破解不完美,直接更改了窗口的framesize而已,第二次为完美破解)。
[ 此帖被yoyokko在2010-01-10 20:57重新编辑 ]
描述:使用附件中的文件替换默认文件
图片:Screen shot 2010-01-10 at 下午12.48.58.png
描述:载入FSCript成功后的截图
图片:Screen shot 2010-01-10 at 下午01.40.21.png
描述:点击SelectView选择NSMatrix
图片:Screen shot 2010-01-10 at 下午08.12.44.png
描述:选中commit cell
图片:Screen shot 2010-01-10 at 下午08.17.51.png
描述:选择如图所示模板
图片:Screen shot 2010-01-10 at 下午08.26.58.png
描述:修改app identifier
图片:Screen shot 2010-01-10 at 下午08.30.52.png
附件: TemplateChooser.plist.zip (1 K) 下载次数:27
原文地址:https://www.cnblogs.com/ligun123/p/2486937.html