From 2b4274afc4fae883d1251a7a420e24fd526a9f16 Mon Sep 17 00:00:00 2001 From: "cdn@chromium.org" Date: Fri, 1 Oct 2010 22:38:10 +0000 Subject: Added libdisasm to the repository. This library is no longer under development so there is no reason not to keep it locally. Implemented a basic disassembler which can be used to scan bytecode for interesting conditions. This should be pretty easy to add to for things other than exploitability if there is a desire. This also adds several tests to the windows exploitability ranking code to take advantage of the disassembler for x86 code. BUG=None TEST=DisassemblerX86Test.* Review URL: http://breakpad.appspot.com/203001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@705 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/processor/disassembler_x86.cc | 232 ++++++++++++++++++++ src/processor/disassembler_x86.h | 118 ++++++++++ src/processor/disassembler_x86_unittest.cc | 240 +++++++++++++++++++++ src/processor/exploitability_unittest.cc | 213 ++++++++++++++++++ src/processor/exploitability_win.cc | 134 +++++++++++- src/processor/minidump_processor_unittest.cc | 18 -- src/processor/testdata/ascii_read_av.dmp | Bin 0 -> 16566 bytes .../testdata/ascii_read_av_block_write.dmp | Bin 0 -> 15194 bytes .../testdata/ascii_read_av_clobber_write.dmp | Bin 0 -> 17402 bytes .../testdata/ascii_read_av_conditional.dmp | Bin 0 -> 16982 bytes src/processor/testdata/ascii_read_av_then_jmp.dmp | Bin 0 -> 16598 bytes .../testdata/ascii_read_av_xchg_write.dmp | Bin 0 -> 16390 bytes src/processor/testdata/ascii_write_av.dmp | Bin 0 -> 17702 bytes .../testdata/ascii_write_av_arg_to_call.dmp | Bin 0 -> 15942 bytes src/processor/testdata/exec_av_on_stack.dmp | Bin 0 -> 16629 bytes src/processor/testdata/null_read_av.dmp | Bin 0 -> 15870 bytes src/processor/testdata/null_write_av.dmp | Bin 0 -> 17378 bytes src/processor/testdata/stack_exhaustion.dmp | Bin 0 -> 1049914 bytes 18 files changed, 926 insertions(+), 29 deletions(-) create mode 100644 src/processor/disassembler_x86.cc create mode 100644 src/processor/disassembler_x86.h create mode 100644 src/processor/disassembler_x86_unittest.cc create mode 100644 src/processor/exploitability_unittest.cc create mode 100755 src/processor/testdata/ascii_read_av.dmp create mode 100755 src/processor/testdata/ascii_read_av_block_write.dmp create mode 100755 src/processor/testdata/ascii_read_av_clobber_write.dmp create mode 100755 src/processor/testdata/ascii_read_av_conditional.dmp create mode 100755 src/processor/testdata/ascii_read_av_then_jmp.dmp create mode 100755 src/processor/testdata/ascii_read_av_xchg_write.dmp create mode 100755 src/processor/testdata/ascii_write_av.dmp create mode 100755 src/processor/testdata/ascii_write_av_arg_to_call.dmp create mode 100755 src/processor/testdata/exec_av_on_stack.dmp create mode 100755 src/processor/testdata/null_read_av.dmp create mode 100755 src/processor/testdata/null_write_av.dmp create mode 100755 src/processor/testdata/stack_exhaustion.dmp (limited to 'src/processor') diff --git a/src/processor/disassembler_x86.cc b/src/processor/disassembler_x86.cc new file mode 100644 index 00000000..5caa016f --- /dev/null +++ b/src/processor/disassembler_x86.cc @@ -0,0 +1,232 @@ +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// disassembler_x86.cc: simple x86 disassembler. +// +// Provides single step disassembly of x86 bytecode and flags instructions +// that utilize known bad register values. +// +// Author: Cris Neckar + +#include "processor/disassembler_x86.h" + +#include +#include + +namespace google_breakpad { + +DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode, + u_int32_t size, + u_int32_t virtual_address) : + bytecode_(bytecode), + size_(size), + virtual_address_(virtual_address), + current_byte_offset_(0), + current_inst_offset_(0), + instr_valid_(false), + register_valid_(false), + pushed_bad_value_(false), + end_of_block_(false), + flags_(0) { + libdis::x86_init(libdis::opt_none, NULL, NULL); +} + +DisassemblerX86::~DisassemblerX86() { + libdis::x86_cleanup(); +} + +u_int32_t DisassemblerX86::NextInstruction() { + if (instr_valid_) + libdis::x86_oplist_free(¤t_instr_); + + if (current_byte_offset_ >= size_) { + instr_valid_ = false; + return 0; + } + u_int32_t instr_size = 0; + instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_, + virtual_address_, current_byte_offset_, + ¤t_instr_); + if (instr_size == 0) { + instr_valid_ = false; + return 0; + } + + current_byte_offset_ += instr_size; + current_inst_offset_++; + instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); + if (!instr_valid_) + return 0; + + if (current_instr_.type == libdis::insn_return) + end_of_block_ = true; + libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_); + libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_); + + if (register_valid_) { + switch (current_instr_.group) { + // Flag branches based off of bad registers and calls that occur + // after pushing bad values. + case libdis::insn_controlflow: + switch (current_instr_.type) { + case libdis::insn_jmp: + case libdis::insn_jcc: + case libdis::insn_call: + case libdis::insn_callcc: + if (dest) { + switch (dest->type) { + case libdis::op_expression: + if (dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BRANCH_TARGET; + break; + case libdis::op_register: + if (dest->data.reg.id == bad_register_.id) + flags_ |= DISX86_BAD_BRANCH_TARGET; + break; + default: + if (pushed_bad_value_ && + (current_instr_.type == libdis::insn_call || + current_instr_.type == libdis::insn_callcc)) + flags_ |= DISX86_BAD_ARGUMENT_PASSED; + break; + } + } + break; + } + break; + + // Flag block data operations that use bad registers for src or dest. + case libdis::insn_string: + if (dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BLOCK_WRITE; + if (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BLOCK_READ; + break; + + // Flag comparisons based on bad data. + case libdis::insn_comparison: + if ((dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) || + (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) || + (dest && dest->type == libdis::op_register && + dest->data.reg.id == bad_register_.id) || + (src && src->type == libdis::op_register && + src->data.reg.id == bad_register_.id)) + flags_ |= DISX86_BAD_COMPARISON; + break; + + // Flag any other instruction which derefs a bad register for + // src or dest. + default: + if (dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_WRITE; + if (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_READ; + break; + } + } + + // When a register is marked as tainted check if it is pushed. + // TODO(cdn): may also want to check for MOVs into EBP offsets. + if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { + switch (dest->type) { + case libdis::op_expression: + if (dest->data.expression.base.id == bad_register_.id || + dest->data.expression.index.id == bad_register_.id) + pushed_bad_value_ = true; + break; + case libdis::op_register: + if (dest->data.reg.id == bad_register_.id) + pushed_bad_value_ = true; + break; + } + } + + // Check if a tainted register value is clobbered. + // For conditional MOVs and XCHGs assume that + // there is a hit. + if (register_valid_) { + switch (current_instr_.type) { + case libdis::insn_xor: + if (src && src->type == libdis::op_register && + dest->type == libdis::op_register && + src->data.reg.id == bad_register_.id && + src->data.reg.id == dest->data.reg.id) + register_valid_ = false; + break; + case libdis::insn_pop: + case libdis::insn_mov: + case libdis::insn_movcc: + if (dest && dest->type == libdis::op_register && + dest->data.reg.id == bad_register_.id) + register_valid_ = false; + break; + case libdis::insn_popregs: + register_valid_ = false; + break; + case libdis::insn_xchg: + case libdis::insn_xchgcc: + if (dest && dest->type == libdis::op_register && + src->type == libdis::op_register) { + if (dest->data.reg.id == bad_register_.id) + memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); + else if (src->data.reg.id == bad_register_.id) + memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); + } + break; + } + } + + return instr_size; +} + +bool DisassemblerX86::setBadRead() { + if (!instr_valid_) + return false; + + libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_); + if (operand->type != libdis::op_expression) + return false; + + memcpy(&bad_register_, &operand->data.expression.base, + sizeof(libdis::x86_reg_t)); + register_valid_ = true; + return true; +} + +bool DisassemblerX86::setBadWrite() { + if (!instr_valid_) + return false; + + libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_); + if (operand->type != libdis::op_expression) + return false; + + memcpy(&bad_register_, &operand->data.expression.base, + sizeof(libdis::x86_reg_t)); + register_valid_ = true; + return true; +} + +} // namespace google_breakpad diff --git a/src/processor/disassembler_x86.h b/src/processor/disassembler_x86.h new file mode 100644 index 00000000..3eea838e --- /dev/null +++ b/src/processor/disassembler_x86.h @@ -0,0 +1,118 @@ +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// disassembler_x86.h: Basic x86 bytecode disassembler +// +// Provides a simple disassembler which wraps libdisasm. This allows simple +// tests to be run against bytecode to test for various properties. +// +// Author: Cris Neckar + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ + +#include "google_breakpad/common/breakpad_types.h" + +namespace libdis { +#include "third_party/libdisasm/libdis.h" +} + +namespace google_breakpad { + +enum { + DISX86_NONE = 0x0, + DISX86_BAD_BRANCH_TARGET = 0x1, + DISX86_BAD_ARGUMENT_PASSED = 0x2, + DISX86_BAD_WRITE = 0x4, + DISX86_BAD_BLOCK_WRITE = 0x8, + DISX86_BAD_READ = 0x10, + DISX86_BAD_BLOCK_READ = 0x20, + DISX86_BAD_COMPARISON = 0x40 +}; + +class DisassemblerX86 { + public: + // TODO(cdn): Modify this class to take a MemoryRegion instead of just + // a raw buffer. This will make it easier to use this on arbitrary + // minidumps without first copying out the code segment. + DisassemblerX86(const u_int8_t *bytecode, u_int32_t, u_int32_t); + ~DisassemblerX86(); + + // This walks to the next instruction in the memory region and + // sets flags based on the type of instruction and previous state + // including any registers marked as bad through setBadRead() + // or setBadWrite(). This method can be called in a loop to + // disassemble until the end of a region. + u_int32_t NextInstruction(); + + // Indicates whether the current disassembled instruction was valid. + bool currentInstructionValid() { return instr_valid_; } + + // Returns the type of the current instruction as defined in libdis.h. + libdis::x86_insn_group currentInstructionGroup() { + return current_instr_.group; + } + + // Indicates whether a return instruction has been encountered. + bool endOfBlock() { return end_of_block_; } + + // The flags set so far for the disassembly. + u_int16_t flags() { return flags_; } + + // This sets an indicator that the register used to determine + // src or dest for the current instruction is tainted. These can + // be used after examining the current instruction to indicate, + // for example that a bad read or write occurred and the pointer + // stored in the register is currently invalid. + bool setBadRead(); + bool setBadWrite(); + + protected: + const u_int8_t *bytecode_; + u_int32_t size_; + u_int32_t virtual_address_; + u_int32_t current_byte_offset_; + u_int32_t current_inst_offset_; + + bool instr_valid_; + libdis::x86_insn_t current_instr_; + + // TODO(cdn): Maybe also track an expression's index register. + // ex: mov eax, [ebx + ecx]; ebx is base, ecx is index. + bool register_valid_; + libdis::x86_reg_t bad_register_; + + bool pushed_bad_value_; + bool end_of_block_; + + u_int16_t flags_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ diff --git a/src/processor/disassembler_x86_unittest.cc b/src/processor/disassembler_x86_unittest.cc new file mode 100644 index 00000000..cc4754b2 --- /dev/null +++ b/src/processor/disassembler_x86_unittest.cc @@ -0,0 +1,240 @@ +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "breakpad_googletest_includes.h" +#include "processor/disassembler_x86.h" +#include "third_party/libdisasm/libdis.h" + +namespace { + +using google_breakpad::DisassemblerX86; + +unsigned char just_return[] = "\xc3"; // retn + +unsigned char invalid_instruction[] = "\x00"; // invalid + +unsigned char read_eax_jmp_eax[] = + "\x8b\x18" // mov ebx, [eax]; + "\x33\xc9" // xor ebx, ebx; + "\xff\x20" // jmp eax; + "\xc3"; // retn; + +unsigned char write_eax_arg_to_call[] = + "\x89\xa8\x00\x02\x00\x00" // mov [eax+200], ebp; + "\xc1\xeb\x02" // shr ebx, 2; + "\x50" // push eax; + "\xe8\xd1\x24\x77\x88" // call something; + "\xc3"; // retn; + +unsigned char read_edi_stosb[] = + "\x8b\x07" // mov eax, [edi]; + "\x8b\xc8" // mov ecx, eax; + "\xf3\xaa" // rep stosb; + "\xc3"; // retn; + +unsigned char read_clobber_write[] = + "\x03\x18" // add ebx, [eax]; + "\x8b\xc1" // mov eax, ecx; + "\x89\x10" // mov [eax], edx; + "\xc3"; // retn; + +unsigned char read_xchg_write[] = + "\x03\x18" // add ebx, [eax]; + "\x91" // xchg eax, ecx; + "\x89\x18" // mov [eax], ebx; + "\x89\x11" // mov [ecx], edx; + "\xc3"; // retn; + +unsigned char read_cmp[] = + "\x03\x18" // add ebx, [eax]; + "\x83\xf8\x00" // cmp eax, 0; + "\x74\x04" // je +4; + "\xc3"; // retn; + +TEST(DisassemblerX86Test, SimpleReturnInstruction) { + DisassemblerX86 dis(just_return, sizeof(just_return)-1, 0); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(true, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); + EXPECT_EQ(0, dis.NextInstruction()); + EXPECT_EQ(false, dis.currentInstructionValid()); +} + +TEST(DisassemblerX86Test, SimpleInvalidInstruction) { + DisassemblerX86 dis(invalid_instruction, sizeof(invalid_instruction)-1, 0); + EXPECT_EQ(0, dis.NextInstruction()); + EXPECT_EQ(false, dis.currentInstructionValid()); +} + +TEST(DisassemblerX86Test, BadReadLeadsToBranch) { + DisassemblerX86 dis(read_eax_jmp_eax, sizeof(read_eax_jmp_eax)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_logic, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_BRANCH_TARGET, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadWriteLeadsToPushedArg) { + DisassemblerX86 dis(write_eax_arg_to_call, + sizeof(write_eax_arg_to_call)-1, 0); + EXPECT_EQ(6, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadWrite()); + EXPECT_EQ(3, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(5, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_ARGUMENT_PASSED, dis.flags()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); + EXPECT_EQ(false, dis.endOfBlock()); +} + + +TEST(DisassemblerX86Test, BadReadLeadsToBlockWrite) { + DisassemblerX86 dis(read_edi_stosb, sizeof(read_edi_stosb)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_BLOCK_WRITE, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_string, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadClobberThenWrite) { + DisassemblerX86 dis(read_clobber_write, sizeof(read_clobber_write)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadXCHGThenWrite) { + DisassemblerX86 dis(read_xchg_write, sizeof(read_xchg_write)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_WRITE, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadThenCMP) { + DisassemblerX86 dis(read_cmp, sizeof(read_cmp)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(3, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_comparison, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); +} +} + diff --git a/src/processor/exploitability_unittest.cc b/src/processor/exploitability_unittest.cc new file mode 100644 index 00000000..fab4b448 --- /dev/null +++ b/src/processor/exploitability_unittest.cc @@ -0,0 +1,213 @@ +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/minidump_processor.h" +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/symbol_supplier.h" + +namespace google_breakpad { +class MockMinidump : public Minidump { + public: + MockMinidump() : Minidump("") { + } + + MOCK_METHOD0(Read, bool()); + MOCK_CONST_METHOD0(path, string()); + MOCK_CONST_METHOD0(header, const MDRawHeader*()); + MOCK_METHOD0(GetThreadList, MinidumpThreadList*()); +}; +} + +namespace { + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::CodeModule; +using google_breakpad::MinidumpProcessor; +using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpThread; +using google_breakpad::MockMinidump; +using google_breakpad::ProcessState; +using google_breakpad::SymbolSupplier; +using google_breakpad::SystemInfo; +using std::string; + +class TestSymbolSupplier : public SymbolSupplier { + public: + TestSymbolSupplier() : interrupt_(false) {} + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file); + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); + + // When set to true, causes the SymbolSupplier to return INTERRUPT + void set_interrupt(bool interrupt) { interrupt_ = interrupt; } + + private: + bool interrupt_; +}; + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file) { + + if (interrupt_) { + return INTERRUPT; + } + + return NOT_FOUND; +} + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + return GetSymbolFile(module, system_info, symbol_file); +} + +TEST(ExploitabilityTest, TestWindowsEngine) { + TestSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver, true); + ProcessState state; + + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_block_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_clobber_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_conditional.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_then_jmp.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_xchg_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_write_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABLITY_MEDIUM, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_write_av_arg_to_call.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABLITY_MEDIUM, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/null_read_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/null_write_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/stack_exhaustion.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/exec_av_on_stack.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); +} +} 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 + #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 (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; diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc index 89195c98..8ceda743 100644 --- a/src/processor/minidump_processor_unittest.cc +++ b/src/processor/minidump_processor_unittest.cc @@ -244,24 +244,6 @@ TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) { google_breakpad::PROCESS_OK); } -TEST_F(MinidumpProcessorTest, TestExploitilityEngine) { - TestSymbolSupplier supplier; - BasicSourceLineResolver resolver; - MinidumpProcessor processor(&supplier, &resolver, true); - - string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + - "/src/processor/testdata/minidump2.dmp"; - - ProcessState state; - ASSERT_EQ(processor.Process(minidump_file, &state), - google_breakpad::PROCESS_OK); - - // Test that the supplied dump registers as HIGH. This dump demonstrates - // a write access violation to an address which is not near null. - ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, - state.exploitability()); -} - TEST_F(MinidumpProcessorTest, TestBasicProcessing) { TestSymbolSupplier supplier; BasicSourceLineResolver resolver; diff --git a/src/processor/testdata/ascii_read_av.dmp b/src/processor/testdata/ascii_read_av.dmp new file mode 100755 index 00000000..3d2429a6 Binary files /dev/null and b/src/processor/testdata/ascii_read_av.dmp differ diff --git a/src/processor/testdata/ascii_read_av_block_write.dmp b/src/processor/testdata/ascii_read_av_block_write.dmp new file mode 100755 index 00000000..265e2b7d Binary files /dev/null and b/src/processor/testdata/ascii_read_av_block_write.dmp differ diff --git a/src/processor/testdata/ascii_read_av_clobber_write.dmp b/src/processor/testdata/ascii_read_av_clobber_write.dmp new file mode 100755 index 00000000..f536c72e Binary files /dev/null and b/src/processor/testdata/ascii_read_av_clobber_write.dmp differ diff --git a/src/processor/testdata/ascii_read_av_conditional.dmp b/src/processor/testdata/ascii_read_av_conditional.dmp new file mode 100755 index 00000000..eba8f9e7 Binary files /dev/null and b/src/processor/testdata/ascii_read_av_conditional.dmp differ diff --git a/src/processor/testdata/ascii_read_av_then_jmp.dmp b/src/processor/testdata/ascii_read_av_then_jmp.dmp new file mode 100755 index 00000000..1124f400 Binary files /dev/null and b/src/processor/testdata/ascii_read_av_then_jmp.dmp differ diff --git a/src/processor/testdata/ascii_read_av_xchg_write.dmp b/src/processor/testdata/ascii_read_av_xchg_write.dmp new file mode 100755 index 00000000..066ebf12 Binary files /dev/null and b/src/processor/testdata/ascii_read_av_xchg_write.dmp differ diff --git a/src/processor/testdata/ascii_write_av.dmp b/src/processor/testdata/ascii_write_av.dmp new file mode 100755 index 00000000..6f3e3bca Binary files /dev/null and b/src/processor/testdata/ascii_write_av.dmp differ diff --git a/src/processor/testdata/ascii_write_av_arg_to_call.dmp b/src/processor/testdata/ascii_write_av_arg_to_call.dmp new file mode 100755 index 00000000..9c0a21cd Binary files /dev/null and b/src/processor/testdata/ascii_write_av_arg_to_call.dmp differ diff --git a/src/processor/testdata/exec_av_on_stack.dmp b/src/processor/testdata/exec_av_on_stack.dmp new file mode 100755 index 00000000..b783c434 Binary files /dev/null and b/src/processor/testdata/exec_av_on_stack.dmp differ diff --git a/src/processor/testdata/null_read_av.dmp b/src/processor/testdata/null_read_av.dmp new file mode 100755 index 00000000..f146dc7f Binary files /dev/null and b/src/processor/testdata/null_read_av.dmp differ diff --git a/src/processor/testdata/null_write_av.dmp b/src/processor/testdata/null_write_av.dmp new file mode 100755 index 00000000..35c59335 Binary files /dev/null and b/src/processor/testdata/null_write_av.dmp differ diff --git a/src/processor/testdata/stack_exhaustion.dmp b/src/processor/testdata/stack_exhaustion.dmp new file mode 100755 index 00000000..4eb378b6 Binary files /dev/null and b/src/processor/testdata/stack_exhaustion.dmp differ -- cgit v1.2.1