
このレッスンを正常に完了するには、アセンブラーとは何かを理解する必要があります。 この記事の著者は、 ARMのチュートリアル (英語)を読むことをお勧めします。
実際、レッスンの意味を理解するために必要な知識のレベルは、Googleが途中で数分かかることです。 さて、記事の終わり近くで、アセンブラーを学ぶ必要があるかどうかを決定します。 :) -約 あたり
始めましょう
必要なもの:
- 前の部分のプロジェクトMeme Collectorをテストします。
- ユーティリティclass-dump-z
- OS X用のオープンソースHEXエディター: Hex Fiend
- IDAデモは、よく知られたマルチプロセッサの逆アセンブラおよびデバッガです。 デモ版には、このレッスンでは重要ではない制限があります。
これらのツールについて多くを学んでいただければ幸いです!
ランタイム操作
前のシリーズでは、.plistファイルを変更してアカウントの残高を変更しました。 では、実行時に変数とメソッドを操作する方法(実行時と呼ばれる方法)を見てみましょう。 これを行うには、LLDBデバッガーを使用します。
元の記事では、すべての例はGDBを使用していますが、
Xcodeを5.0.1(翻訳時の現在のバージョン)にアップグレードした後、GDBを実行するにはタンバリンと踊る必要があります。 したがって、チュートリアルに負担をかけないために、 LLDBとGDBの間のコマンド対応表に従って、チュートリアルをやり直しました。 -注 あたり
ターミナルのiOSシミュレーターにインストールされているメインバンドルフォルダー(
Meme Collector.app
)を開きます。 これを行うために迷っている場合は、 最初の部分を見てください。
開始位置を占有します。シミュレータは実行されていますが、アプリケーションはインストールされていますが、実行されていません。
ターミナルで、次を入力します。
lldb
デバッガーは実行中です。 次の行には、彼からの招待状が表示されます:
(lldb)
デバッガーのコマンドを入力します。
行の先頭に文字
(lldb)
を記述しないので、コピーするときに混乱しないようにします。
attach --name "Meme Collector" --waitfor
attach
コマンドは、特定のプロセスに接続するために使用されます。 ここでは、LLDBに「
Meme Collector
」と呼ばれる新しいプロセスが開始して接続するのを待つように依頼します。
したがって、デバッガは待機しています。 iOSシミュレーターに進み、従来の(レッスンの最後の部分で)マルチタスクからアプリケーションをアンインストールしてから再起動します(IDEからではなく、シミュレーターから実行します)-以降、「再起動」と呼びます。
すべてが正しく行われると、LLDBはシミュレーターのプロセスと
Process 1427 stopped Executable module set to "/Users/dmitriy/Library/Application Support/iPhone Simulator/7.0.3/Applications/9A72F266-8851-4A25-84E4-9CF8EFF95CD4/Meme Collector.app/Meme Collector". Architecture set to: i486-apple-macosx.
そして、新しいコマンドを入力するプロンプト:
(lldb)
各ViewControllerを表示する前にブレークポイントを追加しましょう。 これは、多くの興味深いことが通常起こる場所です。 多くの場合、アプリケーションロジックの大部分はそこで決定されます。 たとえば、
viewDidLoad
メソッドのすべての呼び出しにブレークポイントを追加しましょう。iOSでは、
UIViewController
サブクラスがほとんどの場合
viewDidLoad
オーバーライドするためです。
ターミナルで実行:
b viewDidLoad
メソッド名では大文字と小文字が区別されるため、
viewdidload
オプション
viewdidload
失敗します。
これにより、
viewDidLoad
と呼ばれるすべてのメソッド(C ++およびObjective-Cメソッドを含む)にブレークポイントが設定されます。 必要に応じて、特定のObjCセレクターに対して、
-[UIViewController viewDidLoad]
などの名前を入力できますが、このオプションは
UIViewController
クラスの子孫では
UIViewController
ないことに注意してください。
したがって、LLDBは、ブレークポイントに適した15の場所を見つけたと言っています。
Breakpoint 1: 15 locations.
素晴らしい。 彼がどこに置いたのか見てみましょう。 次のコマンドを入力します。
br l
(これは
breakpoint list
略です。必要に応じて、コマンドの完全なバージョンを書くことができます。)
さて、ここに彼らは:
Current breakpoints: 1: name = 'viewDidLoad', locations = 15, resolved = 15 1.1: where = Meme Collector`-[ViewController viewDidLoad] + 18 at ViewController.m:27, address = 0x0001f482, resolved, hit count = 0 1.2: where = UIKit`-[UIViewController viewDidLoad], address = 0x005d3db5, resolved, hit count = 0 1.3: where = UIKit`-[_UIModalItemsPresentingViewController viewDidLoad], address = 0x0065ab4b, resolved, hit count = 0 1.4: where = UIKit`-[UIKeyboardCandidateGridCollectionViewController viewDidLoad], address = 0x00680729, resolved, hit count = 0 1.5: where = UIKit`-[UIActivityGroupViewController viewDidLoad], address = 0x008d2b6b, resolved, hit count = 0 1.6: where = UIKit`-[UIPrintPanelTableViewController viewDidLoad], address = 0x009be80f, resolved, hit count = 0 1.7: where = UIKit`-[UIPrintStatusViewController viewDidLoad], address = 0x009c8828, resolved, hit count = 0 1.8: where = UIKit`-[UIPrintRangeViewController viewDidLoad], address = 0x009d29ae, resolved, hit count = 0 1.9: where = UIKit`-[_UILongDefinitionViewController viewDidLoad], address = 0x00a10cf4, resolved, hit count = 0 1.10: where = UIKit`-[_UINoDefinitionViewController viewDidLoad], address = 0x00a1249d, resolved, hit count = 0 1.11: where = UIKit`-[UIReferenceLibraryViewController viewDidLoad], address = 0x00a13bd4, resolved, hit count = 0 1.12: where = UIKit`-[_UIFallbackPresentationViewController viewDidLoad], address = 0x00a77877, resolved, hit count = 0 1.13: where = UIKit`-[_UIViewServiceViewControllerOperator viewDidLoad], address = 0x00aba23b, resolved, hit count = 0 1.14: where = UIKit`-[UIActivityViewController viewDidLoad], address = 0x00b4f296, resolved, hit count = 0 1.15: where = UIKit`-[_UITextEditingController viewDidLoad], address = 0x00b9a6ec, resolved, hit count = 0
実際、ブレークポイントを1つだけ残す必要があることは明らかです
-[ViewController viewDidLoad]
、残りはApple Private APIに属しているため。 しかし、私たちは興味があるので、それらを残してください。
アプリケーションの起動に戻りましょう! 次のコマンドを入力します。
c
これはフルバージョンでは
continue
ように見えます。 アプリケーションは、
viewDidLoad
最初の呼び出しまでコードの実行を続け
viewDidLoad
。
Process 1427 resuming Process 1427 stopped * thread #1: tid = 0x83c4, 0x0001f482 Meme Collector`-[ViewController viewDidLoad](self=0x08f7c620, _cmd=0x00c50587) + 18 at ViewController.m:27, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1 frame #0: 0x0001f482 Meme Collector`-[ViewController viewDidLoad](self=0x08f7c620, _cmd=0x00c50587) + 18 at ViewController.m:27 24 25 - (void)viewDidLoad 26 { -> 27 [super viewDidLoad]; 28 self.memeDescriptionTextView.clipsToBounds = YES; 29 self.memeDescriptionTextView.layer.cornerRadius = 20.0f; 30 [self.moneyLabel sizeToFit];
「そして今... e-yer ...それは私たちが楽しむ時間です:そうでなければ私はプレーしません!」

