diff --git a/src/file_analysis/analyzer/pe/PE.cc b/src/file_analysis/analyzer/pe/PE.cc index 59fbad91df..6df2dc8d99 100644 --- a/src/file_analysis/analyzer/pe/PE.cc +++ b/src/file_analysis/analyzer/pe/PE.cc @@ -18,6 +18,8 @@ PE::~PE() bool PE::DeliverStream(const u_char* data, uint64 len) { + if ( conn->is_done() ) + return true; try { interp->NewData(data, data + len); diff --git a/src/file_analysis/analyzer/pe/events.bif b/src/file_analysis/analyzer/pe/events.bif index b6ce808278..3e6bbf8faf 100644 --- a/src/file_analysis/analyzer/pe/events.bif +++ b/src/file_analysis/analyzer/pe/events.bif @@ -1,5 +1,11 @@ event pe_dos_header%(f: fa_file, h: PE::DOSHeader%); + event pe_dos_code%(f: fa_file, code: string%); + event pe_file_header%(f: fa_file, h: PE::FileHeader%); + event pe_optional_header%(f: fa_file, h: PE::OptionalHeader%); -event pe_section_header%(f: fa_file, h: PE::SectionHeader%); \ No newline at end of file + +event pe_section_header%(f: fa_file, h: PE::SectionHeader%); + +event pe_import_entry%(f: fa_file, m: string, name: string%); \ No newline at end of file diff --git a/src/file_analysis/analyzer/pe/pe-analyzer.pac b/src/file_analysis/analyzer/pe/pe-analyzer.pac index e227f9af0d..1baaf93947 100644 --- a/src/file_analysis/analyzer/pe/pe-analyzer.pac +++ b/src/file_analysis/analyzer/pe/pe-analyzer.pac @@ -174,13 +174,23 @@ refine flow File += { return true; %} - - function proc_pe_file(): bool + function proc_import_entry(module_name: bytestring, i: import_entry): bool %{ - printf("PE file processed\n"); + if ( pe_import_entry ) + { + StringVal* name; + if ( ${i.name}.length() > 1 ) + name = new StringVal(${i.name}.length() - 1, (const char*) ${i.name}.begin()); + else + name = new StringVal(0, (const char*) ${i.name}.begin()); + + BifEvent::generate_pe_import_entry((analyzer::Analyzer *) connection()->bro_analyzer(), + connection()->bro_analyzer()->GetFile()->GetVal()->Ref(), + bytestring_to_val(${module_name}), + name); + } return true; %} - }; refine typeattr DOS_Header += &let { @@ -204,10 +214,9 @@ refine typeattr Optional_Header += &let { }; refine typeattr Section_Header += &let { - proc2: bool = $context.flow.proc_section_header(this); + proc: bool = $context.flow.proc_section_header(this); }; -refine typeattr PE_File += &let { - proc: bool = $context.flow.proc_pe_file(); +refine typeattr import_entry += &let { + proc: bool = $context.flow.proc_import_entry($context.connection.get_module_name(), this) &if(!is_module); }; - diff --git a/src/file_analysis/analyzer/pe/pe-file-headers.pac b/src/file_analysis/analyzer/pe/pe-file-headers.pac index c732ed4001..05628917f4 100644 --- a/src/file_analysis/analyzer/pe/pe-file-headers.pac +++ b/src/file_analysis/analyzer/pe/pe-file-headers.pac @@ -39,7 +39,7 @@ type DOS_Code(len: uint32) = record { type NT_Headers = record { PESignature : uint32; file_header : File_Header; - optional_header : Optional_Header(file_header.SizeOfOptionalHeader, file_header.NumberOfSections) &length=file_header.SizeOfOptionalHeader; + optional_header : Optional_Header &length=file_header.SizeOfOptionalHeader; } &let { length: uint32 = file_header.SizeOfOptionalHeader+offsetof(optional_header); } &length=length; @@ -56,7 +56,7 @@ type File_Header = record { }; # The optional header gives us DLL link information, and some structural information -type Optional_Header(len: uint16, number_of_sections: uint16) = record { +type Optional_Header = record { magic : uint16; major_linker_version : uint8; minor_linker_version : uint8; @@ -68,11 +68,11 @@ type Optional_Header(len: uint16, number_of_sections: uint16) = record { have_base_of_data: case pe_format of { PE32 -> base_of_data: uint32; default -> not_present: empty; - }; + } &requires(pe_format); is_pe32: case pe_format of { PE32_PLUS -> image_base_64: uint64; default -> image_base_32: uint32; - }; + } &requires(pe_format); section_alignment : uint32; file_alignment : uint32; os_version_major : uint16; @@ -91,14 +91,14 @@ type Optional_Header(len: uint16, number_of_sections: uint16) = record { PE32 -> i32: Mem_Info32; PE32_PLUS -> i64: Mem_Info64; default -> InvalidPEFile : empty; - }; + } &requires(pe_format); loader_flags : uint32; number_of_rva_and_sizes : uint32; rvas : RVAS(number_of_rva_and_sizes); } &let { pe_format: uint8 = $context.connection.set_pe32_format(magic); image_base: uint64 = pe_format == PE32_PLUS ? image_base_64 : image_base_32; -} &length=len; +}; type Section_Headers(num: uint16) = record { sections : Section_Header[num]; @@ -118,7 +118,7 @@ type Section_Header = record { non_used_num_of_line_nums : uint16; characteristics : uint32; } &let { - proc: bool = $context.connection.proc_section(this); + add_section: bool = $context.connection.add_section(this); } &length=40; refine connection MockConnection += { @@ -132,13 +132,13 @@ refine connection MockConnection += { pe32_format_ = UNKNOWN_VERSION;; %} - function proc_section(h: Section_Header): bool + function add_section(h: Section_Header): bool %{ if ( ${h.size_of_raw_data} + ${h.ptr_to_raw_data} > max_file_location_ ) max_file_location_ = ${h.size_of_raw_data} + ${h.ptr_to_raw_data}; - if ( ${h.virtual_addr} > 0 && ${h.virtual_addr} == import_table_rva_ ) - import_table_va_ = ${h.ptr_to_raw_data}; + if ( ${h.virtual_addr} > 0 && ${h.virtual_addr} <= import_table_rva_ && ( ${h.virtual_addr} + ${h.virtual_size} ) > import_table_rva_ ) + import_table_va_ = ${h.ptr_to_raw_data} + (import_table_rva_ - ${h.virtual_addr}); return true; %} diff --git a/src/file_analysis/analyzer/pe/pe-file-idata.pac b/src/file_analysis/analyzer/pe/pe-file-idata.pac index ec87ca6673..d3ce2e9ffd 100644 --- a/src/file_analysis/analyzer/pe/pe-file-idata.pac +++ b/src/file_analysis/analyzer/pe/pe-file-idata.pac @@ -29,12 +29,12 @@ type import_lookup_table = record { type import_entry(is_module: bool, pad_align: uint8) = record { pad: bytestring &length=pad_align; has_index: case is_module of { - true -> null: empty; + true -> null: empty; false -> index: uint16; }; name: null_terminated_string; } &let { - proc: bool = $context.connection.proc_import_hint(name); + proc_align: bool = $context.connection.proc_import_hint(name, is_module); }; type idata = record { @@ -63,6 +63,9 @@ refine connection MockConnection += { uint32 next_hint_index_; uint8 next_hint_align_; bool next_hint_is_module_; + + // Track the module name, so we know what each import's for + bytestring module_name_; %} %init{ @@ -73,6 +76,12 @@ refine connection MockConnection += { next_hint_is_module_ = true; next_hint_index_ = 0; next_hint_align_ = 0; + + module_name_ = bytestring(); + %} + + %cleanup{ + module_name_.free(); %} # When we read the section header, store the relative virtual address and @@ -101,10 +110,15 @@ refine connection MockConnection += { %} # We need to calculate the length of the next padding field - function proc_import_hint(hint_name: bytestring): bool + function proc_import_hint(hint_name: bytestring, is_module: bool): bool %{ next_hint_align_ = ${hint_name}.length() % 2; - printf("Imported %s\n", ${hint_name}.data()); + if ( is_module && ${hint_name}.length() > 1 ) + { + module_name_.clear(); + module_name_.init(${hint_name}.data(), ${hint_name}.length() - 1); + } + return true; %} @@ -129,6 +143,11 @@ refine connection MockConnection += { return next_hint_index_ == imports_per_module_.size(); %} + function get_module_name(): bytestring + %{ + return module_name_; + %} + function get_import_table_addr(): uint32 %{ return import_table_va_ > 0 ? import_table_va_ : 0; diff --git a/src/file_analysis/analyzer/pe/pe-file-types.pac b/src/file_analysis/analyzer/pe/pe-file-types.pac index 57020a88da..b2fa934dc4 100644 --- a/src/file_analysis/analyzer/pe/pe-file-types.pac +++ b/src/file_analysis/analyzer/pe/pe-file-types.pac @@ -35,4 +35,3 @@ type Padding(length: uint64) = record { }; type null_terminated_string = RE/[A-Za-z0-9.]+\x00/; - diff --git a/src/file_analysis/analyzer/pe/pe-file.pac b/src/file_analysis/analyzer/pe/pe-file.pac index 07129b878d..3d69256682 100644 --- a/src/file_analysis/analyzer/pe/pe-file.pac +++ b/src/file_analysis/analyzer/pe/pe-file.pac @@ -3,7 +3,12 @@ %include pe-file-idata.pac # The base record for a Portable Executable file -type PE_File = record { +type PE_File = case $context.connection.is_done() of { + false -> PE : Portable_Executable; + true -> overlay : bytestring &length=1 &transient; +}; + +type Portable_Executable = record { headers : Headers; pad1 : Padding(iat_loc); iat : idata &length=$context.connection.get_import_table_len(); @@ -12,5 +17,27 @@ type PE_File = record { unparsed_hdr_len: uint32 = headers.pe_header.optional_header.size_of_headers - headers.length; iat_loc: uint64 = $context.connection.get_import_table_addr() - headers.pe_header.optional_header.size_of_headers + unparsed_hdr_len; restofdata: uint64 = $context.connection.get_max_file_location() - $context.connection.get_import_table_addr() - $context.connection.get_import_table_len(); + proc: bool = $context.connection.proc_pe(this); } &byteorder=littleendian; +refine connection MockConnection += { + + %member{ + bool done_; + %} + + %init{ + done_ = false; + %} + + function proc_pe(p: Portable_Executable): bool + %{ + done_ = true; + return true; + %} + + function is_done(): bool + %{ + return done_; + %} +};