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