海賊行為から保護する効果的な方法

iOSアプリケーション開発者であれば、著作権侵害のトピックは苦痛で不快なものである可能性が高いでしょう。 appstoreでリリースしてから1時間後にhackulo.usリポジトリにアプリケーションが表示されないように、それを防ぐ方法と何をする必要があるかを理解してください。



簡単なお金を探して



最初に、Appleがあなたと私たちを保護するために取ったことを検討してください。 このDRMはFairPlayと呼ばれ、その能力には実行可能ファイルの完全な暗号化が含まれます。これは、アップルが考えているように、リバースエンジニアリングと不正コピーを防ぐはずです。 一般に、システムはタスクを処理しますが、アプリケーションがiDeviceに到達する瞬間までです。 事実、プロセッサは暗号化された命令の実行方法を知らないため、オペレーティングシステムはファイルをメモリにロードした後、その実行の直前に完全に復号化します。 すべてのクラッカーソフトウェアを悪用するのはこの機能です。 アプリケーションがメモリ内で完全に復号化され実行の準備が整うのを待って、クラッカーユーティリティはそのイメージをファイルにダンプします。



明確にするために、これをgdbで行う方法を示します。

break main #       command 1 #     ,      output.bin dump memory output.bin 0x2000 cryptsize # cryptsize   otool -l   LC_ENCRYPTION_INFO kill quit end start
      
      





次に、mach-oおよびInfo.plist実行可能ファイルのヘッダーにあるいくつかのサービスフィールドを変更します。 以上です。 私たちのアプリケーションは、無料の水泳に行く準備ができています。 しかし、最も不愉快なことは、Crackulousなどの完全に自動化されたユーティリティのリリースにより、すべてがどのように機能するかについてのわずかなアイデアがなくても、すべての学生がこの操作を実行できることです。



当然、この状況は開発コミュニティを無関心のままにすることはできません。 学童に対処するのに役立つが、初心者のクラッカーに対しても完全に無力である十数の決定で簡単にグーグルで検索できます。 なんで? よく見てみましょう。 チェックの主な目的は、アプリケーションバンドルの変更を追跡することです。 たとえば、実行可能ファイルとInfo.plistの作成の異なる時間、Info.plistのサイズが参照と異なる、または_CodeSignatureディレクトリが欠落しています。 iOSアプリに適しているため、これらのチェックはすべてフレームワークを使用してObjCで記述されています。



アプリケーションの実行可能ファイルをテキストエディターで開き、コンソールユーティリティ文字列を使用してさらに開くようにしてください。 ほら 使用するものはすべて一目でわかります。 バイナリをIDAにロードし、コードの誰がどこでこれらのNSBundle、NSDateおよびNSFileSizeを使用しているかを確認するだけで十分です。 「形成可能な」クラック検出警告はどこで使用されますか。 チェックを発見すると、クラッカーが1〜2バイトを変更することは難しくないので、チェックは常に便利な値を返します。



これらすべてのメソッドの欠点は、ObjCフレームワーク、メソッド、およびクラスを使用するため、静的分析で非常に簡単に見つけられることです。 そのため、クラスとフレームワークを使用しないと、純粋なCで記述された一般的なテンプレートでは見つからない方法が必要です。 ここに、Googleが示した最高のものがあります。

 #import <mach-o/dyld.h> #define LC_ENCRYPTION_INFO 0x21 struct encryption_info_command { uint32_t cmd; uint32_t cmdsize; uint32_t cryptoff; uint32_t cryptsize; uint32_t cryptid; }; static BOOL is_encrypted () { //         dladdr(main, &dlinfo),   iphone  , //  ,       dl- struct mach_header *header = 0x1000; struct load_command *cmd = (struct load_command *) (header+1); for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) { /* Encryption info segment */ if (cmd->cmd == LC_ENCRYPTION_INFO) { struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd; /* Check if binary encryption is enabled */ if (crypt_cmd->cryptid < 1) { /* Disabled, probably pirated */ return NO; } /* Probably not pirated? */ return YES; } cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize); } /* Encryption info not found */ return NO; }
      
      





これは、実行可能ファイルのmach-oヘッダーのLC_ENCRYPTION_INFOセクションに暗号化されたバイナリを表示するかどうかを示すcryptidフラグがあるという考え方です。 その価値に基づいて、このアプリケーションの起源について結論が導き出されます。