ViewController
クラス(ViewController.mファイル)のフレームでプロセスを停止しました。 そのため、インスタンス変数とメソッドにアクセスできます。 かっこいい そしてもっと! コードセクションは既にメモリにロードされています。 したがって、以下を含む他のすべてのクラスにアクセスできます-注意! -シングルトーン。
はい、シングルトーン。 最初の部分でこの点を注意深く調べた場合、
MoneyManager
と呼ばれる「興味深い」クラスに気付くかもしれません。 彼はテストしたい
purchaseCurrency
メソッドを持っていますよね? :)
(lldb)
ターミナルに入力します:
call [[MoneyManager sharedManager] purchaseCurrency]
メソッドを呼び出しました! デバッガーは実行結果を出力します。
(BOOL) $0 = YES
答えが
YES
場合、仮想通貨を正常に「取得」したことを意味します。 (ここで著者は、これはインサイダー情報です。私たち、クラッカーは、それを知るべきではありません。-およそあたり。 )
LLDBは、Enterキーを押して前のコマンドを繰り返します。 したがって、Enterキーを数回押して、Mikhail
(lldb) call [[MoneyManager sharedManager] purchaseCurrency] (BOOL) $0 = YES (lldb) (BOOL) $1 = YES (lldb) (BOOL) $2 = YES (lldb) (BOOL) $3 = YES (lldb) (BOOL) $4 = YES (lldb) (BOOL) $5 = YES (lldb) (BOOL) $6 = YES (lldb) (BOOL) $7 = YES (lldb)
c
...これで、設定したすべてのブレークポイントが終了し、シミュレータで結果が評価されます。

