マシンコードを別のアーキテクチャに変換できますか?


11

これは、ARM上でWindowsサーバーを実行することに関する質問に関連しています。私の質問の前提は、実行するためにコンパイルされたアーキテクチャとは異なるアーキテクチャでバイナリを実行するために、マシンコードをあるアーキテクチャから別のアーキテクチャに変換できるかどうかです。

QEMUおよびその他のエミュレーターは、命令をその場で翻訳できるため、コンパイルされていないコンピューターで実行可能ファイルを実行できます。プロセスをスピードアップするために、オンザフライでではなく、事前にこの翻訳を行わないのはなぜですか?アセンブリの私のやや限られた知識から、のような命令のほとんどMOVADDそして他の人がアーキテクチャ間で移植する必要があります。

すべてのマシンはチューリング完了なので、直接マッピングされていないものは、他の命令セットにマッピングできます。これを行うのは複雑すぎるでしょうか?何らかの理由で私が慣れていないので、それはまったく機能しませんか?動作しますが、エミュレータを使用するよりも良い結果は得られませんか?


この手法は、(そのフレーク性に加えて)あまり必要ないため、好まれない可能性があります。移植性/標準化は、(Wintelが世界を引き継いだという理由だけで)最近では(少し)優れています。また、クロスマシンエミュレーションが本当に必要な場合(たとえば、アプリ開発環境の電話エミュレーター)、直接エミュレーションはより信頼性が高く正確な結果。さらに、プロセッサは十分に高速であるため、エミュレーションのコストは過去ほど深刻ではありません。
ダニエルRヒックス

回答:


6

短い答え:コンパイルされ、リンクされた実行可能ファイルを翻訳することはできません。技術的には可能ですが、達成することはほとんどありません(以下を参照)。 ただしアセンブリのソースファイル(手順とラベルを含む)がある場合は、それを行うことは非常に可能です(ただし、アセンブリソースを取得する場合、プログラムがアセンブリで記述されていない限り、元のプログラムソースコードはまあ、それであなたは最初に異なるアーキテクチャのためにそれをコンパイルする方が良いでしょう)。


長い答え

QEMUおよびその他のエミュレーターは、命令をその場で翻訳できるため、コンパイルされていないコンピューターで実行可能ファイルを実行できます。プロセスをスピードアップするために、オンザフライでではなく、事前にこの翻訳を行わないのはなぜですか?

原則として簡単に思えるかもしれませんが、実際には、いくつかの主な理由でほとんど不可能です。開始するには、命令セットごとに大幅に異なるアドレス指定モード、異なるオペコード構造、異なるワードサイズが使用され、必要な命令さえないものもあります。

あなたは命令を置き換えるために必要としましょうXYZ2つの命令で、ABCDEF。これで、プログラム全体のすべての相対/オフセットアドレスがその時点から効果的にシフトされたので、プログラム全体を分析して調べ、オフセットを変更する必要があります(変更前後)。さて、オフセットの1つが大幅に変更されたとしましょう-アドレスのサイズを変更する可能性のあるアドレス指定モードを変更する必要があります。これにより、再びファイル全体を再スキャンし、すべてのアドレスを再計算する必要があります。

アセンブリプログラムを作成する場合、ラベルを使用できますが、CPUは使用しません。ファイルがアセンブルされると、すべてのラベルは相対、絶対、またはオフセットの位置に計算されます。なぜこれがすぐに重要なタスクになり、不可能に近いのかがわかります。単一の命令を置き換えるには、先に進む前にプログラム全体を何百回もパススルーする必要がある場合があります。

アセンブリに関する多少限られた知識から、MOV、ADDなどのほとんどの命令はアーキテクチャ間で移植可能である必要があります。

はい、しかし上で概説した問題を見てください。マシンのワードサイズはどうですか?アドレスの長さ?同じアドレッシングモードもありますか?繰り返しますが、単に「検索して置換」することはできません。プログラムの各セグメントには、明確に定義されたアドレスがあります。他のラベルへのジャンプは、プログラムのアセンブル時にリテラルまたはオフセットメモリアドレスに置き換えられます。

