Fix a PE analyzer failure where the IAT isn't aligned with a section boundary.

This commit is contained in:
Vlad Grigorescu 2015-04-18 19:41:16 -04:00
parent da89837693
commit 546cbf50c9
7 changed files with 87 additions and 25 deletions

View file

@ -18,6 +18,8 @@ PE::~PE()
bool PE::DeliverStream(const u_char* data, uint64 len) bool PE::DeliverStream(const u_char* data, uint64 len)
{ {
if ( conn->is_done() )
return true;
try try
{ {
interp->NewData(data, data + len); interp->NewData(data, data + len);

View file

@ -1,5 +1,11 @@
event pe_dos_header%(f: fa_file, h: PE::DOSHeader%); event pe_dos_header%(f: fa_file, h: PE::DOSHeader%);
event pe_dos_code%(f: fa_file, code: string%); event pe_dos_code%(f: fa_file, code: string%);
event pe_file_header%(f: fa_file, h: PE::FileHeader%); event pe_file_header%(f: fa_file, h: PE::FileHeader%);
event pe_optional_header%(f: fa_file, h: PE::OptionalHeader%); event pe_optional_header%(f: fa_file, h: PE::OptionalHeader%);
event pe_section_header%(f: fa_file, h: PE::SectionHeader%); event pe_section_header%(f: fa_file, h: PE::SectionHeader%);
event pe_import_entry%(f: fa_file, m: string, name: string%);

View file

@ -174,13 +174,23 @@ refine flow File += {
return true; return true;
%} %}
function proc_import_entry(module_name: bytestring, i: import_entry): bool
function proc_pe_file(): 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; return true;
%} %}
}; };
refine typeattr DOS_Header += &let { refine typeattr DOS_Header += &let {
@ -204,10 +214,9 @@ refine typeattr Optional_Header += &let {
}; };
refine typeattr Section_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 { refine typeattr import_entry += &let {
proc: bool = $context.flow.proc_pe_file(); proc: bool = $context.flow.proc_import_entry($context.connection.get_module_name(), this) &if(!is_module);
}; };

View file

@ -39,7 +39,7 @@ type DOS_Code(len: uint32) = record {
type NT_Headers = record { type NT_Headers = record {
PESignature : uint32; PESignature : uint32;
file_header : File_Header; 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 { } &let {
length: uint32 = file_header.SizeOfOptionalHeader+offsetof(optional_header); length: uint32 = file_header.SizeOfOptionalHeader+offsetof(optional_header);
} &length=length; } &length=length;
@ -56,7 +56,7 @@ type File_Header = record {
}; };
# The optional header gives us DLL link information, and some structural information # 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; magic : uint16;
major_linker_version : uint8; major_linker_version : uint8;
minor_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 { have_base_of_data: case pe_format of {
PE32 -> base_of_data: uint32; PE32 -> base_of_data: uint32;
default -> not_present: empty; default -> not_present: empty;
}; } &requires(pe_format);
is_pe32: case pe_format of { is_pe32: case pe_format of {
PE32_PLUS -> image_base_64: uint64; PE32_PLUS -> image_base_64: uint64;
default -> image_base_32: uint32; default -> image_base_32: uint32;
}; } &requires(pe_format);
section_alignment : uint32; section_alignment : uint32;
file_alignment : uint32; file_alignment : uint32;
os_version_major : uint16; os_version_major : uint16;
@ -91,14 +91,14 @@ type Optional_Header(len: uint16, number_of_sections: uint16) = record {
PE32 -> i32: Mem_Info32; PE32 -> i32: Mem_Info32;
PE32_PLUS -> i64: Mem_Info64; PE32_PLUS -> i64: Mem_Info64;
default -> InvalidPEFile : empty; default -> InvalidPEFile : empty;
}; } &requires(pe_format);
loader_flags : uint32; loader_flags : uint32;
number_of_rva_and_sizes : uint32; number_of_rva_and_sizes : uint32;
rvas : RVAS(number_of_rva_and_sizes); rvas : RVAS(number_of_rva_and_sizes);
} &let { } &let {
pe_format: uint8 = $context.connection.set_pe32_format(magic); pe_format: uint8 = $context.connection.set_pe32_format(magic);
image_base: uint64 = pe_format == PE32_PLUS ? image_base_64 : image_base_32; image_base: uint64 = pe_format == PE32_PLUS ? image_base_64 : image_base_32;
} &length=len; };
type Section_Headers(num: uint16) = record { type Section_Headers(num: uint16) = record {
sections : Section_Header[num]; sections : Section_Header[num];
@ -118,7 +118,7 @@ type Section_Header = record {
non_used_num_of_line_nums : uint16; non_used_num_of_line_nums : uint16;
characteristics : uint32; characteristics : uint32;
} &let { } &let {
proc: bool = $context.connection.proc_section(this); add_section: bool = $context.connection.add_section(this);
} &length=40; } &length=40;
refine connection MockConnection += { refine connection MockConnection += {
@ -132,13 +132,13 @@ refine connection MockConnection += {
pe32_format_ = UNKNOWN_VERSION;; 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_ ) 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}; max_file_location_ = ${h.size_of_raw_data} + ${h.ptr_to_raw_data};
if ( ${h.virtual_addr} > 0 && ${h.virtual_addr} == import_table_rva_ ) 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_va_ = ${h.ptr_to_raw_data} + (import_table_rva_ - ${h.virtual_addr});
return true; return true;
%} %}

