diff options
Diffstat (limited to 'src/processor/exploitability_win.cc')
-rw-r--r-- | src/processor/exploitability_win.cc | 134 |
1 files changed, 123 insertions, 11 deletions
diff --git a/src/processor/exploitability_win.cc b/src/processor/exploitability_win.cc index b5ecb7b9..3d5a9195 100644 --- a/src/processor/exploitability_win.cc +++ b/src/processor/exploitability_win.cc @@ -34,22 +34,29 @@ // // Author: Cris Neckar +#include <vector> + #include "processor/exploitability_win.h" #include "google_breakpad/common/minidump_exception_win32.h" +#include "google_breakpad/processor/minidump.h" +#include "processor/disassembler_x86.h" #include "processor/logging.h" #include "processor/scoped_ptr.h" +#include "third_party/libdisasm/libdis.h" + namespace google_breakpad { // The cutoff that we use to judge if and address is likely an offset -// from null. +// from various interesting addresses. static const u_int64_t kProbableNullOffset = 4096; +static const u_int64_t kProbableStackOffset = 8192; // The various cutoffs for the different ratings. -static const size_t kHighCutoff = 85; -static const size_t kMediumCutoff = 65; -static const size_t kLowCutoff = 45; +static const size_t kHighCutoff = 100; +static const size_t kMediumCutoff = 80; +static const size_t kLowCutoff = 50; static const size_t kInterestingCutoff = 25; // Predefined incremental values for conditional weighting. @@ -59,25 +66,69 @@ static const size_t kMediumBump = 50; static const size_t kLargeBump = 70; static const size_t kHugeBump = 90; +// The maximum number of bytes to disassemble past the program counter. +static const size_t kDisassembleBytesBeyondPC = 2048; + ExploitabilityWin::ExploitabilityWin(Minidump *dump, ProcessState *process_state) : Exploitability(dump, process_state) { } ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() { MinidumpException *exception = dump_->GetException(); - if (!exception) + if (!exception) { + BPLOG(INFO) << "Minidump does not have exception record."; return EXPLOITABILITY_ERR_PROCESSING; + } const MDRawExceptionStream *raw_exception = exception->exception(); - if (!raw_exception) + if (!raw_exception) { + BPLOG(INFO) << "Could not obtain raw exception info."; return EXPLOITABILITY_ERR_PROCESSING; + } - u_int64_t address = raw_exception->exception_record.exception_address; + const MinidumpContext *context = exception->GetContext(); + if (!context) { + BPLOG(INFO) << "Could not obtain exception context."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + MinidumpMemoryList *memory_list = dump_->GetMemoryList(); + bool memory_available = true; + if (!memory_list) { + BPLOG(INFO) << "Minidump memory segments not available."; + memory_available = false; + } + u_int64_t address = process_state_->crash_address(); u_int32_t exception_code = raw_exception->exception_record.exception_code; u_int32_t exception_flags = raw_exception->exception_record.exception_flags; u_int32_t exploitability_weight = 0; + u_int64_t stack_ptr = 0; + u_int64_t instruction_ptr = 0; + u_int64_t this_ptr = 0; + + switch (context->GetContextCPU()) { + case MD_CONTEXT_X86: + stack_ptr = context->GetContextX86()->esp; + instruction_ptr = context->GetContextX86()->eip; + this_ptr = context->GetContextX86()->ecx; + break; + case MD_CONTEXT_AMD64: + stack_ptr = context->GetContextAMD64()->rsp; + instruction_ptr = context->GetContextAMD64()->rip; + this_ptr = context->GetContextAMD64()->rcx; + break; + default: + BPLOG(INFO) << "Unsupported architecture."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + // Check if we are executing on the stack. + if (instruction_ptr <= (stack_ptr + kProbableStackOffset) && + instruction_ptr >= (stack_ptr - kProbableStackOffset)) + exploitability_weight += kHugeBump; + switch (exception_code) { // This is almost certainly recursion. case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: @@ -120,18 +171,22 @@ ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() { case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: bool near_null = (address <= kProbableNullOffset); + bool bad_read = false; + bool bad_write = false; if (raw_exception->exception_record.number_parameters >= 1) { MDAccessViolationTypeWin av_type = static_cast<MDAccessViolationTypeWin> (raw_exception->exception_record.exception_information[0]); switch (av_type) { case MD_ACCESS_VIOLATION_WIN_READ: + bad_read = true; if (near_null) exploitability_weight += kSmallBump; else exploitability_weight += kMediumBump; break; case MD_ACCESS_VIOLATION_WIN_WRITE: + bad_write = true; if (near_null) exploitability_weight += kSmallBump; else @@ -144,22 +199,79 @@ ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() { exploitability_weight += kHugeBump; break; default: + BPLOG(INFO) << "Unrecognized access violation type."; return EXPLOITABILITY_ERR_PROCESSING; break; } + MinidumpMemoryRegion *instruction_region = 0; + if (memory_available) + instruction_region = memory_list->GetMemoryRegionForAddress(instruction_ptr); + if (!near_null && instruction_region && + context->GetContextCPU() == MD_CONTEXT_X86 && + (bad_read || bad_write)) { + // Perform checks related to memory around instruction pointer. + u_int32_t memory_offset = instruction_ptr - instruction_region->GetBase(); + u_int32_t available_memory = instruction_region->GetSize() - memory_offset; + available_memory = available_memory > kDisassembleBytesBeyondPC ? + kDisassembleBytesBeyondPC : available_memory; + if (available_memory) { + const u_int8_t *raw_memory = instruction_region->GetMemory() + memory_offset; + DisassemblerX86 disassembler(raw_memory, available_memory, instruction_ptr); + disassembler.NextInstruction(); + if (bad_read) + disassembler.setBadRead(); + else + disassembler.setBadWrite(); + if (disassembler.currentInstructionValid()) { + // Check if the faulting instruction falls into one of + // several interesting groups. + switch (disassembler.currentInstructionGroup()) { + case libdis::insn_controlflow: + exploitability_weight += kLargeBump; + break; + case libdis::insn_string: + exploitability_weight += kHugeBump; + break; + } + // Loop the disassembler through the code and check if it + // IDed any interesting conditions in the near future. + // Multiple flags may be set so treat each equally. + while (disassembler.NextInstruction() && + disassembler.currentInstructionValid() && + !disassembler.endOfBlock()) + continue; + if (disassembler.flags() & DISX86_BAD_BRANCH_TARGET) + exploitability_weight += kLargeBump; + if (disassembler.flags() & DISX86_BAD_ARGUMENT_PASSED) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_WRITE) + exploitability_weight += kMediumBump; + if (disassembler.flags() & DISX86_BAD_BLOCK_WRITE) + exploitability_weight += kMediumBump; + if (disassembler.flags() & DISX86_BAD_READ) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_BLOCK_READ) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_COMPARISON) + exploitability_weight += kTinyBump; + } + } + } } else { + BPLOG(INFO) << "Access violation type parameter missing."; return EXPLOITABILITY_ERR_PROCESSING; } } // Based on the calculated weight we return a simplified classification. - if (exploitability_weight > kHighCutoff) + BPLOG(INFO) << "Calculated exploitability weight: " << exploitability_weight; + if (exploitability_weight >= kHighCutoff) return EXPLOITABILITY_HIGH; - if (exploitability_weight > kMediumCutoff) + if (exploitability_weight >= kMediumCutoff) return EXPLOITABLITY_MEDIUM; - if (exploitability_weight > kLowCutoff) + if (exploitability_weight >= kLowCutoff) return EXPLOITABILITY_LOW; - if (exploitability_weight > kInterestingCutoff) + if (exploitability_weight >= kInterestingCutoff) return EXPLOITABILITY_INTERESTING; return EXPLOITABILITY_NONE; |