/ src / processor / exploitability_win.cc
exploitability_win.cc
  1  // Copyright 2010 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  // exploitability_win.cc: Windows specific exploitability engine.
 30  //
 31  // Provides a guess at the exploitability of the crash for the Windows
 32  // platform given a minidump and process_state.
 33  //
 34  // Author: Cris Neckar
 35  
 36  #ifdef HAVE_CONFIG_H
 37  #include <config.h>  // Must come first
 38  #endif
 39  
 40  #include <vector>
 41  
 42  #include "processor/exploitability_win.h"
 43  
 44  #include "common/scoped_ptr.h"
 45  #include "google_breakpad/common/minidump_exception_win32.h"
 46  #include "google_breakpad/processor/minidump.h"
 47  #include "processor/disassembler_x86.h"
 48  #include "processor/logging.h"
 49  
 50  #include "third_party/libdisasm/libdis.h"
 51  
 52  namespace google_breakpad {
 53  
 54  // The cutoff that we use to judge if and address is likely an offset
 55  // from various interesting addresses.
 56  static const uint64_t kProbableNullOffset = 4096;
 57  static const uint64_t kProbableStackOffset = 8192;
 58  
 59  // The various cutoffs for the different ratings.
 60  static const size_t kHighCutoff        = 100;
 61  static const size_t kMediumCutoff      = 80;
 62  static const size_t kLowCutoff         = 50;
 63  static const size_t kInterestingCutoff = 25;
 64  
 65  // Predefined incremental values for conditional weighting.
 66  static const size_t kTinyBump          = 5;
 67  static const size_t kSmallBump         = 20;
 68  static const size_t kMediumBump        = 50;
 69  static const size_t kLargeBump         = 70;
 70  static const size_t kHugeBump          = 90;
 71  
 72  // The maximum number of bytes to disassemble past the program counter.
 73  static const size_t kDisassembleBytesBeyondPC = 2048;
 74  
 75  ExploitabilityWin::ExploitabilityWin(Minidump* dump,
 76                                       ProcessState* process_state)
 77      : Exploitability(dump, process_state) { }
 78  
 79  ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() {
 80    MinidumpException* exception = dump_->GetException();
 81    if (!exception) {
 82      BPLOG(INFO) << "Minidump does not have exception record.";
 83      return EXPLOITABILITY_ERR_PROCESSING;
 84    }
 85  
 86    const MDRawExceptionStream* raw_exception = exception->exception();
 87    if (!raw_exception) {
 88      BPLOG(INFO) << "Could not obtain raw exception info.";
 89      return EXPLOITABILITY_ERR_PROCESSING;
 90    }
 91  
 92    const MinidumpContext* context = exception->GetContext();
 93    if (!context) {
 94      BPLOG(INFO) << "Could not obtain exception context.";
 95      return EXPLOITABILITY_ERR_PROCESSING;
 96    }
 97  
 98    MinidumpMemoryList* memory_list = dump_->GetMemoryList();
 99    bool memory_available = true;
100    if (!memory_list) {
101      BPLOG(INFO) << "Minidump memory segments not available.";
102      memory_available = false;
103    }
104    uint64_t address = process_state_->crash_address();
105    uint32_t exception_code = raw_exception->exception_record.exception_code;
106  
107    uint32_t exploitability_weight = 0;
108  
109    uint64_t stack_ptr = 0;
110    uint64_t instruction_ptr = 0;
111  
112    // Getting the instruction pointer.
113    if (!context->GetInstructionPointer(&instruction_ptr)) {
114      return EXPLOITABILITY_ERR_PROCESSING;
115    }
116  
117    // Getting the stack pointer.
118    if (!context->GetStackPointer(&stack_ptr)) {
119      return EXPLOITABILITY_ERR_PROCESSING;
120    }
121  
122    // Check if we are executing on the stack.
123    if (instruction_ptr <= (stack_ptr + kProbableStackOffset) &&
124        instruction_ptr >= (stack_ptr - kProbableStackOffset))
125      exploitability_weight += kHugeBump;
126  
127    switch (exception_code) {
128      // This is almost certainly recursion.
129      case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW:
130        exploitability_weight += kTinyBump;
131        break;
132  
133      // These exceptions tend to be benign and we can generally ignore them.
134      case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO:
135      case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW:
136      case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO:
137      case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT:
138      case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW:
139      case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW:
140      case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR:
141        exploitability_weight += kTinyBump;
142        break;
143  
144      // These exceptions will typically mean that we have jumped where we
145      // shouldn't.
146      case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION:
147      case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION:
148      case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION:
149        exploitability_weight += kLargeBump;
150        break;
151  
152      // These represent bugs in exception handlers.
153      case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION:
154      case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION:
155        exploitability_weight += kSmallBump;
156        break;
157  
158      case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION:
159      case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN:
160        exploitability_weight += kHugeBump;
161        break;
162  
163      case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION:
164        exploitability_weight += kLargeBump;
165        break;
166  
167      case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION:
168        bool near_null = (address <= kProbableNullOffset);
169        bool bad_read = false;
170        bool bad_write = false;
171        if (raw_exception->exception_record.number_parameters >= 1) {
172          MDAccessViolationTypeWin av_type =
173              static_cast<MDAccessViolationTypeWin>
174              (raw_exception->exception_record.exception_information[0]);
175          switch (av_type) {
176            case MD_ACCESS_VIOLATION_WIN_READ:
177              bad_read = true;
178              if (near_null)
179                exploitability_weight += kSmallBump;
180              else
181                exploitability_weight += kMediumBump;
182              break;
183            case MD_ACCESS_VIOLATION_WIN_WRITE:
184              bad_write = true;
185              if (near_null)
186                exploitability_weight += kSmallBump;
187              else
188                exploitability_weight += kHugeBump;
189              break;
190            case MD_ACCESS_VIOLATION_WIN_EXEC:
191              if (near_null)
192                exploitability_weight += kSmallBump;
193              else
194                exploitability_weight += kHugeBump;
195              break;
196            default:
197              BPLOG(INFO) << "Unrecognized access violation type.";
198              return EXPLOITABILITY_ERR_PROCESSING;
199          }
200          MinidumpMemoryRegion* instruction_region = 0;
201          if (memory_available) {
202            instruction_region =
203                memory_list->GetMemoryRegionForAddress(instruction_ptr);
204          }
205          if (!near_null && instruction_region &&
206              context->GetContextCPU() == MD_CONTEXT_X86 &&
207              (bad_read || bad_write)) {
208            // Perform checks related to memory around instruction pointer.
209            uint32_t memory_offset =
210                instruction_ptr - instruction_region->GetBase();
211            uint32_t available_memory =
212                instruction_region->GetSize() - memory_offset;
213            available_memory = available_memory > kDisassembleBytesBeyondPC ?
214                kDisassembleBytesBeyondPC : available_memory;
215            if (available_memory) {
216              const uint8_t* raw_memory =
217                  instruction_region->GetMemory() + memory_offset;
218              DisassemblerX86 disassembler(raw_memory,
219                                           available_memory,
220                                           instruction_ptr);
221              disassembler.NextInstruction();
222              if (bad_read)
223                disassembler.setBadRead();
224              else
225                disassembler.setBadWrite();
226              if (disassembler.currentInstructionValid()) {
227                // Check if the faulting instruction falls into one of
228                // several interesting groups.
229                switch (disassembler.currentInstructionGroup()) {
230                  case libdis::insn_controlflow:
231                    exploitability_weight += kLargeBump;
232                    break;
233                  case libdis::insn_string:
234                    exploitability_weight += kHugeBump;
235                    break;
236                  default:
237                    break;
238                }
239                // Loop the disassembler through the code and check if it
240                // IDed any interesting conditions in the near future.
241                // Multiple flags may be set so treat each equally.
242                while (disassembler.NextInstruction() &&
243                       disassembler.currentInstructionValid() &&
244                       !disassembler.endOfBlock())
245                  continue;
246                if (disassembler.flags() & DISX86_BAD_BRANCH_TARGET)
247                  exploitability_weight += kLargeBump;
248                if (disassembler.flags() & DISX86_BAD_ARGUMENT_PASSED)
249                  exploitability_weight += kTinyBump;
250                if (disassembler.flags() & DISX86_BAD_WRITE)
251                  exploitability_weight += kMediumBump;
252                if (disassembler.flags() & DISX86_BAD_BLOCK_WRITE)
253                  exploitability_weight += kMediumBump;
254                if (disassembler.flags() & DISX86_BAD_READ)
255                  exploitability_weight += kTinyBump;
256                if (disassembler.flags() & DISX86_BAD_BLOCK_READ)
257                  exploitability_weight += kTinyBump;
258                if (disassembler.flags() & DISX86_BAD_COMPARISON)
259                  exploitability_weight += kTinyBump;
260              }
261            }
262          }
263          if (!near_null && AddressIsAscii(address))
264            exploitability_weight += kMediumBump;
265        } else {
266          BPLOG(INFO) << "Access violation type parameter missing.";
267          return EXPLOITABILITY_ERR_PROCESSING;
268        }
269    }
270  
271    // Based on the calculated weight we return a simplified classification.
272    BPLOG(INFO) << "Calculated exploitability weight: " << exploitability_weight;
273    if (exploitability_weight >= kHighCutoff)
274      return EXPLOITABILITY_HIGH;
275    if (exploitability_weight >= kMediumCutoff)
276      return EXPLOITABLITY_MEDIUM;
277    if (exploitability_weight >= kLowCutoff)
278      return EXPLOITABILITY_LOW;
279    if (exploitability_weight >= kInterestingCutoff)
280      return EXPLOITABILITY_INTERESTING;
281  
282    return EXPLOITABILITY_NONE;
283  }
284  
285  }  // namespace google_breakpad