Super Simple Cocoa
Plugin Introduction
nendoki
コレって誰よ
上田 宗一郎(うえだ ソウイチロウ)
id: nendoki (a.k.a ne[tab])
京都大学大学院情報学研究科M1
親の代からMac使い
mac?
スタバドヤ顔マン御用達アイテムNo1
パロアルト研究所からアイディア持ち出したアレ
ディスプレイ上のサイズ=印刷サイズで有名
本日話す事
コレがしたい
前提のお話
今回必要となる技術
実際にアプリを見てみよう
本日話す事
コレがしたい
前提のお話
今回必要となる技術
実際にアプリを見てみよう
Macアプリ作ってますか?
Q. 今使っているプログラム
が*微妙に*機能不足だ
Windows的解法
フリーウェアに
良いのがあるよ!!
富豪的解法
金を払えば良い
プログラマ的解法
自分で作る
FSF的解法
GPLコード片が
埋め込まれていないか探す
よりよい解法
既存のアプリを修正しよう
という事で
Mac用Cocoaアプリケーションに自分で機能を追加
して幸せを得よう
本日話す事
コレがしたい
前提のお話
今回必要となる技術
実際にアプリを見てみよう
前提のお話
Objective-Cってなんそ
ObjC流記法
Cocoa Framework
前提のお話
Objective-Cってなんそ
ObjC流記法
Cocoa Framework
Objective-C!
||
C
+
SmallTalk
SmallTalkな箇所のみ解説します
メッセージ呼び出し
メンバメソッドの呼び出しの事
メッセージ
単項メッセージ
キーワード付きメッセージ
二項メッセージ(SmallTalkのみ
可変長引数や部分的にキーワードの無いメッセージは省略
単項メッセージ
[obj message];
他言語のobj.message()に対応
キーワード付きメッセージ
[obj message:arg0 key1: arg1 key2:arg2 ..];
他言語ではobj.message(arg0...
クラスの定義(ObjC固有)
@interfaceでインターフェース定義(ヘッダー)
@imprementsで実装を定義(ボディー)
インスタンスメソッド
- (型)methodName:(型)arg0 key1:(型)arg1;
クラスメソッ...
@interface AGSampleLayer : CALayer
{
CALayer *floorLayer;
}
+ (id)layer
- (void)resizeWithSize:(CGSize)size;
@end
!
@imple...
カテゴリ
クラス定義を複数回に分けて記述
既存のクラスに動的にメソッド追加が可能
フレームワークのクラスにも可能
実装を機能別に別のヘッダーに…なんてことも
オーバーロードについて
オーバーロードは存在しない
メッセージ名を変える事で区別
メッセージ名にはキーワードを含める
-(id)initWithFrame:(NSRect)frame style:(ViewStyle)style
識別子:-in...
良くある例
-(id)init
-(id)initWithData:(NSData *)data
-(id)initWithData:(NSData *)data withSize:(int)size
それぞれ別の識別子のため問題無し
返り値の...
id?
特殊な型id
全てのオブジェクト型から暗黙のキャスト可能
存在しないメッセージは無視される
コンパイルエラーにはならない
要するに任意のメッセージを受ける事を主張した
Object *と覚えておこう
前提のお話
Objective-Cってなんそ
ObjC流記法
Cocoa Framework
クラス名について
Objective-Cには名前空間が無い
元ネタのC言語には無いから仕方ないね
そこで関数/クラス名に二文字程度の識別子を使う
NSObject (NextStep Famework由来のため)
CGRect (CoreGra...
インスタンス化
インスタンスを「作る」メッセージ
+alloc
インスタンスを「初期化する」メッセージ
-init
-initWithHoge:
実際に使う場合
NSDate *date = [[NSDate alloc] init];
現在時刻で初期化
NSImage *img = [[NSImage alloc] initWithData:bin];
指定したデータで画像オブジェクト...
-initの書き方
親の-initを明示的に呼び出し、自身に代入
返り値も自分自身とする
- (id)init
{
if(self = [super init]) {
// 初期化コードを記入
}
return self;
}
インターフェースの実現
あるメソッドを実装している事を期待したい
1. @protocolを利用する
速度を気にしない限り使わない
2.NSObjectをカテゴリで拡張する
ルートオブジェクトが既に実装済み
= 子クラスの対応は任意
getter/setter
int hogeなメンバ変数を持つとする
getter
-(int)hoge
setter
-(void)setHoge:(int)newhoge
ObjC2.0から@propertyによってこの2つは自動生成可能に
前提のお話
Objective-Cってなんそ
ObjC流記法
Cocoa Framework
Cocoa Framework?
Macアプリケーションの基本ライブラリ
Cocoa Frameworkは複数のフレームワークの集合
Foundation Framework
Appkit Framework
Coredata Framework
標準ライブラリは?
Foundation Frameworkが提供するもの
NSObject
NSArray
NSString
NSDictionary
and so on..
言語の基本的な機能が集まっている
NSObject?
全てのクラスのルートクラス
他のフレームワークも必ず依存している
…事になっている
AppKit?
Appkit Framwork
アプリケーションのビュー
イベント処理
AppleScript
ランループとかもここから
本日話す事
コレがしたい
前提のお話
今回必要となる技術
実際にアプリを見てみよう
本日使う技術
アプリに侵入しよう!
アプリを理解しよう!
アプリを乗っ取ろう!
アプリを操作しよう!
本日使う技術
アプリに侵入しよう!
アプリを理解しよう!
アプリを乗っ取ろう!
アプリを操作しよう!
で、どうやって侵入するの
古い方法
IMのフリをする
/Library/InputManagers
Cocoaアプリは必ずこのフォルダにある実行
ファイルを無条件にロードしていた
今は駄目です
SIMBLに頼ろう
SIMple Bundle Loader
Cocoaアプリに外部プラグインをロードさせる
/Library/Application Support/SIMBL/Plugins
使い方
Info.plist(アプリケーション設定ファイル)に記述
SIMBLTargetApplicationsキーで、以下の辞書の配列を
設定
BundleIdentifier: 対象アプリ
MaxBundleVersion: 読ませる最大バ...
+(void)loadを実装する
プラグインが読み込まれた際に必ず呼ばれるメッ
セージ
シングルトンインスタンスを返す実装が望まし
い
複数回呼ばれる場合がある
-initのつもりで書いてはだめ
SIMBLはIMじゃないの?
AppleScriptを侵入経路として使っているらしい
IMを使う方法は最新ではない
準備
1. アプリケーションの起動監視アプリを用意
2. AppleEvent Handlerアプリを別途用意
手順
1. アプリの起動を検知
2. 起動したアプリに対して、Handlerを起動するAppleEvent
を送信
3. Handl...
本日使う技術
アプリに侵入しよう!
アプリを理解しよう!
アプリを乗っ取ろう!
アプリを操作しよう!
で、何を書き換えればいいの?
侵入したそのあとに
「侵入してもグローバル変数しか書き換えられま
せん」では意味が無い
しかも書き換えられるグローバル変数すら分か
らない
せめて乗っ取れる機能とか調べたいよね
ここで問題だ
バイナリの固まりであるアプリケーションから

どうやってAPIを見つけ出すか
1. ハンサムなデバッガが突如ソレらしいメソッド
を発見してくれる
2. それっぽいツールが助けてくれる
3. 何も無い、現実は非常である
1を試す
1.XCode.appを起動する
2.対象アプリにアタッチし、無
理矢理停止する
3.目標の関数が起動しそうなイ
ベントを発火させる
4.Step & Trace!
(僕には)無理でした
(努力次第ではなんとかなりそう)
2を試す
あー、どこかにコマンド一発で@interface情報をま
とめてくれる便利なコマンド無いかなー
カテゴリとかもちゃんと区別して出力してくれる
と良いなー
メンバ変数とかプライベートメソッドとか出力さ
れたら言う事無いんだけどなー
Class-dump
http://stevenygard.com/projects/class-dump/
command-line utility for examining the Objective-C
runtime informat...
Usage: class-dump [options] <mach-o-file>
-C <regex> 正規表現にマッチするもののみを
出力
-H ヘッダーファイルとして出力
-r 共有ライブラリも出力
--sdk-root SDKのパスを指定
Dock.appで試す
class-dump /System/Library/CoreServices/Dock.app/
Contents/MacOS/Dock -H --sdk-root /Applications/
Xcode.app/C...
CALayer-
CALayerAdditions.h
CALayer-
ECModalEventDeleg
ate.h
CDStructures.h
DBAttachedWindow
.h
DBDebugWidgetPro
cess.h
DB...
無数にソレっぽいファイルが出て来た
概観すると4つくらいの名前空間が見える
WVSystemSpace.h
LPAppManager.h
ECCoverFlowItemLayer.h
DOCKStackListLayer.h
WV系はなぜかSpacesっぽい名前のクラスが多い?
ソレらしいメンバ変数を持つクラスを乗っ取れば
操作が出来るに違いない
ソレらしいイベントリスナーを持つクラスを乗っ
取れば、イベントドリブンが成立するに違いない
本日使う技術
アプリに侵入しよう!
アプリを理解しよう!
アプリを乗っ取ろう!
アプリを操作しよう!
おまたせしました
乗っ取りのお時間です
乗っ取り乗っ取り言うけどさ
具体的な乗っ取り手法って?
poisng(古い)
method swizzling(新しい)
まずはpoisngから
posing: 「クラステーブルを書き換えることでクラ
スの実体を置き換える技法。」
via Wikipedia
既存のクラスを実行時に置き換える手法らしい
ClassAをClassBでposingを用いて乗っ取る例
制限
ClassBはClassAの直接のサブクラスであること
ClassBはメンバ変数を持たないこと
ClassAのインスタンスが存在しないこと
図
ClassA
instanceA2instanceA1
posing
ClassB
instanceA2instanceA1
ClassA
poseAsClass:によって実現できた
現在(ObjC2.0)では推奨されない
Method Swizzling
メソッドの実装を動的に入れ替える技法
libobjcランタイムでサポート
ObjC2.0でも動作
libobjcの関数群
Method class_getInstanceMethod(Class aClass, SEL
aSelector)
Classからインスタンスメソッドを取得
Method class_getClassMethod(C...
libobjcの関数群
id objc_getClass(const char *name)
nameクラスのClassを得る
NSClassFromStringでも可
void method_exchangeImplementations(M...
例: 既存クラスのインスタンスメソッド同士を置換
void SwizzleInstanceMethod (Class cls, SEL old, SEL new) {
	 Method mold = class_getInstanceMetho...
実際の使い方
カテゴリを使って対象クラスにメソッドを追加し、
swizzlingする
元のメソッドを呼び出す場合は自分自身を呼び出
す事で実現
実装が入れ替わっているため
乗っ取り用メソッド実装例
@implementation NSObject (BlackDock)
- (void)BlackDock_DOCKTrashTile_open
{
	 [self BlackDock_DOCKTrashTile_o...
乗っ取り例
@implementation BlackDock
+ (void)load
{
	 Class cls = NSClassFromString(@"DOCKTrashTile");
	 SEL old = @selector(op...
例の解説
DOCKTrashTileというクラスのopenを
BlackDock_DOCKTrashTile_openで置換
openの元の実装の挙動を優先する
乗っ取るメソッドは@selectorで指定
本日使う技術
アプリに侵入しよう!
アプリを理解しよう!
アプリを乗っ取ろう!
アプリを操作しよう!
ヘッダーを再配布したら怒られない?
無理矢理呼び出しましょう
あると分かっているならリフレクション使えば良
いよね
メンバ変数を呼ぶ時: class_getInstanceVariable
メンバメソッドを呼ぶ: NSInvocation
変数読み出し
Ivar class_getInstanceVariable(Class cls,

const char* name)
クラスの表現clsからnameの変数を取り出す
id object_getIvar(id object, I...
例(あるオブジェクトからメンバ変数を取り出す)
Ivar getInstanceVariable(id obj, const char *name, void **value)
{
Ivar ivar;
if ((ivar = class_ge...
メソッド呼び出し
メッセージの識別子としての表現: @selector(hoge:)
方法1
NSInvocation: 静的にメッセージ呼び出しを実現する
プロキシクラス
方法2
NSObjectの-performSelector:を使う
NSProxy
- (NSMethodSignature *)
methodSignatureForSelector:(SEL)aSelector
あるオブジェクトのメソッドを表すシグニチャ
NSInvocation
+ (NSInvocati...
void invoke(id target, SEL selector, void *arg1, void *ret){
	 NSMethodSignature *sig = [target methodSignatureForSelector...
もう少し楽に
え?面倒くさい?
引数無くても書かなきゃならんのか
NSObject
- (id)performSelector:(SEL)aSelector
aSelectorは単項メッセージ限定
例
[spaces performSelector:@selector(displays)]
同じ例
invoke(spaces, @selector(displays), NULL, NULL);
本日話す事
コレがしたい
前提のお話
今回必要となる技術
実際にアプリを見てみよう
本日のテーマ
Dock.app
サンプルアプリ
作ったもの見せたかったけど時間無いしね
サンプルアプリ「BlackDock.app」
ゴミ箱をクリックしたらDockがかっこ良くなる
仕組み
1. ゴミ箱のクリックのイベントを監視する
2. Dockのベースを形作るビューを取得する
3. 自力で書いたレイヤーで置き換える
だします
Cocoaアプリに無理矢理プラグインを導入する入門
Cocoaアプリに無理矢理プラグインを導入する入門
Cocoaアプリに無理矢理プラグインを導入する入門
Cocoaアプリに無理矢理プラグインを導入する入門
Upcoming SlideShare
Loading in …5
×

Cocoaアプリに無理矢理プラグインを導入する入門

1,499 views

Published on

既存のアプリが微妙に使いにくい?そんなときは!自作のプラグインで改造できるニャー

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,499
On SlideShare
0
From Embeds
0
Number of Embeds
37
Actions
Shares
0
Downloads
5
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Cocoaアプリに無理矢理プラグインを導入する入門

  1. 1. Super Simple Cocoa Plugin Introduction nendoki
  2. 2. コレって誰よ 上田 宗一郎(うえだ ソウイチロウ) id: nendoki (a.k.a ne[tab]) 京都大学大学院情報学研究科M1 親の代からMac使い
  3. 3. mac? スタバドヤ顔マン御用達アイテムNo1 パロアルト研究所からアイディア持ち出したアレ ディスプレイ上のサイズ=印刷サイズで有名
  4. 4. 本日話す事 コレがしたい 前提のお話 今回必要となる技術 実際にアプリを見てみよう
  5. 5. 本日話す事 コレがしたい 前提のお話 今回必要となる技術 実際にアプリを見てみよう
  6. 6. Macアプリ作ってますか?
  7. 7. Q. 今使っているプログラム が*微妙に*機能不足だ
  8. 8. Windows的解法 フリーウェアに 良いのがあるよ!!
  9. 9. 富豪的解法 金を払えば良い
  10. 10. プログラマ的解法 自分で作る
  11. 11. FSF的解法 GPLコード片が 埋め込まれていないか探す
  12. 12. よりよい解法 既存のアプリを修正しよう
  13. 13. という事で Mac用Cocoaアプリケーションに自分で機能を追加 して幸せを得よう
  14. 14. 本日話す事 コレがしたい 前提のお話 今回必要となる技術 実際にアプリを見てみよう
  15. 15. 前提のお話 Objective-Cってなんそ ObjC流記法 Cocoa Framework
  16. 16. 前提のお話 Objective-Cってなんそ ObjC流記法 Cocoa Framework
  17. 17. Objective-C! || C + SmallTalk
  18. 18. SmallTalkな箇所のみ解説します
  19. 19. メッセージ呼び出し メンバメソッドの呼び出しの事 メッセージ 単項メッセージ キーワード付きメッセージ 二項メッセージ(SmallTalkのみ 可変長引数や部分的にキーワードの無いメッセージは省略
  20. 20. 単項メッセージ [obj message]; 他言語のobj.message()に対応 キーワード付きメッセージ [obj message:arg0 key1: arg1 key2:arg2 ..]; 他言語ではobj.message(arg0, arg1, arg2…)とか obj.message(arg0, key1=arg1, key2=arg2, …)に対応
  21. 21. クラスの定義(ObjC固有) @interfaceでインターフェース定義(ヘッダー) @imprementsで実装を定義(ボディー) インスタンスメソッド - (型)methodName:(型)arg0 key1:(型)arg1; クラスメソッド + (型)methodName:(型)arg0 key1:(型)arg1;
  22. 22. @interface AGSampleLayer : CALayer { CALayer *floorLayer; } + (id)layer - (void)resizeWithSize:(CGSize)size; @end ! @implementation AGSampleLayer + (id)layer { … return self; } ! - (void)resizeWithSize:(CGSize)size { … } @end
  23. 23. カテゴリ クラス定義を複数回に分けて記述 既存のクラスに動的にメソッド追加が可能 フレームワークのクラスにも可能 実装を機能別に別のヘッダーに…なんてことも
  24. 24. オーバーロードについて オーバーロードは存在しない メッセージ名を変える事で区別 メッセージ名にはキーワードを含める -(id)initWithFrame:(NSRect)frame style:(ViewStyle)style 識別子:-initWithFrame:style:
  25. 25. 良くある例 -(id)init -(id)initWithData:(NSData *)data -(id)initWithData:(NSData *)data withSize:(int)size それぞれ別の識別子のため問題無し 返り値の型を合わせるのはマナー
  26. 26. id? 特殊な型id 全てのオブジェクト型から暗黙のキャスト可能 存在しないメッセージは無視される コンパイルエラーにはならない 要するに任意のメッセージを受ける事を主張した Object *と覚えておこう
  27. 27. 前提のお話 Objective-Cってなんそ ObjC流記法 Cocoa Framework
  28. 28. クラス名について Objective-Cには名前空間が無い 元ネタのC言語には無いから仕方ないね そこで関数/クラス名に二文字程度の識別子を使う NSObject (NextStep Famework由来のため) CGRect (CoreGraphicsの機能のため)
  29. 29. インスタンス化 インスタンスを「作る」メッセージ +alloc インスタンスを「初期化する」メッセージ -init -initWithHoge:
  30. 30. 実際に使う場合 NSDate *date = [[NSDate alloc] init]; 現在時刻で初期化 NSImage *img = [[NSImage alloc] initWithData:bin]; 指定したデータで画像オブジェクトを作成
  31. 31. -initの書き方 親の-initを明示的に呼び出し、自身に代入 返り値も自分自身とする - (id)init { if(self = [super init]) { // 初期化コードを記入 } return self; }
  32. 32. インターフェースの実現 あるメソッドを実装している事を期待したい 1. @protocolを利用する 速度を気にしない限り使わない 2.NSObjectをカテゴリで拡張する ルートオブジェクトが既に実装済み = 子クラスの対応は任意
  33. 33. getter/setter int hogeなメンバ変数を持つとする getter -(int)hoge setter -(void)setHoge:(int)newhoge ObjC2.0から@propertyによってこの2つは自動生成可能に
  34. 34. 前提のお話 Objective-Cってなんそ ObjC流記法 Cocoa Framework
  35. 35. Cocoa Framework? Macアプリケーションの基本ライブラリ Cocoa Frameworkは複数のフレームワークの集合 Foundation Framework Appkit Framework Coredata Framework
  36. 36. 標準ライブラリは? Foundation Frameworkが提供するもの NSObject NSArray NSString NSDictionary and so on..
  37. 37. 言語の基本的な機能が集まっている NSObject? 全てのクラスのルートクラス 他のフレームワークも必ず依存している …事になっている
  38. 38. AppKit? Appkit Framwork アプリケーションのビュー イベント処理 AppleScript ランループとかもここから
  39. 39. 本日話す事 コレがしたい 前提のお話 今回必要となる技術 実際にアプリを見てみよう
  40. 40. 本日使う技術 アプリに侵入しよう! アプリを理解しよう! アプリを乗っ取ろう! アプリを操作しよう!
  41. 41. 本日使う技術 アプリに侵入しよう! アプリを理解しよう! アプリを乗っ取ろう! アプリを操作しよう!
  42. 42. で、どうやって侵入するの
  43. 43. 古い方法 IMのフリをする /Library/InputManagers Cocoaアプリは必ずこのフォルダにある実行 ファイルを無条件にロードしていた 今は駄目です
  44. 44. SIMBLに頼ろう SIMple Bundle Loader Cocoaアプリに外部プラグインをロードさせる /Library/Application Support/SIMBL/Plugins
  45. 45. 使い方 Info.plist(アプリケーション設定ファイル)に記述 SIMBLTargetApplicationsキーで、以下の辞書の配列を 設定 BundleIdentifier: 対象アプリ MaxBundleVersion: 読ませる最大バージョン MinBundleVersion: 対応する最小のバージョン
  46. 46. +(void)loadを実装する プラグインが読み込まれた際に必ず呼ばれるメッ セージ シングルトンインスタンスを返す実装が望まし い 複数回呼ばれる場合がある -initのつもりで書いてはだめ
  47. 47. SIMBLはIMじゃないの? AppleScriptを侵入経路として使っているらしい IMを使う方法は最新ではない
  48. 48. 準備 1. アプリケーションの起動監視アプリを用意 2. AppleEvent Handlerアプリを別途用意 手順 1. アプリの起動を検知 2. 起動したアプリに対して、Handlerを起動するAppleEvent を送信 3. HandlerはSIMBLプラグインを読み込む
  49. 49. 本日使う技術 アプリに侵入しよう! アプリを理解しよう! アプリを乗っ取ろう! アプリを操作しよう!
  50. 50. で、何を書き換えればいいの?
  51. 51. 侵入したそのあとに 「侵入してもグローバル変数しか書き換えられま せん」では意味が無い しかも書き換えられるグローバル変数すら分か らない せめて乗っ取れる機能とか調べたいよね
  52. 52. ここで問題だ バイナリの固まりであるアプリケーションから
 どうやってAPIを見つけ出すか 1. ハンサムなデバッガが突如ソレらしいメソッド を発見してくれる 2. それっぽいツールが助けてくれる 3. 何も無い、現実は非常である
  53. 53. 1を試す 1.XCode.appを起動する 2.対象アプリにアタッチし、無 理矢理停止する 3.目標の関数が起動しそうなイ ベントを発火させる 4.Step & Trace!
  54. 54. (僕には)無理でした (努力次第ではなんとかなりそう)
  55. 55. 2を試す あー、どこかにコマンド一発で@interface情報をま とめてくれる便利なコマンド無いかなー カテゴリとかもちゃんと区別して出力してくれる と良いなー メンバ変数とかプライベートメソッドとか出力さ れたら言う事無いんだけどなー
  56. 56. Class-dump http://stevenygard.com/projects/class-dump/ command-line utility for examining the Objective-C runtime information stored in Mach-O files.
  57. 57. Usage: class-dump [options] <mach-o-file> -C <regex> 正規表現にマッチするもののみを 出力 -H ヘッダーファイルとして出力 -r 共有ライブラリも出力 --sdk-root SDKのパスを指定
  58. 58. Dock.appで試す class-dump /System/Library/CoreServices/Dock.app/ Contents/MacOS/Dock -H --sdk-root /Applications/ Xcode.app/Contents/Developer/Platforms/ MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -H: ヘッダーファイルとして出力 --sdk-root: ヘッダーファイルにシステムのフ レームワークを含めない
  59. 59. CALayer- CALayerAdditions.h CALayer- ECModalEventDeleg ate.h CDStructures.h DBAttachedWindow .h DBDebugWidgetPro cess.h DBDebugWidgetSer ver.h DBRippleInfo.h DBSpringboard.h DBWidget.h DBWidgetProcess.h DOCKAppDeathEve nt.h DOCKAppHideEvent. h DOCKAppLaunchCo mpleteEvent.h DOCKAppLaunchEv ent.h DOCKAppNeedsAtte ntionEvent.h DOCKAppRevealEve nt.h DOCKAutoHideDisa bledEvent.h DOCKAutoHideEnab ledEvent.h DOCKBlockEvent.h DOCKBounceEvent. h DOCKDashboardTile .h DOCKDesktopTile.h DOCKDisplayChang edEvent.h DOCKDownloadDire ctory.h DOCKDownloadMan ager.h DOCKDownloadPro gressHandler- Protocol.h DOCKDragDropEven t.h DOCKDragEnterEve nt.h DOCKDragLeaveEve nt.h DOCKDynamicFolde r.h DOCKEvent.h DOCKExtraDelegate -Protocol.h DOCKFileTile.h DOCKFileTileFactory .h DOCKFloorLayer.h DOCKFloorShadowL ayer.h DOCKFolder.h DOCKFolderTile.h DOCKFolderTileCac heEntry.h DOCKGestureDefaul tHandler.h DOCKGestureEvent. h DOCKGestureHandl er-Protocol.h DOCKGestures.h DOCKGlassFloorLay er.h DOCKGridLayer.h DOCKIndicatorLayer .h DOCKInsertTile.h DOCKInstallAddOpe ration.h DOCKInstallMoveOp eration.h DOCKInstallOperati on.h DOCKInstallRemove Operation.h DOCKInstallTransac tion.h DOCKInstallUpdate Operation.h DOCKInternalDropE vent.h DOCKLabelLayer.h DOCKMaximizeWin dowEvent.h DOCKMaximizeWin dowWithOrderEvent .h DOCKMaximizeWin dowsEvent.h DOCKMiniView.h DOCKMiniViewLayer .h DOCKMinimizedWin dow.h DOCKMinimizedWin dowEvent.h DOCKMinimizedWin dowsEvent.h DOCKMouseEnterEv ent.h DOCKMouseLeaveE vent.h DOCKOrientationCh angedEvent.h DOCKPSNEvent.h DOCKPeriodicMess ageTrace.h DOCKPreferences.h DOCKProcessTile.h DOCKRecentsContr oller.h DOCKRecentsModel .h DOCKRecentsTile.h DOCKReflectionLay er.h DOCKRemoveWindo wsEvent.h DOCKScreenSharin gModel.h DOCKScreenSharin gTile.h DOCKSeparatorTile. h DOCKSetWindowTitl eEvent.h DOCKSideGlassFloo rLayer.h DOCKSmartFolderTi le.h DOCKSpacerTile.h DOCKStack.h DOCKStackActionH andling-Protocol.h DOCKStackActionS ender-Protocol.h DOCKStackAnimato r.h DOCKStackBackgro undLayer.h DOCKStackBlurWin dow.h DOCKStackCollapse dDataSource- Protocol.h DOCKStackDataSou rceClient-Protocol.h DOCKStackExpande dDataSource- Protocol.h DOCKStackExpande dHandler.h DOCKStackExpande dItemLayer.h DOCKStackExpande dLayer.h DOCKStackFanAnim ator.h DOCKStackFanItem Layer.h DOCKStackFanLaye r.h DOCKStackGoBack ButtonLayer.h DOCKStackGridAni mator.h DOCKStackGridHov erLayer.h DOCKStackGridItem Layer.h DOCKStackGridLaye r.h DOCKStackItem- Protocol.h DOCKStackLayer.h DOCKStackListAnim ator.h DOCKStackListItem Layer.h DOCKStackListLaye r.h DOCKStackPreview. h DOCKStaticScreenS haringModel.h DOCKTileEvent.h DOCKTileLabelLayer .h DOCKTileLayer.h DOCKTileRemovedE vent.h DOCKTileShadowLa yer.h DOCKTrashTile.h DOCKURLTile.h DOCKUpdateMinimi zedWindowEvent.h DOCKWindowCopy. h DOCKWindowEvent. h DOCKWindowTile.h DOCKWindowUnreg isteredEvent.h DPAnimationTimer. h DPBackgroundDesk topPicture.h DPBackgroundDesk topPictureInfo.h DPColorSpace.h DPDefaultPictureInf o.h DPDesktopPicture.h DPDesktopPictureM anager.h DPDesktopWindow- Protocol.h DPDirectoryFileListi ng.h DPDummyRenderO bject.h DPFileListing.h DPPictureInfo.h DPPictureStorage.h DPRemoteConnecti on.h DPSharedDesktopI mage.h DPSharedSlot.h DPStatementCache. h DPTiledPictureInfo. h DPWakeupSource.h DPiLifeFileListing.h DRAGTimerInfo.h DockExtra- Protocol.h DockExtra.h DockExtraManager. h DockExtraServer.h DockNotificationCe nter.h ECAlertPanel.h ECAlertPanelDelega te-Protocol.h ECAliasHandle.h ECButtonLayer.h ECCalloutLayer.h ECConsedEvent.h ECCoverFlow.h
  60. 60. 無数にソレっぽいファイルが出て来た 概観すると4つくらいの名前空間が見える WVSystemSpace.h LPAppManager.h ECCoverFlowItemLayer.h DOCKStackListLayer.h
  61. 61. WV系はなぜかSpacesっぽい名前のクラスが多い? ソレらしいメンバ変数を持つクラスを乗っ取れば 操作が出来るに違いない ソレらしいイベントリスナーを持つクラスを乗っ 取れば、イベントドリブンが成立するに違いない
  62. 62. 本日使う技術 アプリに侵入しよう! アプリを理解しよう! アプリを乗っ取ろう! アプリを操作しよう!
  63. 63. おまたせしました 乗っ取りのお時間です
  64. 64. 乗っ取り乗っ取り言うけどさ 具体的な乗っ取り手法って? poisng(古い) method swizzling(新しい)
  65. 65. まずはpoisngから posing: 「クラステーブルを書き換えることでクラ スの実体を置き換える技法。」 via Wikipedia 既存のクラスを実行時に置き換える手法らしい
  66. 66. ClassAをClassBでposingを用いて乗っ取る例 制限 ClassBはClassAの直接のサブクラスであること ClassBはメンバ変数を持たないこと ClassAのインスタンスが存在しないこと
  67. 67. 図 ClassA instanceA2instanceA1 posing ClassB instanceA2instanceA1 ClassA
  68. 68. poseAsClass:によって実現できた 現在(ObjC2.0)では推奨されない
  69. 69. Method Swizzling メソッドの実装を動的に入れ替える技法 libobjcランタイムでサポート ObjC2.0でも動作
  70. 70. libobjcの関数群 Method class_getInstanceMethod(Class aClass, SEL aSelector) Classからインスタンスメソッドを取得 Method class_getClassMethod(Class aClass, SEL aSelector) Classからクラスメソッドを取得
  71. 71. libobjcの関数群 id objc_getClass(const char *name) nameクラスのClassを得る NSClassFromStringでも可 void method_exchangeImplementations(Method m1, Method m2) Methodの実装を置換する
  72. 72. 例: 既存クラスのインスタンスメソッド同士を置換 void SwizzleInstanceMethod (Class cls, SEL old, SEL new) { Method mold = class_getInstanceMethod(cls, old); Method mnew = class_getInstanceMethod(cls, new); if (mold && mnew) method_exchangeImplementations(mold, mnew); }
  73. 73. 実際の使い方 カテゴリを使って対象クラスにメソッドを追加し、 swizzlingする 元のメソッドを呼び出す場合は自分自身を呼び出 す事で実現 実装が入れ替わっているため
  74. 74. 乗っ取り用メソッド実装例 @implementation NSObject (BlackDock) - (void)BlackDock_DOCKTrashTile_open { [self BlackDock_DOCKTrashTile_open]; [self someEventHook]; } @end
  75. 75. 乗っ取り例 @implementation BlackDock + (void)load { Class cls = NSClassFromString(@"DOCKTrashTile"); SEL old = @selector(open); SEL new = @selector(BlackDock_DOCKTrashTile_open); SwizzleInstanceMethod(cls, old, new); } @end
  76. 76. 例の解説 DOCKTrashTileというクラスのopenを BlackDock_DOCKTrashTile_openで置換 openの元の実装の挙動を優先する 乗っ取るメソッドは@selectorで指定
  77. 77. 本日使う技術 アプリに侵入しよう! アプリを理解しよう! アプリを乗っ取ろう! アプリを操作しよう!
  78. 78. ヘッダーを再配布したら怒られない?
  79. 79. 無理矢理呼び出しましょう あると分かっているならリフレクション使えば良 いよね メンバ変数を呼ぶ時: class_getInstanceVariable メンバメソッドを呼ぶ: NSInvocation
  80. 80. 変数読み出し Ivar class_getInstanceVariable(Class cls,
 const char* name) クラスの表現clsからnameの変数を取り出す id object_getIvar(id object, Ivar ivar) objectから表現ivarに対応する変数の値を得る
  81. 81. 例(あるオブジェクトからメンバ変数を取り出す) Ivar getInstanceVariable(id obj, const char *name, void **value) { Ivar ivar; if ((ivar = class_getInstanceVariable(object_getClass(obj), name))) { if (value) *value = (__bridge void *)object_getIvar(obj, ivar); return ivar; } }
  82. 82. メソッド呼び出し メッセージの識別子としての表現: @selector(hoge:) 方法1 NSInvocation: 静的にメッセージ呼び出しを実現する プロキシクラス 方法2 NSObjectの-performSelector:を使う
  83. 83. NSProxy - (NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector あるオブジェクトのメソッドを表すシグニチャ NSInvocation + (NSInvocation *)invocationWithMethodSignature: (NSMethodSignature *)signature シグニチャに対して実際に値を適用する
  84. 84. void invoke(id target, SEL selector, void *arg1, void *ret){ NSMethodSignature *sig = [target methodSignatureForSelector:selector]; NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig]; [inv setSelector:selector]; // メッセージを [inv setTarget:target]; // どのオブジェクトに対して [inv setArgument:arg1 atIndex:2]; // 第1引数をarg1として [inv invoke]; // 呼び出す [inv getReturnValue:ret]; // 返り値をポインタに代入 }
  85. 85. もう少し楽に え?面倒くさい? 引数無くても書かなきゃならんのか NSObject - (id)performSelector:(SEL)aSelector aSelectorは単項メッセージ限定
  86. 86. 例 [spaces performSelector:@selector(displays)] 同じ例 invoke(spaces, @selector(displays), NULL, NULL);
  87. 87. 本日話す事 コレがしたい 前提のお話 今回必要となる技術 実際にアプリを見てみよう
  88. 88. 本日のテーマ Dock.app
  89. 89. サンプルアプリ 作ったもの見せたかったけど時間無いしね サンプルアプリ「BlackDock.app」 ゴミ箱をクリックしたらDockがかっこ良くなる
  90. 90. 仕組み 1. ゴミ箱のクリックのイベントを監視する 2. Dockのベースを形作るビューを取得する 3. 自力で書いたレイヤーで置き換える
  91. 91. だします

×