iZsh-ragweed 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +29 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +30 -0
- data/examples/hittracertux.rb +49 -0
- data/examples/hittracerx.rb +63 -0
- data/examples/hook_notepad.rb +9 -0
- data/examples/snicker.rb +183 -0
- data/examples/tux-example.rb +23 -0
- data/lib/ragweed.rb +84 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +338 -0
- data/lib/ragweed/debuggerosx.rb +419 -0
- data/lib/ragweed/debuggertux.rb +345 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm.rb +53 -0
- data/lib/ragweed/rasm/isa.rb +1046 -0
- data/lib/ragweed/rasm/util.rb +26 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +88 -0
- data/lib/ragweed/wrap32.rb +53 -0
- data/lib/ragweed/wrap32/debugging.rb +163 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +23 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +506 -0
- data/lib/ragweed/wrap32/process_token.rb +59 -0
- data/lib/ragweed/wrap32/thread_context.rb +208 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +526 -0
- data/lib/ragweed/wraposx.rb +53 -0
- data/lib/ragweed/wraposx/constants.rb +112 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +250 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +225 -0
- data/lib/ragweed/wraposx/wraposx.rb +376 -0
- data/lib/ragweed/wraptux.rb +53 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_ragweed.rb +0 -0
- metadata +127 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
class Ragweed::Detour
|
2
|
+
# "Ghetto Detours", as Scott Stender might say. Patch subprograms
|
3
|
+
# in to running programs as a hooking mechanism.
|
4
|
+
class Detour
|
5
|
+
attr_reader :snarfed
|
6
|
+
attr_reader :dpoint
|
7
|
+
attr_reader :stack
|
8
|
+
|
9
|
+
# Easiest way to do this is just to ask WinProcess#detour. Wants
|
10
|
+
# "p" to be a pointer into the process, presumable returned from
|
11
|
+
# WinProcess#get_proc.
|
12
|
+
#
|
13
|
+
# In theory, "p" should be OK anywhere as long as there are 5
|
14
|
+
# bytes of instructions before the end of the basic block its
|
15
|
+
# in. In practice, this only really stands a chance of working
|
16
|
+
# if "p" points to a function prologue.
|
17
|
+
def initialize(p, opts={})
|
18
|
+
@p = p.p
|
19
|
+
@dpoint = p
|
20
|
+
@opts = opts
|
21
|
+
@a = @opts[:arena] || @p.arena
|
22
|
+
@stack = @a.alloc(2048)
|
23
|
+
@snarfed = snarf_prologue
|
24
|
+
end
|
25
|
+
|
26
|
+
# Release the detour and its associated memory, unpatch the
|
27
|
+
# target function.
|
28
|
+
def release
|
29
|
+
@dpoint.write(@snarfed)
|
30
|
+
@a.release if not @opts[:arena]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Patch the target function. There is a 70% chance this will
|
34
|
+
# totally fuck your process.
|
35
|
+
#
|
36
|
+
# You would be wise to have the threads in the process suspended
|
37
|
+
# while you do this, but I'm not going to do it for you.
|
38
|
+
def call
|
39
|
+
# a Detours-style trampoline --- the location we patch the
|
40
|
+
# target function to jump to --- consists of:
|
41
|
+
#
|
42
|
+
# - A stack switch (to push/pop w/o fucking the program)
|
43
|
+
# - A context save
|
44
|
+
# - The Detour code
|
45
|
+
# - A context restore
|
46
|
+
# - A stack restore
|
47
|
+
# - The code we patched out of the target
|
48
|
+
# - A jump back to the target function (after the prologue)
|
49
|
+
|
50
|
+
# Do this now to make room for the (probably 5 byte) jump.
|
51
|
+
# We don't know what the address will be until we allocate.
|
52
|
+
jumpback = (Jmp 0xDEADBEEF) # patch back later
|
53
|
+
|
54
|
+
# Build the trampoline
|
55
|
+
tramp = trampoline(@stack).assemble
|
56
|
+
|
57
|
+
# Figure out how big the whole mess will be, allocate it
|
58
|
+
tsz = tramp.size + @snarfed.size + jumpback.to_s.size
|
59
|
+
tbuf = @a.alloc(tsz + 10)
|
60
|
+
|
61
|
+
# assume trampoline is ahead of the patched program text;
|
62
|
+
# jump to [dpoint+patch]
|
63
|
+
jumpback.dst = (@dpoint.to_i + @snarfed.size) - (tbuf + tsz)
|
64
|
+
|
65
|
+
# Write it into memory. It's not "live" yet because we haven't
|
66
|
+
# patched the target function.
|
67
|
+
@p.write(tbuf, tramp + @snarfed + jumpback.to_s)
|
68
|
+
|
69
|
+
# But now it is. =)
|
70
|
+
@p.write(@dpoint, injection(tbuf).assemRASble)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Hook function. Override this in subclasses to provide different
|
74
|
+
# behavior.
|
75
|
+
def inner_block
|
76
|
+
i = Rasm::Subprogram.new
|
77
|
+
i.<< Int(3)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# No user-servicable parts below.
|
83
|
+
|
84
|
+
# Pull at least 5 bytes of instructions out of the prologue, using
|
85
|
+
# the disassembler, to make room for our patch jump. Save it, so
|
86
|
+
# we can unpatch later.
|
87
|
+
def snarf_prologue
|
88
|
+
i = 0
|
89
|
+
(buf = @dpoint.read(20)).distorm.each do |insn|
|
90
|
+
i += insn.size
|
91
|
+
break if i >= 5
|
92
|
+
end
|
93
|
+
buf[0...i]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create the Jmp instruction that implements the patch; you can't
|
97
|
+
# do this until you know where the trampoline was actually injected
|
98
|
+
# into the process.
|
99
|
+
def injection(tramp)
|
100
|
+
here = @dpoint
|
101
|
+
there = tramp
|
102
|
+
|
103
|
+
if there < here
|
104
|
+
goto = -((here - there) + 5)
|
105
|
+
else
|
106
|
+
goto = there - here
|
107
|
+
end
|
108
|
+
|
109
|
+
i = Rasm::Subprogram.new
|
110
|
+
i.<< Rasm::Jmp(goto.to_i)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create the detours trampoline:
|
114
|
+
def trampoline(stack)
|
115
|
+
i = Rasm::Subprogram.new
|
116
|
+
i.concat push_stack(stack) # 1. Give us a new stack
|
117
|
+
i.concat save_all # 2. Save all the GPRs just in case
|
118
|
+
i.concat inner_block # 3. The hook function
|
119
|
+
i.concat restore_all # 4. Restore all the GPRs.
|
120
|
+
i.concat pop_stack # 5. Restore the stack
|
121
|
+
return i
|
122
|
+
end
|
123
|
+
|
124
|
+
# Swap in a new stack, pushing the old stack address
|
125
|
+
# onto the top of it.
|
126
|
+
def push_stack(addr, sz=2048)
|
127
|
+
i = Rasm::Subprogram.new
|
128
|
+
i.<< Rasm::Push(eax)
|
129
|
+
i.<< Rasm::Mov(eax, addr+(sz-4))
|
130
|
+
i.<< Rasm::Mov([eax], esp)
|
131
|
+
i.<< Rasm::Pop(eax)
|
132
|
+
i.<< Rasm::Mov(esp, addr+(sz-4))
|
133
|
+
end
|
134
|
+
|
135
|
+
# Swap out the new stack.
|
136
|
+
def pop_stack
|
137
|
+
i = Rasm::Subprogram.new
|
138
|
+
i.<< Rasm::Pop(esp)
|
139
|
+
i.<< Rasm::Add(esp, 4)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Just push all the registers in order
|
143
|
+
def save_all
|
144
|
+
i = Rasm::Subprogram.new
|
145
|
+
[eax,ecx,edx,ebx,ebp,esi,edi].each do |r|
|
146
|
+
i.<< Rasm::Push(r)
|
147
|
+
end
|
148
|
+
i
|
149
|
+
end
|
150
|
+
|
151
|
+
# Just pop all the registers
|
152
|
+
def restore_all
|
153
|
+
i = Rasm::Subprogram.new
|
154
|
+
[edi,esi,ebp,ebx,edx,ecx,eax].each do |r|
|
155
|
+
i.<< Rasm::Pop(r)
|
156
|
+
end
|
157
|
+
i
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# A breakpoint implemented as a Detour. XXX not tested.
|
162
|
+
class Dbreak < Detour
|
163
|
+
attr_reader :ev1, :ev2
|
164
|
+
|
165
|
+
# accepts:
|
166
|
+
# :ev1: reuse events from somewhere else
|
167
|
+
# :ev2:
|
168
|
+
def initialize(*args)
|
169
|
+
super
|
170
|
+
@ev1 = @opts[:ev1] || WinEvent.new
|
171
|
+
@ev2 = @opts[:ev2] || WinEvent.new
|
172
|
+
|
173
|
+
# create the state block that the eventpair shim wants:
|
174
|
+
mem = @a.alloc(100)
|
175
|
+
@data = mem
|
176
|
+
|
177
|
+
# ghetto vtbl
|
178
|
+
swch = ["OpenProcess",
|
179
|
+
"DuplicateHandle",
|
180
|
+
"ResetEvent",
|
181
|
+
"SetEvent",
|
182
|
+
"WaitForSingleObject",
|
183
|
+
"GetCurrentThreadId"].
|
184
|
+
map {|x| @p.get_proc("kernel32!#{x}").to_i}.
|
185
|
+
pack("LLLLLL")
|
186
|
+
|
187
|
+
# ghetto instance vars
|
188
|
+
state = [@p.w.get_current_process_id, @ev1.handle, @ev2.handle].
|
189
|
+
pack("LLL")
|
190
|
+
@data.write(swch + state)
|
191
|
+
end
|
192
|
+
|
193
|
+
def inner_block
|
194
|
+
i = Rasm::Subprogram.new
|
195
|
+
i.<< Push(eax)
|
196
|
+
i.<< Xor(eax, eax)
|
197
|
+
i.<< Or(eax, @data)
|
198
|
+
i.<< Push(eax)
|
199
|
+
i.<< Call(1) # cheesy in the extreme: fake a call
|
200
|
+
# so I don't have to change my event shim
|
201
|
+
i.<< Nop.new
|
202
|
+
i.<< Nop.new
|
203
|
+
i.<< Nop.new
|
204
|
+
i.<< Nop.new
|
205
|
+
i.<< Nop.new
|
206
|
+
s = event_pair_stub
|
207
|
+
s[-1] = Add(esp, 4)
|
208
|
+
i.concat(s)
|
209
|
+
i.<< Pop(eax)
|
210
|
+
return i
|
211
|
+
end
|
212
|
+
|
213
|
+
# in theory, loop on this breakpoint
|
214
|
+
def on(&block)
|
215
|
+
puts "#{ @p.pid }: #{ @ev1.handle }" # in case we need to release
|
216
|
+
loop do
|
217
|
+
@ev1.wait
|
218
|
+
yield
|
219
|
+
@ev2.signal
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/ragweed/ptr.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#XXX - TODO: make read/write work for other oses
|
2
|
+
|
3
|
+
class Ragweed::Ptr
|
4
|
+
# A dubious achievement. Wrap Integers in a pointer class, which,
|
5
|
+
# when you call to_s, returns the marshalled type, and which exports
|
6
|
+
# read/write methods.
|
7
|
+
attr_accessor :p
|
8
|
+
attr_reader :val
|
9
|
+
|
10
|
+
# ptr-to-zero?
|
11
|
+
def null?
|
12
|
+
@val == 0
|
13
|
+
end
|
14
|
+
|
15
|
+
# initialize with a number or another pointer (implements copy-ctor)
|
16
|
+
def initialize(i)
|
17
|
+
if i.kind_of? self.class
|
18
|
+
@val = i.val
|
19
|
+
@p = i.p
|
20
|
+
elsif not i
|
21
|
+
@val = 0
|
22
|
+
else
|
23
|
+
@val = i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# return the raw pointer bits
|
28
|
+
def to_s; @val.to_l32; end
|
29
|
+
|
30
|
+
# return the underlying number
|
31
|
+
def to_i; @val; end
|
32
|
+
|
33
|
+
# only works if you attach a process
|
34
|
+
def write(arg); p.write(self, arg); end
|
35
|
+
def read(sz); p.read(self, sz); end
|
36
|
+
|
37
|
+
# everything else: work like an integer --- also, where these
|
38
|
+
# calls return numbers, turn them back into pointers, so pointer
|
39
|
+
# math doesn't shed the class wrapper
|
40
|
+
def method_missing(meth, *args)
|
41
|
+
ret = @val.send meth, *args
|
42
|
+
if ret.kind_of? Numeric
|
43
|
+
ret = Ptr.new(ret)
|
44
|
+
ret.p = self.p
|
45
|
+
end
|
46
|
+
ret
|
47
|
+
end
|
48
|
+
end
|
data/lib/ragweed/rasm.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Dir[File.expand_path("#{File.dirname(__FILE__)}/rasm/*.rb")].each do |file|
|
2
|
+
# require file
|
3
|
+
# end
|
4
|
+
module Ragweed; end
|
5
|
+
module Ragweed::Rasm
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
VERSION = '0.1.6'
|
9
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
10
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
11
|
+
# :startdoc:
|
12
|
+
|
13
|
+
# Returns the version string for the library.
|
14
|
+
#
|
15
|
+
def self.version
|
16
|
+
VERSION
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the library path for the module. If any arguments are given,
|
20
|
+
# they will be joined to the end of the libray path using
|
21
|
+
# <tt>File.join</tt>.
|
22
|
+
#
|
23
|
+
def self.libpath( *args )
|
24
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the lpath for the module. If any arguments are given,
|
28
|
+
# they will be joined to the end of the path using
|
29
|
+
# <tt>File.join</tt>.
|
30
|
+
#
|
31
|
+
def self.path( *args )
|
32
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Utility method used to require all files ending in .rb that lie in the
|
36
|
+
# directory below this file that has the same name as the filename passed
|
37
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
38
|
+
# the _filename_ does not have to be equivalent to the directory.
|
39
|
+
#
|
40
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
41
|
+
dir ||= ::File.basename(fname, '.*')
|
42
|
+
search_me = ::File.expand_path(
|
43
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
44
|
+
|
45
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
46
|
+
# require File.dirname(File.basename(__FILE__)) + "/#{x}"
|
47
|
+
|
48
|
+
end
|
49
|
+
end # module Ragweed::Rasm
|
50
|
+
|
51
|
+
Ragweed::Rasm.require_all_libs_relative_to(__FILE__)
|
52
|
+
|
53
|
+
# EOF
|
@@ -0,0 +1,1046 @@
|
|
1
|
+
## ------------------------------------------------------------------------
|
2
|
+
|
3
|
+
# Rasm: a half-assed X86 assembler.
|
4
|
+
#
|
5
|
+
# Rasm implements a small subset of the X86 instruction set, and only in
|
6
|
+
# simple encodings.
|
7
|
+
#
|
8
|
+
# However, Rasm implements enough X86 to do interesting things. I wrote it
|
9
|
+
# to inject trampoline functions and detours into remote processes. This
|
10
|
+
# is Ruby code; you'd never use it where performance matters. It's not
|
11
|
+
# enough to write a decent compiler, but it's enough to fuck up programs.
|
12
|
+
module Ragweed::Rasm
|
13
|
+
class NotImp < RuntimeError; end
|
14
|
+
class Insuff < RuntimeError; end
|
15
|
+
class BadArg < RuntimeError; end
|
16
|
+
class TooMan < RuntimeError; end
|
17
|
+
|
18
|
+
## ------------------------------------------------------------------------
|
19
|
+
|
20
|
+
def method_missing(meth, *args)
|
21
|
+
Rasm.const_get(meth).new *args
|
22
|
+
end
|
23
|
+
|
24
|
+
## ------------------------------------------------------------------------
|
25
|
+
|
26
|
+
# A register target encoding, including [EAX+10] disp/indir
|
27
|
+
class Register
|
28
|
+
attr_accessor :code
|
29
|
+
attr_accessor :disp
|
30
|
+
attr_accessor :indir
|
31
|
+
attr_accessor :byte
|
32
|
+
attr_accessor :index
|
33
|
+
attr_accessor :combined
|
34
|
+
attr_accessor :scale
|
35
|
+
|
36
|
+
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI = [0,1,2,3,4,5,6,7]
|
37
|
+
|
38
|
+
def self.comb(rc1, rc2)
|
39
|
+
(rc1 << 8|rc2)
|
40
|
+
end
|
41
|
+
|
42
|
+
def reg1
|
43
|
+
if combined?
|
44
|
+
self.class.new((code>>8)&0xff)
|
45
|
+
else
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def reg2
|
51
|
+
self.class.new(code&0xFF)
|
52
|
+
end
|
53
|
+
|
54
|
+
def *(x)
|
55
|
+
if x.kind_of? Numeric
|
56
|
+
ret = clone
|
57
|
+
ret.scale = x
|
58
|
+
return ret
|
59
|
+
end
|
60
|
+
raise BadArg, "bad operand type for *"
|
61
|
+
end
|
62
|
+
|
63
|
+
def +(x)
|
64
|
+
if x.kind_of? Numeric
|
65
|
+
ret = clone
|
66
|
+
ret.disp = x
|
67
|
+
return ret
|
68
|
+
elsif x.kind_of? Register
|
69
|
+
ret = clone
|
70
|
+
ret.combined = true
|
71
|
+
ret.indir = 1
|
72
|
+
ret.scale = x.scale
|
73
|
+
ret.code = self.class.comb(ret.code, x.code)
|
74
|
+
return ret
|
75
|
+
end
|
76
|
+
|
77
|
+
raise BadArg, "bad operand type for +"
|
78
|
+
end
|
79
|
+
|
80
|
+
def -(x)
|
81
|
+
ret = clone
|
82
|
+
ret.disp = -x
|
83
|
+
return ret
|
84
|
+
end
|
85
|
+
|
86
|
+
def regopts(opts={})
|
87
|
+
if (v = opts[:disp])
|
88
|
+
@disp = v
|
89
|
+
end
|
90
|
+
|
91
|
+
if (v = opts[:indir])
|
92
|
+
@indir = v
|
93
|
+
end
|
94
|
+
|
95
|
+
if (v = opts[:byte])
|
96
|
+
@byte = v
|
97
|
+
end
|
98
|
+
|
99
|
+
if (v = opts[:index])
|
100
|
+
@index = v
|
101
|
+
end
|
102
|
+
|
103
|
+
return self
|
104
|
+
end
|
105
|
+
|
106
|
+
def scaled?; @scale and @scale > 0; end
|
107
|
+
def combined?; @combined; end
|
108
|
+
|
109
|
+
def self.eax(opts={}); Eax.clone.regopts opts; end
|
110
|
+
def self.ecx(opts={}); Ecx.clone.regopts opts; end
|
111
|
+
def self.edx(opts={}); Edx.clone.regopts opts; end
|
112
|
+
def self.ebx(opts={}); Ebx.clone.regopts opts; end
|
113
|
+
def self.esp(opts={}); Esp.clone.regopts opts; end
|
114
|
+
def self.ebp(opts={}); Ebp.clone.regopts opts; end
|
115
|
+
def self.esi(opts={}); Esi.clone.regopts opts; end
|
116
|
+
def self.edi(opts={}); Edi.clone.regopts opts; end
|
117
|
+
|
118
|
+
def initialize(code, opts={})
|
119
|
+
@combined = false
|
120
|
+
@code = code
|
121
|
+
@disp = opts[:disp]
|
122
|
+
@indir = opts[:indir]
|
123
|
+
@byte = opts[:byte]
|
124
|
+
@index = nil
|
125
|
+
@scale = nil
|
126
|
+
|
127
|
+
@indir = true if @disp
|
128
|
+
disp = 0 if (@indir and not @disp)
|
129
|
+
end
|
130
|
+
|
131
|
+
def displace(disp)
|
132
|
+
@disp = disp
|
133
|
+
@indir = true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
## ------------------------------------------------------------------------
|
138
|
+
|
139
|
+
# Clone these to get access to the GPRs.
|
140
|
+
# There's a very clean way to do this in Ruby, with like, module_eval
|
141
|
+
# or something, but... time's a wasting.
|
142
|
+
Eax = Register.new(Register::EAX)
|
143
|
+
Ecx = Register.new(Register::ECX)
|
144
|
+
Edx = Register.new(Register::EDX)
|
145
|
+
Ebx = Register.new(Register::EBX)
|
146
|
+
Esp = Register.new(Register::ESP)
|
147
|
+
Ebp = Register.new(Register::EBP)
|
148
|
+
Esi = Register.new(Register::ESI)
|
149
|
+
Edi = Register.new(Register::EDI)
|
150
|
+
def eax(opts={}); Register.eax opts; end
|
151
|
+
def ecx(opts={}); Register.ecx opts; end
|
152
|
+
def edx(opts={}); Register.edx opts; end
|
153
|
+
def ebx(opts={}); Register.ebx opts; end
|
154
|
+
def esp(opts={}); Register.esp opts; end
|
155
|
+
def ebp(opts={}); Register.ebp opts; end
|
156
|
+
def esi(opts={}); Register.esi opts; end
|
157
|
+
def edi(opts={}); Register.edi opts; end
|
158
|
+
module_function :eax
|
159
|
+
module_function :ecx
|
160
|
+
module_function :edx
|
161
|
+
module_function :ebx
|
162
|
+
module_function :esp
|
163
|
+
module_function :ebp
|
164
|
+
module_function :esi
|
165
|
+
module_function :edi
|
166
|
+
|
167
|
+
## ------------------------------------------------------------------------
|
168
|
+
|
169
|
+
# A code fragment. Push instructions into it. You can push Label
|
170
|
+
# objects to create jump targets.
|
171
|
+
class Subprogram < Array
|
172
|
+
|
173
|
+
# Patch code offsets into the instructions to replace abstract
|
174
|
+
# labels. Produces raw instruction stream.
|
175
|
+
def assemble
|
176
|
+
patches = {}
|
177
|
+
buf = Ragweed::Sbuf.new
|
178
|
+
|
179
|
+
each do |i|
|
180
|
+
if i.kind_of? Instruction
|
181
|
+
i.locate(buf.size)
|
182
|
+
buf.straw i.to_s
|
183
|
+
else
|
184
|
+
patches[i] = buf.size
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
select {|i| i.kind_of? Instruction}.each {|i| i.patch(patches)}
|
189
|
+
buf.clear!
|
190
|
+
select {|i| i.kind_of? Instruction}.each {|i|
|
191
|
+
buf.straw(i.to_s)
|
192
|
+
}
|
193
|
+
|
194
|
+
buf.content
|
195
|
+
end
|
196
|
+
|
197
|
+
# Produce an array of insns. This is pretty much broken, because
|
198
|
+
# it doesn't pre-patch the instructions.
|
199
|
+
def disassemble
|
200
|
+
select {|i| i.kind_of? Rasm::Intruction}.map {|i| i.decode}
|
201
|
+
end
|
202
|
+
|
203
|
+
def dump_disassembly
|
204
|
+
disassemble.each do |insn|
|
205
|
+
puts "#{ insn.off.to_x } #{ insn.mnem }"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
## ------------------------------------------------------------------------
|
211
|
+
|
212
|
+
# An immediate value. Basically just a Fixnum with a type wrapper.
|
213
|
+
class Immed
|
214
|
+
attr_reader :val
|
215
|
+
def initialize(i); @val = i; end
|
216
|
+
def method_missing(meth, *args); @val.send meth, *args; end
|
217
|
+
end
|
218
|
+
|
219
|
+
## ------------------------------------------------------------------------
|
220
|
+
|
221
|
+
# A label. Like an Immed with a default value and a different wrapper.
|
222
|
+
class Label < Immed
|
223
|
+
def initialize(i=rand(0x1FFFFFFF)); super i; end
|
224
|
+
end
|
225
|
+
|
226
|
+
## ------------------------------------------------------------------------
|
227
|
+
|
228
|
+
# An X86 instruction. For the most part, you do two things with an
|
229
|
+
# instruction in Rasm: create it, and call to_s to get its raw value.
|
230
|
+
class Instruction
|
231
|
+
attr_accessor :src
|
232
|
+
attr_accessor :dst
|
233
|
+
|
234
|
+
def self.i(*args); self.new *args; end
|
235
|
+
|
236
|
+
# Are the source/dest operands registers?
|
237
|
+
def src_reg?; @src and @src.kind_of? Register; end
|
238
|
+
def dst_reg?; @dst and @dst.kind_of? Register; end
|
239
|
+
|
240
|
+
# Are the source/dest operands immediates?
|
241
|
+
def src_imm?; @src and @src.kind_of? Immed; end
|
242
|
+
def dst_imm?; @dst and @dst.kind_of? Immed; end
|
243
|
+
|
244
|
+
# Are the source/dest operands labels?
|
245
|
+
def src_lab?; @src and @src.kind_of? Label; end
|
246
|
+
def dst_lab?; @dst and @dst.kind_of? Label; end
|
247
|
+
|
248
|
+
def coerce(v)
|
249
|
+
if v
|
250
|
+
v = v.derive
|
251
|
+
v = v.to_i if v.kind_of? Ptr
|
252
|
+
if v.kind_of? Array
|
253
|
+
v = v[0].clone
|
254
|
+
v.indir = true
|
255
|
+
end
|
256
|
+
v = Immed.new(v) if v.number?
|
257
|
+
end
|
258
|
+
v
|
259
|
+
end
|
260
|
+
|
261
|
+
def src=(v); @src = coerce(v); end
|
262
|
+
def dst=(v); @dst = coerce(v); end
|
263
|
+
|
264
|
+
# Never called directly (see subclasses below)
|
265
|
+
def initialize(x=nil, y=nil)
|
266
|
+
@buf = Ragweed::Sbuf.new
|
267
|
+
self.src = y
|
268
|
+
self.dst = x
|
269
|
+
@loc = nil
|
270
|
+
end
|
271
|
+
|
272
|
+
# Disassemble the instruction (mostly for testing)
|
273
|
+
def decode; Frasm::DistormDecoder.new.decode(self.to_s)[0]; end
|
274
|
+
|
275
|
+
# What Subprogram#assemble uses to patch instruction locations.
|
276
|
+
# Not user-servicable
|
277
|
+
def locate(loc); @loc = loc; end
|
278
|
+
# Not user-servicable
|
279
|
+
def patch(patches)
|
280
|
+
if @dst.kind_of? Label
|
281
|
+
raise(BadArg, "need to have location first") if not @loc
|
282
|
+
offset = patches[@dst.val] - @Loc
|
283
|
+
@dst = Immed.new offset
|
284
|
+
if offset < 0
|
285
|
+
offset -= self.to_s
|
286
|
+
@dst = Immed.new offset
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Calculate ModR/M bits for the instruction; this is
|
292
|
+
# the source/destination operand encoding.
|
293
|
+
def modrm(op1, op2)
|
294
|
+
raise(BadArg, "two indirs") if op1.indir and op2.indir
|
295
|
+
|
296
|
+
if op1.indir or op2.indir
|
297
|
+
base = 0x80
|
298
|
+
(op1.indir) ? (o, alt = [op1,op2]) : (o,alt = [op2,op1])
|
299
|
+
if o.disp and (o.disp < 0x1000)
|
300
|
+
base = 0x40
|
301
|
+
end
|
302
|
+
if (not o.disp) or (o.disp == 0)
|
303
|
+
base = 0x0
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
if op1.indir
|
308
|
+
if op1.combined? or op1.scaled? or (op1.code == Register::EBP)
|
309
|
+
sib(op1, op2, base)
|
310
|
+
else
|
311
|
+
return base + (op2.code << 3) + op1.code
|
312
|
+
end
|
313
|
+
elsif op2.indir
|
314
|
+
if op2.combined? or op2.scaled? or (op2.code == Register::EBP)
|
315
|
+
sib(op2, op1, base)
|
316
|
+
else
|
317
|
+
return base + (op1.code << 3) + op2.code
|
318
|
+
end
|
319
|
+
else
|
320
|
+
return 0xc0 + (op2.code << 3) + op1.code
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def sib(indir, alt, base)
|
325
|
+
modpart = (base+4) + (alt.code << 3)
|
326
|
+
|
327
|
+
if indir.scaled?
|
328
|
+
case indir.scale
|
329
|
+
when 2
|
330
|
+
sbase = 0x40
|
331
|
+
when 4
|
332
|
+
sbase = 0x80
|
333
|
+
when 8
|
334
|
+
sbase = 0xc0
|
335
|
+
else
|
336
|
+
raise BadArg, "scale must be 2, 4, or 8"
|
337
|
+
end
|
338
|
+
else
|
339
|
+
sbase = 0
|
340
|
+
end
|
341
|
+
|
342
|
+
col = indir.reg1.code
|
343
|
+
|
344
|
+
if indir.combined?
|
345
|
+
row = indir.reg2.code
|
346
|
+
else
|
347
|
+
row = 4
|
348
|
+
end
|
349
|
+
|
350
|
+
pp [col,row,sbase]
|
351
|
+
|
352
|
+
sibpart = sbase + (row << 3) + (col)
|
353
|
+
|
354
|
+
return (modpart.chr) + (sibpart.chr)
|
355
|
+
end
|
356
|
+
|
357
|
+
# Add material to the instruction. Not user-servicable.
|
358
|
+
def add(v, immed=false, half=false)
|
359
|
+
return(nil) if not v
|
360
|
+
|
361
|
+
if v.number?
|
362
|
+
if (v < 0x100) and (v > -128) and not immed
|
363
|
+
if v < 0
|
364
|
+
@buf.stl8(v.sx8)
|
365
|
+
else
|
366
|
+
@buf.stl8(v)
|
367
|
+
end
|
368
|
+
else
|
369
|
+
if not half
|
370
|
+
@buf.stl32(v.sx32)
|
371
|
+
else
|
372
|
+
@buf.stl16(v.sx16)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
else
|
376
|
+
@buf.straw v
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# Never called directly (see subclasses).
|
381
|
+
def to_s; ret = @buf.content; @buf.clear!; ret; end
|
382
|
+
end
|
383
|
+
|
384
|
+
## ------------------------------------------------------------------------
|
385
|
+
|
386
|
+
# Jump to a relative offset (pos or neg), a register, or an address
|
387
|
+
# in memory. Can take Labels instead of values, let patch figure out
|
388
|
+
# the rest.
|
389
|
+
class Jmp < Instruction
|
390
|
+
# eb rel8
|
391
|
+
# e9 rel
|
392
|
+
# ff r/m
|
393
|
+
# no far yet
|
394
|
+
|
395
|
+
def initialize(x=nil); super x; end
|
396
|
+
|
397
|
+
def to_s
|
398
|
+
raise Insuff if not @dst
|
399
|
+
if dst_imm? or dst_lab?
|
400
|
+
if @dst.val < 0x100 and @dst.val > -127
|
401
|
+
add(0xeb)
|
402
|
+
add(@dst.val)
|
403
|
+
else
|
404
|
+
add(0xe9)
|
405
|
+
add(@dst.val)
|
406
|
+
end
|
407
|
+
else
|
408
|
+
add(0xff)
|
409
|
+
add(modrm(@dst, Esp.clone))
|
410
|
+
add(sx32(@dst.disp)) if @dst.disp
|
411
|
+
end
|
412
|
+
super
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
## ------------------------------------------------------------------------
|
417
|
+
|
418
|
+
# Pop into a register
|
419
|
+
class Pop < Instruction
|
420
|
+
# 8f m32
|
421
|
+
# 58+ # reg
|
422
|
+
|
423
|
+
def initialize(dst=nil); super dst; end
|
424
|
+
|
425
|
+
def to_s
|
426
|
+
raise Insuff if not @dst
|
427
|
+
raise(BadArg, "need register") if not dst_reg?
|
428
|
+
|
429
|
+
if @dst.indir
|
430
|
+
add(0x8f)
|
431
|
+
add(modrm(@dst, Eax.clone))
|
432
|
+
add(@dst.disp)
|
433
|
+
else
|
434
|
+
add(0x58 + @dst.code)
|
435
|
+
end
|
436
|
+
super
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
## ------------------------------------------------------------------------
|
441
|
+
|
442
|
+
# Push a register, register-addressed memory location, or immediate
|
443
|
+
# onto the stack.
|
444
|
+
class Push < Instruction
|
445
|
+
# ff r/m
|
446
|
+
# 50+ r
|
447
|
+
# 6a imm8
|
448
|
+
# 68 imm
|
449
|
+
|
450
|
+
def initialize( dst=nil); super dst; end
|
451
|
+
|
452
|
+
def to_s
|
453
|
+
raise Insuff if not @dst
|
454
|
+
|
455
|
+
if dst_reg?
|
456
|
+
if @dst.indir
|
457
|
+
add(0xff)
|
458
|
+
add(modrm(@dst, Esi.clone))
|
459
|
+
add(@dst.disp)
|
460
|
+
else
|
461
|
+
add(0x50 + @dst.code)
|
462
|
+
end
|
463
|
+
elsif dst_imm?
|
464
|
+
if @dst.val < 0x100
|
465
|
+
add(0x6a)
|
466
|
+
add(@dst.val)
|
467
|
+
else
|
468
|
+
add(0x68)
|
469
|
+
add(@dst.val)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
super
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
## ------------------------------------------------------------------------
|
477
|
+
|
478
|
+
# Call a register/memory location
|
479
|
+
class Call < Instruction
|
480
|
+
# e8 rel
|
481
|
+
# ff/2 r/m
|
482
|
+
# no far yet
|
483
|
+
|
484
|
+
def initialize( dst=nil); super dst; end
|
485
|
+
|
486
|
+
def to_s
|
487
|
+
raise Insuff if not @dst
|
488
|
+
|
489
|
+
if dst_reg?
|
490
|
+
add(0xff)
|
491
|
+
add(modrm(@dst, Edx.clone))
|
492
|
+
else
|
493
|
+
add(0xe8)
|
494
|
+
add(@dst.val, true)
|
495
|
+
end
|
496
|
+
super
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
## ------------------------------------------------------------------------
|
501
|
+
|
502
|
+
# Return; provide immediate for stack adjustment if you want.
|
503
|
+
class Ret < Instruction
|
504
|
+
# c3
|
505
|
+
# c2 imm16
|
506
|
+
|
507
|
+
def initialize( dst=nil); super dst; end
|
508
|
+
|
509
|
+
def to_s
|
510
|
+
if not @dst
|
511
|
+
add(0xc3)
|
512
|
+
elsif dst_imm?
|
513
|
+
add(0xc2)
|
514
|
+
add(@dst.val, true, true)
|
515
|
+
else
|
516
|
+
raise BadArg, "imm16 only"
|
517
|
+
end
|
518
|
+
super
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
class Retn < Ret; end
|
523
|
+
|
524
|
+
## ------------------------------------------------------------------------
|
525
|
+
|
526
|
+
# Wrapper class for arithmatic instructions. Never called directly;
|
527
|
+
# see below.
|
528
|
+
class Arith < Instruction
|
529
|
+
# 05 imm32 to eax
|
530
|
+
# 04 imm8 to al
|
531
|
+
# 80/0 r/m8, imm8
|
532
|
+
# 81/0 r/m, imm
|
533
|
+
# no sign extend yet
|
534
|
+
# 01 r/m, r
|
535
|
+
# 03 r, r/m
|
536
|
+
|
537
|
+
def initialize( dst=nil, src=nil); super dst, src; end
|
538
|
+
|
539
|
+
def to_s
|
540
|
+
if not @dst
|
541
|
+
# fucked up
|
542
|
+
if src_imm?
|
543
|
+
if @src.val < 0x100
|
544
|
+
add(@imp8)
|
545
|
+
add(@src.val)
|
546
|
+
else
|
547
|
+
add(@imp)
|
548
|
+
add(@src.val)
|
549
|
+
end
|
550
|
+
else
|
551
|
+
raise BadArg, "need immed for implicit eax"
|
552
|
+
end
|
553
|
+
else
|
554
|
+
if src_imm?
|
555
|
+
if @src.val < 0x100
|
556
|
+
add(@imm8)
|
557
|
+
add(modrm(@dst, @x))
|
558
|
+
add(@src.val)
|
559
|
+
else
|
560
|
+
add(@imm)
|
561
|
+
add(modrm(@dst, @x))
|
562
|
+
add(@src.val)
|
563
|
+
end
|
564
|
+
else
|
565
|
+
raise(BadArg, "need two r/m") if not src_reg? or not dst_reg?
|
566
|
+
raise(BadArg, "can't both be indir") if @src.indir and @dst.indir
|
567
|
+
if @src.indir
|
568
|
+
add(@rm)
|
569
|
+
add(modrm(@dst, @src))
|
570
|
+
else
|
571
|
+
add(@mr)
|
572
|
+
add(modrm(@dst, @src))
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
super
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
## ------------------------------------------------------------------------
|
581
|
+
|
582
|
+
# ADD
|
583
|
+
class Add < Arith
|
584
|
+
def initialize(*args)
|
585
|
+
super *args
|
586
|
+
@imp8, @imp, @imm8, @imm, @rm, @mr = [0x04, 0x05, 0x83, 0x81, 0x03, 0x01]
|
587
|
+
@x = Eax.clone
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
## ------------------------------------------------------------------------
|
592
|
+
|
593
|
+
# SUB
|
594
|
+
class Sub < Arith
|
595
|
+
def initialize(*args)
|
596
|
+
super *args
|
597
|
+
@imp8, @imp, @imm8, @imm, @rm, @mr = [0x2c, 0x2d, 0x83, 0x81, 0x2b, 0x29]
|
598
|
+
@x = Ebp.clone
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
## ------------------------------------------------------------------------
|
603
|
+
|
604
|
+
# XOR
|
605
|
+
class Xor < Arith
|
606
|
+
def initialize(*args)
|
607
|
+
super *args
|
608
|
+
@imp8, @imp, @imm8, @imm, @rm, @mr = [0x34, 0x35, 0x83, 0x81, 0x33, 0x31]
|
609
|
+
@x = Esi.clone
|
610
|
+
end
|
611
|
+
end
|
612
|
+
## ------------------------------------------------------------------------
|
613
|
+
|
614
|
+
# AND
|
615
|
+
class And < Arith
|
616
|
+
def initialize(*args)
|
617
|
+
super *args
|
618
|
+
@imp8, @imp, @imm8, @imm, @rm, @mr = [0x24, 0x25, 0x83, 0x81, 0x23, 0x21]
|
619
|
+
@x = Esp.clone
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
## ------------------------------------------------------------------------
|
624
|
+
|
625
|
+
# OR
|
626
|
+
class Or < Arith
|
627
|
+
def initialize(*args)
|
628
|
+
super *args
|
629
|
+
@imp8, @imp, @imm8, @imm, @rm, @mr = [0x0c, 0x0d, 0x83, 0x81, 0x0b, 0x09]
|
630
|
+
@x = Ecx.clone
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
## ------------------------------------------------------------------------
|
635
|
+
|
636
|
+
# Test is AND + condition code
|
637
|
+
class Test < Instruction
|
638
|
+
# a8 imm8
|
639
|
+
# a9 imm
|
640
|
+
# f7/0, r/m
|
641
|
+
# 85 r/m, r
|
642
|
+
|
643
|
+
def initialize( dst=nil, src=nil); super(dst,src); end
|
644
|
+
|
645
|
+
def to_s
|
646
|
+
if not @dst
|
647
|
+
raise(BadArg, "need imm for implied ax") if not src_imm?
|
648
|
+
if @src.val < 0x100
|
649
|
+
add(0xa8)
|
650
|
+
add(@src.val)
|
651
|
+
else
|
652
|
+
add(0xa9)
|
653
|
+
add(@src.val)
|
654
|
+
end
|
655
|
+
else
|
656
|
+
if src_imm?
|
657
|
+
add(0xf7)
|
658
|
+
add(modrm(@dst.val, Eax.clone))
|
659
|
+
add(@src.val)
|
660
|
+
else
|
661
|
+
add(0x85)
|
662
|
+
add(modrm(@dst.val, @src))
|
663
|
+
end
|
664
|
+
end
|
665
|
+
super
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
## ------------------------------------------------------------------------
|
670
|
+
|
671
|
+
# CMP is SUB + condition code
|
672
|
+
class Cmp < Instruction
|
673
|
+
# 3c imm8
|
674
|
+
# 3d imm
|
675
|
+
# 80/7 r/m8, imm8
|
676
|
+
# 81/7 r/m, imm
|
677
|
+
# 83/7 r/m, imm8
|
678
|
+
# 38 r/m8, r8
|
679
|
+
# 39 r/m, r
|
680
|
+
# 3a r8, r/m8
|
681
|
+
# 3b r, r/m
|
682
|
+
|
683
|
+
def initialize( dst=nil, src=nil); super dst, src; end
|
684
|
+
|
685
|
+
def to_s
|
686
|
+
if not @dst
|
687
|
+
raise(BadArg, "need immed for implicit ax") if not src_imm?
|
688
|
+
if @src.val < 0x100
|
689
|
+
add(0x3c)
|
690
|
+
add(@src.val)
|
691
|
+
else
|
692
|
+
add(0x3d)
|
693
|
+
add(@src.val)
|
694
|
+
end
|
695
|
+
else
|
696
|
+
raise(BadArg, "need reg dst") if not dst_reg?
|
697
|
+
if src_imm?
|
698
|
+
raise NotImp if @dst.byte
|
699
|
+
if @src.val < 0x100
|
700
|
+
add(0x83)
|
701
|
+
add(modrm(@dst, Edi.clone))
|
702
|
+
add(@src.val)
|
703
|
+
else
|
704
|
+
add(0x81)
|
705
|
+
add(modrm(@dst, Edi.clone))
|
706
|
+
add(@src.val)
|
707
|
+
end
|
708
|
+
else
|
709
|
+
if @dst.indir
|
710
|
+
add(0x39)
|
711
|
+
add(modrm(@src, @dst))
|
712
|
+
add(@dst.disp)
|
713
|
+
else
|
714
|
+
add(0x3b)
|
715
|
+
add(modrm(@src, @dst))
|
716
|
+
add(@dst.disp)
|
717
|
+
end
|
718
|
+
end
|
719
|
+
end
|
720
|
+
super
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
## ------------------------------------------------------------------------
|
725
|
+
|
726
|
+
# Wrapper for INC and DEC, not called directly.
|
727
|
+
class IncDec < Instruction
|
728
|
+
# fe/0 r/m8
|
729
|
+
# ff/0 r/m
|
730
|
+
# 40+ (reg)
|
731
|
+
|
732
|
+
def initialize( dst=nil); super dst; end
|
733
|
+
|
734
|
+
def to_s
|
735
|
+
raise Insuff if not @dst
|
736
|
+
raise(BadArg, "need a register") if not dst_reg?
|
737
|
+
|
738
|
+
if @dst.indir
|
739
|
+
add(0xff)
|
740
|
+
add(modrm(@dst, @var))
|
741
|
+
else
|
742
|
+
add(@bas + @dst.code)
|
743
|
+
end
|
744
|
+
super
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
## ------------------------------------------------------------------------
|
749
|
+
|
750
|
+
# INC memory or register
|
751
|
+
class Inc < IncDec
|
752
|
+
def initialize(*args)
|
753
|
+
super *args
|
754
|
+
@var = Eax.clone
|
755
|
+
@bas = 0x40
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
## ------------------------------------------------------------------------
|
760
|
+
|
761
|
+
# DEC memory or register
|
762
|
+
class Dec < IncDec
|
763
|
+
def initialize(*args)
|
764
|
+
super *args
|
765
|
+
@var = Ecx.clone
|
766
|
+
@bas = 0x48
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
## ------------------------------------------------------------------------
|
771
|
+
|
772
|
+
# MOV, from reg to mem or v/v, or imm to reg, v/v
|
773
|
+
class Mov < Instruction
|
774
|
+
# 89 r/m, r
|
775
|
+
# 8b r, r/m
|
776
|
+
# b8+ r, imm
|
777
|
+
# c7+ r/m, imm
|
778
|
+
def to_s
|
779
|
+
raise Insuff if not @src or not @dst
|
780
|
+
raise NotImp if (src_reg? and @src.index) or (dst_reg? and @dst.index)
|
781
|
+
|
782
|
+
if src_imm?
|
783
|
+
if @dst.indir
|
784
|
+
add(0xc7)
|
785
|
+
add(@dst.code)
|
786
|
+
add(@src.val)
|
787
|
+
else
|
788
|
+
add(0xb8 + @dst.code)
|
789
|
+
add(@src.val, true)
|
790
|
+
end
|
791
|
+
elsif dst_imm?
|
792
|
+
raise BadArg, "mov to immed"
|
793
|
+
else
|
794
|
+
raise(BadArg, "two r/m") if @src.indir and @dst.indir
|
795
|
+
if not @src.indir and not @dst.indir
|
796
|
+
add(0x89)
|
797
|
+
add(modrm(@dst, @src))
|
798
|
+
elsif @src.indir # ie, ld
|
799
|
+
add(0x8b)
|
800
|
+
add(modrm(@dst, @src))
|
801
|
+
add(@src.disp)
|
802
|
+
elsif @dst.indir # ie, st
|
803
|
+
add(0x89)
|
804
|
+
add(modrm(@dst, @src))
|
805
|
+
add(@dst.disp)
|
806
|
+
end
|
807
|
+
end
|
808
|
+
super
|
809
|
+
end
|
810
|
+
|
811
|
+
def initialize( x=nil, y=nil); super x, y; end
|
812
|
+
end
|
813
|
+
|
814
|
+
## ------------------------------------------------------------------------
|
815
|
+
|
816
|
+
# Wrapper for the shift operations below.
|
817
|
+
class Shift < Instruction
|
818
|
+
once = nil
|
819
|
+
bycl = nil
|
820
|
+
imm = nil
|
821
|
+
x = nil
|
822
|
+
|
823
|
+
def initialize( dst=nil, src=nil); super dst, src; end
|
824
|
+
|
825
|
+
def to_s
|
826
|
+
raise Insuff if not @dst
|
827
|
+
raise(BadArg, "need reg dst") if not dst_reg?
|
828
|
+
|
829
|
+
if not @src
|
830
|
+
add(@once)
|
831
|
+
add(modrm(@dst, @x))
|
832
|
+
else
|
833
|
+
if src_imm?
|
834
|
+
add(@imm)
|
835
|
+
add(modrm(@dst, @x))
|
836
|
+
add(@src.val)
|
837
|
+
else
|
838
|
+
add(@bycl)
|
839
|
+
add(modrm(@dst, @x))
|
840
|
+
end
|
841
|
+
end
|
842
|
+
super
|
843
|
+
end
|
844
|
+
|
845
|
+
def magic(x, y, z, r); @once, @bycl, @imm, @x = [x,y,z,r.clone]; end
|
846
|
+
end
|
847
|
+
|
848
|
+
# XXX looks wrong
|
849
|
+
|
850
|
+
# Left arith shift
|
851
|
+
class Sal < Shift; def initialize(*args); super *args; magic 0xd1, 0xd3, 0xc1, Esp; end; end
|
852
|
+
|
853
|
+
# Right arith shift
|
854
|
+
class Sar < Shift; def initialize(*args); super *args; magic 0xd1, 0xd3, 0xc1, Edi; end; end
|
855
|
+
|
856
|
+
# Left logic shift
|
857
|
+
class Shl < Shift; def initialize(*args); super *args; magic 0xd1, 0xd3, 0xc1, Esp; end; end
|
858
|
+
|
859
|
+
# Right logic shift
|
860
|
+
class Shr < Shift; def initialize(*args); super *args; magic 0xd1, 0xd3, 0xc1, Ebp; end; end
|
861
|
+
|
862
|
+
## ------------------------------------------------------------------------
|
863
|
+
|
864
|
+
# NOP
|
865
|
+
class Nop < Instruction
|
866
|
+
# 90
|
867
|
+
def initialize; super; end
|
868
|
+
|
869
|
+
def to_s
|
870
|
+
add(0x90)
|
871
|
+
super
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
## ------------------------------------------------------------------------
|
876
|
+
|
877
|
+
# NOT a register
|
878
|
+
class Not < Instruction
|
879
|
+
# f7/2 r/m
|
880
|
+
|
881
|
+
def initialize( dst=nil); super dst; end
|
882
|
+
|
883
|
+
def to_s
|
884
|
+
raise Insuff if not @dst
|
885
|
+
raise(BadArg, "need reg for not") if not dst_reg?
|
886
|
+
|
887
|
+
add(0xf7)
|
888
|
+
add(modrm(@dst, Edx.clone))
|
889
|
+
super
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
## ------------------------------------------------------------------------
|
894
|
+
|
895
|
+
# Load a memory address from a register into another register;
|
896
|
+
# uses memory notation, but is a pure arith insn
|
897
|
+
class Lea < Instruction
|
898
|
+
# 8d r, m
|
899
|
+
|
900
|
+
def initialize( dst=nil, src=nil); super dst, src; end
|
901
|
+
|
902
|
+
def to_s
|
903
|
+
raise Insuff if not @src or not @dst
|
904
|
+
raise(BadArg, "need reg src") if not src_reg?
|
905
|
+
raise(BadArg, "need indirected src") if not @src.indir
|
906
|
+
|
907
|
+
add(0x8d)
|
908
|
+
add(modrm(@dst, @src))
|
909
|
+
add(@src.disp)
|
910
|
+
super
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
## ------------------------------------------------------------------------
|
915
|
+
|
916
|
+
# Wrapper for conditional jumps, see below
|
917
|
+
class Jcc < Instruction
|
918
|
+
|
919
|
+
def m; [nil,nil]; end
|
920
|
+
def initialize( dst)
|
921
|
+
super dst
|
922
|
+
@short, @near = m()
|
923
|
+
end
|
924
|
+
|
925
|
+
def to_s
|
926
|
+
raise Insuff if not @dst
|
927
|
+
raise(BadArg, "need immed") if not dst_imm? and not dst_lab?
|
928
|
+
|
929
|
+
if @dst.val < 0
|
930
|
+
if @dst.val.abs & 0x80
|
931
|
+
add(0x0f)
|
932
|
+
add(@near)
|
933
|
+
add(@dst.sx32)
|
934
|
+
else
|
935
|
+
add(@short)
|
936
|
+
add(@dst.sx8)
|
937
|
+
end
|
938
|
+
else
|
939
|
+
if @dst.val < 0x100
|
940
|
+
add(@short)
|
941
|
+
add(@dst.val)
|
942
|
+
else
|
943
|
+
add(0x0f)
|
944
|
+
add(@near)
|
945
|
+
add(@dst.val, true)
|
946
|
+
end
|
947
|
+
end
|
948
|
+
super
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
# Somewhere in the SDM there's a table of what condition codes
|
953
|
+
# each of these check.
|
954
|
+
|
955
|
+
# Above
|
956
|
+
class Ja < Jcc; def m; [0x77, 0x87]; end; end
|
957
|
+
# Above/eq
|
958
|
+
class Jae <Jcc; def m; [0x73, 0x83]; end; end
|
959
|
+
# Below
|
960
|
+
class Jb < Jcc; def m; [0x72, 0x82]; end; end
|
961
|
+
# Below/eq
|
962
|
+
class Jbe < Jcc; def m; [0x76, 0x86]; end; end
|
963
|
+
# Carry
|
964
|
+
class Jc < Jcc; def m; [0x72, 0x82]; end; end
|
965
|
+
# Equal
|
966
|
+
class Je < Jcc; def m; [0x74, 0x84]; end; end
|
967
|
+
# Greater (SIGNED)
|
968
|
+
class Jg < Jcc; def m; [0x7f, 0x8f]; end; end
|
969
|
+
# Greater/eq (SIGNED)
|
970
|
+
class Jge < Jcc; def m; [0x7d, 0x8d]; end; end
|
971
|
+
# Less (SIGNED)
|
972
|
+
class Jl < Jcc; def m; [0x7c, 0x8c]; end; end
|
973
|
+
# Less/eq (SIGNED)
|
974
|
+
class Jle < Jcc; def m; [0x7e, 0x8e]; end; end
|
975
|
+
# Not above
|
976
|
+
class Jna < Jcc; def m; [0x76, 0x86]; end; end
|
977
|
+
# Not above/eq
|
978
|
+
class Jnae < Jcc; def m; [0x72, 0x82]; end; end
|
979
|
+
# Not below
|
980
|
+
class Jnb < Jcc; def m; [0x73, 0x83]; end; end
|
981
|
+
# Not below/eq
|
982
|
+
class Jnbe < Jcc; def m; [0x77, 0x87]; end; end
|
983
|
+
# Not carry
|
984
|
+
class Jnc < Jcc; def m; [0x73, 0x83]; end; end
|
985
|
+
# Not equal
|
986
|
+
class Jne < Jcc; def m; [0x75, 0x85]; end; end
|
987
|
+
# Not greater (SIGNED)
|
988
|
+
class Jng < Jcc; def m; [0x7e, 0x8e]; end; end
|
989
|
+
# Not greater/eq (SIGNED)
|
990
|
+
class Jnge < Jcc; def m; [0x7c, 0x8c]; end; end
|
991
|
+
# Not less (SIGNED)
|
992
|
+
class Jnl < Jcc; def m; [0x7d, 0x8d]; end; end
|
993
|
+
# Not less/eq (SIGNED)
|
994
|
+
class Jnle < Jcc; def m; [0x7f, 0x8f]; end; end
|
995
|
+
# Not overflow
|
996
|
+
class Jno < Jcc; def m; [0x71, 0x81]; end; end
|
997
|
+
# Not parity
|
998
|
+
class Jnp < Jcc; def m; [0x7b, 0x8b]; end; end
|
999
|
+
# Not sign
|
1000
|
+
class Jns < Jcc; def m; [0x79, 0x89]; end; end
|
1001
|
+
# Not zero
|
1002
|
+
class Jnz < Jcc; def m; [0x75, 0x85]; end; end
|
1003
|
+
# Overflow
|
1004
|
+
class Jo < Jcc; def m; [0x70, 0x80]; end; end
|
1005
|
+
# Parity
|
1006
|
+
class Jp < Jcc; def m; [0x7a, 0x8a]; end; end
|
1007
|
+
# Parity/eq
|
1008
|
+
class Jpe < Jcc; def m; [0x7a, 0x8a]; end; end
|
1009
|
+
# Parity/overflow
|
1010
|
+
class Jpo < Jcc; def m; [0x7b, 0x8b]; end; end
|
1011
|
+
# Signed
|
1012
|
+
class Js < Jcc; def m; [0x78, 0x88]; end; end
|
1013
|
+
# Zero
|
1014
|
+
class Jz < Jcc; def m; [0x74, 0x84]; end; end
|
1015
|
+
|
1016
|
+
## ------------------------------------------------------------------------
|
1017
|
+
|
1018
|
+
# INT 3, mostly, but will do INT X
|
1019
|
+
class Int < Instruction
|
1020
|
+
## cc int 3
|
1021
|
+
## cd imm int n
|
1022
|
+
## ce int 4 notimp
|
1023
|
+
|
1024
|
+
def initialize(dst=nil); super dst; end
|
1025
|
+
|
1026
|
+
def to_s
|
1027
|
+
raise(TooMan, "too many arguments for int") if @src
|
1028
|
+
raise(BadArg, "int takes immed") if @dst and not dst_imm?
|
1029
|
+
|
1030
|
+
if @dst
|
1031
|
+
raise(BadArg, "need 8 bit immed") if @dst.val >= 0x100
|
1032
|
+
if @dst.val == 3
|
1033
|
+
add(0xcc)
|
1034
|
+
elsif @dst.val == 4
|
1035
|
+
add(0xce)
|
1036
|
+
else
|
1037
|
+
add(0xcd)
|
1038
|
+
add(@dst.val)
|
1039
|
+
end
|
1040
|
+
else
|
1041
|
+
add(0xcc)
|
1042
|
+
end
|
1043
|
+
super
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
end
|