悪くないでしょ? さて、それについて何ができるか見てみましょう。
アプリケーションを一時停止してコマンドラインに再び戻るには、ターミナルに切り替えて、そこでCtrl + Cを押します。 LLDBデバッガーは、コマンドを再度実行する準備ができています。
とりあえずデバッグセッションを終了しましょう:
q
コマンドを入力してから
y
を確認してください:
(lldb) q Quitting LLDB will detach from one or more processes. Do you really want to proceed: [Y/n] y
開発者の側に戻ります。 デバッガーを介してアプリケーションを操作したい人を裏切ることは可能ですか?
ランタイム操作に対する保護
幸い、デバッガがコードに接続されているかどうかを確認する方法があります! しかし、1つの問題があります。 このチェックは、この特定の時間にデバッガが接続されているかどうかを判断します。 ハッカー(クラッカー、詐欺師など)は、このチェックの後、アプリケーションが危険を認識しなくなったときに、アプリケーションに接続できます。 この問題は、少なくとも2つの方法で解決できます。
- チェックが継続的に実行されるように、実行ループにチェックを含めます。
- セキュリティについて最も懸念しているコードの最も重要な部分にチェックを入れます。
通常、最初のオプションは望ましくありません。 その価格は
エレガントなソリューションの1つは、
MoneyManager
シングルトンでデバッガのアクティビティをチェックすることです。 たとえば、デバッグが行われていると判断した場合、クラスの静的インスタンスの代わりに
nil
を返します。
拡張モード
Objective-Cでは、これを簡単に行うことができます。 Objective-Cのメソッドは本質的にメソッドではなく、 メッセージです。 これは、空のオブジェクトにメッセージを送信することは絶対に安全であることを意味します-それは何もしません。 コードはクラッシュしません。
さて、最後に、コードを操作しましょう! (お気に入りのIDEまたはXcodeで)プロジェクトを開き、MoneyManager.mファイルに移動します。 ここでは、アプリケーションがどの構成で構築されているかを確認するプリプロセッサマクロを追加し、リリースの場合はデバッガが実行されているかどうかを確認します。 開始すると、
nil
を返します。 それ以外の場合は、すべてが通常どおり行われます。
sharedManager
クラスの
sharedManager
メソッドの先頭に3行を追加します。
#ifndef DEBUG SEC_IS_BEING_DEBUGGED_RETURN_NIL(); #endif
これで、メソッドは次のようになります。

SEC_IS_BEING_DEBUGGED_RETURN_NIL()
は、デバッガがアプリケーションに接続されている場合に
nil
を返す標準プリプロセッサマクロの呼び出しです。
注:このマクロは、リリース構成でのみ使用可能です。 最初の部分で私たちをフォローしている場合は、すでにリリースに切り替えているはずです。
(念のため、思い出させてください)
Xcode: 製品>スキーム>スキームの編集... ( ⌘< )-左側の[実行...]、右側の[情報]> [ビルド構成:リリース]タブを選択します。
AppCode: 実行>構成の編集... >構成:リリース。
AppCode: 実行>構成の編集... >構成:リリース。
拡張モード
プリプロセッサマクロの代わりに、ObjCメソッドまたはC関数を記述する方が良いと言う人がいるかもしれません。 しかし! マクロを使用する非常に具体的な理由があります。 すべてのメソッドの名前をスパイしたり、振る舞いを変更したりできることを既に知っているので(先を見て:これが次に行います)-これを知っているので、チェックを非表示にします(たとえば、シングルトンメソッド内)。 一般に、クラッカーがセキュリティチェックを見つけてパッチを当てるのははるかに難しくなります。マクロの場合、アセンブラをいじる必要があります。
IDEからアプリケーションを起動します(リリース構成を選択するのを忘れていませんか?)
Xcode: 実行 (⌘R)
AppCode: デバッグ (Ctrl + D)
[実行]を選択すると、XcodeはLLDBデバッガーを自動的に有効にします。 結果:口座残高は表示されません! 確かに、どこかにあり
nil
:

