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