フォーム上には、ソースを入力するテキストボックスと、コンパイルを実行するコマンドボタンがある。テキストボックスに、以下のソースを入力してコマンドボタンを押下すると、test.exeという実行ファイルが作成される。
let a 1 let b 2 add a b disp a
このコンパイラが受け付ける言語は、英小文字一字の変数、数値、三つの命令からなる。let命令は、変数に値を代入する。add文は指定された一つ目の変数に二つ目の変数の値が加算される。disp文は、指定された変数の値をメッセージボックスで表示する。
上記のソースでは、まず変数aに1を、変数bに2を代入し、次にaにbの値を足す。従ってaの値は1+2=3となり、最後のdisp文でaの値である3を表示する。
作成されるexeは、コードセクション、初期化データセクション、インポートセクションを含む。インポートセクションでは、kernel32.dllからExitProcess、user32.dllからMessageBoxAとwsprintfAをインポートしている。初期化データセクションは、メッセージボックス用のタイトル、メッセージボックスに表示する際にwsprintf用に使用するフォーマット文字列、wsprintfの結果を受け取るバッファ、及び変数値の格納用エリアからなる。
'------------------------ Module1.bas ------------------------ Dim DataSize As Long Public Sub MakeExe(CodeData As String) Dim i As Long Dim CodeSize As Long Dim AlCodeSize As Long Open App.Path & "\test.exe" For Binary Access Write As #2 CodeData = Trim(CodeData) CodeSize = (Len(CodeData) + 1) / 3 AlCodeSize = ((CodeSize + &H1FF) \ &H200) * &H200 'stub(そこらのEXEのを適当に貼り付けただけ) 'stub1 PutData "4d 5a 50 00 02 00 00 00 04 00 0f 00 ff ff 00 00" PutData "b8 00 00 00 00 00 00 00 40 00 1a 00 00 00 00 00" PutData "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" PutData "00 00 00 00 00 00 00 00 00 00 00 00" PutData "00 01 00 00" 'PEヘッダ位置 = 100h 'stub2 PutData "ba 10 00 0e 1f b4 09 cd 21 b8 01 4c cd 21 90 90" PutData "54 68 69 73 20 70 72 6f 67 72 61 6d 20 6d 75 73" PutData "74 20 62 65 20 72 75 6e 20 75 6e 64 65 72 20 57" PutData "69 6e 33 32 0d 0a 24 37 00 00 00 00 00 00 00 00" PutPadding &H100 'header PutData "50 45 00 00" 'PEシグネチャ = "PE\0\0" '標準COFFヘッダ PutData "4c 01" 'マシンタイプ = I386 PutData "03 00" 'セクション数 = 3 PutData "00 00 00 00" '作成日時 PutData "00 00 00 00" 'COFFシンボルテーブル位置 = 0 PutData "00 00 00 00" 'シンボルテーブルエントリ数 = 0 PutData "E0 00" 'オプションヘッダサイズ = e0h PutData "0F 03" '特性 'オプションヘッダ - 標準フィールド PutData "0b 01" 'イメージファイル = 通常の実行ファイル PutData "01 00" 'リンカのバージョン = 1.0 PutLong AlCodeSize 'コードセクションのサイズ = AlCodeSize PutData "00 02 00 00" '初期化データセクションのサイズ = 200h PutData "00 00 00 00" '未初期化セクションのサイズ = 0 PutData "00 00 01 00" '開始アドレス = 10000h PutData "00 00 01 00" 'コードセクションのRVA = 10000h PutData "00 00 00 00" 'データセクションのRVA = 30000h 'オプションヘッダ - Windows固有フィールド PutData "00 00 40 00" 'イメージがメモリにロードされるアドレス = 400000h PutData "00 00 01 00" 'メモリロード時のセクションのアラインメント = 10000h PutData "00 02 00 00" 'ファイル中のセクションのアラインメント = 512 PutData "01 00 00 00" '必要なOSのバージョン = 1.0 PutData "00 00 00 00" 'イメージのバージョン = 0.0 PutData "04 00 00 00" 'サブシステムのバージョン = 4.0 PutData "00 00 00 00" 'Reserved PutData "00 00 04 00" 'イメージのサイズ = 40000h PutData "00 04 00 00" 'スタブ+PEヘッダ+セクションヘッダのサイズ = 400h PutData "00 00 00 00" 'チェックサム = 0 PutData "02 00" 'サブシステム = GUI PutData "00 00" 'DLL特性 = 0 PutData "00 00 10 00" '予約スタックサイズ = 100000h PutData "00 20 00 00" 'コミットスタックサイズ = 2000h PutData "00 00 10 00" '予約ヒープサイズ = 100000h PutData "00 10 00 00" 'コミットヒープサイズ = 1000h PutData "00 00 00 00" 'ローダフラグ = 0 PutData "10 00 00 00" 'データディクショナリエントリ数 = 16 'データディクショナリ PutData "00 00 00 00 00 00 00 00" 'エクスポートテーブル無し PutData "00 00 02 00 00 02 00 00" 'インポートテーブル = 20000h, Size = 200h PutData "00 00 00 00 00 00 00 00" 'リソーステーブル無し PutData "00 00 00 00 00 00 00 00" '例外テーブル無し PutData "00 00 00 00 00 00 00 00" '属性認証テーブル無し PutData "00 00 00 00 00 00 00 00" 'ベース再配置テーブル無し PutData "00 00 00 00 00 00 00 00" 'デバッグデータ無し PutData "00 00 00 00 00 00 00 00" 'アーキテクチャ固有データ無し PutData "00 00 00 00 00 00 00 00" 'グローバルポインタレジスタ無し PutData "00 00 00 00 00 00 00 00" 'スレッドローカルストレージテーブル無し PutData "00 00 00 00 00 00 00 00" 'ロードコンフィギュレーションテーブル無し PutData "00 00 00 00 00 00 00 00" 'バウンドインポートテーブル無し PutData "00 00 00 00 00 00 00 00" 'インポートアドレステーブル無し PutData "00 00 00 00 00 00 00 00" '遅延インポート記述子無し PutData "00 00 00 00 00 00 00 00" 'Reserved PutData "00 00 00 00 00 00 00 00" 'Reserved 'セクションテーブル - コードセクションヘッダ PutData "2e 74 65 78 74 00 00 00" 'セクション名 = ".text" PutLong AlCodeSize 'メモリ上でのセクションサイズ = AlCodeSize PutData "00 00 01 00" 'メモリ上でのセクションの先頭のRVA = 10000h PutLong AlCodeSize 'ファイル上のセクションサイズ = AlCodeSize PutData "00 08 00 00" 'ファイル上のセクションの位置 = 800h PutData "00 00 00 00" 'セクションの再配置エントリへのファイルポインタ = 実行イメージは無し PutData "00 00 00 00" '行番号エントリ無し PutData "00 00" 'セクション内の再配置エントリの数 = 実行イメージは無し PutData "00 00" 'セクションの行番号エントリの数 = 0 PutData "20 00 00 60" ' 特性 'セクションテーブル - インポートセクションヘッダ PutData "2E 69 64 61 74 61 00 00" 'セクション名 = ".idata" PutData "9e 00 00 00" 'メモリ上でのセクションサイズ = 9eh PutData "00 00 02 00" 'メモリ上でのセクションの先頭のRVA = 20000h PutData "00 02 00 00" 'ファイル上のセクションサイズ = 200h PutData "00 04 00 00" 'ファイル上のセクションの位置 = 400h PutData "00 00 00 00" 'セクションの再配置エントリへのファイルポインタ = 実行イメージは無し PutData "00 00 00 00" '行番号エントリ無し PutData "00 00" 'セクション内の再配置エントリの数 = 実行イメージは無し PutData "00 00" 'セクションの行番号エントリの数 = 0 PutData "40 00 00 C0" ' 特性 'セクションテーブル - データセクションヘッダ PutData "2e 64 61 74 61 00 00 00" 'セクション名 = ".data" PutData "00 02 00 00" 'メモリ上でのセクションサイズ = 200h PutData "00 00 03 00" 'メモリ上でのセクションの先頭のRVA = 30000h PutData "00 02 00 00" 'ファイル上のセクションサイズ = 200h PutData "00 06 00 00" 'ファイル上のセクションの位置 = 600h PutData "00 00 00 00" 'セクションの再配置エントリへのファイルポインタ = 実行イメージは無し PutData "00 00 00 00" '行番号エントリ無し PutData "00 00" 'セクション内の再配置エントリの数 = 実行イメージは無し PutData "00 00" 'セクションの行番号エントリの数 = 0 PutData "40 00 00 C0" ' 特性 PutPadding &H400 '.idata 'インポートディレクトリテーブル - kernel32.dll PutData "3C 00 02 00" 'インポートルックアップテーブルのRVA = 2003ch PutData "00 00 00 00" '(結合後に使用される) PutData "00 00 00 00" '最初のフォワーダ参照のインデックス = 0 PutData "50 00 02 00" 'DLL名のRVA = 20050h PutData "70 00 02 00" 'インポートアドレステーブルのRVA = 20070h 'インポートディレクトリテーブル - user32.dll PutData "44 00 02 00" 'インポートルックアップテーブルのRVA = 20044h PutData "00 00 00 00" '(結合後に使用される) PutData "00 00 00 00" '最初のフォワーダ参照のインデックス = 0 PutData "60 00 02 00" 'DLL名のRVA = 20060h PutData "78 00 02 00" 'インポートアドレステーブルのRVA = 20078h 'ヌルディレクトリエントリ PutData "00 00 00 00" PutData "00 00 00 00" PutData "00 00 00 00" PutData "00 00 00 00" PutData "00 00 00 00" 'kernel32 ルックアップテーブル PutData "84 00 02 00" 'ExitProcessの名前のRVA = 20084h PutData "00 00 00 00" '終端のNULL 'user32 ルックアップテーブル PutData "92 00 02 00" 'MessageBoxAの名前のRVA = 20092h PutData "a0 00 02 00" 'wsprintfAの名前RVA = 200a0h PutData "00 00 00 00" '終端のNULL 'DLL名 PutData "4b 45 52 4e 45 4c 33 32 2e 64 6c 6c 00 00 00 00" 'kernel32名 = "KERNEL32.dll" PutData "55 53 45 52 33 32 2e 64 6c 6c 00 00 00 00 00 00" 'user32名 = "USER32.dll" 'kernel32 インポートアドレステーブル PutData "84 00 02 00" 'ExitProcessのヒント/名前のRVA = 20084h PutData "00 00 00 00" '終端のNULL 'user32 インポートアドレステーブル PutData "92 00 02 00" 'MessageBoxAのヒント/名前のRVA = 20092h PutData "a0 00 02 00" 'wsprintfAのヒント/名前RVA = 200a0h PutData "00 00 00 00" '終端のNULL 'kernel32 ヒント/名前テーブル(ドキュメントではヒント=4バイトとなっているが…) PutData "71 00" 'ExitProcessのヒント = 71h PutData "45 78 69 74 50 72 6F 63 65 73 73 00" 'ExitProcessの名前 = "ExitProcess" PutData "76 01" 'MessageBoxAのヒント = 176h PutData "4d 65 73 73 61 67 65 42 6f 78 41 00" 'MessageBoxAの名前 = "MessageBoxA" PutData "2d 02" 'wsprintfAのヒント = 22dh PutData "77 73 70 72 69 6e 74 66 41 00" 'wsprintfAの名前 = "wsprintfA" PutPadding &H600 '.data PutData "4d 65 73 73 61 67 65 00 00 00 00 00 00 00 00 00" '"Message" PutData "25 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00" '"%d" PutData "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" 'wsprintf用バッファ PutPadding &H800 '.data+100h以降は変数用とする '.text PutData CodeData PutPadding &H800 + AlCodeSize Close #2 End Sub Public Function GetLongStr(L As Long) As String Dim S As String S = Right("00000000" + Hex(L), 8) S = Mid(S, 7, 2) + " " + Mid(S, 5, 2) + " " + Mid(S, 3, 2) + " " + Mid(S, 1, 2) GetLongStr = S End Function Public Sub PutData(S As String) Dim i As Long Dim B As Byte For i = 1 To Len(S) Step 3 B = Val("&h" + Mid(S, i, 2)) Put #2, , B DataSize = DataSize + 1 Next End Sub Public Sub PutLong(L As Long) Dim S As String PutData GetLongStr(L) End Sub Public Sub PutPadding(Offset As Long) Dim i As Long While DataSize < Offset PutData "00" Wend End Sub '------------------------ Form1.frm ------------------------ Public Function GetToken(S As String, ByRef P As Long) As String Dim i As Long If P > Len(S) Then GetToken = "" Exit Function End If While Mid(S, P, 1) = " " Or Mid(S, P, 1) = vbCr Or Mid(S, P, 1) = vbLf P = P + 1 If P > Len(S) Then GetToken = "" Exit Function End If Wend i = P While Mid(S, P, 1) <> " " And Mid(S, P, 1) <> vbCr And Mid(S, P, 1) <> vbLf P = P + 1 If P > Len(S) Then GetToken = Mid(S, P) Exit Function End If Wend GetToken = Mid(S, i, P - i) End Function Private Sub Command1_Click() Dim Src As String Dim Token As String Dim P As Long Dim Code As String Src = Text1.Text P = 1 Do Token = GetToken(Src, P) If Token = "" Then Exit Do Select Case Token Case "let" Code = Code + "ba " + GetLongStr(&H430100 + (Asc(GetToken(Src, P)) - Asc("a")) * 4) + " " Code = Code + "c7 02 " + GetLongStr(CLng(GetToken(Src, P))) + " " Case "add" Code = Code + "ba " + GetLongStr(&H430100 + (Asc(GetToken(Src, P)) - Asc("a")) * 4) + " " Code = Code + "a1 " + GetLongStr(&H430100 + (Asc(GetToken(Src, P)) - Asc("a")) * 4) + " " Code = Code + "01 02 " Case "disp" Code = Code + "a1 " + GetLongStr(&H430100 + (Asc(GetToken(Src, P)) - Asc("a")) * 4) + " " Code = Code + "50 " Code = Code + "68 10 00 43 00 " Code = Code + "68 20 00 43 00 " Code = Code + "a1 7c 00 42 00 " Code = Code + "ff d0 " Code = Code + "83 c4 0c " Code = Code + "68 00 00 00 00 " Code = Code + "68 00 00 43 00 " Code = Code + "68 20 00 43 00 " Code = Code + "68 00 00 00 00 " Code = Code + "a1 78 00 42 00 " Code = Code + "ff d0 " End Select Loop Code = Code + "68 00 00 00 00 " Code = Code + "a1 70 00 42 00 " Code = Code + "ff d0 " MakeExe Code MsgBox "Complete." End Sub