また、AppCodeには、2つの異なるコマンドがあります。Runコマンドはデバッガーを接続しません。Debugコマンドは接続します。 便利に。
最終的に私たちの保護が機能することを確認するには、チェックしてください:今すぐ何かを購入できますか?
MoneyManager
使用できません。つまり、使用できません。
IDEの[ 停止 ]ボタン(四角)をクリックして、アプリケーションを停止します。 LLDBデバッガーも停止します。 シミュレーターに切り替えて、そこからアプリケーションを実行します。 アプリケーションは通貨を表示します、なぜなら デバッガーが接続されていません。
既に述べたように、起動時だけでなく、通常は任意の時点でデバッガをプロセスに接続できます。 ターミナルで実行:
ps aux | grep "Meme Collector"
このコマンドの出力には、「Meme Collector」というフレーズが表示される名前のすべてのプロセスのリストが含まれます。
dmitriy 2008 0,0 0,0 2432784 636 s001 S+ 1:05 0:00.00 grep Meme Collector dmitriy 2001 0,0 0,4 857416 32240 ?? S 1:04 0:00.65 /Users/dmitriy/Library/Application Support/iPhone Simulator/7.0.3/Applications/9A72F266-8851-4A25-84E4-9CF8EFF95CD4/Meme Collector.app/Meme Collector
2行目がシミュレーターのアプリケーションフォルダーに対応していることがわかります。 このプロセスの数に注意してください(2列目)。 私の場合、これは2001年の数字です。
端末から
-p
スイッチを指定してLLDBを実行し、番号でプロセスに接続します。
lldb -p { }
たとえば、「lldb -p 2001」と入力する必要があります。
LLDBが開始し、プロセスへの接続が成功したことを報告します。
Attaching to process with: process attach -p 2001 Process 2001 stopped Executable module set to "/Users/dmitriy/Library/Application Support/iPhone Simulator/7.0.3/Applications/9A72F266-8851-4A25-84E4-9CF8EFF95CD4/Meme Collector.app/Meme Collector". Architecture set to: i486-apple-macosx.
LLDBの実行中に
MoneyManager
シングルトンに
MoneyManager
みてください。
call [[MoneyManager sharedManager] purchaseCurrency]
通貨を「購入」しようとすると、
NO
が返されます。つまり、通過しません。
そして
sharedManager
オブジェクトの説明を印刷してみてください。 次のコマンドを入力します。
po [MoneyManager sharedManager]
そして、この説明には何が含まれていますか?
nil
達成するために必要なもの! シングルトンは、少なくともわかりやすい結果を返さず、購入プロセス中にエラーメッセージを表示しません。 クラッカー
nil
単純で理解できない。
次のコマンドを使用して、アプリケーションの実行を継続します。
c
[通貨の購入]ボタンを使用して、アカウントを法的に補充してください。 何も起こりません! 結局のところ、LLDBはまだプロセスに接続されています。

プロセスからデバッガーを切断します。Ctrl+ Cを押してから、 qコマンドを入力します。 [通貨の購入]ボタンが再び機能します。
デバッガーの確認に加えて、より厳密なアプローチを取ることができます。
ptrace
関数は
これを行うには、IDEに戻ってmain.mを開きます。 1つのヘッダーファイルを追加します。
#include <sys/ptrace.h>
そして、
main
関数の先頭までの3行:
#ifndef DEBUG ptrace(PT_DENY_ATTACH, 0, 0, 0); #endif
ptrace
関数は、GDBやLLDBが行うように、デバッガーで一般的にプロセスに接続するために使用されます。
ptrace
への呼び出しを追加しました。これは、特別なパラメーター
PT_DENY_ATTACH
を使用して、オペレーティングシステムに他のプロセス(つまり、デバッガー)がアプリケーションに接続しないように要求します。
IDEからアプリケーションを実行します。
Xcode :アプリケーションが起動していないようです。 何が起こっているの? すぐに消える黒い画面が表示されます-このアプリケーションはメモリにロードされ、実行を開始します。 同時に、XcodeはLLDBを接続することを望んでいますが、iOSはそれを許可せず、デバッガープロセスを終了します。 「デバッガーが完了すると、Xcodeは「アプリケーションが終了したので、停止します」と考えます。 最後のフレーズはワイルドに聞こえますが、そのように機能します。 -注 あたり
AppCode : 実行 (⌘R)コマンドを使用すると、アプリケーションが正常に起動し、 デバッグコマンド(Ctrl + D)を使用すると、Xcodeのようにクラッシュします。
そして、シミュレーターから正しく起動します。 上記のように、デバッガーを今すぐ接続してみてください。
lldb -p { Meme Collector}
結果は予測可能です:
Attaching to process with: process attach -p 3435 error: attach failed: process did not stop (no such process or permission problem?)
これは、habrを読んだ幼児がアプリケーションで遊んでしまうのを防ぐ良いツールです。 しかし、それはあごひげを生やしたハッカーを止めません。 続行する前に、
ptrace
関数を呼び出して変更するとプロセスが停止します。