View file

@ -29,12 +29,12 @@ type import_lookup_table = record {
type import_entry(is_module: bool, pad_align: uint8) = record { type import_entry(is_module: bool, pad_align: uint8) = record {
pad: bytestring &length=pad_align; pad: bytestring &length=pad_align;
has_index: case is_module of { has_index: case is_module of {
true -> null: empty; true -> null: empty;
false -> index: uint16; false -> index: uint16;
}; };
name: null_terminated_string; name: null_terminated_string;
} &let { } &let {
proc: bool = $context.connection.proc_import_hint(name); proc_align: bool = $context.connection.proc_import_hint(name, is_module);
}; };
type idata = record { type idata = record {
@ -63,6 +63,9 @@ refine connection MockConnection += {
uint32 next_hint_index_; uint32 next_hint_index_;
uint8 next_hint_align_; uint8 next_hint_align_;
bool next_hint_is_module_; bool next_hint_is_module_;
// Track the module name, so we know what each import's for
bytestring module_name_;
%} %}
%init{ %init{
@ -73,6 +76,12 @@ refine connection MockConnection += {
next_hint_is_module_ = true; next_hint_is_module_ = true;
next_hint_index_ = 0; next_hint_index_ = 0;
next_hint_align_ = 0; next_hint_align_ = 0;
module_name_ = bytestring();
%}
%cleanup{
module_name_.free();
%} %}
# When we read the section header, store the relative virtual address and # 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 # 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; 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; return true;
%} %}
@ -129,6 +143,11 @@ refine connection MockConnection += {
return next_hint_index_ == imports_per_module_.size(); return next_hint_index_ == imports_per_module_.size();
%} %}
function get_module_name(): bytestring
%{
return module_name_;
%}
function get_import_table_addr(): uint32 function get_import_table_addr(): uint32
%{ %{
return import_table_va_ > 0 ? import_table_va_ : 0; return import_table_va_ > 0 ? import_table_va_ : 0;

View file

@ -35,4 +35,3 @@ type Padding(length: uint64) = record {
}; };
type null_terminated_string = RE/[A-Za-z0-9.]+\x00/; type null_terminated_string = RE/[A-Za-z0-9.]+\x00/;

View file

@ -3,7 +3,12 @@
%include pe-file-idata.pac %include pe-file-idata.pac
# The base record for a Portable Executable file # 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; headers : Headers;
pad1 : Padding(iat_loc); pad1 : Padding(iat_loc);
iat : idata &length=$context.connection.get_import_table_len(); 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; 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; 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(); 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; } &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_;
%}
};