diff --git a/scripts/base/files/pe/main.bro b/scripts/base/files/pe/main.bro index 754b788318..b05f1e3b72 100644 --- a/scripts/base/files/pe/main.bro +++ b/scripts/base/files/pe/main.bro @@ -39,15 +39,15 @@ hook set_file(f: fa_file) &priority=5 event pe_dos_header(f: fa_file, h: PE::DOSHeader) &priority=5 { - print "DOS header"; - print h; +# print "DOS header"; +# print h; hook set_file(f); } event pe_file_header(f: fa_file, h: PE::FileHeader) &priority=5 { - print "File header"; - print h; +# print "File header"; +# print h; hook set_file(f); f$pe$compile_ts = h$ts; f$pe$machine = machine_types[h$machine]; @@ -57,8 +57,8 @@ event pe_file_header(f: fa_file, h: PE::FileHeader) &priority=5 event pe_optional_header(f: fa_file, h: PE::OptionalHeader) &priority=5 { - print "Optional header"; - print h; +# print "Optional header"; +# print h; hook set_file(f); f$pe$os = os_versions[h$os_version_major, h$os_version_minor]; f$pe$subsystem = windows_subsystems[h$subsystem]; @@ -66,11 +66,10 @@ event pe_optional_header(f: fa_file, h: PE::OptionalHeader) &priority=5 event pe_section_header(f: fa_file, h: PE::SectionHeader) &priority=5 { - print "Section header"; - print h; +# print "Section header"; +# print h; hook set_file(f); - print h; if ( ! f$pe?$section_names ) f$pe$section_names = vector(); f$pe$section_names[|f$pe$section_names|] = h$name; diff --git a/src/file_analysis/analyzer/pe/pe-analyzer.pac b/src/file_analysis/analyzer/pe/pe-analyzer.pac index 2b49cd2c23..1d7d0dbbff 100644 --- a/src/file_analysis/analyzer/pe/pe-analyzer.pac +++ b/src/file_analysis/analyzer/pe/pe-analyzer.pac @@ -7,12 +7,6 @@ refine flow File += { - function proc_the_file(): bool - %{ - printf("Processed\n"); - return true; - %} - function characteristics_to_bro(c: uint32, len: uint8): TableVal %{ uint64 mask = (len==16) ? 0xFFFF : 0xFFFFFFFF; @@ -70,7 +64,7 @@ refine flow File += { return true; %} - function proc_nt_headers(h: IMAGE_NT_HEADERS): bool + function proc_nt_headers(h: NT_Headers): bool %{ if ( ${h.PESignature} != 17744 ) // Number is uint32 version of "PE\0\0" { @@ -80,7 +74,7 @@ refine flow File += { return true; %} - function proc_file_header(h: IMAGE_FILE_HEADER): bool + function proc_file_header(h: File_Header): bool %{ if ( pe_file_header ) { @@ -98,7 +92,7 @@ refine flow File += { return true; %} - function proc_optional_header(h: IMAGE_OPTIONAL_HEADER): bool + function proc_optional_header(h: Optional_Header): bool %{ if ( ${h.magic} != 0x10b && // normal pe32 executable ${h.magic} != 0x107 && // rom image @@ -145,7 +139,7 @@ refine flow File += { return true; %} - function proc_section_header(h: IMAGE_SECTION_HEADER): bool + function proc_section_header(h: Section_Header): bool %{ if ( pe_section_header ) { @@ -176,6 +170,14 @@ refine flow File += { } return true; %} + + + function proc_pe_file(): bool + %{ + printf("PE file processed\n"); + return true; + %} + }; refine typeattr DOS_Header += &let { @@ -186,23 +188,23 @@ refine typeattr DOS_Code += &let { proc : bool = $context.flow.proc_dos_code(code); }; -refine typeattr IMAGE_NT_HEADERS += &let { +refine typeattr NT_Headers += &let { proc : bool = $context.flow.proc_nt_headers(this); }; -refine typeattr IMAGE_FILE_HEADER += &let { +refine typeattr File_Header += &let { proc : bool = $context.flow.proc_file_header(this); }; -refine typeattr IMAGE_OPTIONAL_HEADER += &let { +refine typeattr Optional_Header += &let { proc : bool = $context.flow.proc_optional_header(this); }; -refine typeattr IMAGE_SECTION_HEADER += &let { - proc: bool = $context.flow.proc_section_header(this); +refine typeattr Section_Header += &let { + proc2: bool = $context.flow.proc_section_header(this); }; -refine typeattr TheFile += &let { - proc: bool = $context.flow.proc_the_file(); +refine typeattr PE_File += &let { + proc: bool = $context.flow.proc_pe_file(); }; diff --git a/src/file_analysis/analyzer/pe/pe-file.pac b/src/file_analysis/analyzer/pe/pe-file.pac index 58278a7ffd..ef048079a5 100644 --- a/src/file_analysis/analyzer/pe/pe-file.pac +++ b/src/file_analysis/analyzer/pe/pe-file.pac @@ -1,17 +1,27 @@ - -type TheFile = record { - dos_header : DOS_Header; - dos_code : DOS_Code(dos_code_len); - pe_header : IMAGE_NT_HEADERS; - section_headers : IMAGE_SECTIONS(pe_header.file_header.NumberOfSections); - #pad : bytestring &length=offsetof(pe_header.data_directories + pe_header.data_directories[1].virtual_address); - #data_sections : DATA_SECTIONS[pe_header.file_header.NumberOfSections]; - #data_sections : DATA_SECTIONS[] &length=data_len; +# The base record for a Portable Executable file +type PE_File = record { + headers : Headers; + pad : Padding(iat_loc); + iat : IMPORT_ADDRESS_TABLE &length=$context.connection.get_import_table_len(); } &let { - dos_code_len: uint32 = dos_header.AddressOfNewExeHeader - 64; - data_len: uint32 = pe_header.optional_header.size_of_init_data; + unparsed_hdr_len: uint32 = headers.pe_header.optional_header.size_of_headers - headers.length; + iat_loc: uint32 = $context.connection.get_import_table_addr() - headers.pe_header.optional_header.size_of_headers + unparsed_hdr_len; + } &byteorder=littleendian; +## Headers + +type Headers = record { + dos_header : DOS_Header; + dos_code : DOS_Code(dos_code_len); + pe_header : NT_Headers; + section_headers : Section_Headers(pe_header.file_header.NumberOfSections); +} &let { + dos_code_len: uint32 = dos_header.AddressOfNewExeHeader > 64 ? dos_header.AddressOfNewExeHeader - 64 : 0; + length: uint64 = 64 + dos_code_len + pe_header.length + section_headers.length; +}; + +# The DOS header gives us the offset of the NT headers type DOS_Header = record { signature : bytestring &length=2; UsedBytesInTheLastPage : uint16; @@ -38,13 +48,17 @@ type DOS_Code(len: uint32) = record { code : bytestring &length=len; }; -type IMAGE_NT_HEADERS = record { +# The NT headers give us the file and the optional headers. +type NT_Headers = record { PESignature : uint32; - file_header : IMAGE_FILE_HEADER; - optional_header : IMAGE_OPTIONAL_HEADER(file_header.SizeOfOptionalHeader, file_header.NumberOfSections) &length=file_header.SizeOfOptionalHeader; -} &byteorder=littleendian &length=file_header.SizeOfOptionalHeader+offsetof(optional_header); + file_header : File_Header; + optional_header : Optional_Header(file_header.SizeOfOptionalHeader, file_header.NumberOfSections) &length=file_header.SizeOfOptionalHeader; +} &let { + length: uint32 = file_header.SizeOfOptionalHeader+offsetof(optional_header); +} &byteorder=littleendian &length=length; -type IMAGE_FILE_HEADER = record { +# The file header is mainly self-describing +type File_Header = record { Machine : uint16; NumberOfSections : uint16; TimeDateStamp : uint32; @@ -54,7 +68,8 @@ type IMAGE_FILE_HEADER = record { Characteristics : uint16; }; -type IMAGE_OPTIONAL_HEADER(len: uint16, number_of_sections: uint16) = record { +# The optional header gives us DLL link information, and some structural information +type Optional_Header(len: uint16, number_of_sections: uint16) = record { magic : uint16; major_linker_version : uint8; minor_linker_version : uint8; @@ -80,34 +95,47 @@ type IMAGE_OPTIONAL_HEADER(len: uint16, number_of_sections: uint16) = record { subsystem : uint16; dll_characteristics : uint16; mem: case magic of { - 267 -> i32 : MEM_INFO32; - 268 -> i64 : MEM_INFO64; + 267 -> i32 : Mem_Info32; + 268 -> i64 : Mem_Info64; default -> InvalidPEFile : empty; }; loader_flags : uint32; number_of_rva_and_sizes : uint32; - rvas : IMAGE_RVAS(number_of_rva_and_sizes); + rvas : RVAS(number_of_rva_and_sizes); } &byteorder=littleendian &length=len; -type MEM_INFO32 = record { +type Mem_Info32 = record { size_of_stack_reserve : uint32; size_of_stack_commit : uint32; size_of_heap_reserve : uint32; size_of_heap_commit : uint32; } &byteorder=littleendian &length=16; -type MEM_INFO64 = record { +type Mem_Info64 = record { size_of_stack_reserve : uint64; size_of_stack_commit : uint64; size_of_heap_reserve : uint64; size_of_heap_commit : uint64; } &byteorder=littleendian &length=32; -type IMAGE_SECTIONS(num: uint16) = record { - sections : IMAGE_SECTION_HEADER[num]; -} &length=num*40; +type RVAS(num: uint32) = record { + rvas : RVA[num]; +}; -type IMAGE_SECTION_HEADER = record { +type RVA = record { + virtual_address : uint32; + size : uint32; +} &let { + proc: bool = $context.connection.proc_rva(this); +} &length=8; + +type Section_Headers(num: uint16) = record { + sections : Section_Header[num]; +} &let { + length: uint32 = num*40; +} &length=length; + +type Section_Header = record { name : bytestring &length=8; virtual_size : uint32; virtual_addr : uint32; @@ -118,31 +146,214 @@ type IMAGE_SECTION_HEADER = record { non_used_num_of_relocs : uint16; non_used_num_of_line_nums : uint16; characteristics : uint32; +} &let { + proc: bool = $context.connection.proc_section(this); } &byteorder=littleendian &length=40; +## The BinPAC padding type doens't work here. -type IMAGE_DATA_DIRECTORY = record { - virtual_address : uint32; - size : uint16; +type Padding(length: uint32) = record { + blah: bytestring &length=length &transient; }; +## Support for parsing the .idata section + type IMAGE_IMPORT_DIRECTORY = record { rva_import_lookup_table : uint32; time_date_stamp : uint32; forwarder_chain : uint32; rva_module_name : uint32; rva_import_addr_table : uint32; +} &let { + is_null: bool = rva_module_name == 0; + proc: bool = $context.connection.proc_image_import_directory(this); +} &length=20; + +type IMPORT_LOOKUP_ATTRS = record { + attrs: uint32; +} &let { + is_null: bool = attrs == 0; +} &length=4; + +type IMPORT_LOOKUP_TABLE = record { + attrs: IMPORT_LOOKUP_ATTRS[] &until($element.is_null); +} &let { + proc: bool = $context.connection.proc_import_lookup_table(this); }; -type IMAGE_RVAS(num: uint32) = record { - rvas : IMAGE_RVA[num]; -} &length=num*8; +#type null_terminated_string = RE/[^\x00]+\x00/; +type null_terminated_string = RE/[A-Za-z0-9.]+\x00/; -type IMAGE_RVA = record { - virtual_address : uint32; - size : uint32; -} &length=8; +type IMPORT_ENTRY(is_module: bool, pad_align: uint8) = case is_module of { + true -> module: IMPORT_MODULE(pad_align); + false -> hint: IMPORT_HINT(pad_align); +}; -type DATA_SECTIONS = record { - blah: uint8; +type IMPORT_MODULE(pad_align: uint8) = record { + pad: bytestring &length=pad_align; + name: null_terminated_string; +} &let { + proc: bool = $context.connection.proc_import_module(this); +}; + +type IMPORT_HINT(pad_align: uint8) = record { + pad: bytestring &length=pad_align; + index: uint16; + name: null_terminated_string; +} &let { + proc: bool = $context.connection.proc_import_hint(this); + last: bool = sizeof(name) == 0; +}; + +type IMPORT_ADDRESS_TABLE = record { + directory_table : IMAGE_IMPORT_DIRECTORY[] &until $element.is_null; + lookup_tables : IMPORT_LOOKUP_TABLE[] &until $context.connection.get_num_imports() <= 0; + hint_table : IMPORT_ENTRY($context.connection.get_next_hint_type(), $context.connection.get_next_hint_align())[] &until($context.connection.imports_done()); +} &let { + proc: bool = $context.connection.proc_iat(this); +}; + +refine connection MockConnection += { + %member{ + uint8 rvas_seen_; + uint8 num_imports_; + uint32 rva_offset_; + + bool has_import_table_; + uint32 import_table_va_; + uint32 import_table_rva_; + uint32 import_table_len_; + vector imports_per_module_; + uint32 next_hint_index_; + uint8 next_hint_align_; + bool next_hint_is_module_; + + bool has_export_table_; + uint32 export_table_va_; + uint32 export_table_rva_; + %} + + %init{ + rvas_seen_ = 0; + rva_offset_ = 0; + num_imports_ = -1; + has_import_table_ = false; + has_export_table_ = false; + + next_hint_is_module_ = true; + next_hint_index_ = 0; + next_hint_align_ = 0; + %} + + function proc_rva(r: RVA): bool + %{ + if ( rvas_seen_ == 1 ) + { + has_import_table_ = ${r.virtual_address} > 0; + if ( has_import_table_ ) { + import_table_rva_ = ${r.virtual_address}; + import_table_len_ = ${r.size}; + } + } + if ( rvas_seen_ == 2 ) + { + has_export_table_ = ${r.virtual_address} > 0; + if ( has_export_table_ ) + export_table_rva_ = ${r.virtual_address}; + } + ++rvas_seen_; + return true; + %} + + function proc_section(h: Section_Header): bool + %{ + if ( has_import_table_ && ${h.virtual_addr} == import_table_rva_ ){ + printf("Found import table %d\n", ${h.ptr_to_raw_data}); + rva_offset_ = ${h.virtual_addr} - ${h.ptr_to_raw_data}; + + import_table_va_ = ${h.ptr_to_raw_data}; + get_import_table_addr(); + } + if ( has_export_table_ && ${h.virtual_addr} == export_table_rva_ ) + export_table_va_ = ${h.ptr_to_raw_data}; + return true; + %} + + function proc_image_import_directory(i: IMAGE_IMPORT_DIRECTORY): bool + %{ + num_imports_++; + return true; + %} + + function proc_iat(i: IMPORT_ADDRESS_TABLE): bool + %{ + printf("IAT processed\n"); + return true; + %} + + function get_import_table_addr(): uint32 + %{ + return has_import_table_ ? import_table_va_ : 0; + %} + + function get_import_table_len(): uint32 + %{ + return has_import_table_ ? import_table_len_ : 0; + %} + + function get_rva_offset(): uint32 + %{ + return rva_offset_; + %} + + function get_num_imports(): uint8 + %{ + return num_imports_; + %} + + function get_next_hint_align(): uint8 + %{ + return next_hint_align_; + %} + + function proc_import_lookup_table(t: IMPORT_LOOKUP_TABLE): bool + %{ + --num_imports_; + imports_per_module_.push_back(${t.attrs}->size()); + return true; + %} + + function get_next_hint_type(): bool + %{ + if ( next_hint_is_module_ ) + { + next_hint_is_module_ = false; + return true; + } + if ( --imports_per_module_[next_hint_index_] == 0) + { + ++next_hint_index_; + return true; + } + return false; + %} + + function imports_done(): bool + %{ + return next_hint_index_ == imports_per_module_.size(); + %} + + function proc_import_hint(h: IMPORT_HINT): bool + %{ + printf(" Imported function '%s'\n", ${h.name}.data()); + next_hint_align_ = ${h.name}.length() % 2; + return true; + %} + + function proc_import_module(m: IMPORT_MODULE): bool + %{ + printf("Imported module '%s'\n", ${m.name}.data()); + next_hint_align_ = ${m.name}.length() % 2; + return true; + %} }; \ No newline at end of file diff --git a/src/file_analysis/analyzer/pe/pe.pac b/src/file_analysis/analyzer/pe/pe.pac index 8a20fa3c62..df7c3011d9 100644 --- a/src/file_analysis/analyzer/pe/pe.pac +++ b/src/file_analysis/analyzer/pe/pe.pac @@ -14,7 +14,7 @@ connection MockConnection(bro_analyzer: BroFileAnalyzer) { %include pe-file.pac flow File { - flowunit = TheFile withcontext(connection, this); + flowunit = PE_File withcontext(connection, this); } %include pe-analyzer.pac