id:n7shi:20110421の続きです。リフレクションを用いて、クラスの中のクラスを追えるようにします。
Function writeAny%(image As Byte(), pos%, v As Object) If TypeOf v Is Byte Then image(pos) = CByte(v) Return pos + 1 ElseIf TypeOf v Is UShort Then Return write16(image, pos, CUShort(v)) ElseIf TypeOf v Is Integer Then Return write32(image, pos, CInt(v)) ElseIf TypeOf v Is Byte() Then Return write8s(image, pos, CType(v, Byte())) ElseIf TypeOf v Is UShort() Then Return write16s(image, pos, CType(v, UShort())) ElseIf TypeOf v Is Integer() Then Return write32s(image, pos, CType(v, Integer())) Else Dim t = v.GetType If t.IsArray Then For Each obj In CType(v, Array) pos = writeAny(image, pos, obj) Next Return pos Else Dim fs = t.GetFields If fs.Length > 0 Then Return writeFields(image, pos, v) Else Throw New ArgumentException("不明な型: ", t.FullName) End If End If End If End Function Function writeFields%(image As Byte(), pos%, obj As Object) For Each f In obj.GetType.GetFields pos = writeAny(image, pos, f.GetValue(obj)) Next Return pos End Function
writeFieldsの中で、フィールド1つずつに対しwriteAnyを呼んでいます。もしそのフィールドの中にフィールドがあれば、再帰的にwriteFieldsを呼んで処理します。配列の場合は要素を1つずつwriteAnyで処理します。
これによりWin32の構造体をそのまま書き写せるようになりました。
Public Class IMAGE_NT_HEADERS32 Public Signature% Public FileHeader As New IMAGE_FILE_HEADER Public OptionalHeader As New IMAGE_OPTIONAL_HEADER32 End Class Public Class IMAGE_FILE_HEADER Public Machine, NumberOfSections As UShort Public TimeDateStamp, PointerToSymbolTable, NumberOfSymbols As Integer Public SizeOfOptionalHeader, Characteristics As UShort End Class Public Class IMAGE_OPTIONAL_HEADER32 Public Magic As UShort Public MajorLinkerVersion, MinorLinkerVersion As Byte Public SizeOfCode, SizeOfInitializedData, SizeOfUninitializedData, AddressOfEntryPoint, BaseOfCode, BaseOfData, ImageBase, SectionAlignment, FileAlignment As Integer Public MajorOperatingSystemVersion, MinorOperatingSystemVersion, MajorImageVersion, MinorImageVersion, MajorSubsystemVersion, MinorSubsystemVersion As UShort Public Win32VersionValue, SizeOfImage, SizeOfHeaders, CheckSum As Integer Public Subsystem, DllCharacteristics As UShort Public SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit, LoaderFlags, NumberOfRvaAndSizes As Integer Public DataDirectory(15) As IMAGE_DATA_DIRECTORY End Class Public Class IMAGE_DATA_DIRECTORY Public VirtualAddress, Size As Integer End Class
これでようやくC++のサンプル(id:n7shi:20110416)とほぼ同じ構成でEXEが出力できるようになりました。
Imports System.IO Module Module1 Sub Main() Dim image(&H3FF) As Byte Dim dosh = New IMAGE_DOS_HEADER With { .e_magic = conv16("MZ"), .e_cblp = &H90, .e_cp = 3, .e_cparhdr = 4, .e_maxalloc = &HFFFF, .e_sp = &HB8, .e_lfarlc = &H40, .e_lfanew = &H80} writeFields(image, 0, dosh) write8s(image, &H40, &HB8, 1, &H4C, &HCD, &H21) Dim peh = New IMAGE_NT_HEADERS32 With { .Signature = conv32("PE")} With peh.FileHeader .Machine = &H14C .NumberOfSections = 1 .SizeOfOptionalHeader = &HE0 .Characteristics = &H102 End With With peh.OptionalHeader .Magic = &H10B .MajorLinkerVersion = 10 .SizeOfCode = &H200 .AddressOfEntryPoint = &H1000 .BaseOfCode = &H1000 .BaseOfData = &H2000 .ImageBase = &H400000 .SectionAlignment = &H1000 .FileAlignment = &H200 .MajorOperatingSystemVersion = 5 .MinorOperatingSystemVersion = 1 .MajorSubsystemVersion = 5 .MinorSubsystemVersion = 1 .SizeOfImage = &H2000 .SizeOfHeaders = &H200 .Subsystem = 2 .DllCharacteristics = &H8500 .SizeOfStackReserve = &H100000 .SizeOfStackCommit = &H1000 .SizeOfHeapReserve = &H100000 .SizeOfHeapCommit = &H1000 .NumberOfRvaAndSizes = 16 End With writeFields(image, &H80, peh) Dim text = New IMAGE_SECTION_HEADER With { .Name = getBytes(".text", 8), .VirtualSize = 1, .VirtualAddress = &H1000, .SizeOfRawData = &H200, .PointerToRawData = &H200, .Characteristics = &H60000020} writeFields(image, &H178, text) image(&H200) = &HC3 Using fs = New FileStream("output.exe", FileMode.Create) fs.Write(image, 0, image.Length) End Using End Sub End Module
実質的にここがスタート地点です。今後は、第3回でうやむやなまま終わった.idataの生成処理について、コードでわかりやすく説明できないか考えていきます。