EXE/DLLファイルを分析・解析する
EXE/DLLファイルを分析・解析するサンプルプログラムです。内容としてはファイルを読み込みファイルフォーマットの状態を表示します。次に「Import」でそのファイルで使用するDLLとAPIの一覧を取得します。DLLファイルの場合は「Export」で外部から使用可能なAPI名と序数を表示します。基本的な読込は出来ていますので、発展させればリソースの書き換えや逆アセンブルなども可能です。
2016年7月15日
実行ファイルのリソースを編集できる「リソースエディタ」をオープンソースで公開しましたので、そちらをご利用ください。
iResEditor.js
http://www.petitmonte.com/labo/iResEditor/
※言語はJavaScriptですので他の言語に移植しやすいと思います。
実行ファイルのリソースを編集できる「リソースエディタ」をオープンソースで公開しましたので、そちらをご利用ください。
iResEditor.js
http://www.petitmonte.com/labo/iResEditor/
※言語はJavaScriptですので他の言語に移植しやすいと思います。
以下のDelphiコードは32bitのみ対応ですが、iResEditor.jsは32bit/64bitの両方に対応しています。
サンプルの実行画面
サンプルコード
[Forms_ExeViewr.pas (一部)]
unit Forms_EXEViewr;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ToolWin, ImgList, Menus, ExtCtrls,ShellAPI;
{
[ EXEヘッダ ]
MS_DOSスタブ(64byte)
.
.
.
CoffHeader(24byte)
OptionalHeader(224byte) (オブジェクトファイルの場合はなし)
Section Header (40byte * CoffHeader.NumberOfSections)
[ リソースヘッダ]
ResourceDirectoryTables(16byte)
ResourceDirectoryEntries(8byte * RDT.NumberofNameEntries+RDT.NumberofIDEntries)
ResourceDirectoryTables(16byte)+ResourceDirectoryEntries(8BYte)
}
type
// MS_DOSスタブ(64byte)
TIMAGE_DOS_HEADER =packed record
e_magic : array [0..1] of char; // シグネチャ 'MZ'
e_cblp : WORD; // 最終ページのバイト数
e_cp : WORD; // ページ数
e_crlc : WORD; // リロケーションテーブルの項目数
e_cparhdr : WORD; // リロケーションテーブルのサイズ(16byte単位)
e_minalloc: WORD; // 最小必要メモリー(16byte単位)
e_maxalloc: WORD; // 最大必要メモリー(16byte単位)
e_ss : WORD; // スタックセグメントの位置(16byte単位)
e_sp : WORD; // スタックポインタ初期値
e_csum : WORD; // チェックサム
e_ip : WORD; // イニシャル IP値
e_cs : WORD; // イニシャル CS値
e_lfarlc : WORD; // リロケーションテーブルの位置
e_ovno : WORD; // オーバレイ
// ListViewではココから下は表示しない
e_res : array [0..3] of WORD; // 予約値
e_oemid : WORD; // OEM identifier (for e_oeminfo)
e_oeminfo : WORD; // OEM information; e_oemid specific
e_res2 : array [0..9] of WORD; // Reserved words
NextOffset : Dword; // 'PEヘッダ位置
end;
type
// CoffHeader(24byte)
TIMAGE_Coff_HEADER =packed record
PEMagic : array [0..3] of char ; // シグネチャ'PE'
Machine : WORD; // マシンタイプ
NumberOfSections : WORD; // この次の次に続くSection Headerの数
TimeDateStamp : DWORD; // 作成日時
PointerToSymbolTable : DWORD; // COFFシンボルテーブル位置 (常に0)
NumberOfSymbols : DWORD; // シンボルテーブルエントリ数 (常に0)
SizeOfOptionalHeader : WORD; // この次に続くOptionalHeaderのサイズ (常に224byte)
Characteristics : WORD; // 特徴(下記参照)
end;
type
TIMAGE_DATA_DIRECTORY =packed record
VirtualAddress: Dword; //アドレス
Size: Dword; //サイズ
end;
type
// OptionalHeader(224byte)
TIMAGE_Optional_HEADER =packed record
Magic : WORD; //イメージファイルの状態を識別 (実行ファイル 0x10b Romイメージ 0x107)
MajorLinkerVersion : BYTE; //リンカのメジャー バージョン番号
MinorLinkerVersion : BYTE; //リンカのマイナー バージョン番号。
SizeOfCode : DWORD; //コードセクションのサイズ
SizeOfInitializedData: DWORD; //初期化データセクションのサイズ
SizeOfUninitializedData: DWORD; //未初期化セクションのサイズ
AddressOfEntryPoint: DWORD; //開始アドレス
BaseOfCode: DWORD; //コード セクションの先頭アドレス
BaseOfData: DWORD; //データ セクションの先頭アドレス
// 以下、Windows NT固有フィールド
ImageBase: DWORD; //イメージファイルの先頭アドレス
SectionAlignment: DWORD; //メモリロード時のセクションのアラインメント
FileAlignment: DWORD; //ファイルのセクションのアラインメント
MajorOperatingSystemVersion: WORD; //必要なOSのメジャーバージョン
MinorOperatingSystemVersion: WORD; //必要なOSのマイナーバージョン
MajorImageVersion: WORD; //イメージのメジャーバージョン (常に0)
MinorImageVersion: WORD; //イメージのマイナーバージョン (常に0)
MajorSubsystemVersion: WORD; //サブシステムのメジャーバージョン(4で良い)
MinorSubsystemVersion: WORD; //サブシステムのマイナーバージョン(0で良い)
Win32VersionValue : Dword; //予約値
SizeOfImage: DWORD; //イメージのサイズ
SizeOfHeaders: DWORD; //MS-DOSスタブ+PEヘッダ+セクションヘッダのサイズ (常に1024)
CheckSum: DWORD; //イメージ ファイルのチェックサム
Subsystem: WORD; //イメージを実行するために必要なサブシステム(フラグあり)
DllCharacteristics: WORD; //DLLの特性
SizeOfStackReserve: DWORD; //保存するスタックのサイズ
SizeOfStackCommit: DWORD; //コミットするスタックのサイズ
SizeOfHeapReserve: DWORD; //保存するローカル ヒープ スペースのサイズ
SizeOfHeapCommit: DWORD; //コミットするローカル ヒープ スペースのサイズ。
LoaderFlags: DWORD; //ローダフラグ(無効)
NumberOfRvaAndSizes: DWORD; //データディクショナリエントリ数
DataDirectory : array [0..15] of TIMAGE_DATA_DIRECTORY; //上記のディレクトリ構造を参照
end;
type
//Section Header (40byte)
TIMAGE_Section_HEADER =packed record
Name : array [0..7] of char; //セクション名
PhysicalAddress : DWORD; //メモリ上でのセクションサイズ
VirtualAddress : DWORD; //メモリ上でのセクション仮想アドレス
SizeOfRawData : DWORD; //セクションのサイズまたはディスク上の初期化されたデータ
PointerToRawData : DWORD; //セクションの最初のページへのポインタ
PointerToRelocations : DWORD; //セクションの再配置エントリへのファイルポインタ
PointerToLinenumbers : DWORD; //行番号エントリ
NumberOfRelocations : WORD; //セクション内の再配置エントリの数
NumberOfLinenumbers : WORD; //セクションの行番号エントリの数
Characteristics : DWORD; //セクションの特性 (IMAGE_SCNxxx参照)
end;
// リソース
type
//ResourceDirectoryTables(16byte)
TResourceDirectoryTables =Packed record
Characteristics : Dword; // リソース フラグ。将来の使用のために予約(現在はゼロ)
TimeDateStamp : Dword; // リソース データが作成された時刻。
MajorVersion : Word; // メジャー バージョン番号
MinorVersion : Word; // マイナー バージョン番号
NumberofNameEntries : word; // ディレクトリ(リソース) エントリの数 文字列バージョン
NumberofIDEntries : word; // ディレクトリ(リソース) エントリの数 数値識別子バージョン
end;
type
//ResourceDirectoryEntries(8byte)
TResourceDirectoryEntries=Packed record
ID_RVA : Dword ; // リソースのタイプ
Data_SubEntry : Dword ; // 最上位ビットが0の場合 リソースデータエントリのアドレス
// 最上位ビットが1の場合 下位31ビットは他のリソースディレクトリテーブルのアドレスです(1つ下のレベル)。
end;
type
//ResourceDataEntries(16byte)
TResourceDataEntries=Packed record
DataRVA : Dword ; // リソース データ領域のリソース データの単位のアドレス。
Size : Dword ; // Data RVAフィールドによって指し示されているリソースデータのサイズ(バイト単位)。
Codepage : Dword ; // リソース データ内部のコードポイント値をでコードするために使われるコードページ
Reserved : Dword ; // 予約値
end;
// インポート
type
// ImportDirectoryTable(20byte)
TImportDirectoryTable =packed record
ImportLookupTableRVA : Dword; // インポート ルックアップ テーブルの相対仮想アドレス。
TimeDateStamp : Dword; // 結合されるまではゼロがセットされています。結合後はこのフィールドにはDLLのタイムスタンプがセットされます。
FowarderChain : Dword; // 最初のフォワーダ参照のインデックス。
NameRVA : Dword; // DLL名を含んでいるASCII文字列のアドレス。このアドレスはイメージ ベースに対する相対アドレスです
ImportAddressTableRVA : Dword; // インポート アドレス テーブルの相対仮想アドレス。
end;
// エキスポート
type
// ExportDirectoryTable(40byte)
TExportDirectoryTable =packed record
ExportFlags : Dword; // 予約済みフィールド。現在は0にセットされています。
TimeDateStamp : Dword; // エクスポート データの作成日時。
MajorVersion : word; // メジャー バージョン番号。
MinorVersion : word; // マイナー バージョン番号。
NameRVA : Dword; // DLLの名前を含んでいるASCII文字列のアドレス。イメージベース相対。
OrdinalBase : Dword; // エクスポートのための最初の序数(通常は1)
AddressTableEntries : Dword; // エクスポート アドレス テーブルのエントリ数。
NumberofNamePointers : Dword; // 名前ポインタ テーブル内のエントリ数
ExportAddressTableRVA : Dword ; // エクスポート アドレス テーブルのアドレス。イメージ ベースに対する相対アドレスです。
NamePointerRVA : Dword; //エクスポート名ポインタ テーブルのアドレス。イメージ ベースに対する相対アドレス。テーブル サイズはNumber of Name Pointersによって与えられます。
OrdinalTableRVA : Dword; //序数テーブルのアドレス。イメージ ベースに対する相対アドレスです。
end;
type
TForm_EXEViewr = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
N1: TMenuItem;
N2: TMenuItem;
Exit1: TMenuItem;
ToolBar1: TToolBar;
ToolButton2: TToolButton;
ToolButton5: TToolButton;
ToolButton3: TToolButton;
ToolButton4: TToolButton;
TreeView1: TTreeView;
Splitter1: TSplitter;
ListView1: TListView;
ImageList1: TImageList;
procedure ToolButton4Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure TreeView1Changing(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
procedure FormCreate(Sender: TObject);
procedure ToolButton5Click(Sender: TObject);
procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
private
{ Private 宣言 }
OpenDialog : TOpenDialog;
LstID : LongInt;
LstBool : Boolean;
ExportPos,ImportPos,ImportSize,ImportVirtualAddress{,ResourcePos,ResourceVirtualAddress} :Dword;// 再度、読み込み用
IMAGE_DOS_HEADER : TIMAGE_DOS_HEADER; // MS_DOSスタブ
IMAGE_Coff_HEADER : TIMAGE_Coff_HEADER; // COFFヘッダ
IMAGE_Optional_HEADER : TIMAGE_Optional_HEADER; // Optionalヘッダ
IMAGE_Section_HEADER : array [0..15] of TIMAGE_Section_HEADER; //Sectionヘッダ
procedure ListItemsClear();
function ShowImport(MemoryStream: TMemorySTream; VirtualAddress:Dword;Index : Byte):String;
procedure ShowExport(MemoryStream: TMemorySTream);
procedure Run(FileName : String);
procedure WMDropFiles(Var Msg: TMsg); Message WM_DROPFILES ;
public
{ Public 宣言 }
OPenFileName :String;
end;
var
Section_Index :byte; // セクションのインデックスを格納
var
Form_EXEViewr: TForm_EXEViewr;
implementation
uses Tokikaze_Link;
{$R *.DFM}
/////////////////////////////// 汎用関数 /////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////////////
// 仮想アドレスを手掛かりにセクションサイズとファイルオフセットを検索する
function Find_SectionFileOffset(ISH : array of TIMAGE_Section_HEADER; NumberOfSections : Integer;VirtualAddress : Dword;var SectionSize : dword) : Integer;
Var
i,offset : Integer;
begin
for i:=0 to NumberOfSections -1 do
begin
offset := VirtualAddress - ISH[i].VirtualAddress;
//仮想アドレスの挟み打ち
if((offset >= 0) and ( Dword(offset)< ISH[i].SizeOfRawData )) then
begin
SectionSize := ISH[i].SizeOfRawData - Dword(offset); // セクションのバイト数.
Result := ISH[i].PointerToRawData + Dword(offset); // 発見した仮想アドレスに対するRVA(セクションのファイルオフセット)
Section_Index := i; // セクションのインデックスを格納(大域変数)
Exit;
end;
end;
Result :=0;//与えられた仮想アドレスを含んでいるセクションを発見できなかった.
end;
// Exprotセクションからエキスポート名のみを取得
function GetExportFileName(MemoryStream: TMemorySTream;IOH :TIMAGE_Optional_HEADER): string;
Var
Line : Pchar ;
LineMem : Pointer;
Buffer : String;
ExportDirectoryTable : TExportDirectoryTable;
begin
Result:='';
ZeroMemory(@ExportDirectoryTable,sizeof(TExportDirectoryTable));
MemoryStream.ReadBUffer(ExportDirectoryTable,sizeof(TExportDirectoryTable));
// エクスポートアドレステーブルを無視
MemoryStream.Position :=Dword(MemoryStream.Position) +Dword(ExportDirectoryTable.AddressTableEntries*4);
// 名前ポインタテーブルを無視
MemoryStream.Position:=Dword(MemoryStream.Position)+Dword(ExportDirectoryTable.NumberofNamePointers)*4;
// エクスポート序数テーブルを無視(
MemoryStream.Position:=Dword(MemoryStream.Position)+Dword(ExportDirectoryTable.NumberofNamePointers)*2;
// 関数の名前を取得
// if ExportDirectoryTable.NumberofNamePointers<>0 then
// begin
GetMem(LineMem,MemoryStream.size- MemoryStream.Position);
MemoryStream.ReadBUffer(LineMem^,MemoryStream.size- MemoryStream.Position);
Line :=Pchar(LineMem);
Try
// DLLの名前を取得
while True do
begin
if Line^='' then break ;
Buffer:=Buffer+Line^;
inc(Line);
end;
Result:=BUffer;
finally
FreeMem(LineMem);
end;
// end;
end;
// Importセクションからインポート名のみを取得
function GetImportFileName(MemoryStream: TMemorySTream; VirtualAddress:Dword;StringList:TStringList):String;
Var
Line : char;
OffSet : Dword;
Buffer : String;
ImportDirectoryTable : TImportDirectoryTable;
begin
ZeroMemory(@ImportDirectoryTable,sizeof(TImportDirectoryTable));
// インポートディレクトリテーブルの取得
while True do
begin
MemoryStream.ReadBUffer(ImportDirectoryTable,sizeof(TImportDirectoryTable));
// 最後には必ず空の構造が入っている
if (ImportDirectoryTable.TimeDateStamp=0) and (ImportDirectoryTable.NameRVA=0) then Break;
// 元のポジション
OffSet :=MemoryStream.Position;
if Integer(ImportDirectoryTable.NameRVA - VirtualAddress) <0 then
begin
StringList.Add('(Error)');
Continue;
end
else
MemoryStream.Position :=ImportDirectoryTable.NameRVA - VirtualAddress;
// DLLの名前を取得
Buffer:='';
while True do
begin
MemoryStream.ReadBUffer(line,1);
if Line='' then break ;
Buffer:=Buffer+Line;
end;
StringList.Add(Buffer);
// 元のポジションの復帰
MemoryStream.Position:=OffSet;
end;
end;
サンプルプログラム一式のダウンロード
exe_analysis.zip 16.4 KB (16,861 バイト)
このサンプルは...
このサンプルは私が2000年頃に作成した「疾風 -tokikaze-」というソフトウェアの隠し機能だったものをサンプルプログラムとしています。(疾風ではEXEファイルをドロップすればばこれと同様の画面が開きます。)
ソースコードは当時のままで粗雑です。あと、リソース辺りを対応しようとして途中で挫折しているみたいです :-)
スポンサーリンク
関連記事
公開日:2015年02月27日 最終更新日:2016年07月19日
記事NO:00309
プチモンテ ※この記事を書いた人
![]() | |
![]() | 💻 ITスキル・経験 サーバー構築からWebアプリケーション開発。IoTをはじめとする電子工作、ロボット、人工知能やスマホ/OSアプリまで分野問わず経験。 画像処理/音声処理/アニメーション、3Dゲーム、会計ソフト、PDF作成/編集、逆アセンブラ、EXE/DLLファイルの書き換えなどのアプリを公開。詳しくは自己紹介へ |
| 🎵 音楽制作 BGMは楽器(音源)さえあれば、何でも制作可能。歌モノは主にロック、バラード、ポップスを制作。歌詞は抒情詩、抒情的な楽曲が多い。楽曲制作は🔰2023年12月中旬 ~ | |