一般的に、あまりにも快適に感じることはありません。 ハッカーは、特に実行時にObjCアプリケーションを操作するために、 Cycript (JavaScriptを連想させるスクリプト言語)の使用を好みます。 私たちが行ったデバッガ保護は、Cycriptからあなたを保護しません。 前の記事で会話を始めた方法を思い出してください。
安全なアプリケーションはありません!
バイナリを準備しています
バイナリファイルの変更に進む前に、それをパーツに分解する方法と、その内容を確認しましょう。
特定の概念を説明するために、バイナリ内の特定のアドレスを定期的に参照します。 私のようなバージョンのコンパイラ(たとえば、新しいXcodeに付属)がない場合は、リリースの代わりにデバッグ構成をコンパイルするか、プロジェクトに自分で変更を加えます。アドレスは異なる場合があります。 これは気にしないかもしれません-アイデアを理解するためにプレゼンテーションに従ってください。
OS XおよびiOSの実行可能ファイル形式はMach-Oと呼ばれます。 原則として、バイナリは、バイナリの場所とデータに関するすべての情報を含むヘッダーで始まります。 この情報の後には、セグメントごとにファイルをマークアップする方法を示すロードコマンドが続きます。 さらに、これらのコマンドは特別なフラグを定義します。たとえば、ファイル内のバイナリデータが暗号化されるかどうかです。
各セグメント (セグメント)には、1つまたは複数のセクション (セクション)があります。 2つのタイプのセクションに注目する価値があります。
- テキストセクション 。 主に読み取り専用データ用。 たとえば、ソースコード、C行、定数など。 読み取り専用データの特性は、システムがRAMを使い果たすと、これらのセクションからデータを簡単に解放し、後で(必要に応じて)ファイルから再度ロードできることです。
- データセクション 。 主にコードから変更できるデータ用。 静的変数のBSSセクション、グローバル変数の共通セクションなどが含まれます。
Appleには、英語の優れたMach-O形式のリファレンスもあります 。 -注 あたり
次に、
Meme Collector
バイナリを調べて、すべての動作を確認します。 見出しから始めましょう。 ターミナルのメインバンドルフォルダー「Meme Collector.app」に、次のように入力します。
otool -h "Meme Collector"
このコマンドは、Meme Collectorバイナリ実行可能ファイルのヘッダーを出力します。 このようなもの:
Meme Collector: Mach header magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 0xfeedface 7 3 0x00 2 25 3372 0x01000085
拡張モード
注:ファイルには25個のロードコマンド(
cmds
)があり、それらは3372バイト(
sizeofcmds
)を占有します。 これらのコマンドを見てみましょう:
otool -l "Meme Collector"
(この前に、⌘Kを押してターミナルウィンドウをクリアできます。スクロールすると便利です。- 注 )
たくさんの行を取得します。 これらの行から(予備的な準備がなくても)、セグメントとセクションをメモリにロードする順序に関する多くの興味深いことがわかります。 しかし、この研究はこのチュートリアルの範囲を超えています; 独立した研究のために最も好奇心の強い読者にお任せしましょう。
そして、レッスンを続けます。 __objc_classnameという( ⌘F )セクションを見つけて、
offset
注意してください。これは、アプリケーションが占有する仮想メモリの先頭に対するこのセクションの「位置」または「シフト」です。
拡張モード
について 最も好奇心、盛な、おそらく、すでに
と
違いを理解しているのは、なぜこの違いはどこでも0x1000 = 4096バイトに等しいのでしょうか? まだ読んでいない場合は、非常に興味深いページである
詳細を
読みください。
offset
について 最も好奇心、盛な、おそらく、すでに
addr
と
offset
違いを理解しているのは、なぜこの違いはどこでも0x1000 = 4096バイトに等しいのでしょうか? まだ読んでいない場合は、非常に興味深いページである
__PAGEZERO
詳細を
__PAGEZERO
読みください。
ここで、
__objc_classname
セクションのシフトは159942バイト(10進数)です。 下の画像では、左側に赤の下線が引かれています。
ターミナルに移動します。 新しいターミナルウィンドウ( ⌘N )を開き、同じフォルダー「Meme Collector.app」から以下を実行します。
strings -o "Meme Collector"
strings
コマンドはバイナリファイル内の行を検索し、
-o
フラグは各行にファイルの先頭からの相対位置を書き込みます。
さて、159942には何がありますか? クラス名! (赤で強調表示されています。)論理的に、
__objc_classname
セクションを探していました。

