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