すべてのマシンはチューリング完了なので、直接マッピングされていないものは、他の命令セットにマッピングできます。これを行うのは複雑すぎるでしょうか?何らかの理由で私が慣れていないので、それはまったく機能しませんか?動作しますが、エミュレータを使用するよりも良い結果は得られませんか?

あなたはそれが両方可能であり、はるかに速くなることを100%正しいです。ただし、これを達成するためのプログラムを作成することは、上記で説明した問題を除いて、非常に困難であり、非常にありそうにありません。

実際のアセンブリソースコードがあれば、マシンコードを別の命令セットアーキテクチャに変換するのは簡単です。ただし、マシンコード自体はアセンブルされるため、アセンブリソース(メモリアドレスの計算に使用されるさまざまなラベルを含む)がないと、非常に困難になります。繰り返しますが、単一の命令を変更すると、プログラム全体のメモリオフセットが変更され、アドレスを再計算するために何百ものパスが必要になる場合があります。

数千の命令を持つプログラムに対してこれを行うには、数十万のパスではなくても数十のパスが必要になります。比較的小さなプログラムの場合、これは可能かもしれませんが、プログラム内のマシン命令の数とともにパスの数が指数関数的に増加することに注意してください。まともなサイズのプログラムでは、ほぼ不可能です。


基本的に、ソースオブジェクトコードを「逆コンパイル」または「逆アセンブル」する必要があります。比較的簡単なコード(特に、特定のコンパイラーまたは既知の「スタイル」があるコード生成パッケージによって生成されるコード)の場合、ラベルなどの再挿入は非常に簡単です。ただし、確かに、最新の高度に最適化されたコンパイラーは、この方法で「grock」するのがはるかに難しいコードを生成します。
ダニエルRヒックス

@DanHソースオブジェクトコードがある場合、アセンブリコード(マシンコードではない)がほとんどあります。オブジェクトファイルには、リンクされるマシンコードの名前付き(読み取り:ラベル付き)シーケンスが含まれています。問題は、オブジェクトコードファイルを実行可能ファイルにリンクするときに発生します。これらの小さなセグメントは、リンクされた実行可能ファイル全体よりもはるかに簡単に処理(またはリバースエンジニアリング)できます。
ブレークスルー

確かに、特定のオブジェクトファイル形式により、ジョブが少し簡単になります。デバッグ情報を含むも​​のもあり、ほとんどのラベルを復元できます。その他はあまり役に立ちません。場合によっては、この情報の多くはリンクされたファイル形式でも保存されますが、そうでない場合もあります。非常に多くの異なるファイル形式があります。
ダニエルRヒックス

2

はい、あなたが提案することができ、行われています。それはあまり一般的ではなく、この技術を使用する現在のシステムは知りませんが、技術的な実現可能性の領域内に間違いなくあります。

あるシステムから別のシステムへのコードの移植を可能にするために、かつて誰もが私たちが持っている粗雑な「移植性」を達成する前に、それは多く行われていました。「ソース」の複雑な分析が必要であり、コードの変更やその他の奇妙な手法によって妨害される可能性がありましたが、それでも実行されました。

最近では、IBM System / 38-iSeries-System iのようなシステムは、コンパイルされたプログラムと共に保存された中間コード(Javaバイトコードに類似)の移植性を利用して、互換性のない命令セットアーキテクチャ間の移植性を実現しています。


通常ははるかに古い(単純な)命令セットでこれが行われたことに同意します。1970年代には、古い7xxバイナリプログラムをSystem / 360に変換するIBMプロジェクトがありました。
おがくず

1

マシンコード自体はアーキテクチャ固有です。

複数のアーキテクチャ(Javaがおそらく最もよく知られている)全体で簡単に移植できる言語は、非常に高いレベルである傾向があり、それらを機能させるためにマシンにインタープリターまたはフレームワークをインストールする必要があります。

