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)
{
if ( conn->is_done() )
return true;
try
{
interp->NewData(data, data + len);

View file

@ -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%);
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;
%}
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);
};

View file

@ -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;
%}

View file

@ -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;

View file

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

View file

@ -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_;
%}
};