File Coverage

blib/lib/Disassemble/X86.pm
Criterion Covered Total %
statement 1623 2079 78.0
branch 894 1404 63.6
condition 114 211 54.0
subroutine 337 349 96.5
pod 14 109 12.8
total 2982 4152 71.8


line stmt bran cond sub pod time code
1             package Disassemble::X86;
2              
3 1     1   24446 use 5.006;
  1         4  
  1         42  
4 1     1   5 use strict;
  1         2  
  1         45  
5 1     1   5 use warnings;
  1         7  
  1         54  
6 1     1   1264 use AutoLoader qw( AUTOLOAD );
  1         1705  
  1         6  
7 1     1   851 use integer;
  1         9  
  1         5  
8 1     1   548 use Disassemble::X86::MemRegion;
  1         2  
  1         32  
9              
10 1     1   5 use vars qw( $VERSION );
  1         2  
  1         61  
11             $VERSION = "0.13";
12              
13 1     1   5 use vars qw( $max_instr_len );
  1         2  
  1         37  
14             $max_instr_len = 15;
15              
16 1     1   5 use vars qw( @long_regs @word_regs @byte_regs @seg_regs );
  1         8  
  1         109  
17             @long_regs = qw( eax ecx edx ebx esp ebp esi edi );
18             @word_regs = qw( ax cx dx bx sp bp si di );
19             @byte_regs = qw( al cl dl bl ah ch dh bh );
20             @seg_regs = qw( es cs ss ds fs gs );
21              
22 1         174 use vars qw( @immed_grp @shift_grp @unary_grp @bittst_grp
23 1     1   3 @float_op @floatr_op @prefetch_op @cond_code @sse_comp );
  1         2  
24             @immed_grp = qw( add or adc sbb and sub xor cmp );
25             @shift_grp = qw( rol ror rcl rcr shl shr xxx sar );
26             @unary_grp = qw( test xxx not neg mul imul div idiv );
27             @bittst_grp = qw( bt bts btr btc );
28             @float_op = qw( add mul com comp sub subr div divr );
29             @floatr_op = qw( add mul com comp subr sub divr div );
30             @prefetch_op = qw( nta t0 t1 t2 );
31             @cond_code = qw( o no b ae e ne be a s ns pe po l ge le g );
32             @sse_comp = qw( eq lt le unord neq nlt nle ord );
33              
34 1         2969 use vars qw( $mmx_proc $tdnow_proc $tdnow2_proc $sse_proc $sse2_proc
35 1     1   4 %proc_xlat );
  1         2  
