iZsh-ragweed 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/History.txt +29 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +49 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed.rb +84 -0
  11. data/lib/ragweed/arena.rb +55 -0
  12. data/lib/ragweed/blocks.rb +128 -0
  13. data/lib/ragweed/debugger32.rb +338 -0
  14. data/lib/ragweed/debuggerosx.rb +419 -0
  15. data/lib/ragweed/debuggertux.rb +345 -0
  16. data/lib/ragweed/detour.rb +223 -0
  17. data/lib/ragweed/ptr.rb +48 -0
  18. data/lib/ragweed/rasm.rb +53 -0
  19. data/lib/ragweed/rasm/isa.rb +1046 -0
  20. data/lib/ragweed/rasm/util.rb +26 -0
  21. data/lib/ragweed/sbuf.rb +197 -0
  22. data/lib/ragweed/trampoline.rb +103 -0
  23. data/lib/ragweed/utils.rb +88 -0
  24. data/lib/ragweed/wrap32.rb +53 -0
  25. data/lib/ragweed/wrap32/debugging.rb +163 -0
  26. data/lib/ragweed/wrap32/device.rb +49 -0
  27. data/lib/ragweed/wrap32/event.rb +50 -0
  28. data/lib/ragweed/wrap32/hooks.rb +23 -0
  29. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  30. data/lib/ragweed/wrap32/process.rb +506 -0
  31. data/lib/ragweed/wrap32/process_token.rb +59 -0
  32. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  33. data/lib/ragweed/wrap32/winx.rb +16 -0
  34. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  35. data/lib/ragweed/wraposx.rb +53 -0
  36. data/lib/ragweed/wraposx/constants.rb +112 -0
  37. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  38. data/lib/ragweed/wraposx/region_info.rb +250 -0
  39. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  40. data/lib/ragweed/wraposx/thread_info.rb +225 -0
  41. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  42. data/lib/ragweed/wraptux.rb +53 -0
  43. data/lib/ragweed/wraptux/constants.rb +68 -0
  44. data/lib/ragweed/wraptux/threads.rb +7 -0
  45. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. 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
@@ -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
@@ -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