これらのフレームワークまたはインタープリターは、実行する特定のシステムアーキテクチャごとに記述されているため、「通常の」プログラムよりも移植性は高くありません。


2
コンパイル言語も、インタープリター言語だけでなく、移植性があります。最終的にコードをプラットフォームが認識できるものに変換するのは、アーキテクチャ固有のコンパイラです。唯一の違いは、コンパイルされた言語がコンパイル時に翻訳され、解釈された言語が必要に応じて行ごとに翻訳されることです。
MaQleod

1

絶対に可能です。マシンコードとは何ですか?そのまさに言語特定のコンピューターが理解すること。自分をコンピューターと考え、ドイツ語で書かれた本を理解しようとしています。あなたは言語を理解していないので、あなたはそれをすることはできません。ドイツ語の辞書を使って「Kopf」という単語を調べると、英語の「head」という単語に翻訳されていることがわかります。使用した辞書は、コンピューターの世界ではエミュレーション層と呼ばれるものです。簡単でしょう?まあ、それはもっと難しくなります。ドイツ語の「Schadenfruede」を取り、英語に翻訳します。英語には単語がありませんが、定義があります。同じ問題がコンピューターの世界にも存在し、同等の単語を持たないものを翻訳します。エミュレーションレイヤーの開発者は、その単語の意味を解釈し、ホストコンピューターに理解させる必要があるため、これにより直接ポートが困難になります。時には期待どおりに機能しないことがあります。インターネットで本やフレーズなどの変な翻訳を見たことはありますか?


1

説明するプロセスは静的再コンパイルと呼ばれ、一般的に適用可能な方法ではなく、実行されています。可能な範囲を超えていることを意味し、何度も行われていますが、手作業が必要でした。

調査に値する多くの歴史的な例がありますが、現代の懸念を実証することはできません。私は、完全な懐疑論者がすべてを難しいと主張する人々に本質的に疑問を抱かせる2つの例を見つけました。

最初に、この男はNES ROMの完全な静的アーキテクチャとプラットフォームを実行しました。 http://andrewkelley.me/post/jamulator.html

彼はいくつかの非常に良い点を指摘していますが、JITはさらに実用的であると結論付けています。私は実際、彼がこの状況について、これがほとんどの人が考慮する状況のタイプであるかもしれないことを彼がまだ知らなかった理由が確かではない。近道をとらず、完全なサイクル精度を要求し、基本的にABIをまったく使用しません。それがすべてだったら、概念をゴミ箱に捨てて1日と呼ぶことができますが、すべてではなく、決してそうではありませんでした。成功したすべてのプロジェクトがこのアプローチを使用しなかったためです。

さて、それほど明白ではない可能性については、すでにお持ちのプラットフォームを活用してください... Linux ARMハンドヘルドのStarcraft?ええ、このアプローチは、タスクを動的に行うことに厳密に制約しない場合に機能します。Winlibを使用することにより、Windowsプラットフォームの呼び出しはすべてネイティブになります。心配する必要があるのはアーキテクチャだけです。

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

ARMのハンドヘルドパンドラはPiよりも少しだけ強力であることを考えると、スローダウンはほとんど無視できるとドーナツにドルを投げます。彼が使用したツールはこのリポジトリにあります。

https://github.com/notaz/ia32rtools

その男は非常に手作業で逆コンパイルしたので、より少ない作業でプロセスを大幅に自動化できると信じています... 誰にも何か不可能だと言わせたり、実用的ではないと言わせたりしないでください...それを実現する新しい方法を考案したらすぐに、それは実用的かもしれません。


0

理論的には、これは可能です。より大きな問題は、あるオペレーティングシステム(またはカーネル)のアプリケーションを別のオペレーティングシステムに変換することです。Windows、Linux、OSX、iOSカーネルの低レベル操作には大きな違いがあり、それらのデバイスのすべてのアプリケーションが使用する必要があります。

