1 // copyright notice, this list of conditions and the following disclaimer 2 // in the documentation and/or other materials provided with the 3 // distribution. 4 // * Neither the name of Google Inc. nor the names of its 5 // contributors may be used to endorse or promote products derived from 6 // this software without specific prior written permission. 7 // 8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 11 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 12 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 13 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 14 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 15 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 16 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 17 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 18 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 20 // disassembler_x86.cc: simple x86 disassembler. 21 // 22 // Provides single step disassembly of x86 bytecode and flags instructions 23 // that utilize known bad register values. 24 // 25 // Author: Cris Neckar 26 27 #include "processor/disassembler_x86.h" 28 29 #include <string.h> 30 #include <unistd.h> 31 32 namespace google_breakpad { 33 34 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode, 35 uint32_t size, 36 uint32_t virtual_address) : 37 bytecode_(bytecode), 38 size_(size), 39 virtual_address_(virtual_address), 40 current_byte_offset_(0), 41 current_inst_offset_(0), 42 instr_valid_(false), 43 register_valid_(false), 44 pushed_bad_value_(false), 45 end_of_block_(false), 46 flags_(0) { 47 libdis::x86_init(libdis::opt_none, NULL, NULL); 48 } 49 50 DisassemblerX86::~DisassemblerX86() { 51 if (instr_valid_) 52 libdis::x86_oplist_free(¤t_instr_); 53 54 libdis::x86_cleanup(); 55 } 56 57 uint32_t DisassemblerX86::NextInstruction() { 58 if (instr_valid_) 59 libdis::x86_oplist_free(¤t_instr_); 60 61 if (current_byte_offset_ >= size_) { 62 instr_valid_ = false; 63 return 0; 64 } 65 uint32_t instr_size = 0; 66 instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_, 67 virtual_address_, current_byte_offset_, 68 ¤t_instr_); 69 if (instr_size == 0) { 70 instr_valid_ = false; 71 return 0; 72 } 73 74 current_byte_offset_ += instr_size; 75 current_inst_offset_++; 76 instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); 77 if (!instr_valid_) 78 return 0; 79 80 if (current_instr_.type == libdis::insn_return) 81 end_of_block_ = true; 82 libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_); 83 libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_); 84 85 if (register_valid_) { 86 switch (current_instr_.group) { 87 // Flag branches based off of bad registers and calls that occur 88 // after pushing bad values. 89 case libdis::insn_controlflow: 90 switch (current_instr_.type) { 91 case libdis::insn_jmp: 92 case libdis::insn_jcc: 93 case libdis::insn_call: 94 case libdis::insn_callcc: 95 if (dest) { 96 switch (dest->type) { 97 case libdis::op_expression: 98 if (dest->data.expression.base.id == bad_register_.id) 99 flags_ |= DISX86_BAD_BRANCH_TARGET; 100 break; 101 case libdis::op_register: 102 if (dest->data.reg.id == bad_register_.id) 103 flags_ |= DISX86_BAD_BRANCH_TARGET; 104 break; 105 default: 106 if (pushed_bad_value_ && 107 (current_instr_.type == libdis::insn_call || 108 current_instr_.type == libdis::insn_callcc)) 109 flags_ |= DISX86_BAD_ARGUMENT_PASSED; 110 break; 111 } 112 } 113 break; 114 default: 115 break; 116 } 117 break; 118 119 // Flag block data operations that use bad registers for src or dest. 120 case libdis::insn_string: 121 if (dest && dest->type == libdis::op_expression && 122 dest->data.expression.base.id == bad_register_.id) 123 flags_ |= DISX86_BAD_BLOCK_WRITE; 124 if (src && src->type == libdis::op_expression && 125 src->data.expression.base.id == bad_register_.id) 126 flags_ |= DISX86_BAD_BLOCK_READ; 127 break; 128 129 // Flag comparisons based on bad data. 130 case libdis::insn_comparison: 131 if ((dest && dest->type == libdis::op_expression && 132 dest->data.expression.base.id == bad_register_.id) || 133 (src && src->type == libdis::op_expression && 134 src->data.expression.base.id == bad_register_.id) || 135 (dest && dest->type == libdis::op_register && 136 dest->data.reg.id == bad_register_.id) || 137 (src && src->type == libdis::op_register && 138 src->data.reg.id == bad_register_.id)) 139 flags_ |= DISX86_BAD_COMPARISON; 140 break; 141 142 // Flag any other instruction which derefs a bad register for 143 // src or dest. 144 default: 145 if (dest && dest->type == libdis::op_expression && 146 dest->data.expression.base.id == bad_register_.id) 147 flags_ |= DISX86_BAD_WRITE; 148 if (src && src->type == libdis::op_expression && 149 src->data.expression.base.id == bad_register_.id) 150 flags_ |= DISX86_BAD_READ; 151 break; 152 } 153 } 154 155 // When a register is marked as tainted check if it is pushed. 156 // TODO(cdn): may also want to check for MOVs into EBP offsets. 157 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { 158 switch (dest->type) { 159 case libdis::op_expression: 160 if (dest->data.expression.base.id == bad_register_.id || 161 dest->data.expression.index.id == bad_register_.id) 162 pushed_bad_value_ = true; 163 break; 164 case libdis::op_register: 165 if (dest->data.reg.id == bad_register_.id) 166 pushed_bad_value_ = true; 167 break; 168 default: 169 break; 170 } 171 } 172 173 // Check if a tainted register value is clobbered. 174 // For conditional MOVs and XCHGs assume that 175 // there is a hit. 176 if (register_valid_) { 177 switch (current_instr_.type) { 178 case libdis::insn_xor: 179 if (src && src->type == libdis::op_register && 180 dest && dest->type == libdis::op_register && 181 src->data.reg.id == bad_register_.id && 182 src->data.reg.id == dest->data.reg.id) 183 register_valid_ = false; 184 break; 185 case libdis::insn_pop: 186 case libdis::insn_mov: 187 case libdis::insn_movcc: 188 if (dest && dest->type == libdis::op_register && 189 dest->data.reg.id == bad_register_.id) 190 register_valid_ = false; 191 break; 192 case libdis::insn_popregs: 193 register_valid_ = false; 194 break; 195 case libdis::insn_xchg: 196 case libdis::insn_xchgcc: 197 if (dest && dest->type == libdis::op_register && 198 src && src->type == libdis::op_register) { 199 if (dest->data.reg.id == bad_register_.id) 200 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); 201 else if (src->data.reg.id == bad_register_.id) 202 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); 203 } 204 break; 205 default: 206 break; 207 } 208 } 209 210 return instr_size; 211 } 212 213 bool DisassemblerX86::setBadRead() { 214 if (!instr_valid_) 215 return false; 216 217 libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_); 218 if (!operand || operand->type != libdis::op_expression) 219 return false; 220 221 memcpy(&bad_register_, &operand->data.expression.base, 222 sizeof(libdis::x86_reg_t)); 223 register_valid_ = true; 224 return true; 225 } 226 227 bool DisassemblerX86::setBadWrite() { 228 if (!instr_valid_) 229 return false; 230 231 libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_); 232 if (!operand || operand->type != libdis::op_expression) 233 return false; 234 235 memcpy(&bad_register_, &operand->data.expression.base, 236 sizeof(libdis::x86_reg_t)); 237 register_valid_ = true; 238 return true; 239 } 240 241 } // namespace google_breakpad 242