blog

コマンドラインツール

日常的な開発プロセスでは、cd、Clutchなどの多くのコマンドラインツールの使用は、これらのツールは、ターミナルで実行することができます。そして、これらのツールの本質は実はMach-O型の実行可能フ...

May 26, 2020 · 5 min. read
シェア

コマンドラインツールの本質

日々の開発現場では、cdやClutchなど、ターミナル上で実行するコマンドラインツールが多く使われています。そして、これらのツールの本質は、実はMach-O型の実行ファイルです。

コマンドラインツールの開発

Xcodeでプロジェクトをコンパイルし、そのプロジェクトの実行ファイルを.appパッケージで見つけることができます。

  • iosプロジェクトの作成と冗長ファイルの削除
  • プロジェクトをコンパイルすると、Products/.appに生成された実行ファイルがあります。
  • 実行ファイルをiFunBox経由でiPhoneの/usr/binディレクトリにコピーします。
  • iPhoneを接続し、コマンドラインにツール名を入力してコマンドを実行したところ、実行権限がないことが判明。
5s:~ root# CommandToolDemo
-sh: /usr/bin/CommandToolDemo: Permission denied
  • コマンドラインツールへの実行可能パーミッションの追加
5s:~ root# chmod +x /usr/bin/CommandToolDemo
  • コマンドを実行すると、次のようなエラーが報告されます。
5s:~ root# CommandToolDemo
-sh: /usr/bin/CommandToolDemo: Bad CPU type in executable
  • 理由は、コンパイラがコンパイルする実機を選択し、iPhoneの/usr/bin/ディレクトリに再ドラッグし、実行可能な権限を割り当てていないため、コマンドラインツールは、端末で実行することができます

実行ファイルのアーキテクチャの取得

  • iPhone上の実行ファイルへのパスが与えられた場合、あなたが開発したコマンドラインツールを使用して、実行ファイルを読み、実行ファイルが含むアーキテクチャの種類を決定し、それを表示します。

  • 要件の完了では、最初のステップは、MachOViewツールを使用して、Mach-Oファイルの内部構造を表示し、Mach-Oファイルの内部構造を理解することです。

  • Mach-Oファイルのヘッダの1行目は、現在のMach-Oファイルがどのようなバイナリファイルであるかを示しています。そして2行目は、現在のMach-Oファイルがどの種類のアーキテクチャを含んでいるかを示しています。つまり、マッハOファイルの種類を判断したい場合、マッハOファイルの最初の4バイトを読むだけで判断できるのです。

  • どのような条件に基づいて、Mach-Oファイルがどのアーキテクチャ・タイプに属するかを判断するのですか?xnuのソースコードを見ると、EXTERNAL_HEADERS/mach-o/fat.hとEXTERNAL_HEADERS/mach-o/loader.hにいくつかの一般的なアーキテクチャタイプに対応する定義があります。

  • 上の図では、FAT_MAGICとFAT_CIGAMは一般的なバイナリ・ファイル・タイプを表しており、FAT_MAGICとFAT_CIGAMは以下の構造体によってuint32_t型であることがわかります。
  • 上図はloader.hで定義されているファイルタイプで、MH_MAGICとMH_CIGAMは64ビット以外のアーキテクチャタイプ、つまりarmv7またはarmv7sタイプのアーキテクチャを表し、MH_MAGIC_64とMH_CIGAM_64は64ビットタイプのアーキテクチャ、つまりarm64アーキテクチャを表します。

ソースコードを見ると問題が見つかります。上記の型定義には2種類あり、対応する値は全く逆なのです。実はこれがビッグエンドモードとスモールエンドモードです。

ビッグエンドモード:データの上位バイトがメモリの下位アドレスに保存され、データの下位バイトがメモリの上位アドレスに保存されます。

スモール・エンド・モード:データのハイ・バイトがメモリのハイ・アドレスに、データのロー・バイトがメモリのロー・アドレスに保存されます。

このことから、データを読み出す場合、ハイ・アドレスからロー・アドレスに読み出す場合と、ロー・アドレスからハイ・アドレスに読み出す場合があり、上記のように2つの定義があります。

  • まず、コマンドライン・プロジェクトを作成し、余分なファイルを削除した後、main.mに以下のコードを追加します。
#import <UIKit/UIKit.h>
#import <mach-o/loader.h>
#import <mach-o/fat.h>
int main(int argc, char * argv[]) {
 @autoreleasepool {
 
 NSString *pathStr = @"/var/containers/Bundle/Application/19EDB123-E540-4570-90FC-50768A917A43/.app/";
 NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:pathStr];
 
 NSUInteger length = sizeof(uint32_t);
 //読み取り uint32_tバイト長
 NSData *magicData = [handle readDataOfLength:length];
 //magicNumberにデータを保存する。
 uint32_t magicNumber;
 [magicData getBytes:&magicNumber length:length];
 
 if (magicNumber == FAT_MAGIC || magicNumber == FAT_CIGAM) { //汎用バイナリ
 printf("Universal Binary
");
 }else if (magicNumber == MH_MAGIC_64 || magicNumber == MH_CIGAM_64){//64 
 printf("64 bit Binary
");
 }else if (magicNumber == MH_MAGIC || magicNumber == MH_CIGAM){//非64ビットアーキテクチャ
 printf("32 bit Binary
");
 }else{
 printf("読み込みに失敗した
");
 }
 
 printf("magicNumber = 0x%x
", magicNumber);
 
 [handle closeFile];
 return 0;
 
 }
}
  • iPhoneの/usr/bin/ディレクトリに実行ファイルを置き、コマンドを実行します:
5s:~ root# CommandToolDemo
64 bit Binary
magicNumber = 0xfeedfacf

コマンドラインツールにパラメータを追加

int main(int argc, char * argv[])
argcパラメータの数を示す
argv[]ストレージパラメータ(デフォルトでは、現在のコマンドラインツールのパス"/var/containers/Bundle/Application/615972A2-4B62-4B89-A572-590B4FA2485E/CommandToolDemo "というパラメータがある).app/CommandToolDemo")

コマンドラインツールにおけるパーミッションの問題

  • デフォルトでは、開発されたコマンドラインツールは、他のアプリのファイルにアクセスできません。
  • 署名 -> 他のアプリの実行ファイルにアクセスできるように、特定のパーミッションで実行ファイルに署名します。
  • よく使われる署名ツール:ldibとcodesign

エンタイトルメント

  • パーミッション: entitlements
uhoodeMacBook-Pro:コマンドラインツール pinba$ ldid -e CommandToolDemo > CommandTool.entitlements
  • パーミッションの追加とは、対応するkey-valueをplistに追加することです。
  • 追加すべきパーミッションがわからない場合、システムのSpringBoardアプリケーションのパーミッションを使用することができます。
ldid -SSpringBoard.entitlements CommandToolDemo
-S コマンドの後にパーミッションファイルをスペースなしで記述する。
Read next

golangのruneとbyte

最初にテストコードを貼り付けます。\n// 文字列トラバーサル文字型のテスト\nパッケージ main\nインポート\nfunc main() {\ns := "

May 26, 2020 · 1 min read