理論的には、アプリケーションと、実行するためにコンパイルされたオペレーティングシステムに関連付けられたすべてのマシンコードを分解できるアプリケーションを作成し、そのマシンコードをすべて別のデバイスに再コンパイルできます。しかし、それはほぼすべてのケースで非常に違法であり、書くのは非常に難しいでしょう。事実、私の頭の中の歯車は、それについて考えるだけで捕まり始めています。

更新

以下のいくつかのコメントは私の回答に同意していないようですが、私の意見が欠けていると思います。私の知る限り、1つのアーキテクチャの実行可能バイトのシーケンスを取得し、バイトコードレベルで分解するアプリケーションはありません。これには、基礎となるOSカーネルへの呼び出しを含む外部ライブラリへのすべての必要な呼び出しを含み、別のシステム用に再構築して保存します結果の実行可能バイトコード。言い換えれば、Notepad.exeのような単純なものを使用して、それが存在する小さな190kファイルを分解し、LinuxまたはOSXで実行できるアプリケーションに100%再アセンブルできるアプリケーションはありません。

質問の質問者は、ソフトウェアを仮想化したり、WineやParallelsのようなプログラムでアプリケーションを実行したりできるのなら、なぜ異なるシステムのバイトコードを単純に再変換できないのかを知りたいと思っていたと理解しています。理由は、別のアーキテクチャ用にアプリケーションを完全に再構築する場合、再構築する前に、実行に必要なすべてのバイトコードを分解する必要があるためです。すべてのアプリケーションには、たとえばWindowsマシン用のexeファイルだけではありません。すべてのWindowsアプリケーションは、低レベルのWindowsカーネルオブジェクトと関数を使用して、メニュー、テキスト領域、ウィンドウのサイズ変更、ディスプレイへの描画、OSメッセージの送受信などを作成します。

アプリケーションに再アセンブルして別のアーキテクチャで実行するには、そのバイトコードをすべて逆アセンブルする必要があります。

Wineのようなアプリケーションは、Windowsバイナリをバイトレベルで解釈します。カーネルへの呼び出しを認識し、それらの呼び出しを関連するLinux関数に変換するか、Windows環境をエミュレートします。ただし、これはバイト単位(またはオペコードの場合はオペコード)の再変換ではありません。これは、関数ごとの変換であり、かなり異なっています。


まったく理論的ではありません。また、異なるオペレーティングシステムで他のバイナリを実行するアプリケーションがたくさんあります。ワインについて聞いたことがありますか?Linux、Solaris、Mac OSX、BSDなど、さまざまなOSでWindowsバイナリを実行します。
ケルタリ

オペレーティングシステムの違いは、ハイパーバイザーを使用して複数のオペレーティングシステムを実行する(または、あるシステムでWineなどの「レイヤー」をエミュレートする)ことにより、ほとんどのシステムで簡単に微調整できます。私の知る限り、すべての「最新の」非組み込みプロセッサは「仮想化可能」なので、これには命令セットのエミュレーション/翻訳は必要ありません。
ダニエルRヒックス

0

すべての専門家がこの点を見逃しているようです。「翻訳」は複雑ですが、コンピューターに非常に適しています(インテリジェントではなく、単に面倒です)。しかし、翻訳後、プログラムにはOSサポートが必要です。例:GetWindowVersionはLinuxに存在しません。これは通常、エミュレータによって提供されます(非常に大きい)。したがって、単純なプログラムを「事前翻訳」できますが、独立して実行するには巨大なライブラリにリンクする必要があります。すべてのWindowsのプログラムのイメージングには、独自のkernel.dll + user.dll + shell.dllが付属しています...


面倒なだけでなく、知性が必要です。たとえば、結果がジャンプ先のアドレスを決定する計算を見たとします。この計算は、単一の命令のように見えるものの途中にある場合があります。
デビッドシュワルツ14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.