この時点で、パブリックエンドのすべての良いアイデア、そしてプライベートのみ



上記のコードは静的分析で検出するのが非常に難しいという事実にもかかわらず、攻撃者はgdbの下でジェイルブレイクした電話でアプリケーションを実行できます。 そしてターンバイターンモードでは、彼は記録的な速さで私たちの防御を解くことができます!

幸いなことに、Appleはそのようなトレースを禁止することを許可しています。

 ptrace(PT_DENY_ATTACH, 0, 0, 0);
      
      





実行後、アプリケーションをデバッグしようとすると、セグメンテーション違反が発生し、「神聖に侵食された」プロセスになります。 この面では、完全かつ無条件の勝利を収めたと言えます!

ただし、ptraceは関数であり、実際にはラッパーであり、実際のシステムコールではないので、急いで喜ばないでください。たとえば、次のように簡単に見つけて無効化できます。

 break ptrace #    ptrace commands 1 return #     ,      continue end run
      
      





どうする? 派手なAPIを拒否し、自分の手でシステムコールを行います!

遵守者Cは次のことを思い出すかもしれません

 #include <sys/syscall.h> syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0);
      
      





しかし、第一に、syscallはptraceと同じことができる関数であり、第二に、それはプライベートAPIの一部であり、検閲者はその使用のためにプログラムを見逃さないかもしれません。

そのため、アセンブラーで直接仲介者なしでシステムコールを行う必要があります。これはまったく怖くないからです。

 asm { mov r0, #31 // PT_DENY_ATTACH mov r1, #0 mov r2, #0 mov r3, #0 mov ip, #26 // SYS_ptrace svc #0x80 //    }
      
      





しかし、これらの命令を見つけて無力化することは、特に5〜10個の呼び出しがあり、それらがプログラムコード全体に散在している場合、比類なく困難になります。



保護の最重要点は、実行可能ファイルの整合性をチェックすることです。コード(__text)を含むセクションのチェックサムをチェックする必要があります。 ポイントは、このセクションの1バイトを変更しても、チェックサムも変更されるため、ファイルを変更しようとする試みを追跡することです! 通常の状態では、ファイルは暗号化されており、その内容を読み取ろうとする意味がないため、クラッカーとして機能し、オペレーティングシステムがそれ自体をダウンロードして復号化した後にセクションを読み取ります。 オフセットと長さに関する必要な情報は、以下で見つけることができます。

 otool -l myPrecious.app/binary ... Section sectname __text segname __TEXT addr 0x00002000 size 0x00096980 offset 4096 align 2^2 (4) reloff 0 nreloc 0 flags 0x80000400 reserved1 0 reserved2 0 ...
      
      





関心のあるコードは、すでにわかっているアドレス0x2000から始まり、長さはそれぞれ0x96980です。

このチェックのテンプレートは、たとえば次のようになります。

 __attribute__((always_inline)) void my_secret_checksum() { u_char *buf = 0x2000; u_int res; for (int i = 0; i < 0x96980; i++) { res += buf[i] ..... //    crc/adler   } if (res != ...) { // Achtung! Partizanen! } }
      
      





always_inline属性に注意してください。プログラムのさまざまな場所でこの関数を呼び出すだけでなく、そのコード自体を配置できます。 したがって、このチェックを複数の場所に配置すると、クラッカーはそれらすべてを見つけて無力化する必要があります。 そして、彼が実行するまで、実行可能ファイルを変更しようとする試みは阻止されます。 理想的には、各メソッドまたは関数に配置する価値があります。 メモリからの読み取りは非常に高速なプロセスであるため、頻繁に使用してもパフォーマンスに影響はありません。 そして、コード全体からそれを切り取る見通しは、最も決定的なクラッカーでさえも疎外するはずです。



あとがき



防御は攻撃に対する反作用です。 1つの攻撃に対抗するために、別の攻撃から保護されていません。 したがって、チェックのセットのみが効果的な保護になります。 私は必要最小限をカバーしようとしました。 そして、この資料が読者に彼自身の検索と研究に刺激を与えることを願っています。 頑張って! そして、あなたの売り上げのレベルが、高速道路からのロマンスに隠れないようにしましょう!



文学



Iphoneアプリケーションにパッチを当てて、実例を使用した良いチュートリアルは、 ここにあります



All Articles