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 
DisassemblerX86(const uint8_t * bytecode,uint32_t size,uint32_t virtual_address)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 
~DisassemblerX86()50 DisassemblerX86::~DisassemblerX86() {
51   if (instr_valid_)
52     libdis::x86_oplist_free(&current_instr_);
53 
54   libdis::x86_cleanup();
55 }
56 
NextInstruction()57 uint32_t DisassemblerX86::NextInstruction() {
58   if (instr_valid_)
59     libdis::x86_oplist_free(&current_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                           &current_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(&current_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(&current_instr_);
83   libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_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 
setBadRead()213 bool DisassemblerX86::setBadRead() {
214   if (!instr_valid_)
215     return false;
216 
217   libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_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 
setBadWrite()227 bool DisassemblerX86::setBadWrite() {
228   if (!instr_valid_)
229     return false;
230 
231   libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_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