36             $mmx_proc = 995;
37             $tdnow_proc = 996;
38             $tdnow2_proc = 997;
39             $sse_proc = 998;
40             $sse2_proc = 999;
41             %proc_xlat = (
42             $mmx_proc => "mmx",
43             $tdnow_proc => "3dnow",
44             $tdnow2_proc => "3dnow-e",
45             $sse_proc => "sse",
46             $sse2_proc => "sse2",
47             );
48              
49             sub new {
50 2     2 1 3248 my ($class, %args) = @_;
51 2         47 my $self = bless {
52             addr_size => 32,
53             data_size => 32,
54             asize => undef, # address size override
55             dsize => undef, # data size override
56             seg_pre => undef,
57             mmx_pre => 0,
58             def_proc => 386,
59             } => $class;
60              
61 2         5 my $text = $args{text};
62 2 50       8 unless (ref $text) {
63 2   50     27 $text = Disassemble::X86::MemRegion->new(
64             mem => $text,
65             start => $args{start} || 0,
66             );
67             }
68 2         11 $self->{text} = $text;
69              
70 2   50     17 $self->addr_size($args{addr_size} || $args{size} || 32);
71 2   50     16 $self->data_size($args{data_size} || $args{size} || 32);
72              
73 2 50       18 $self->pos( exists($args{pos}) ? $args{pos} : $text->start() );
74 2   50     17 $self->set_format($args{format} || "Text");
75 2         10 return $self;
76             } # new
77              
78             sub addr_size {
79 2     2 1 6 my ($self, $size) = @_;
80 2 50       6 if ($size) {
81 2 100 66     22 if ($size eq "16" || $size eq "word") { $self->{addr_size} = 16 }
  1 50 33     2  
      33        
82             elsif ($size eq "32" || $size eq "dword" || $size eq "long") {
83 1         4 $self->{addr_size} = 32;
84             }
85 0         0 else { return }
86 2         11 $self->set_def_proc();
87             }
88 2         3 return $self->{addr_size};
89             } # addr_size
90              
91             sub data_size {
92 2     2 1 4 my ($self, $size) = @_;
93 2 50       7 if ($size) {
94 2 100 66     19 if ($size eq "16" || $size eq "word") { $self->{data_size} = 16 }
  1 50 33     3  
      33        
95             elsif ($size eq "32" || $size eq "dword" || $size eq "long") {
96 1         3 $self->{data_size} = 32;
97             }
98 0         0 else { return }
99 2         6 $self->set_def_proc();
100             }
101 2         4 return $self->{data_size};
102             } # data_size
103              
104             sub set_format {
105 2     2 0 4 my ($self, $fmt) = @_;
106 2 50       7 return $self->{format} = $fmt if ref($fmt);
107 2 50       16 die "Invalid characters in format name: $fmt" if $fmt =~ /[^\w:]/;
108 2         14 foreach ("Disassemble::X86::Format$fmt", $fmt) {
109 2         230 eval "require $_";
110 2 50       9 next if $@;
111 2         8 return $self->{format} = $_;
112             }
113 0         0 die "Invalid format module: $fmt";
114             } # set_format
115              
116             sub set_def_proc {
117 4     4 0 6 my ($self) = @_;
118 4 100 100     33 $self->{def_proc} = ($self->{addr_size} == 16
119             && $self->{data_size} == 16) ? 86 : 386;
120             } # set_def_proc
121              
122 0     0 1 0 sub text { $_[0]->{text} }
123 0     0 1 0 sub at_end { $_[0]->{pos} >= $_[0]->{text}->end() }
124 0     0 1 0 sub contains { $_[0]->{text}->contains($_[1]) }
125 0     0 1 0 sub error { $_[0]->{error} }
126 0     0 1 0 sub op { $_[0]->{op} }
127 0     0 1 0 sub op_start { $_[0]->{op_start} }
128              
129             sub op_len {
130 0 0   0 1 0 my $op = $_[0]->{op} or return 0;
131 0         0 return $op->{len};
132             } # op_len
133              
134             sub op_proc {
135 0 0   0 1 0 my $op = $_[0]->{op} or return 0;
136 0         0 return $op->{proc};
137             } # op_len
138              
139             sub pos {
140 2     2 1 3 my ($self, $pos) = @_;
141 2 50       6 if (defined $pos) {
142 2         5 $self->{pos} = $pos;
143 2         5 $self->{lim} = $pos + $max_instr_len;
144             }
145 2         4 return $self->{pos};
146             } # pos
147              
148             sub disasm {
149 456     456 1 414210 my ($self) = @_;
150 456         1276 my $start = $self->{op_start} = $self->{pos};
151 456         1133 $self->{lim} = $start + $max_instr_len;
152 456         824 $self->{error} = "";
153              
154 456         24780 my $op = $self->_disasm();
155              
156 456 50       1566 $self->{pos} > $self->{lim} and $self->{error} = "opcode too long";
157 456 100       1037 $self->{error} and undef $op;
158 456         836 $self->{op} = $op;
159 456 100       3187 if ($op) {
160 454   100     1495 my $proc = $op->{proc} || 0;
161 454         786 my $def_proc = $self->{def_proc};
162 454 100       943 $proc = $def_proc if $proc < $def_proc;
163 454   66     7507 $op->{proc} = $proc_xlat{$proc} || $proc;
164 454         1424 $op->{start} = $start;
165 454         1059 $op->{len} = $self->{pos} - $start;
166 454         2720 return $self->{format}->format_instr($op);
167             }
168             else {
169 2         5 $self->{pos} = $start; # back off from the bad opcode
170 2         11 return undef;
171             }
172             } # disasm
173              
174             sub bad_op {
175 0     0 0 0 my ($self) = @_;
176 0         0 $self->{error} = "bad opcode";
177 0         0 return undef;
178             } # bad_op
179              
180             sub next_byte {
181 979     979 1 1142 my ($self) = @_;
182 979         1665 my $pos = $self->{pos};
183 979         2022 $self->{pos} = $pos + 1;
184 979 50       3329 return 0 if $pos >= $self->{lim};
185 979         4569 my $byte = $self->{text}->get_byte($pos);
186 979 100       13821 if (!defined $byte) { $self->{error} = "end of data"; return 0; }
  2         6  
  2         48  
187 977         29438 return $byte;
188             } # next_byte
189              
190             sub split_next_byte {
191 354     354 0 865 my ($self) = @_;
192 354         1070 my $pos = $self->{pos};
193 354         627 $self->{pos} = $pos + 1;
194 354 50       1237 return (0,0,0) if $pos >= $self->{lim};
195 354         2569 my $byte = $self->{text}->get_byte($pos);
196 354 100       1380 if (!defined $byte) { $self->{error} = "end of data"; return (0,0,0); }
  2         5  
  2         49  
197 352         10375 return ( ($byte >> 6) & 3, ($byte >> 3) & 7, $byte & 7 );
198             } # split_next_byte
199              
200             sub next_word {
201 64     64 0 105 my ($self) = @_;
202 64         130 my $pos = $self->{pos};
203 64         169 my $newpos = $self->{pos} = $pos + 2;
204 64 50       200 return 0 if $newpos > $self->{lim};
205 64         266 my $word = $self->{text}->get_word($pos);
206 64 50       213 if (!defined $word) { $self->{error} = "end of data"; return 0; }
  0         0  
  0         0  
207 64         278 return $word;
208             } # next_word
209              
210             sub next_long {
211 56     56 0 99 my ($self) = @_;
212 56         121 my $pos = $self->{pos};
213 56         107 my $newpos = $self->{pos} = $pos + 4;
214 56 50       168 return 0 if $newpos > $self->{lim};
215 56         373 my $long = $self->{text}->get_long($pos);
216 56 50       194 if (!defined $long) { $self->{error} = "end of data"; return 0; }
  0         0  
  0         0  
217 56         167 return $long;
218             } # next_long
219              
220             sub get_byteval {
221 68     68 0 249 my ($self, $size) = @_;
222 68   33     698 $size ||= $self->dsize();
223 68         186 my $b = $self->next_byte();
224 68 100       189 if ($b & 0x80) {
225 32 100       114 if ($size == 32) { $b |= 0xffffff00 }
  18 50       81  
226 14         34 elsif ($size == 16) { $b |= 0xff00 }
227             }
228 68         2196 return { op=>"lit", arg=>[$b], size=>$size };
229             } # get_byteval
230              
231             sub get_val {
232 152     152 0 433 my ($self, $size) = @_;
233 152   33     326 $size ||= $self->dsize();
234 152         207 my $val;
235 152 100       522 if ($size == 32) { $val = $self->next_long() }
  54 100       368  
    50          
236 58         204 elsif ($size == 16) { $val = $self->next_word() }
237 40         92 elsif ($size == 8) { $val = $self->next_byte() }
238 0         0 else { die "can't happen" }
239 152         4526 return { op=>"lit", arg=>[$val], size=>$size };
240             } # get_val
241              
242             sub iflong_op {
243 12     12 0 29 my ($self, $if, $else, $proc) = @_;
244 12 100       42 return { op=>($self->dsize() == 32 ? $if : $else), proc=>$proc }
245             } # iflong_op
246              
247             sub op_r_rm {
248 20     20 0 44 my ($self, $op, $size, $proc) = @_;
249 20   33     178 $size ||= $self->dsize();
250 20         59 my ($mod, $reg, $rm) = $self->split_next_byte();
251 20         752 my $src = $self->modrm($mod, $rm, $size);
252 20         632 my $dest = $self->get_reg($reg, $size);
253 20         511 return { op=>$op, arg=>[$dest, $src], proc=>$proc };
254             } # op_r_rm
255              
256             sub op_rm_r {
257 12     12 0 32 my ($self, $op, $size, $proc) = @_;
258 12   66     52 $size ||= $self->dsize();
259 12         60 my ($mod, $reg, $rm) = $self->split_next_byte();
260 12         570 my $src = $self->get_reg($reg, $size);
261 12         321 my $dest = $self->modrm($mod, $rm, $size);
262 12         252 return { op=>$op, arg=>[$dest,$src], proc=>$proc };
263             } # op_rm_r
264              
265             sub mov_imm {
266 4     4 0 11 my ($self, $size) = @_;
267 4   66     17 $size ||= $self->dsize();
268 4         18 my ($mod, $op, $rm) = $self->split_next_byte();
269 4 50       13 return $self->bad_op() unless $op == 0;
270 4         235 my $dest = $self->modrm($mod, $rm, $size);
271 4         16 return { op=>"mov", arg=>[$dest, $self->get_val($size)] };
272             } # mov_imm
273              
274             sub unary_op {
275 10     10 0 21 my ($self, $size) = @_;
276 10   66     38 $size ||= $self->dsize();
277 10         27 my ($mod, $op, $rm) = $self->split_next_byte();
278 10         301 my $arg = $self->modrm($mod, $rm, $size);
279 10 50       41 if ($op == 0) {
    50          
280 0         0 return { op=>"test", arg=>[$arg, $self->get_val($size)] };
281             }
282 0         0 elsif ($op == 1) { return $self->bad_op() }
283 10         234 else { return { op=>$unary_grp[$op], arg=>[$arg] } }
284             } # unary_op
285              
286             sub abs_addr {
287 8     8 0 10 my ($self, $data_size) = @_;
288 8   33     19 $data_size ||= $self->dsize();
289 8         30 my $addr_size = $self->asize();
290 8         26 my $addr = $self->get_val($addr_size);
291 8         36 my $seg = $self->seg_prefix();
292 8 100       32 $addr = { op=>"seg", arg=>[$seg,$addr], size=>$addr_size } if $seg;
293 8         340 return { op=>"mem", arg=>[$addr], size=>$data_size };
294             } # abs_addr
295              
296             sub eipbyte {
297 0     0 0 0 my ($self) = @_;
298 0         0 my $off = $self->next_byte();
299 0 0       0 $off |= 0xffffff00 if $off & 0x80;
300 0         0 $off += $self->{pos};
301 0         0 my $size = $self->dsize();
302 0 0       0 if ($size == 32) { $off &= 0xffffffff }
  0 0       0  
303 0         0 elsif ($size == 16) { $off &= 0xffff }
304 0         0 else { die "can't happen" }
305 0         0 return { op=>"lit", arg=>[$off], size=>$size };
306             } # eipbyte
307              
308             sub eipoff {
309 0     0 0 0 my ($self, $op, $proc) = @_;
310 0         0 my $size = $self->dsize();
311 0         0 my $off;
312 0 0       0 if ($size == 32) {
    0          
313 0         0 $off = $self->next_long();
314 0         0 $off = ($off + $self->{pos}) & 0xffffffff;
315             }
316             elsif ($size == 16) {
317 0         0 $off = $self->next_word();
318 0         0 $off = ($off + $self->{pos}) & 0xffff;
319             }
320 0         0 else { die "can't happen" }
321 0         0 return { op=>"lit", arg=>[$off], size=>$size };
322             } # eipoff
323              
324             sub jcond_op {
325 0     0 0 0 my ($self, $cond, $addr, $proc) = @_;
326 0         0 my $arg = [$addr];
327 0         0 my $op = { op=>"j".$cond_code[$cond & 0xf], arg=>[$addr] };
328 0         0 my $seg = $self->{seg_pre};
329 0 0       0 if ($seg) {
330             # Branch hints. Someone please suggest some better mnemonics.
331 0 0       0 if ($seg == 1) {
    0          
332 0         0 $self->{seg_pre} = undef;
333 0         0 return { op=>"hint_no", prefix=>1, arg=>[$op], proc=>$sse2_proc };
334             }
335             elsif ($seg == 3) {
336 0         0 $self->{seg_pre} = undef;
337 0         0 return { op=>"hint_yes", prefix=>1, arg=>[$op], proc=>$sse2_proc };
338             }
339             }
340 0         0 $op->{proc} = $proc;
341 0         0 return $op;
342             } # jcond_op
343              
344             sub seg_prefix {
345 212     212 0 334 my ($self) = @_;
346 212         555 my $prefix = $self->{seg_pre};
347 212 100       6230 return undef unless defined $prefix;
348 12         26 $self->{seg_pre} = undef;
349 12         357 return $self->seg_reg($prefix);
350             } # seg_prefix
351              
352             sub dsize {
353 168     168 0 340 my ($self) = @_;
354 168   66     794 my $dsize = $self->{dsize} || $self->{data_size};
355 168         321 $self->{dsize} = undef;
356 168         3833 return $dsize;
357             } # dsize
358              
359             sub asize {
360 218     218 0 365 my ($self) = @_;
361 218   66     904 my $asize = $self->{asize} || $self->{addr_size};
362 218         347 $self->{asize} = undef;
363 218         8459 return $asize;
364             } # asize
365              
366             1 # end X86.pm
367             __END__