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 |