このセクションのすぐ上には、
__objc_methname
セクションがあり、140887から始まります。ここでは、
init
メソッドで始まるメソッド名(青で強調表示)があり
init
。
拡張モード
メソッドが最初に来るのはなぜだろうか?
init
メソッドが最初に来るのはなぜだろうか?
メソッド名が終わるところで、クラス名はすぐに始まります。
__objc_classname
セクションは、
__objc_methname
セクションの直後に
__objc_methname
ます。 ブートコマンドでは、それらは次々に実行され、メモリに順次ロードされます。
したがって、ロードコマンドを使用すると、Mach-Oバイナリの混を整理することができます。 この知識で私たちは進みます... tadaaam! コードセクションの修正に。
重砲:逆アセンブラーとリバースエンジニアリング
深刻な銃を発射する準備はできていますか? 最後に、アプリケーションバイナリファイルを変更する方法を学習します。
あなたはおそらくあなたの人生でしばしばフレーズを聞くでしょう:アプリケーションは「ハッキングされています」。 これは、誰かがアプリケーションを修正して、... mmm ...開発者が意図したものとは異なる動作をすることを意味します。 たとえば、登録を求めません。 したがって、私たち(著者および翻訳者)は、私たちの仕事があなたに役立つことを心から願っています。 アプリケーションを保護するためだけに。
IDA Demoと、 Hex FiendなどのHEXエディターをダウンロードします。 IDAは、ハッカーがバイナリを学習するときに最も頻繁に使用するツールです。 これは非常に強力な逆アセンブラ、デバッガ、デコンパイラです。 そして、フルバージョンはそれほど高価ではありません。
しかし、約15秒前に聞いたプログラムを購入する準備ができていない場合、IDAは機能が制限されたデモ版を提供します。 デモ版には、学習できるアセンブラファイルの種類が限られています。また、コードを変更する機能は無効になっています。
ただし、x86アセンブラー型があります。そして、すべての変更を別のプログラム-Hex Fiendで手動で行います。
拡張モード
« … x86? ARM?» — . , iOS- ARM-. x86. , , .
IDAをインストールして実行します。世界初のプログラマーであるAda Lovelaceに歓迎されてい

ます。Goボタンを押してください。ターミナルでは、(はい、はい)まだバンドルフォルダー(
Meme Collector.app
)にいます。次のコマンドを入力して、このフォルダーをFinderに表示します。
open -R .
最後のポイントを忘れないでください。ここのドット記号は「現在のフォルダー」を意味します。
次に、開いたFinderウィンドウで、右クリック> パッケージの内容を表示:

(OS Mavericksはロシア語でバンドルを「パッケージ」と呼びますが、この名前は私には情報がないようです。- 注)
バンドルパッケージ内には、実行可能ファイルがあります。
Meme Collector
、IDAウィンドウにドラッグしてダイアログボックスを表示します。

