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 
31 namespace google_breakpad {
32 
DisassemblerX86(const uint8_t * bytecode,uint32_t size,uint32_t virtual_address)33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
34                                  uint32_t size,
35                                  uint32_t virtual_address) :
36                                      bytecode_(bytecode),
37                                      size_(size),
38                                      virtual_address_(virtual_address),
39                                      current_byte_offset_(0),
40                                      current_inst_offset_(0),
41                                      instr_valid_(false),
42                                      register_valid_(false),
43                                      pushed_bad_value_(false),
44                                      end_of_block_(false),
45                                      flags_(0) {
46   libdis::x86_init(libdis::opt_none, NULL, NULL);
47 }
48 
~DisassemblerX86()49 DisassemblerX86::~DisassemblerX86() {
50   if (instr_valid_)
51     libdis::x86_oplist_free(&current_instr_);
52 
53   libdis::x86_cleanup();
54 }
55 
NextInstruction()56 uint32_t DisassemblerX86::NextInstruction() {
57   if (instr_valid_)
58     libdis::x86_oplist_free(&current_instr_);
59 
60   if (current_byte_offset_ >= size_) {
61     instr_valid_ = false;
62     return 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                           &current_instr_);
68   if (instr_size == 0) {
69     instr_valid_ = false;
70     return 0;
71   }
72 
73   current_byte_offset_ += instr_size;
74   current_inst_offset_++;
75   instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
76   if (!instr_valid_)
77     return 0;
78 
79   if (current_instr_.type == libdis::insn_return)
80     end_of_block_ = true;
81   libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
82   libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
83 
84   if (register_valid_) {
85     switch (current_instr_.group) {
86       // Flag branches based off of bad registers and calls that occur
87       // after pushing bad values.
88       case libdis::insn_controlflow:
89         switch (current_instr_.type) {
90           case libdis::insn_jmp:
91           case libdis::insn_jcc:
92           case libdis::insn_call:
93           case libdis::insn_callcc:
94             if (dest) {
95               switch (dest->type) {
96                 case libdis::op_expression:
97                   if (dest->data.expression.base.id == bad_register_.id)
98                     flags_ |= DISX86_BAD_BRANCH_TARGET;
99                   break;
100                 case libdis::op_register:
101                   if (dest->data.reg.id == bad_register_.id)
102                     flags_ |= DISX86_BAD_BRANCH_TARGET;
103                   break;
104                 default:
105                   if (pushed_bad_value_ &&
106                       (current_instr_.type == libdis::insn_call ||
107                       current_instr_.type == libdis::insn_callcc))
108                     flags_ |= DISX86_BAD_ARGUMENT_PASSED;
109                   break;
110               }
111             }
112             break;
113           default:
114             break;
115         }
116         break;
117 
118       // Flag block data operations that use bad registers for src or dest.
119       case libdis::insn_string:
120         if (dest && dest->type == libdis::op_expression &&
121             dest->data.expression.base.id == bad_register_.id)
122           flags_ |= DISX86_BAD_BLOCK_WRITE;
123         if (src && src->type == libdis::op_expression &&
124             src->data.expression.base.id == bad_register_.id)
125           flags_ |= DISX86_BAD_BLOCK_READ;
126         break;
127 
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     }
152   }
153 
154   // 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.
156   if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
157     switch (dest->type) {
158       case libdis::op_expression:
159         if (dest->data.expression.base.id == bad_register_.id ||
160             dest->data.expression.index.id == bad_register_.id)
161           pushed_bad_value_ = true;
162         break;
163       case libdis::op_register:
164         if (dest->data.reg.id == bad_register_.id)
165           pushed_bad_value_ = true;
166         break;
167       default:
168         break;
169     }
170   }
171 
172   // Check if a tainted register value is clobbered.
173   // For conditional MOVs and XCHGs assume that
174   // there is a hit.
175   if (register_valid_) {
176     switch (current_instr_.type) {
177       case libdis::insn_xor:
178         if (src && src->type == libdis::op_register &&
179             dest && dest->type == libdis::op_register &&
180             src->data.reg.id == bad_register_.id &&
181             src->data.reg.id == dest->data.reg.id)
182           register_valid_ = false;
183         break;
184       case libdis::insn_pop:
185       case libdis::insn_mov:
186       case libdis::insn_movcc:
187         if (dest && dest->type == libdis::op_register &&
188             dest->data.reg.id == bad_register_.id)
189           register_valid_ = false;
190         break;
191       case libdis::insn_popregs:
192         register_valid_ = false;
193         break;
194       case libdis::insn_xchg:
195       case libdis::insn_xchgcc:
196         if (dest && dest->type == libdis::op_register &&
197             src && src->type == libdis::op_register) {
198           if (dest->data.reg.id == bad_register_.id)
199             memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
200           else if (src->data.reg.id == bad_register_.id)
201             memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
202         }
203         break;
204       default:
205         break;
206     }
207   }
208 
209   return instr_size;
210 }
211 
setBadRead()212 bool DisassemblerX86::setBadRead() {
213   if (!instr_valid_)
214     return false;
215 
216   libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
217   if (!operand || operand->type != libdis::op_expression)
218     return false;
219 
220   memcpy(&bad_register_, &operand->data.expression.base,
221          sizeof(libdis::x86_reg_t));
222   register_valid_ = true;
223   return true;
224 }
225 
setBadWrite()226 bool DisassemblerX86::setBadWrite() {
227   if (!instr_valid_)
228     return false;
229 
230   libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
231   if (!operand || operand->type != libdis::op_expression)
232     return false;
233 
234   memcpy(&bad_register_, &operand->data.expression.base,
235          sizeof(libdis::x86_reg_t));
236   register_valid_ = true;
237   return true;
238 }
239 
240 }  // namespace google_breakpad
241