Delphiでクリンクラーを使用する

はじめに



実行可能ファイルのサイズを小さくする方法はたくさんあります。 システムRTLユニットの去勢がすでに完了しており、4kイントロまたは64kデモの方向で動作するデモシーンの想像のためにサイズがまだ切り開かれている場合、コンプレッサーが働き始めます。 その中で最も有名なものの1つはUPXです。 大量のプラットフォームの下に存在し、実行可能ファイルの内容には無関係です。 しかし、リンカーコンプレッサーがあります。リンカーコンプレッサーは、その詳細のために、アセンブリ段階でも実行可能ファイルでより微妙な操作を行うことができます。

この記事では、デモシーンの輪で広く知られているリンカーのクリンクラーと、この奇跡をDelphiプロジェクトの構築に適応させるために解決しなければならなかった問題について説明します。



どのように機能しますか?



最初のステップは、動作原理を説明することです。 コンパイラの結果は、既にコンパイルされたマシンコードが置かれているオブジェクトファイルです。 リンカのタスクは、これらすべてのファイルを正しく収集し、呼び出された関数をリンクすることです(アドレスを配布します)。 Windowsの場合、ファイルはPEセクションで構成され、主なものは直接コードセクションと外部ライブラリの関数のインポートテーブルです。 PE実行可能ファイルのドキュメントに記載されていない機能の1つは、ヘッダーサイズをほぼ10倍削減できることです( 詳細 )。 PEにはインポートテーブルの優れた機能の1つもあります。インデックスを作成できます。 つまり、dllからインポートされた各関数の名前を保存する必要はまったくありませんが、序数のインデックスで十分です。 これは、dllの関数のインデックス付けがバージョンごとに変わる場合、確かに安全ではありませんが、Windowsシステムライブラリに関しては機能します。



実行可能ファイルの圧縮プログラムは、圧縮解除コードを実行可能ファイルに導入することにより、コードとインポートテーブルを圧縮できます。 クリンクラーは文字通り、人類(パトス)によって蓄積されたPE圧縮に関するすべての知識を収集し、exeカットを骨に与えます。



準備とコンパイル



そのため、理論を整理し、今度はビジネスに取り掛かり、オブジェクトファイルの生成に関連するレーキに出会います。 実際、5以降のDelphiの最新バージョンはオブジェクトオブジェクトのOMF形式を使用し、すべてのC ++コンパイラ(Crinklerの設計対象)はCOFF形式ファイルを生成します。 いくつかの「構文糖」を放棄したので、COFFを作成できたDelphi 3に安全にロールバックできます。 しかし、この問題は消えません。なぜなら、 結果のCOFFでさえ、必要なCOFFと完全には一致しません。 形式の違いの詳細に入らないように、Delphi 3 COFFでコンパイルされたものをCrinklerで使用するために修正し、最も重要なことを修正できるMicrosoftの標準リンカーがあると言います。

その結果、コンパイルと修正は次のようになります。

dcc32.exe myunit.pas -jP link.exe -edit myunit.obj
      
      





それでは、目的の形式のオブジェクトファイルをコンパイルする方法を学びましょう。 残っている問題は、システムモジュールのトリミングとエントリポイントの命名の違いの2つだけです。



DelphiのシステムモジュールはSystem.pasSysInit.pasの形式で提供され、プログラムで使用されるほぼすべての基本機能(文字列、動的配列、クラス、インターフェイス、デモシーンに必要なその他のナンセンス)が含まれています。 これは、プログラムのエントリポイントがある場所です。 コンパイラ自体はその内容に大きく依存しているため、コンパイルを成功させるにはコンパイラの存在が必要です。 Delphiコンパイラは、完全に空であっても、各ユニットのセクション呼び出しコードの初期化/ファイナライズを暗黙的に保存することに注意してください。 したがって、実行可能ファイルのサイズを保存するために、プロジェクトで別のユニットを作成する前に、パスカルのデモシーンをよく考える必要があります。

数多くの実験を通じて、System.pasをこのような喜びに変えることができました。

unit System;

interface

type

TGUID = Byte;

var

_HandleFinally : Byte;

implementation

end .








参考のために、オリジナルの_HandleFinallyは関数であり、コンパイラの腸で使用されますが、 リンカーに彼が知らない関数を誓わせたくないので、変数を使用してこのような「ハック」を行います。



SysInit.pasでは、ユニットヒープの不要な初期化コードを生成しないように、デモシーンのコードを含めることをお勧めします。 さらに、初期化セクションにコードを直接含めてください。

unit SysInit;

interface

var

_HandleFinally : Byte;

procedure ExitProcess(uExitCode: Cardinal); stdcall; external 'kernel32.dll' name '_ExitProcess@4';

implementation

initialization

//... ...

ExitProcess(0);

end .








確かに読者の鋭い目は、外部dll関数の命名の特殊性に気づきました。 意味が明確でない場合は、先頭に「_」を追加し、末尾に「@parameter_size」を追加するか、メモ帳で目的のlibファイルを開いて関数の定義を見つけることができます。 また、偽の変数があります。

このケースは、単純なbat'nikによってコンパイルされます。

 rm system.dcu rm system.obj rm sysinit.dcu rm sysinit.obj echo -------------- dcc32.exe system.pas sysinit.pas -jP link.exe -edit sysinit.obj crinkler.exe kernel32.lib sysinit.obj /OUT:test.exe /ENTRY:initialization$qqrv /PRINT:IMPORTS /PRINT:LABELS /SUBSYSTEM:WINDOWS /COMPMODE:SLOW /UNSAFEIMPORT /HASHSIZE:256 /HASHTRIES:1000 /ORDERTRIES:10000 /TRUNCATEFLOATS:8 pause
      
      





必要に応じて、パラメーターをいじって特定の状況で利益を得ることができます。

コンパイル後、サイズが572バイトのクリーンなファイルを取得します 。 ダミーファイルでこれが大きな成果とは思えない場合、コードが成長するにつれて、すべての利点を備えたコンプレッサーが機能します。



クリンクラーのリンク処理にはかなりの時間がかかるため、デバッグを行うには、リンクをアセンブリに適合させることが非常に役立ちます。

 link.exe kernel32.lib user32.lib gdi32.lib opengl32.lib sysinit.obj /OUT:test_orig.exe /ENTRY:initialization$qqrv /MERGE:.rdata=.text /MERGE:_INIT_=.text /FILEALIGN:512 /SECTION:.text,ERWX /IGNORE:4078 /IGNORE:4108 /IGNORE:4089 /NODEFAULTLIB /SUBSYSTEM:WINDOWS
      
      





この場合、1.5kbのダミーアプリケーションを即座に取得します。



デモ例を含む完全な弾薬



All Articles