実際、IDAはこのバイナリがi386アーキテクチャの実行可能ファイルであると判断しました。
設定は上記の設定に対応している必要があります(何も変更する必要はないと思います)-[
「Objective-C 2.0構造が検出されました。それらを解析し、メソッドの名前を変更しますか? "-はい、答えます。 「近接ビュー」について質問する場合は、いいえと答えます。
IDAがバイナリファイルの処理を完了すると、
start
クリックし、美しいフローチャートが表示されるまでスペースバーを押します:(

私の場合、1つのスペースでは不十分でしたが、何回かEnterキーを押さなければなりませんでしたが、何が何なのかすぐにわかります-およそPer)
そして、XcodeまたはAppCodeでプロジェクトを開きます。プレゼンテーションを減らすために、コードを少し見ていきます。
開くMoneyManager.mとメソッドを見てみましょう
buyObject:
- (BOOL)buyObject:(id<PurchasableItemProtocol>)object { NSUInteger totalMoney = self.money.unsignedIntegerValue; NSUInteger cost = [object cost].unsignedIntegerValue; if (totalMoney < cost) { return NO; } _money = @(totalMoney - cost); return [self saveState]; }
アルゴリズムを学ぶ、それは非常に簡単です。インスタンス変数に
_money
十分な支払いがない場合、関数は戻り
NO
、トランザクションは完了しません。購入を許可/禁止するこの条件文は、1つのブール値に依存しています。ユーザーには十分なお金がありますか? (ストア内の一部の人々の行動を思い出させますか?- 約)
このテストを回避する場合(アセンブラー用語で「ジャンプオーバー」)-好きなものを購入できた場合、値
_money
は購入時の要素とは見なされなくなります。
これで、逆アセンブラで同じコードが見つかりました。 IDAに戻り、[関数]パネルで任意の関数をクリックして(このパネルをアクティブにするだけ)、Ctrl + Fを押します(またはメニューから:[ 編集 ] > [クイックフィルター])。入力ボックスが表示され、関数を検索します。
buyObject:

そう、見つかった、メソッドの名前をダブルクリックする必要があります。 IDAには逆アセンブラウィンドウが表示され、条件付きステートメントとコード分岐を完全に実証します。アセンブラ

知識がなくても、ソースコードからは、緑色の「右」矢印がまさにハッカーに行きたい場所であると想定できます。そこで多くのアクションが実行されます。赤い矢印「左」の下の短いコードは、より簡潔な「」に似ています。アセンブラを少しロードします。上のブロックの一番下にある条件演算子(「ジャンプ」)を見てみましょう。そこから2つの矢印(赤と緑)が出ます。それは
buyObject:
return nil
jnb
、「以下でない場合はジャンプ」(「未満」の場合は移動)を意味します。どうやら、この命令を「常にジャンプ」-命令に置き換える必要があります
jmp
。
指示を置き換えるには、それを見つける必要があります。オペランドをダブルクリックします
jnb
。黄色で強調表示されます。次に、スペースバーを押してテキストモードに切り替えます。
これは同じ情報ですが、線形形式です。コマンドで行番号を見つけます
jnb
(この行は強調表示されています):

私の場合、アドレスは0x00018D88でした。他のアドレスを使用できます。
オペランドコード「
jnb short
」は
0x73??
、疑問符が表示される相対オフセットをバイト単位で示す場所です。オペランドコードを
0xEB??
-無条件ジャンプコード " に変更する必要があります
jmp short
「(同じバイト数の場合)。オペランドコードはどこで入手しましたか?たとえば、Intel Software Developer's Manualから(ちなみに、エキサイティングな読み物です!)
ダウンロード(まだダウンロードしていない場合)Hex Fiend。たとえば、フォルダにコピーしてインストールします
/Applications
。 (まだバンドルフォルダー「Meme Collector.app」にいると仮定して)コマンドを入力します:
open -a "/Applications/Hex Fiend.app/" "Meme Collector"
ウィンドウが開き、バイナリが表示されます。いいですね ここにあります-私たちの友人:
__objc_classname
と他のセクション。私たちの前には、明らかに実行可能ファイルのヘッダーがあります。

ターミナルに入力します:
otool -l "Meme Collector" | grep -a10 "sectname __text"
前に見たように、
otool -l
バイナリファイルをメモリにロードするコマンドを表示します。コードセクション(「テキスト」セクション)に関心があるため、コマンドで検索領域を絞り込み
grep
ます。次のようなものを得ました:
segname __TEXT vmaddr 0x00001000 vmsize 0x0002e000 fileoff 0 filesize 188416 maxprot 0x00000007 initprot 0x00000005 nsects 11 flags 0x0 Section sectname __text segname __TEXT addr 0x00002970 size 0x0001dec3 offset 6512 align 2^4 (16) reloff 0 nreloc 0 flags 0x80000400 reserved1 0 reserved2 0
ここでは、セクションの開始アドレス(
addr
)0x00002970、およびシフト(
offset
)-6512 (10進数)が表示されます。IDAを取得し、コードが開始する開始アドレスが正確に0x2970であることを確認できます。そのためには、最上部までスクロール(「線形」形式)する必要があります。(特定の値は異なる場合がありますが、意味は同じです)。
いいね!算術を行う時間:命令のオフセット
jnb
(「テキスト」セクションで見つかった)をバイナリファイル内の絶対値に再計算する必要があります。 IDAで見つかったアドレスのバイトを変更しようとすると、おそらくどこかでクラッシュをキャッチします。それらは一致しません。
気が散らないように、次の式を用意しました。
{バイナリファイル内のコマンドの絶対位置} =
{コマンドアドレス}-{テキストセクションの開始アドレス} + {テキストセクションのシフト}
私の場合:
コマンドアドレス
jnb
= 0x18D88(IDAから)
テキストセクションの開始アドレス= 0x2970(of
otool
)
テキストセクションのシフト= 10進数6512(of
otool
)
計算機を取得し、メニューから切り替えます:[表示]> [プログラマー用 ] (10進数と16進数を入力するときは、目的の番号システムに切り替えることを忘れないでください)。
私が得た:
0x18D88-0x2970 + 6512 = 0x17D88
拡張モード
«Advanced Mode» , 0x1000 (0x18D88 – 0x17D88).
計算が正しいことが判明した場合、これは
jnb
IDA で確認した命令のアドレスになります。 Hex Fiendで、⌘L(または[編集]> [オフセットへジャンプ ]メニュー)を押して、アドレス入力フィールドを開きます。アドレス値を入力します(16進形式で入力する場合
0x
は、最初に忘れないでください)。
うーん、何らかの理由で10進数の行番号。さて、再計算してみましょう:0x17D88 = 97672、つまり97664の位置から、さらに8バイトを右に数える必要があります。 8バイト= 16桁の16進数= 2つの4バイトワード。ご覧のとおり、Hex Fiendは単語に従ってバイナリ「テキスト」をグループ化します。

最初の2つの単語をスキップし、3番目の単語の先頭で-ここに-操作コードを挿入します
0x7304
。
0x73
-命令コード、および
0x04
-オフセット、プロセッサが前方に「ジャンプ」するバイト数。
正しい
0x73
上
0xEB
(慎重に:1 [Backspace]を押しはすぐに1バイト= 2進数の文字を削除します)。ファイルを保存(⌘S)して閉じます。シミュレータを開き、メモリからアプリケーションを削除して、再度実行します(IDEからではなく、シミュレータから再コンパイルしないように)。お金がなくなるまでミームを買う。 「お金」よりも高価な製品を購入しようとしたときに何が起きましたか?
はい、「ユーザーはお金を持っていますか?」という条件のチェックを本当に捨てました。お金がなくても、トランザクションは実行されます。そして小さなボーナス:符号なしの値
_money
「ループ」は、メモリ内の数値の表現の特性により、負ではなく10 32(約40億)弱になります。
リバースエンジニアリング保護
どうやって自分を守るのですか?覚えている、私は言った:「何も安全ではありません。」このステートメントはここでも機能します。リバースエンジニアリングは非常に難しい場合がありますが、攻撃者が深刻な場合、攻撃者を止めることはできません。あなたの唯一の望みは、攻撃者をあまりにも混乱させて、このビジネスを放棄し、他のアプリケーションを破壊することです。
1つの方法は、プリプロセッサを介して重要なクラスとメソッドの名前を変更することです。IDEでプロジェクトを開き、ファイル「Meme Collector-Prefix.pch」を見つけます。行を追加します。
#define MoneyManager DS_UIColor_Theme
このコードは、出現するすべての「
MoneyManager
」を、クラッカーにとってあまり面白くない名前「」に置き換えます
DS_UIColor_Theme
。
このアプローチは、何も壊さないように細心の注意を払って使用する必要があります。選択した新しい名前がアプリケーションのどこにも見つからないことを100%確認する必要があります。そうしないと、混乱してしまい、アプリケーションで不可解なことが起こり始めます。
通常、実行可能ファイルには、アドレスから関数やメソッドの読み取り可能な名前へのマッピングが保存されているシンボルテーブルがあります。そして今、コードを混同する別の方法は、プロジェクトをビルドした後にキャラクターテーブルを削除することです。 Objective-Cメッセージは単一の関数で処理されるため、これはCおよびC ++関数の非表示に適しています
objc_msgSend()
。MoneyManager.mを再度
開きます 次のC関数を先頭に追加します。
BOOL aSecretFunction(void) { return YES; }
次に、アプリケーションを再度コンパイルします。シンボルテーブルでこの関数の存在を確認してください。ターミナルから:
nm "Meme Collector" | grep aSecretFunction
このコマンド
nm
は、文字の表を表示し、
grep
関数名でフィルターします。ここにあります:
00018b8f t _aSecretFunction
iOSアプリケーションから文字テーブルを削除する簡単な方法は、プロジェクト設定で2つのオプションを見つけることです:Deployment PostprocessingとStrip Linked Product、

そしてYesに設定:次に、プロジェクトを「クリーン」する必要があります(Xcode:Product> CleanまたはAppCode:Run> Clean)そして再コンパイルします。次に、ターミナルに移動して同じコマンドを実行します。
nm "Meme Collector" | grep aSecretFunction
いいね!を参照したキャラクターを削除しました
aSecretFunction()
。これで、クラッカーはコード内の重要なポイントを見つけるためにより多くの時間を費やす必要があります。
次は?
攻撃者ができることを確認しました:
- Objective-Cセレクターの名前を見やすい。
- アプリケーションがアクセスするファイルを操作する
- ネットワークの相互作用を傍受および変更します。
- ランタイムを管理する
- アプリケーションの実行可能ファイルを変更します。
アプリケーションを作成するとき、これらのことを覚えておくことが重要です。アプリケーションをより安全にするためにどれだけの労力を費やすかを考えてください。セキュリティとは何ですか?これは常に、リソース(時間)、ユーザーの問題のレベル、およびハッキングの可能性の間のバランスです。
IOSアプリのセキュリティは深刻なトピックです。あなたはまだ多くを学ぶことができます。これまでのところ、表面に少しだけ傷を付けました。デバッガーおよびその他の分析ツールの機能の全範囲は、さらに深いところにあります。このトピックに興味がある場合は、テストデバイスのジェイルブレイクについて考えることをお勧めします。ファイルシステムは、研究のための豊富な食品を提供します。
英語に問題がない場合は、iOSアプリケーションのハッキングと保護を確認してください。(著者Jonathan Zdziarski)。それは少し時代遅れですが(Appleアプリケーションの暗号化メカニズムの変更をグーグルで調べる必要があります)、この記事の著者はiOSとセキュリティに関する彼のお気に入りの本の1つです。
さらに2冊の本:
ハッキング:The Art of Exploitation、2nd Edition by Jon Erickson
Mac OS X and iOS Internals:To the Apple's Core by Jonathan Levin
フォーラム:
http://www.woodmann.com
http://www.reddit.com/r/ReverseEngineering
コードインジェクションに関する記事:
http : //blog.timac.org/?p=761
著者はコメント、および翻訳で記述できます。dev
@
x128.ruメールに書き込みます。