disassembler_x86.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 // disassembler_x86.cc: simple x86 disassembler. 30 // 31 // Provides single step disassembly of x86 bytecode and flags instructions 32 // that utilize known bad register values. 33 // 34 // Author: Cris Neckar 35 36 #ifdef HAVE_CONFIG_H 37 #include <config.h> // Must come first 38 #endif 39 40 #include "processor/disassembler_x86.h" 41 42 #include <string.h> 43 44 namespace google_breakpad { 45 46 DisassemblerX86::DisassemblerX86(const uint8_t* bytecode, 47 uint32_t size, 48 uint32_t virtual_address) : 49 bytecode_(bytecode), 50 size_(size), 51 virtual_address_(virtual_address), 52 current_byte_offset_(0), 53 current_inst_offset_(0), 54 instr_valid_(false), 55 register_valid_(false), 56 pushed_bad_value_(false), 57 end_of_block_(false), 58 flags_(0) { 59 libdis::x86_init(libdis::opt_none, NULL, NULL); 60 } 61 62 DisassemblerX86::~DisassemblerX86() { 63 if (instr_valid_) 64 libdis::x86_oplist_free(¤t_instr_); 65 66 libdis::x86_cleanup(); 67 } 68 69 uint32_t DisassemblerX86::NextInstruction() { 70 if (instr_valid_) 71 libdis::x86_oplist_free(¤t_instr_); 72 73 if (current_byte_offset_ >= size_) { 74 instr_valid_ = false; 75 return 0; 76 } 77 uint32_t instr_size = 0; 78 instr_size = libdis::x86_disasm((unsigned char*)bytecode_, size_, 79 virtual_address_, current_byte_offset_, 80 ¤t_instr_); 81 if (instr_size == 0) { 82 instr_valid_ = false; 83 return 0; 84 } 85 86 current_byte_offset_ += instr_size; 87 current_inst_offset_++; 88 instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); 89 if (!instr_valid_) 90 return 0; 91 92 if (current_instr_.type == libdis::insn_return) 93 end_of_block_ = true; 94 libdis::x86_op_t* src = libdis::x86_get_src_operand(¤t_instr_); 95 libdis::x86_op_t* dest = libdis::x86_get_dest_operand(¤t_instr_); 96 97 if (register_valid_) { 98 switch (current_instr_.group) { 99 // Flag branches based off of bad registers and calls that occur 100 // after pushing bad values. 101 case libdis::insn_controlflow: 102 switch (current_instr_.type) { 103 case libdis::insn_jmp: 104 case libdis::insn_jcc: 105 case libdis::insn_call: 106 case libdis::insn_callcc: 107 if (dest) { 108 switch (dest->type) { 109 case libdis::op_expression: 110 if (dest->data.expression.base.id == bad_register_.id) 111 flags_ |= DISX86_BAD_BRANCH_TARGET; 112 break; 113 case libdis::op_register: 114 if (dest->data.reg.id == bad_register_.id) 115 flags_ |= DISX86_BAD_BRANCH_TARGET; 116 break; 117 default: 118 if (pushed_bad_value_ && 119 (current_instr_.type == libdis::insn_call || 120 current_instr_.type == libdis::insn_callcc)) 121 flags_ |= DISX86_BAD_ARGUMENT_PASSED; 122 break; 123 } 124 } 125 break; 126 default: 127 break; 128 } 129 break; 130 131 // Flag block data operations that use bad registers for src or dest. 132 case libdis::insn_string: 133 if (dest && dest->type == libdis::op_expression && 134 dest->data.expression.base.id == bad_register_.id) 135 flags_ |= DISX86_BAD_BLOCK_WRITE; 136 if (src && src->type == libdis::op_expression && 137 src->data.expression.base.id == bad_register_.id) 138 flags_ |= DISX86_BAD_BLOCK_READ; 139 break; 140 141 // Flag comparisons based on bad data. 142 case libdis::insn_comparison: 143 if ((dest && dest->type == libdis::op_expression && 144 dest->data.expression.base.id == bad_register_.id) || 145 (src && src->type == libdis::op_expression && 146 src->data.expression.base.id == bad_register_.id) || 147 (dest && dest->type == libdis::op_register && 148 dest->data.reg.id == bad_register_.id) || 149 (src && src->type == libdis::op_register && 150 src->data.reg.id == bad_register_.id)) 151 flags_ |= DISX86_BAD_COMPARISON; 152 break; 153 154 // Flag any other instruction which derefs a bad register for 155 // src or dest. 156 default: 157 if (dest && dest->type == libdis::op_expression && 158 dest->data.expression.base.id == bad_register_.id) 159 flags_ |= DISX86_BAD_WRITE; 160 if (src && src->type == libdis::op_expression && 161 src->data.expression.base.id == bad_register_.id) 162 flags_ |= DISX86_BAD_READ; 163 break; 164 } 165 } 166 167 // When a register is marked as tainted check if it is pushed. 168 // TODO(cdn): may also want to check for MOVs into EBP offsets. 169 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { 170 switch (dest->type) { 171 case libdis::op_expression: 172 if (dest->data.expression.base.id == bad_register_.id || 173 dest->data.expression.index.id == bad_register_.id) 174 pushed_bad_value_ = true; 175 break; 176 case libdis::op_register: 177 if (dest->data.reg.id == bad_register_.id) 178 pushed_bad_value_ = true; 179 break; 180 default: 181 break; 182 } 183 } 184 185 // Check if a tainted register value is clobbered. 186 // For conditional MOVs and XCHGs assume that 187 // there is a hit. 188 if (register_valid_) { 189 switch (current_instr_.type) { 190 case libdis::insn_xor: 191 if (src && src->type == libdis::op_register && 192 dest && dest->type == libdis::op_register && 193 src->data.reg.id == bad_register_.id && 194 src->data.reg.id == dest->data.reg.id) 195 register_valid_ = false; 196 break; 197 case libdis::insn_pop: 198 case libdis::insn_mov: 199 case libdis::insn_movcc: 200 if (dest && dest->type == libdis::op_register && 201 dest->data.reg.id == bad_register_.id) 202 register_valid_ = false; 203 break; 204 case libdis::insn_popregs: 205 register_valid_ = false; 206 break; 207 case libdis::insn_xchg: 208 case libdis::insn_xchgcc: 209 if (dest && dest->type == libdis::op_register && 210 src && src->type == libdis::op_register) { 211 if (dest->data.reg.id == bad_register_.id) 212 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); 213 else if (src->data.reg.id == bad_register_.id) 214 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); 215 } 216 break; 217 default: 218 break; 219 } 220 } 221 222 return instr_size; 223 } 224 225 bool DisassemblerX86::setBadRead() { 226 if (!instr_valid_) 227 return false; 228 229 libdis::x86_op_t* operand = libdis::x86_get_src_operand(¤t_instr_); 230 if (!operand || operand->type != libdis::op_expression) 231 return false; 232 233 memcpy(&bad_register_, &operand->data.expression.base, 234 sizeof(libdis::x86_reg_t)); 235 register_valid_ = true; 236 return true; 237 } 238 239 bool DisassemblerX86::setBadWrite() { 240 if (!instr_valid_) 241 return false; 242 243 libdis::x86_op_t* operand = libdis::x86_get_dest_operand(¤t_instr_); 244 if (!operand || operand->type != libdis::op_expression) 245 return false; 246 247 memcpy(&bad_register_, &operand->data.expression.base, 248 sizeof(libdis::x86_reg_t)); 249 register_valid_ = true; 250 return true; 251 } 252 253 } // namespace google_breakpad