iOSアプリを破壊します! パート2

最初の部分では、データの保存と送信のセキュリティ問題のいくつかを調べました。 次に、 実行可能コードの保護について説明します 。 実行時にiOSアプリケーションの機能を変更し、リバースエンジニアリングを行います。 そして、もう一度覚えておいてください! 私たちの目標は、見苦しいクラッカーになることではなく、悪意のあるアクションからアプリケーションとユーザーを保護することです。 これを行うには、クラッカーができることを理解する必要があります。







このレッスンを正常に完了するには、アセンブラーとは何かを理解する必要があります。 この記事の著者は、 ARMチュートリアル (英語)を読むことをお勧めします。



実際、レッスンの意味を理解するために必要な知識のレベルは、Googleが途中で数分かかることです。 さて、記事の終わり近くで、アセンブラーを学ぶ必要があるかどうかを決定します。 :) -約 あたり



始めましょう



必要なもの:





これらのツールについて多くを学んでいただければ幸いです!



ランタイム操作



前のシリーズでは、.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 Mikhalkovをもう少し強奪します。



 (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. チェックが継続的に実行されるように、実行ループにチェックを含めます。

  2. セキュリティについて最も懸念しているコードの最も重要な部分にチェックを入れます。



通常、最初のオプションは望ましくありません。 その価格は、デバイスを加熱するための貴重なプロセッサ時間の無駄です。 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: 実行>構成の編集... >構成:リリース。




拡張モード
プリプロセッサマクロの代わりに、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



関数は、可能な限り GDB / LLDBをアプリケーションに接続しないようにするのに役立ちます。



これを行うには、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つのタイプのセクションに注目する価値があります。





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
      
      





拡張モード
0xfeedface(0xFEEDFACE)は16進数のアドレスか...英語のフレーズですよね? ウィキペディアの回答。 -注 あたり



注:ファイルには25個のロードコマンド( cmds



)があり、それらは3372バイト( sizeofcmds



)を占有します。 これらのコマンドを見てみましょう:



 otool -l "Meme Collector"
      
      





(この前に、⌘Kを押してターミナルウィンドウをクリアできます。スクロールすると便利です。-



たくさんの行を取得します。 これらの行から(予備的な準備がなくても)、セグメントとセクションをメモリにロードする順序に関する多くの興味深いことがわかります。 しかし、この研究はこのチュートリアルの範囲を超えています; 独立した研究のために最も好奇心の強い読者にお任せしましょう。



そして、レッスンを続けます。 __objc_classnameという( ⌘F )セクションを見つけて、 offset



注意してください。これは、アプリケーションが占有する仮想メモリの先頭に対するこのセクションの「位置」または「シフト」です。



拡張モード
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アーキテクチャの実行可能ファイルであると判断しました。



設定は上記の設定に対応している必要があります(何も変更する必要はないと思います)-[ Let's go! ]をクリックしますOK逆アセンブラーは、ファイルを小さな断片に解析し、そのスキーム(マッピング)を作成します-上記で行ったこと、しかし...言い方...より専門的に。 :)



「Objective-C 2.0構造が検出されました。それらを解析し、メソッドの名前を変更しますか? "-はい、答えます。 「近接ビュー」について質問する場合は、いいえと答えます。



IDAがバイナリファイルの処理を完了すると、もちろん、ショックを受けてメイン画面が表示されます。 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()



これで、クラッカーはコード内の重要なポイントを見つけるためにより多くの時間を費やす必要があります。



次は?



攻撃者ができることを確認しました:





アプリケーションを作成するとき、これらのことを覚えておくことが重要です。アプリケーションをより安全にするためにどれだけの労力を費やすかを考えてください。セキュリティとは何ですか?これは常に、リソース(時間)、ユーザーの問題のレベル、およびハッキングの可能性の間のバランスです。



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
メールに書き込みます。



All Articles