diff --git a/CHANGELOG.md b/CHANGELOG.md index b6643bf..0e6eb6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Support parsing the different `retpoline` types: Imported Address, Indirect Branch and Switchable retpoline [#70](https://github.com/saferwall/pe/pull/69). +- Permit more granular control over which data directories are parsed by [rabbitstack](https://github.com/rabbitstack) [#72](https://github.com/saferwall/pe/pull/72). +- Support parsing the different `retpoline` types: Imported Address, Indirect Branch and Switchable retpoline [#70](https://github.com/saferwall/pe/pull/70). - Unit tests for load config directory [#70](https://github.com/saferwall/pe/pull/69). - Unit tests for TLS directory [#69](https://github.com/saferwall/pe/pull/69). - Unit tests for debug directory [#68](https://github.com/saferwall/pe/pull/68). diff --git a/README.md b/README.md index d2f4388..059887b 100644 --- a/README.md +++ b/README.md @@ -254,3 +254,4 @@ To validate the parser we use the [go-fuzz](https://github.com/dvyukov/go-fuzz) - [Portable Executable File Format](https://blog.kowalczyk.info/articles/pefileformat.html) - [PE Format MSDN spec](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format) - [DotNET format](https://www.ntcore.com/files/dotnetformat.htm) +- [BlackHat 2011 - CONSTANT INSECURITY: (PECOFF) Portable Executable FIle Format](https://www.youtube.com/watch?v=uoQL3CE24ls) diff --git a/cmd/dump.go b/cmd/dump.go index 40439a6..1514913 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -7,6 +7,7 @@ package main import ( "bytes" "encoding/binary" + "encoding/hex" "encoding/json" "fmt" "os" @@ -68,6 +69,15 @@ func hexDump(b []byte) { func hexDumpSize(b []byte, size int) { var a [16]byte + + // Append null bytes when length of the buffer + // is smaller than the requested size. + if len(b) < size { + temp := make([]byte, size) + copy(temp, b) + b = temp + } + n := (size + 15) &^ 15 for i := 0; i < n; i++ { if i%16 == 0 { @@ -220,7 +230,7 @@ func parsePE(filename string, cfg config) { magic := string(IntToByteArray(uint64(DOSHeader.Magic))) signature := string(IntToByteArray(uint64(pe.NtHeader.Signature))) w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) - fmt.Print("\n---DOS Header ---\n\n") + fmt.Print("\n\t------[ DOS Header ]------\n\n") fmt.Fprintf(w, "Magic:\t 0x%x (%s)\n", DOSHeader.Magic, magic) fmt.Fprintf(w, "Bytes On Last Page Of File:\t 0x%x\n", DOSHeader.BytesOnLastPageOfFile) fmt.Fprintf(w, "Pages In File:\t 0x%x\n", DOSHeader.PagesInFile) @@ -243,7 +253,7 @@ func parsePE(filename string, cfg config) { if cfg.wantRichHeader && pe.FileInfo.HasRichHdr { richHeader := pe.RichHeader - fmt.Printf("RICH HEADER\n\n") + fmt.Printf("\nRICH HEADER\n***********\n") w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) fmt.Fprintf(w, "\t0x%x\t XOR Key\n", richHeader.XORKey) fmt.Fprintf(w, "\t0x%x\t DanS offset\n", richHeader.DansOffset) @@ -390,7 +400,7 @@ func parsePE(filename string, cfg config) { } if cfg.wantImport && pe.FileInfo.HasImport { - fmt.Printf("IMPORTS\n\n") + fmt.Printf("\nIMPORTS\n********\n") w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) for _, imp := range pe.Imports { desc := imp.Descriptor @@ -474,31 +484,18 @@ func parsePE(filename string, cfg config) { } - fmt.Printf("\n\nRESOURCES\n**********\n") + fmt.Printf("\nRESOURCES\n**********\n") printRsrcDir(pe.Resources) } - if cfg.wantCLR && pe.FileInfo.HasCLR { - dotnetMetadata, _ := json.Marshal(pe.CLR) - log.Info(prettyPrint(dotnetMetadata)) - if modTable, ok := pe.CLR.MetadataTables[peparser.Module]; ok { - if modTable.Content != nil { - modTableRow := modTable.Content.(peparser.ModuleTableRow) - modName := pe.GetStringFromData(modTableRow.Name, pe.CLR.MetadataStreams["#Strings"]) - moduleName := string(modName) - log.Info(moduleName) - } - } - } - if cfg.wantException && pe.FileInfo.HasException { - fmt.Printf("\n\nEXCEPTIONS\n***********\n") + fmt.Printf("\nEXCEPTIONS\n***********\n") for _, exception := range pe.Exceptions { entry := exception.RuntimeFunction fmt.Printf("\n\u27A1 BeginAddress: 0x%x EndAddress:0x%x UnwindInfoAddress:0x%x\t\n", entry.BeginAddress, entry.EndAddress, entry.UnwindInfoAddress) - ui := exception.UnwinInfo + ui := exception.UnwindInfo handlerFlags := peparser.PrettyUnwindInfoHandlerFlags(ui.Flags) prettyFlags := strings.Join(handlerFlags, ",") fmt.Printf("|- Version: 0x%x\n", ui.Version) @@ -631,7 +628,8 @@ func parsePE(filename string, cfg config) { } if cfg.wantBoundImp && pe.FileInfo.HasBoundImp { - fmt.Printf("BOUND IMPORTS\n\n") + fmt.Printf("\nBOUND IMPORTS\n************\n") + w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) for _, bndImp := range pe.BoundImports { fmt.Printf("\n\t------[ %s ]------\n\n", bndImp.Name) @@ -714,5 +712,96 @@ func parsePE(filename string, cfg config) { w.Flush() } + if cfg.wantCLR && pe.FileInfo.HasCLR { + fmt.Printf("\nCLR\n****\n") + + fmt.Print("\n\t------[ CLR Header ]------\n\n") + clr := pe.CLR + w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) + + clrHdr := clr.CLRHeader + flags := strings.Join(clrHdr.Flags.String(), " | ") + fmt.Fprintf(w, "Size Of Header:\t 0x%x\n", clrHdr.Cb) + fmt.Fprintf(w, "Major Runtime Version:\t 0x%x\n", clrHdr.MajorRuntimeVersion) + fmt.Fprintf(w, "Minor Runtime Version:\t 0x%x\n", clrHdr.MinorRuntimeVersion) + fmt.Fprintf(w, "MetaData RVA:\t 0x%x\n", clrHdr.MetaData.VirtualAddress) + fmt.Fprintf(w, "MetaData Size:\t 0x%x\n", clrHdr.MetaData.Size) + fmt.Fprintf(w, "Flags:\t 0x%x (%v)\n", clrHdr.Flags, flags) + fmt.Fprintf(w, "EntryPoint RVA or Token:\t 0x%x\n", clrHdr.EntryPointRVAorToken) + fmt.Fprintf(w, "Resources RVA:\t 0x%x\n", clrHdr.Resources.VirtualAddress) + fmt.Fprintf(w, "Resources Size:\t 0x%x (%s)\n", clrHdr.Resources.Size, BytesSize(float64(clrHdr.Resources.Size))) + fmt.Fprintf(w, "Strong Name Signature RVA:\t 0x%x\n", clrHdr.StrongNameSignature.VirtualAddress) + fmt.Fprintf(w, "Strong Name Signature Size:\t 0x%x (%s)\n", clrHdr.StrongNameSignature.Size, BytesSize(float64(clrHdr.StrongNameSignature.Size))) + fmt.Fprintf(w, "Code Manager Table RVA:\t 0x%x\n", clrHdr.CodeManagerTable.VirtualAddress) + fmt.Fprintf(w, "Code Manager Table Size:\t 0x%x (%s)\n", clrHdr.CodeManagerTable.Size, BytesSize(float64(clrHdr.CodeManagerTable.Size))) + fmt.Fprintf(w, "VTable Fixups RVA:\t 0x%x\n", clrHdr.VTableFixups.VirtualAddress) + fmt.Fprintf(w, "VTable Fixups Size:\t 0x%x (%s)\n", clrHdr.VTableFixups.Size, BytesSize(float64(clrHdr.VTableFixups.Size))) + fmt.Fprintf(w, "Export Address Table Jumps RVA:\t 0x%x\n", clrHdr.ExportAddressTableJumps.VirtualAddress) + fmt.Fprintf(w, "Export Address Table Jumps Size:\t 0x%x (%s)\n", clrHdr.ExportAddressTableJumps.Size, BytesSize(float64(clrHdr.ExportAddressTableJumps.Size))) + fmt.Fprintf(w, "Managed Native Header RVA:\t 0x%x\n", clrHdr.ManagedNativeHeader.VirtualAddress) + fmt.Fprintf(w, "Managed Native Header Size:\t 0x%x (%s)\n", clrHdr.ManagedNativeHeader.Size, BytesSize(float64(clrHdr.ManagedNativeHeader.Size))) + w.Flush() + + fmt.Print("\n\t------[ MetaData Header ]------\n\n") + mdHdr := clr.MetadataHeader + fmt.Fprintf(w, "Signature:\t 0x%x (%s)\n", mdHdr.Signature, + string(IntToByteArray(uint64(mdHdr.Signature)))) + fmt.Fprintf(w, "Major Version:\t 0x%x\n", mdHdr.MajorVersion) + fmt.Fprintf(w, "Minor Version:\t 0x%x\n", mdHdr.MinorVersion) + fmt.Fprintf(w, "Extra Data:\t 0x%x\n", mdHdr.ExtraData) + fmt.Fprintf(w, "Version String Length:\t 0x%x\n", mdHdr.VersionString) + fmt.Fprintf(w, "Version String:\t %s\n", mdHdr.Version) + fmt.Fprintf(w, "Flags:\t 0x%x\n", mdHdr.Flags) + fmt.Fprintf(w, "Streams Count:\t 0x%x\n", mdHdr.Streams) + w.Flush() + + fmt.Print("\n\t------[ MetaData Streams ]------\n\n") + for _, sh := range clr.MetadataStreamHeaders { + fmt.Fprintf(w, "Stream Name:\t %s\n", sh.Name) + fmt.Fprintf(w, "Offset:\t 0x%x\n", sh.Offset) + fmt.Fprintf(w, "Size:\t 0x%x (%s)\n", sh.Size, BytesSize(float64(sh.Size))) + w.Flush() + fmt.Print("\n ---Stream Content---\n") + hexDumpSize(clr.MetadataStreams[sh.Name], 128) + fmt.Print("\n") + } + + fmt.Print("\n\t------[ MetaData Tables Stream Header ]------\n\n") + mdTablesStreamHdr := clr.MetadataTablesStreamHeader + fmt.Fprintf(w, "Reserved:\t 0x%x\n", mdTablesStreamHdr.Reserved) + fmt.Fprintf(w, "Major Version:\t 0x%x\n", mdTablesStreamHdr.MajorVersion) + fmt.Fprintf(w, "Minor Version:\t 0x%x\n", mdTablesStreamHdr.MinorVersion) + fmt.Fprintf(w, "Heaps:\t 0x%x\n", mdTablesStreamHdr.Heaps) + fmt.Fprintf(w, "RID:\t 0x%x\n", mdTablesStreamHdr.RID) + fmt.Fprintf(w, "MaskValid:\t 0x%x\n", mdTablesStreamHdr.MaskValid) + fmt.Fprintf(w, "Sorted:\t 0x%x\n", mdTablesStreamHdr.Sorted) + w.Flush() + + fmt.Print("\n\t------[ MetaData Tables ]------\n\n") + mdTables := clr.MetadataTables + for _, mdTable := range mdTables { + fmt.Fprintf(w, "Name:\t %s | Items Count:\t 0x%x\n", mdTable.Name, mdTable.CountCols) + } + w.Flush() + + for table, modTable := range pe.CLR.MetadataTables { + switch table { + case peparser.Module: + fmt.Print("\n\t[Modules]\n\t---------\n") + modTableRow := modTable.Content.(peparser.ModuleTableRow) + modName := pe.GetStringFromData(modTableRow.Name, pe.CLR.MetadataStreams["#Strings"]) + Mvid := pe.GetStringFromData(modTableRow.Mvid, pe.CLR.MetadataStreams["#GUID"]) + MvidStr := hex.EncodeToString(Mvid) + fmt.Fprintf(w, "Generation:\t 0x%x\n", modTableRow.Generation) + fmt.Fprintf(w, "Name:\t 0x%x (%s)\n", modTableRow.Name, string(modName)) + fmt.Fprintf(w, "Mvid:\t 0x%x (%s)\n", modTableRow.Mvid, MvidStr) + fmt.Fprintf(w, "EncID:\t 0x%x\n", modTableRow.EncID) + fmt.Fprintf(w, "EncBaseID:\t 0x%x\n", modTableRow.EncBaseID) + w.Flush() + + } + } + } + fmt.Print("\n") } diff --git a/dotnet.go b/dotnet.go index df43c73..8db3aa4 100644 --- a/dotnet.go +++ b/dotnet.go @@ -1,4 +1,4 @@ -// Copyright 2022 Saferwall. All rights reserved. +// Copyright 2018 Saferwall. All rights reserved. // Use of this source code is governed by Apache v2 license // license that can be found in the LICENSE file. @@ -8,6 +8,12 @@ import ( "encoding/binary" ) +// References +// https://www.ntcore.com/files/dotnetformat.htm + +// COMImageFlagsType represents a COM+ header entry point flag type. +type COMImageFlagsType uint32 + // COM+ Header entry point flags. const ( // The image file contains IL code only, with no embedded native unmanaged @@ -20,7 +26,7 @@ const ( // This flag is obsolete and should not be set. Setting it—as the IL // assembler allows, using the .corflags directive—will render your module - // unloadable. + // un-loadable. COMImageFlagILLibrary = 0x00000004 // The image file is protected with a strong name signature. @@ -97,7 +103,7 @@ const ( // Custom attribute descriptors. CustomAttribute = 12 // Field or parameter marshaling descriptors for managed/unmanaged - // interoperations. + // inter-operations. FieldMarshal = 13 // Security descriptors. DeclSecurity = 14 @@ -137,7 +143,7 @@ const ( // Type specification descriptors. TypeSpec = 27 // Implementation map descriptors used for the platform invocation - // (P/Invoke) type of managed/unmanaged code interoperation. + // (P/Invoke) type of managed/unmanaged code inter-operation. ImplMap = 28 // Field-to-data mapping descriptors. FieldRVA = 29 @@ -191,9 +197,9 @@ const ( BlobStream = 2 ) -// MetadataTableIndextToString returns the string representation of the metadata +// MetadataTableIndexToString returns the string representation of the metadata // table index. -func MetadataTableIndextToString(k int) string { +func MetadataTableIndexToString(k int) string { metadataTablesMap := map[int]string{ Module: "Module", TypeRef: "TypeRef", @@ -269,30 +275,30 @@ func (pe *File) GetMetadataStreamIndexSize(BitPosition int) int { type ImageDataDirectory struct { // The relative virtual address of the table. - VirtualAddress uint32 + VirtualAddress uint32 `json:"virtual_address"` // The size of the table, in bytes. - Size uint32 + Size uint32 `json:"size"` } // ImageCOR20Header represents the CLR 2.0 header structure. type ImageCOR20Header struct { // Size of the header in bytes. - Cb uint32 + Cb uint32 `json:"cb"` // Major number of the minimum version of the runtime required to run the // program. - MajorRuntimeVersion uint16 + MajorRuntimeVersion uint16 `json:"major_runtime_version"` // Minor number of the version of the runtime required to run the program. - MinorRuntimeVersion uint16 + MinorRuntimeVersion uint16 `json:"minor_runtime_version"` // RVA and size of the metadata. - MetaData ImageDataDirectory + MetaData ImageDataDirectory `json:"meta_data"` // Bitwise flags indicating attributes of this executable. - Flags uint32 + Flags COMImageFlagsType `json:"flags"` // Metadata identifier (token) of the entry point for the image file; can // be 0 for DLL images. This field identifies a method belonging to this @@ -309,40 +315,40 @@ type ImageCOR20Header struct { // EntryPointRVA represents an RVA to a native entrypoint // DWORD EntryPointRVA; //}; - EntryPointRVAorToken uint32 + EntryPointRVAorToken uint32 `json:"entry_point_rva_or_token"` // This is the blob of managed resources. Fetched using // code:AssemblyNative.GetResource and code:PEFile.GetResource and accessible // from managed code from System.Assembly.GetManifestResourceStream. The // metadata has a table that maps names to offsets into this blob, so // logically the blob is a set of resources. - Resources ImageDataDirectory + Resources ImageDataDirectory `json:"resources"` // RVA and size of the hash data for this PE file, used by the loader for // binding and versioning. IL assemblies can be signed with a public-private // key to validate who created it. The signature goes here if this feature // is used. - StrongNameSignature ImageDataDirectory + StrongNameSignature ImageDataDirectory `json:"strong_name_signature"` // RVA and size of the Code Manager table. In the existing releases of the // runtime, this field is reserved and must be set to 0. - CodeManagerTable ImageDataDirectory + CodeManagerTable ImageDataDirectory `json:"code_manager_table"` // RVA and size in bytes of an array of virtual table (v-table) fixups. // Among current managed compilers, only the VC++ linker and the IL // assembler can produce this array. - VTableFixups ImageDataDirectory + VTableFixups ImageDataDirectory `json:"vtable_fixups"` // RVA and size of an array of addresses of jump thunks. Among managed // compilers, only the VC++ of versions pre-8.0 could produce this table, // which allows the export of unmanaged native methods embedded in the // managed PE file. In v2.0+ of CLR this entry is obsolete and must be set // to 0. - ExportAddressTableJumps ImageDataDirectory + ExportAddressTableJumps ImageDataDirectory `json:"export_address_table_jumps"` // Reserved for precompiled images; set to 0 // NGEN images it points at a code:CORCOMPILE_HEADER structure - ManagedNativeHeader ImageDataDirectory + ManagedNativeHeader ImageDataDirectory `json:"managed_native_header"` } // ImageCORVTableFixup defines the v-table fixups that contains the @@ -354,9 +360,9 @@ type ImageCOR20Header struct { // turn each entry into a pointer to machine code for the CPU and can be // called directly. type ImageCORVTableFixup struct { - RVA uint32 // Offset of v-table array in image. - Count uint16 // How many entries at location. - Type uint16 // COR_VTABLE_xxx type of entries. + RVA uint32 `json:"rva"` // Offset of v-table array in image. + Count uint16 `json:"count"` // How many entries at location. + Type uint16 `json:"type"` // COR_VTABLE_xxx type of entries. } // MetadataHeader consists of a storage signature and a storage header. @@ -367,61 +373,61 @@ type MetadataHeader struct { // as characters, BSJB—the initials of four “founding fathers” Brian Harry, // Susa Radke-Sproull, Jason Zander, and Bill Evans, who started the // runtime development in 1998. - Signature uint32 + Signature uint32 `json:"signature"` // Major version. - MajorVersion uint16 + MajorVersion uint16 `json:"major_version"` // Minor version. - MinorVersion uint16 + MinorVersion uint16 `json:"minor_version"` // Reserved; set to 0. - ExtraData uint32 + ExtraData uint32 `json:"extra_data"` // Length of the version string. - VersionString uint32 + VersionString uint32 `json:"version_string"` // Version string. - Version string + Version string `json:"version"` // The storage header follows the storage signature, aligned on a 4-byte // boundary. // // Reserved; set to 0. - Flags uint8 + Flags uint8 `json:"flags"` // Another byte used for [padding] // Number of streams. - Streams uint16 + Streams uint16 `json:"streams"` } // MetadataStreamHeader represents a Metadata Stream Header Structure. type MetadataStreamHeader struct { // Offset in the file for this stream. - Offset uint32 + Offset uint32 `json:"offset"` // Size of the stream in bytes. - Size uint32 + Size uint32 `json:"size"` // Name of the stream; a zero-terminated ASCII string no longer than 31 // characters (plus zero terminator). The name might be shorter, in which // case the size of the stream header is correspondingly reduced, padded to // the 4-byte boundary. - Name string + Name string `json:"name"` } // MetadataTableStreamHeader represents the Metadata Table Stream Header Structure. type MetadataTableStreamHeader struct { // Reserved; set to 0. - Reserved uint32 + Reserved uint32 `json:"reserved"` // Major version of the table schema (1 for v1.0 and v1.1; 2 for v2.0 or later). - MajorVersion uint8 + MajorVersion uint8 `json:"major_version"` // Minor version of the table schema (0 for all versions). - MinorVersion uint8 + MinorVersion uint8 `json:"minor_version"` // Binary flags indicate the offset sizes to be used within the heaps. // 4-byte unsigned integer offset is indicated by: @@ -432,36 +438,33 @@ type MetadataTableStreamHeader struct { // during an edit-and-continue session, and; // - flag 0x80, indicating that the metadata might contain items marked as // deleted. - Heaps uint8 + Heaps uint8 `json:"heaps"` - // it width of the maximal record index to all tables of the metadata; + // Bit width of the maximal record index to all tables of the metadata; // calculated at run time (during the metadata stream initialization). - Rid uint8 + RID uint8 `json:"rid"` // Bit vector of present tables, each bit representing one table (1 if // present). - MaskValid uint64 + MaskValid uint64 `json:"mask_valid"` // Bit vector of sorted tables, each bit representing a respective table (1 // if sorted) - Sorted uint64 + Sorted uint64 `json:"sorted"` } // MetadataTable represents the content of a particular table in the metadata. // The metadata schema defines 45 tables. type MetadataTable struct { // The name of the table. - Name string - - // Number of columns in the table - CountCols uint8 + Name string `json:"name"` - // Size of a record in the table/ - SizeRecord uint16 + // Number of columns in the table. + CountCols uint32 `json:"count_cols"` // Every table has a different layout, defined in the ECMA-335 spec. - // Content asbtract the type each table is pointing to. - Content interface{} + // Content abstract the type each table is pointing to. + Content interface{} `json:"content"` } // ModuleTableRow represents the `Module` metadata table contains a single @@ -469,24 +472,24 @@ type MetadataTable struct { // structure of the table is as follows: type ModuleTableRow struct { // Used only at run time, in edit-and-continue mode. - Generation uint16 + Generation uint16 `json:"generation"` // (offset in the #Strings stream) The module name, which is the same as // the name of the executable file with its extension but without a path. // The length should not exceed 512 bytes in UTF-8 encoding, counting the // zero terminator. - Name uint32 + Name uint32 `json:"name"` // (offset in the #GUID stream) A globally unique identifier, assigned // to the module as it is generated. - Mvid uint32 + Mvid uint32 `json:"mvid"` // (offset in the #GUID stream): Used only at run time, in // edit-and-continue mode. - EncID uint32 + EncID uint32 `json:"enc_id"` // (offset in the #GUID stream): Used only at run time, in edit-and-continue mode. - EncBaseID uint32 + EncBaseID uint32 `json:"enc_base_id"` } // CLRData embeds the Common Language Runtime Header structure as well as the @@ -664,7 +667,7 @@ func (pe *File) parseCLRHeaderDirectory(rva, size uint32) error { return err } - // Name requires a special treatement. + // Name requires a special treatment. offset += 8 for j := uint32(0); j <= 32; j++ { var c uint8 @@ -682,7 +685,7 @@ func (pe *File) parseCLRHeaderDirectory(rva, size uint32) error { } // The streams #~ and #- are mutually exclusive; that is, the metadata - // structure of the module is either optimized or unoptimized; it + // structure of the module is either optimized or un-optimized; it // cannot be both at the same time or be something in between. if sh.Name == "#~" || sh.Name == "#-" { mdStreamHdrOff = sh.Offset @@ -718,18 +721,16 @@ func (pe *File) parseCLRHeaderDirectory(rva, size uint32) error { // This header is followed by a sequence of 4-byte unsigned integers // indicating the number of records in each table marked 1 in the MaskValid // bit vector. - tablesCount := 0 offset += uint32(binary.Size(mdTableStreamHdr)) pe.CLR.MetadataTables = make(map[int]*MetadataTable) for i := 0; i < GenericParamConstraint; i++ { if IsBitSet(mdTableStreamHdr.MaskValid, i) { mdTable := MetadataTable{} - mdTable.Name = MetadataTableIndextToString(i) - mdTable.CountCols, err = pe.ReadUint8(offset) + mdTable.Name = MetadataTableIndexToString(i) + mdTable.CountCols, err = pe.ReadUint32(offset) if err != nil { break } - tablesCount++ offset += 4 pe.CLR.MetadataTables[i] = &mdTable } @@ -747,3 +748,25 @@ func (pe *File) parseCLRHeaderDirectory(rva, size uint32) error { return nil } + +// String returns a string interpretation of a COMImageFlags type. +func (flags COMImageFlagsType) String() []string { + COMImageFlags := map[COMImageFlagsType]string{ + COMImageFlagsILOnly: "IL Only", + COMImageFlags32BitRequired: "32-Bit Required", + COMImageFlagILLibrary: "IL Library", + COMImageFlagsStrongNameSigned: "Strong Name Signed", + COMImageFlagsNativeEntrypoint: "Native Entrypoint", + COMImageFlagsTrackDebugData: "Track Debug Data", + COMImageFlags32BitPreferred: "32-Bit Preferred", + } + + var values []string + for k, v := range COMImageFlags { + if (k & flags) == k { + values = append(values, v) + } + } + + return values +} diff --git a/dotnet_test.go b/dotnet_test.go index c3f7f39..d7a7ada 100644 --- a/dotnet_test.go +++ b/dotnet_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 Saferwall. All rights reserved. +// Copyright 2018 Saferwall. All rights reserved. // Use of this source code is governed by Apache v2 license // license that can be found in the LICENSE file. @@ -20,7 +20,7 @@ type TestClrEntry struct { mdTables map[int]*MetadataTable } -func TestDotNet(t *testing.T) { +func TestClrDirectory(t *testing.T) { tests := []struct { in string @@ -92,35 +92,30 @@ func TestDotNet(t *testing.T) { MajorVersion: 0x2, MinorVersion: 0x0, Heaps: 0x0, - Rid: 0x1, + RID: 0x1, MaskValid: 0x8900005407, Sorted: 0x16003301fa00, }, mdTables: map[int]*MetadataTable{ - 12: { + CustomAttribute: { Name: "CustomAttribute", CountCols: 0x13, - SizeRecord: 0x0, }, - 32: { + Assembly: { Name: "Assembly", CountCols: 0x1, - SizeRecord: 0x0, }, - 35: { + AssemblyRef: { Name: "AssemblyRef", CountCols: 0x1e, - SizeRecord: 0x0, }, - 39: { + ExportedType: { Name: "ExportedType", - CountCols: 0x27, - SizeRecord: 0x0, + CountCols: 0x527, }, - 0: { + Module: { Name: "Module", CountCols: 0x1, - SizeRecord: 0x0, Content: ModuleTableRow{ Generation: 0x0, Name: 0x2cd7, @@ -129,25 +124,21 @@ func TestDotNet(t *testing.T) { EncBaseID: 0x0, }, }, - 1: { + TypeRef: { Name: "TypeRef", CountCols: 0x13, - SizeRecord: 0x0, }, - 2: { + TypeDef: { Name: "TypeDef", CountCols: 0x1, - SizeRecord: 0x0, }, - 10: { + MemberRef: { Name: "MemberRef", CountCols: 0x11, - SizeRecord: 0x0, }, - 14: { + DeclSecurity: { Name: "DeclSecurity", CountCols: 0x1, - SizeRecord: 0x0, }, }, }, diff --git a/exception.go b/exception.go index 2c97a9d..ebd54e5 100644 --- a/exception.go +++ b/exception.go @@ -356,7 +356,7 @@ type ScopeTable struct { // Exception represent an entry in the function table. type Exception struct { RuntimeFunction ImageRuntimeFunctionEntry `json:"runtime_function"` - UnwinInfo UnwindInfo `json:"unwind_info"` + UnwindInfo UnwindInfo `json:"unwind_info"` } func (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) { @@ -364,7 +364,7 @@ func (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) unwindCode := UnwindCode{} advanceBy := 0 - // Read the unwince code at offset (2 bytes) + // Read the unwind code at offset (2 bytes) uc, err := pe.ReadUint16(offset) if err != nil { return unwindCode, advanceBy @@ -410,7 +410,7 @@ func (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) case UwOpSaveXmm128: fo := binary.LittleEndian.Uint16(pe.data[offset+2:]) unwindCode.FrameOffset = fo * 16 - unwindCode.Operand = "Rgister=XMM" + strconv.Itoa(int(unwindCode.OpInfo)) + + unwindCode.Operand = "Register=XMM" + strconv.Itoa(int(unwindCode.OpInfo)) + ", Offset=" + strconv.Itoa(int(unwindCode.FrameOffset)) advanceBy += 2 case UwOpSaveXmm128Far: @@ -439,7 +439,7 @@ func (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) return unwindCode, advanceBy } -func (pe *File) parseUnwinInfo(unwindInfo uint32) UnwindInfo { +func (pe *File) parseUnwindInfo(unwindInfo uint32) UnwindInfo { ui := UnwindInfo{} @@ -536,7 +536,7 @@ func (pe *File) parseExceptionDirectory(rva, size uint32) error { exception := Exception{RuntimeFunction: functionEntry} if pe.Is64 { - exception.UnwinInfo = pe.parseUnwinInfo(functionEntry.UnwindInfoAddress) + exception.UnwindInfo = pe.parseUnwindInfo(functionEntry.UnwindInfoAddress) } exceptions = append(exceptions, exception) diff --git a/exception_test.go b/exception_test.go index 8aff93f..5c89942 100644 --- a/exception_test.go +++ b/exception_test.go @@ -108,9 +108,9 @@ func TestParseExceptionDirectory(t *testing.T) { t.Errorf("RuntimeFunction assertion failed, got %v, want %v", len(got), tt.out.entryCount) } - unwinInfo := file.Exceptions[tt.out.entryIndex].UnwinInfo - if !reflect.DeepEqual(unwinInfo, tt.out.unwindInfo) { - t.Errorf("UnwinInfo assertion failed, got %v, want %v", unwinInfo, tt.out.unwindInfo) + unwindInfo := file.Exceptions[tt.out.entryIndex].UnwindInfo + if !reflect.DeepEqual(unwindInfo, tt.out.unwindInfo) { + t.Errorf("UnwindInfo assertion failed, got %v, want %v", unwindInfo, tt.out.unwindInfo) } })