| OLD | NEW |
| 1 // copyright notice, this list of conditions and the following disclaimer | 1 // copyright notice, this list of conditions and the following disclaimer |
| 2 // in the documentation and/or other materials provided with the | 2 // in the documentation and/or other materials provided with the |
| 3 // distribution. | 3 // distribution. |
| 4 // * Neither the name of Google Inc. nor the names of its | 4 // * Neither the name of Google Inc. nor the names of its |
| 5 // contributors may be used to endorse or promote products derived from | 5 // contributors may be used to endorse or promote products derived from |
| 6 // this software without specific prior written permission. | 6 // this software without specific prior written permission. |
| 7 // | 7 // |
| 8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 8 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 9 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 10 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 #include <string.h> | 29 #include <string.h> |
| 30 | 30 |
| 31 namespace google_breakpad { | 31 namespace google_breakpad { |
| 32 | 32 |
| 33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode, | 33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode, |
| 34 uint32_t size, | 34 uint32_t size, |
| 35 uint32_t virtual_address) : | 35 uint32_t virtual_address) : |
| 36 bytecode_(bytecode), | 36 bytecode_(bytecode), |
| 37 size_(size), | 37 size_(size), |
| 38 virtual_address_(virtual_address), | 38 virtual_address_(virtual_address), |
| 39 current_byte_offset_(0), | 39 handle_(0), |
| 40 current_inst_offset_(0), | |
| 41 instr_valid_(false), | 40 instr_valid_(false), |
| 41 current_instr_(nullptr), |
| 42 register_valid_(false), | 42 register_valid_(false), |
| 43 bad_register_(X86_REG_INVALID), |
| 43 pushed_bad_value_(false), | 44 pushed_bad_value_(false), |
| 44 end_of_block_(false), | 45 end_of_block_(false), |
| 45 flags_(0) { | 46 flags_(0) { |
| 46 libdis::x86_init(libdis::opt_none, NULL, NULL); | 47 //TODO: allow selecting 32 or 64-bit. |
| 48 if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle_) == CS_ERR_OK) { |
| 49 // Enable detailed instruction information. |
| 50 cs_option(handle_, CS_OPT_DETAIL, CS_OPT_ON); |
| 51 current_instr_ = cs_malloc(handle_); |
| 52 } |
| 47 } | 53 } |
| 48 | 54 |
| 49 DisassemblerX86::~DisassemblerX86() { | 55 DisassemblerX86::~DisassemblerX86() { |
| 50 if (instr_valid_) | 56 if (current_instr_) { |
| 51 libdis::x86_oplist_free(¤t_instr_); | 57 cs_free(current_instr_, 1); |
| 58 } |
| 52 | 59 |
| 53 libdis::x86_cleanup(); | 60 cs_close(&handle_); |
| 54 } | 61 } |
| 55 | 62 |
| 56 uint32_t DisassemblerX86::NextInstruction() { | 63 // Return a pointer to the `nth` (zero-based) operand in `detail`, or NULL if |
| 57 if (instr_valid_) | 64 // no such operand exists. |
| 58 libdis::x86_oplist_free(¤t_instr_); | 65 static cs_x86_op* get_operand(cs_x86& detail, size_t nth) { |
| 66 if (nth > detail.op_count) { |
| 67 return nullptr; |
| 68 } |
| 69 return &detail.operands[nth]; |
| 70 } |
| 59 | 71 |
| 60 if (current_byte_offset_ >= size_) { | 72 size_t DisassemblerX86::NextInstruction() { |
| 61 instr_valid_ = false; | 73 instr_valid_ = false; |
| 62 return 0; | 74 if (size_ == 0) { |
| 63 } | |
| 64 uint32_t instr_size = 0; | |
| 65 instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_, | |
| 66 virtual_address_, current_byte_offset_, | |
| 67 ¤t_instr_); | |
| 68 if (instr_size == 0) { | |
| 69 instr_valid_ = false; | |
| 70 return 0; | 75 return 0; |
| 71 } | 76 } |
| 72 | 77 |
| 73 current_byte_offset_ += instr_size; | 78 if (!cs_disasm_iter(handle_, &bytecode_, &size_, &virtual_address_, |
| 74 current_inst_offset_++; | 79 current_instr_)) { |
| 75 instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); | |
| 76 if (!instr_valid_) | |
| 77 return 0; | 80 return 0; |
| 81 } |
| 78 | 82 |
| 79 if (current_instr_.type == libdis::insn_return) | 83 instr_valid_ = true; |
| 84 if (cs_insn_group(handle_, current_instr_, CS_GRP_RET)) |
| 80 end_of_block_ = true; | 85 end_of_block_ = true; |
| 81 libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_); | 86 cs_x86_op* src = get_operand(current_instr_->detail->x86, 1); |
| 82 libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_); | 87 cs_x86_op* dest = get_operand(current_instr_->detail->x86, 0); |
| 83 | 88 |
| 84 if (register_valid_) { | 89 if (register_valid_) { |
| 85 switch (current_instr_.group) { | 90 if (cs_insn_group(handle_, current_instr_, CS_GRP_JUMP) || |
| 91 cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) { |
| 86 // Flag branches based off of bad registers and calls that occur | 92 // Flag branches based off of bad registers and calls that occur |
| 87 // after pushing bad values. | 93 // after pushing bad values. |
| 88 case libdis::insn_controlflow: | 94 if (dest) { |
| 89 switch (current_instr_.type) { | 95 switch (dest->type) { |
| 90 case libdis::insn_jmp: | 96 case X86_OP_MEM: |
| 91 case libdis::insn_jcc: | 97 if (dest->mem.base == bad_register_) |
| 92 case libdis::insn_call: | 98 flags_ |= DISX86_BAD_BRANCH_TARGET; |
| 93 case libdis::insn_callcc: | 99 break; |
| 94 if (dest) { | 100 case X86_OP_REG: |
| 95 switch (dest->type) { | 101 if (dest->reg == bad_register_) |
| 96 case libdis::op_expression: | 102 flags_ |= DISX86_BAD_BRANCH_TARGET; |
| 97 if (dest->data.expression.base.id == bad_register_.id) | 103 break; |
| 98 flags_ |= DISX86_BAD_BRANCH_TARGET; | 104 default: |
| 99 break; | 105 if (pushed_bad_value_ && |
| 100 case libdis::op_register: | 106 cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) { |
| 101 if (dest->data.reg.id == bad_register_.id) | 107 flags_ |= DISX86_BAD_ARGUMENT_PASSED; |
| 102 flags_ |= DISX86_BAD_BRANCH_TARGET; | 108 } |
| 103 break; | 109 break; |
| 104 default: | 110 } |
| 105 if (pushed_bad_value_ && | 111 } |
| 106 (current_instr_.type == libdis::insn_call || | 112 } else if(currentInstructionIsBlockData()) { |
| 107 current_instr_.type == libdis::insn_callcc)) | 113 // Flag block data operations that use bad registers for src or dest. |
| 108 flags_ |= DISX86_BAD_ARGUMENT_PASSED; | 114 if (dest && dest->type == X86_OP_MEM && |
| 109 break; | 115 dest->mem.base == bad_register_) { |
| 110 } | 116 flags_ |= DISX86_BAD_BLOCK_WRITE; |
| 111 } | 117 } |
| 112 break; | 118 if (src && src->type == X86_OP_MEM && |
| 113 default: | 119 src->mem.base == bad_register_) { |
| 114 break; | 120 flags_ |= DISX86_BAD_BLOCK_READ; |
| 121 } |
| 122 } else { |
| 123 switch (current_instr_->id) { |
| 124 // Flag comparisons based on bad data. |
| 125 case X86_INS_CMP: |
| 126 case X86_INS_CMPPS: |
| 127 case X86_INS_PCMPEQD: |
| 128 case X86_INS_PCMPEQW: |
| 129 case X86_INS_PFCMPEQ: |
| 130 case X86_INS_PFCMPGE: |
| 131 case X86_INS_PFCMPGT: |
| 132 case X86_INS_TEST: |
| 133 if ((dest && dest->type == X86_OP_MEM && |
| 134 dest->mem.base == bad_register_) || |
| 135 (src && src->type == X86_OP_MEM && |
| 136 src->mem.base == bad_register_) || |
| 137 (dest && dest->type == X86_OP_REG && |
| 138 dest->reg == bad_register_) || |
| 139 (src && src->type == X86_OP_REG && |
| 140 src->reg == bad_register_)) { |
| 141 flags_ |= DISX86_BAD_COMPARISON; |
| 115 } | 142 } |
| 116 break; | 143 break; |
| 117 | 144 |
| 118 // Flag block data operations that use bad registers for src or dest. | 145 // Flag any other instruction which derefs a bad register for |
| 119 case libdis::insn_string: | 146 // src or dest. |
| 120 if (dest && dest->type == libdis::op_expression && | 147 default: |
| 121 dest->data.expression.base.id == bad_register_.id) | 148 if (dest && dest->type == X86_OP_MEM && |
| 122 flags_ |= DISX86_BAD_BLOCK_WRITE; | 149 dest->mem.base == bad_register_) { |
| 123 if (src && src->type == libdis::op_expression && | 150 flags_ |= DISX86_BAD_WRITE; |
| 124 src->data.expression.base.id == bad_register_.id) | 151 } |
| 125 flags_ |= DISX86_BAD_BLOCK_READ; | 152 if (src && src->type == X86_OP_MEM && |
| 153 src->mem.base == bad_register_) { |
| 154 flags_ |= DISX86_BAD_READ; |
| 155 } |
| 126 break; | 156 break; |
| 127 | 157 } |
| 128 // Flag comparisons based on bad data. | |
| 129 case libdis::insn_comparison: | |
| 130 if ((dest && dest->type == libdis::op_expression && | |
| 131 dest->data.expression.base.id == bad_register_.id) || | |
| 132 (src && src->type == libdis::op_expression && | |
| 133 src->data.expression.base.id == bad_register_.id) || | |
| 134 (dest && dest->type == libdis::op_register && | |
| 135 dest->data.reg.id == bad_register_.id) || | |
| 136 (src && src->type == libdis::op_register && | |
| 137 src->data.reg.id == bad_register_.id)) | |
| 138 flags_ |= DISX86_BAD_COMPARISON; | |
| 139 break; | |
| 140 | |
| 141 // Flag any other instruction which derefs a bad register for | |
| 142 // src or dest. | |
| 143 default: | |
| 144 if (dest && dest->type == libdis::op_expression && | |
| 145 dest->data.expression.base.id == bad_register_.id) | |
| 146 flags_ |= DISX86_BAD_WRITE; | |
| 147 if (src && src->type == libdis::op_expression && | |
| 148 src->data.expression.base.id == bad_register_.id) | |
| 149 flags_ |= DISX86_BAD_READ; | |
| 150 break; | |
| 151 } | 158 } |
| 152 } | 159 } |
| 153 | 160 |
| 154 // When a register is marked as tainted check if it is pushed. | 161 // When a register is marked as tainted check if it is pushed. |
| 155 // TODO(cdn): may also want to check for MOVs into EBP offsets. | 162 // TODO(cdn): may also want to check for MOVs into EBP offsets. |
| 156 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { | 163 if (register_valid_ && dest && current_instr_->id == X86_INS_PUSH) { |
| 157 switch (dest->type) { | 164 switch (dest->type) { |
| 158 case libdis::op_expression: | 165 case X86_OP_MEM: |
| 159 if (dest->data.expression.base.id == bad_register_.id || | 166 if (dest->mem.base == bad_register_ || |
| 160 dest->data.expression.index.id == bad_register_.id) | 167 dest->mem.index == bad_register_) |
| 161 pushed_bad_value_ = true; | 168 pushed_bad_value_ = true; |
| 162 break; | 169 break; |
| 163 case libdis::op_register: | 170 case X86_OP_REG: |
| 164 if (dest->data.reg.id == bad_register_.id) | 171 if (dest->reg == bad_register_) |
| 165 pushed_bad_value_ = true; | 172 pushed_bad_value_ = true; |
| 166 break; | 173 break; |
| 167 default: | 174 default: |
| 168 break; | 175 break; |
| 169 } | 176 } |
| 170 } | 177 } |
| 171 | 178 |
| 172 // Check if a tainted register value is clobbered. | 179 // Check if a tainted register value is clobbered. |
| 173 // For conditional MOVs and XCHGs assume that | 180 // For conditional MOVs and XCHGs assume that |
| 174 // there is a hit. | 181 // there is a hit. |
| 175 if (register_valid_) { | 182 if (register_valid_) { |
| 176 switch (current_instr_.type) { | 183 switch (current_instr_->id) { |
| 177 case libdis::insn_xor: | 184 case X86_INS_PXOR: |
| 178 if (src && src->type == libdis::op_register && | 185 case X86_INS_XOR: |
| 179 dest && dest->type == libdis::op_register && | 186 case X86_INS_XORPD: |
| 180 src->data.reg.id == bad_register_.id && | 187 case X86_INS_XORPS: |
| 181 src->data.reg.id == dest->data.reg.id) | 188 if (src && src->type == X86_OP_REG && |
| 189 dest && dest->type == X86_OP_REG && |
| 190 src->reg == bad_register_ && |
| 191 src->reg == dest->reg) { |
| 182 register_valid_ = false; | 192 register_valid_ = false; |
| 193 } |
| 183 break; | 194 break; |
| 184 case libdis::insn_pop: | 195 case X86_INS_POP: |
| 185 case libdis::insn_mov: | 196 case X86_INS_CVTDQ2PS: |
| 186 case libdis::insn_movcc: | 197 case X86_INS_CVTPI2PS: |
| 187 if (dest && dest->type == libdis::op_register && | 198 case X86_INS_CVTPS2PD: |
| 188 dest->data.reg.id == bad_register_.id) | 199 case X86_INS_CVTPS2PI: |
| 200 case X86_INS_CVTTPS2PI: |
| 201 case X86_INS_LAHF: |
| 202 case X86_INS_LDS: |
| 203 case X86_INS_LEA: |
| 204 case X86_INS_LES: |
| 205 case X86_INS_LFS: |
| 206 case X86_INS_LGS: |
| 207 case X86_INS_LSS: |
| 208 case X86_INS_MASKMOVQ: |
| 209 case X86_INS_MOVD: |
| 210 case X86_INS_MOVQ: |
| 211 case X86_INS_MOV: |
| 212 case X86_INS_MOVAPD: |
| 213 case X86_INS_MOVAPS: |
| 214 case X86_INS_MOVHLPS: |
| 215 case X86_INS_MOVHPD: |
| 216 case X86_INS_MOVLHPS: |
| 217 case X86_INS_MOVLPD: |
| 218 case X86_INS_MOVLPS: |
| 219 case X86_INS_MOVMSKPS: |
| 220 case X86_INS_MOVNTI: |
| 221 case X86_INS_MOVNTPS: |
| 222 case X86_INS_MOVNTQ: |
| 223 case X86_INS_MOVSX: |
| 224 case X86_INS_MOVSXD: |
| 225 case X86_INS_MOVUPD: |
| 226 case X86_INS_MOVUPS: |
| 227 case X86_INS_MOVZX: |
| 228 case X86_INS_PSHUFW: |
| 229 case X86_INS_SAHF: |
| 230 case X86_INS_CMOVA: |
| 231 case X86_INS_CMOVBE: |
| 232 case X86_INS_CMOVG: |
| 233 case X86_INS_CMOVGE: |
| 234 case X86_INS_CMOVL: |
| 235 case X86_INS_CMOVLE: |
| 236 case X86_INS_CMOVNO: |
| 237 case X86_INS_CMOVNP: |
| 238 case X86_INS_CMOVNS: |
| 239 case X86_INS_CMOVO: |
| 240 case X86_INS_CMOVP: |
| 241 case X86_INS_CMOVS: |
| 242 case X86_INS_SETA: |
| 243 case X86_INS_SETBE: |
| 244 case X86_INS_SETG: |
| 245 case X86_INS_SETGE: |
| 246 case X86_INS_SETL: |
| 247 case X86_INS_SETLE: |
| 248 case X86_INS_SETNO: |
| 249 case X86_INS_SETNS: |
| 250 case X86_INS_SETO: |
| 251 case X86_INS_SETS: |
| 252 if (dest && dest->type == X86_OP_REG && |
| 253 dest->reg == bad_register_) { |
| 189 register_valid_ = false; | 254 register_valid_ = false; |
| 255 } |
| 190 break; | 256 break; |
| 191 case libdis::insn_popregs: | 257 case X86_INS_POPAW: |
| 192 register_valid_ = false; | 258 register_valid_ = false; |
| 193 break; | 259 break; |
| 194 case libdis::insn_xchg: | 260 case X86_INS_BSWAP: |
| 195 case libdis::insn_xchgcc: | 261 case X86_INS_XCHG: |
| 196 if (dest && dest->type == libdis::op_register && | 262 case X86_INS_CMPXCHG: |
| 197 src && src->type == libdis::op_register) { | 263 case X86_INS_CMPXCHG8B: |
| 198 if (dest->data.reg.id == bad_register_.id) | 264 if (dest && dest->type == X86_OP_REG && |
| 199 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); | 265 src && src->type == X86_OP_REG) { |
| 200 else if (src->data.reg.id == bad_register_.id) | 266 if (dest->reg == bad_register_) |
| 201 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); | 267 bad_register_ = src->reg; |
| 268 else if (src->reg == bad_register_) |
| 269 bad_register_ = dest->reg; |
| 202 } | 270 } |
| 203 break; | 271 break; |
| 204 default: | 272 default: |
| 205 break; | 273 break; |
| 206 } | 274 } |
| 207 } | 275 } |
| 208 | 276 |
| 209 return instr_size; | 277 return current_instr_->size; |
| 278 } |
| 279 |
| 280 bool DisassemblerX86::currentInstructionIsBlockData() { |
| 281 if (!instr_valid_) |
| 282 return false; |
| 283 |
| 284 switch (current_instr_->id) { |
| 285 case X86_INS_CMPSB: |
| 286 case X86_INS_CMPSD: |
| 287 case X86_INS_CMPSQ: |
| 288 case X86_INS_CMPSW: |
| 289 case X86_INS_LODSB: |
| 290 case X86_INS_LODSD: |
| 291 case X86_INS_LODSQ: |
| 292 case X86_INS_LODSW: |
| 293 case X86_INS_MOVSB: |
| 294 case X86_INS_MOVSD: |
| 295 case X86_INS_MOVSQ: |
| 296 case X86_INS_MOVSW: |
| 297 case X86_INS_SCASB: |
| 298 case X86_INS_SCASD: |
| 299 case X86_INS_SCASQ: |
| 300 case X86_INS_SCASW: |
| 301 case X86_INS_STOSB: |
| 302 case X86_INS_STOSD: |
| 303 case X86_INS_STOSQ: |
| 304 case X86_INS_STOSW: |
| 305 case X86_INS_XLATB: |
| 306 return true; |
| 307 default: |
| 308 return false; |
| 309 } |
| 210 } | 310 } |
| 211 | 311 |
| 212 bool DisassemblerX86::setBadRead() { | 312 bool DisassemblerX86::setBadRead() { |
| 213 if (!instr_valid_) | 313 if (!instr_valid_) |
| 214 return false; | 314 return false; |
| 215 | 315 |
| 216 libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_); | 316 cs_x86_op* operand = get_operand(current_instr_->detail->x86, 1); |
| 217 if (!operand || operand->type != libdis::op_expression) | 317 if (!operand || operand->type != X86_OP_MEM) |
| 218 return false; | 318 return false; |
| 219 | 319 |
| 220 memcpy(&bad_register_, &operand->data.expression.base, | 320 bad_register_ = static_cast<x86_reg>(operand->mem.base); |
| 221 sizeof(libdis::x86_reg_t)); | |
| 222 register_valid_ = true; | 321 register_valid_ = true; |
| 322 |
| 223 return true; | 323 return true; |
| 224 } | 324 } |
| 225 | 325 |
| 226 bool DisassemblerX86::setBadWrite() { | 326 bool DisassemblerX86::setBadWrite() { |
| 327 |
| 227 if (!instr_valid_) | 328 if (!instr_valid_) |
| 228 return false; | 329 return false; |
| 229 | 330 |
| 230 libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_); | 331 cs_x86_op* operand = get_operand(current_instr_->detail->x86, 0); |
| 231 if (!operand || operand->type != libdis::op_expression) | 332 if (!operand || operand->type != X86_OP_MEM) |
| 232 return false; | 333 return false; |
| 233 | 334 |
| 234 memcpy(&bad_register_, &operand->data.expression.base, | 335 bad_register_ = static_cast<x86_reg>(operand->mem.base); |
| 235 sizeof(libdis::x86_reg_t)); | |
| 236 register_valid_ = true; | 336 register_valid_ = true; |
| 337 |
| 237 return true; | 338 return true; |
| 238 } | 339 } |
| 239 | 340 |
| 240 } // namespace google_breakpad | 341 } // namespace google_breakpad |
| OLD | NEW |