ronin-code-asm 1.0.0.beta1
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.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.editorconfig +11 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +11 -0
- data/.mailmap +1 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +44 -0
- data/Gemfile +25 -0
- data/README.md +166 -0
- data/Rakefile +39 -0
- data/data/os/freebsd/amd64/syscalls.yml +415 -0
- data/data/os/freebsd/x86/syscalls.yml +415 -0
- data/data/os/linux/amd64/syscalls.yml +306 -0
- data/data/os/linux/x86/syscalls.yml +339 -0
- data/gemspec.yml +26 -0
- data/lib/ronin/code/asm/archs/amd64.rb +100 -0
- data/lib/ronin/code/asm/archs/x86.rb +170 -0
- data/lib/ronin/code/asm/archs.rb +22 -0
- data/lib/ronin/code/asm/config.rb +33 -0
- data/lib/ronin/code/asm/immediate_operand.rb +84 -0
- data/lib/ronin/code/asm/instruction.rb +66 -0
- data/lib/ronin/code/asm/memory_operand.rb +119 -0
- data/lib/ronin/code/asm/os/freebsd.rb +35 -0
- data/lib/ronin/code/asm/os/linux.rb +35 -0
- data/lib/ronin/code/asm/os/os.rb +47 -0
- data/lib/ronin/code/asm/os.rb +57 -0
- data/lib/ronin/code/asm/program.rb +509 -0
- data/lib/ronin/code/asm/register.rb +111 -0
- data/lib/ronin/code/asm/shellcode.rb +75 -0
- data/lib/ronin/code/asm/syntax/att.rb +164 -0
- data/lib/ronin/code/asm/syntax/common.rb +241 -0
- data/lib/ronin/code/asm/syntax/intel.rb +150 -0
- data/lib/ronin/code/asm/syntax.rb +22 -0
- data/lib/ronin/code/asm/version.rb +28 -0
- data/lib/ronin/code/asm.rb +68 -0
- data/ronin-code-asm.gemspec +62 -0
- data/spec/asm_spec.rb +14 -0
- data/spec/config_spec.rb +10 -0
- data/spec/immediate_operand_spec.rb +79 -0
- data/spec/instruction_spec.rb +62 -0
- data/spec/memory_operand_spec.rb +80 -0
- data/spec/os_spec.rb +68 -0
- data/spec/program_spec.rb +439 -0
- data/spec/register_spec.rb +112 -0
- data/spec/shellcode_spec.rb +58 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/syntax/att_spec.rb +181 -0
- data/spec/syntax/common_spec.rb +42 -0
- data/spec/syntax/intel_spec.rb +174 -0
- metadata +143 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-asm - A Ruby DSL for crafting Assembly programs and shellcode.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-asm is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-asm is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-asm. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/asm/config'
|
22
|
+
|
23
|
+
require 'yaml'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Code
|
27
|
+
module ASM
|
28
|
+
module OS
|
29
|
+
#
|
30
|
+
# Collection of all known syscalls, grouped by OS and Arch.
|
31
|
+
#
|
32
|
+
# @return [Hash{Symbol => Hash{Symbol => Hash{Symbol => Integer}}}]
|
33
|
+
# Syscall names and numbers, organized by OS then Arch.
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
SYSCALLS = Hash.new do |hash,os|
|
38
|
+
hash[os] = Hash.new do |subhash,arch|
|
39
|
+
subhash[arch] = YAML.load_file(
|
40
|
+
File.join(Config::DATA_DIR,'os',os.to_s.downcase,arch.to_s,'syscalls.yml')
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-asm - A Ruby DSL for crafting Assembly programs and shellcode.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-asm is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-asm is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-asm. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/asm/os/os'
|
22
|
+
require 'ronin/code/asm/os/freebsd'
|
23
|
+
require 'ronin/code/asm/os/linux'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Code
|
27
|
+
module ASM
|
28
|
+
module OS
|
29
|
+
# The mapping of OS names to modules.
|
30
|
+
NAMES = {
|
31
|
+
linux: Linux,
|
32
|
+
freebsd: FreeBSD
|
33
|
+
}
|
34
|
+
|
35
|
+
#
|
36
|
+
# Fetches the OS module with the given name.
|
37
|
+
#
|
38
|
+
# @param [Symbol] name
|
39
|
+
# The OS name (ex: `:linux`).
|
40
|
+
#
|
41
|
+
# @return [Linux, FreeBSD]
|
42
|
+
# The OS module.
|
43
|
+
#
|
44
|
+
# @raise [ArgumentError]
|
45
|
+
# The OS name was unknown.
|
46
|
+
#
|
47
|
+
# @since 1.0.0
|
48
|
+
#
|
49
|
+
def self.[](name)
|
50
|
+
NAMES.fetch(name) do
|
51
|
+
raise(ArgumentError,"unknown OS name: #{name.inspect}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,509 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-asm - A Ruby DSL for crafting Assembly programs and shellcode.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-asm is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-asm is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-asm. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/asm/archs'
|
22
|
+
require 'ronin/code/asm/os'
|
23
|
+
require 'ronin/code/asm/register'
|
24
|
+
require 'ronin/code/asm/instruction'
|
25
|
+
require 'ronin/code/asm/immediate_operand'
|
26
|
+
require 'ronin/code/asm/syntax'
|
27
|
+
|
28
|
+
require 'tempfile'
|
29
|
+
require 'yasm/program'
|
30
|
+
|
31
|
+
module Ronin
|
32
|
+
module Code
|
33
|
+
module ASM
|
34
|
+
#
|
35
|
+
# Represents a full Assembly program.
|
36
|
+
#
|
37
|
+
class Program
|
38
|
+
|
39
|
+
# Supported Assembly Syntaxs
|
40
|
+
SYNTAX = {
|
41
|
+
att: Syntax::ATT,
|
42
|
+
intel: Syntax::Intel
|
43
|
+
}
|
44
|
+
|
45
|
+
# The Assembly Parsers
|
46
|
+
PARSERS = {
|
47
|
+
att: :gas,
|
48
|
+
intel: :nasm
|
49
|
+
}
|
50
|
+
|
51
|
+
# The targeted architecture
|
52
|
+
#
|
53
|
+
# @return [Symbol]
|
54
|
+
attr_reader :arch
|
55
|
+
|
56
|
+
# The targeted Operating System
|
57
|
+
#
|
58
|
+
# @return [Symbol, nil]
|
59
|
+
attr_reader :os
|
60
|
+
|
61
|
+
# The default word size
|
62
|
+
#
|
63
|
+
# @return [Integer]
|
64
|
+
attr_reader :word_size
|
65
|
+
|
66
|
+
# The registers available to the program
|
67
|
+
#
|
68
|
+
# @return [Hash{Symbol => Register}]
|
69
|
+
# The names and registers.
|
70
|
+
attr_reader :registers
|
71
|
+
|
72
|
+
# The syscalls available to the program
|
73
|
+
#
|
74
|
+
# @return [Hash{Symbol => Integer}]
|
75
|
+
# The syscall names and numbers.
|
76
|
+
attr_reader :syscalls
|
77
|
+
|
78
|
+
# The registers used by the program
|
79
|
+
#
|
80
|
+
# @return [Array<Symbol>]
|
81
|
+
attr_reader :allocated_registers
|
82
|
+
|
83
|
+
# The instructions of the program
|
84
|
+
#
|
85
|
+
# @return [Array<Instruction>]
|
86
|
+
attr_reader :instructions
|
87
|
+
|
88
|
+
#
|
89
|
+
# Initializes a new Assembly Program.
|
90
|
+
#
|
91
|
+
# @param [Symbol] arch
|
92
|
+
# The Architecture to target.
|
93
|
+
#
|
94
|
+
# @param [Symbol, nil] os
|
95
|
+
# The Operating System to target.
|
96
|
+
#
|
97
|
+
# @param [Hash{Symbol => Object}] define
|
98
|
+
# Constants to define in the program.
|
99
|
+
#
|
100
|
+
# @yield []
|
101
|
+
# The given block will be evaluated within the program.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# Program.new(arch: :amd64) do
|
105
|
+
# push rax
|
106
|
+
# push rbx
|
107
|
+
#
|
108
|
+
# mov rsp, rax
|
109
|
+
# mov rax[8], rbx
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
def initialize(arch: :x86, os: nil, define: {}, &block)
|
113
|
+
@arch = arch
|
114
|
+
|
115
|
+
arch = Archs.const_get(@arch.to_s.upcase)
|
116
|
+
|
117
|
+
@word_size = arch::WORD_SIZE
|
118
|
+
@registers = arch::REGISTERS
|
119
|
+
|
120
|
+
extend arch
|
121
|
+
|
122
|
+
@syscalls = {}
|
123
|
+
|
124
|
+
if os
|
125
|
+
@os = os
|
126
|
+
@syscalls = OS::SYSCALLS[@os][@arch]
|
127
|
+
|
128
|
+
extend OS[@os]
|
129
|
+
end
|
130
|
+
|
131
|
+
define.each do |name,value|
|
132
|
+
instance_variable_set("@#{name}",value)
|
133
|
+
end
|
134
|
+
|
135
|
+
@allocated_registers = []
|
136
|
+
@instructions = []
|
137
|
+
|
138
|
+
instance_eval(&block) if block
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Determines if a register exists.
|
143
|
+
#
|
144
|
+
# @param [Symbol] name
|
145
|
+
# The name of the register.
|
146
|
+
#
|
147
|
+
# @return [Boolean]
|
148
|
+
# Specifies whether the register exists.
|
149
|
+
#
|
150
|
+
def register?(name)
|
151
|
+
@registers.has_key?(name.to_sym)
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Accesses a register.
|
156
|
+
#
|
157
|
+
# @param [String, Symbol] name
|
158
|
+
# The name of the register.
|
159
|
+
#
|
160
|
+
# @return [Register]
|
161
|
+
# The register.
|
162
|
+
#
|
163
|
+
# @raise [ArgumentError]
|
164
|
+
# The register could not be found.
|
165
|
+
#
|
166
|
+
def register(name)
|
167
|
+
name = name.to_sym
|
168
|
+
|
169
|
+
unless register?(name)
|
170
|
+
raise(ArgumentError,"unknown register: #{name}")
|
171
|
+
end
|
172
|
+
|
173
|
+
unless @allocated_registers.include?(name)
|
174
|
+
# mark the register as being used, when it was first accessed
|
175
|
+
@allocated_registers << name
|
176
|
+
end
|
177
|
+
|
178
|
+
return @registers[name]
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Adds a new instruction to the program.
|
183
|
+
#
|
184
|
+
# @param [String, Symbol] name
|
185
|
+
#
|
186
|
+
# @param [Array] operands
|
187
|
+
#
|
188
|
+
# @return [Instruction]
|
189
|
+
# The newly created instruction.
|
190
|
+
#
|
191
|
+
def instruction(name,*operands)
|
192
|
+
insn = Instruction.new(name.to_sym,operands)
|
193
|
+
|
194
|
+
@instructions << insn
|
195
|
+
return insn
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# Creates an operand of size 1 (byte).
|
200
|
+
#
|
201
|
+
# @param [MemoryOperand, Integer] op
|
202
|
+
# The value of the operand.
|
203
|
+
#
|
204
|
+
# @return [MemoryOperand, ImmediateOperand]
|
205
|
+
# The new operand value.
|
206
|
+
#
|
207
|
+
def byte(op)
|
208
|
+
case op
|
209
|
+
when MemoryOperand
|
210
|
+
MemoryOperand.new(op.base,op.offset,op.index,op.scale,1)
|
211
|
+
else
|
212
|
+
ImmediateOperand.new(op,1)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Creates a operand of size 2 (bytes).
|
218
|
+
#
|
219
|
+
# @param [MemoryOperand, Integer] op
|
220
|
+
# The value of the operand.
|
221
|
+
#
|
222
|
+
# @return [MemoryOperand, ImmediateOperand]
|
223
|
+
# The new operand value.
|
224
|
+
#
|
225
|
+
def word(op)
|
226
|
+
case op
|
227
|
+
when MemoryOperand
|
228
|
+
MemoryOperand.new(op.base,op.offset,op.index,op.scale,2)
|
229
|
+
else
|
230
|
+
ImmediateOperand.new(op,2)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
#
|
235
|
+
# Creates a operand of size 4 (bytes).
|
236
|
+
#
|
237
|
+
# @param [MemoryOperand, Integer] op
|
238
|
+
# The value of the operand.
|
239
|
+
#
|
240
|
+
# @return [ImmediateOperand]
|
241
|
+
# The new operand value.
|
242
|
+
#
|
243
|
+
def dword(op)
|
244
|
+
case op
|
245
|
+
when MemoryOperand
|
246
|
+
MemoryOperand.new(op.base,op.offset,op.index,op.scale,4)
|
247
|
+
else
|
248
|
+
ImmediateOperand.new(op,4)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
# Creates a operand of size 8 (bytes).
|
254
|
+
#
|
255
|
+
# @param [MemoryOperand, Integer] op
|
256
|
+
# The value of the operand.
|
257
|
+
#
|
258
|
+
# @return [MemoryOperand, ImmediateOperand]
|
259
|
+
# The new operand.
|
260
|
+
#
|
261
|
+
def qword(op)
|
262
|
+
case op
|
263
|
+
when MemoryOperand
|
264
|
+
MemoryOperand.new(op.base,op.offset,op.index,op.scale,8)
|
265
|
+
else
|
266
|
+
ImmediateOperand.new(op,8)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
#
|
271
|
+
# Adds a label to the program.
|
272
|
+
#
|
273
|
+
# @param [Symbol, String] name
|
274
|
+
# The name of the label.
|
275
|
+
#
|
276
|
+
# @yield []
|
277
|
+
# The given block will be evaluated after the label has been
|
278
|
+
# added.
|
279
|
+
#
|
280
|
+
# @return [Symbol]
|
281
|
+
# The label name.
|
282
|
+
#
|
283
|
+
def label(name,&block)
|
284
|
+
name = name.to_sym
|
285
|
+
|
286
|
+
@instructions << name
|
287
|
+
instance_eval(&block)
|
288
|
+
return name
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
# Generic method for generating the instruction for causing an interrupt.
|
293
|
+
#
|
294
|
+
# @param [Integer] number
|
295
|
+
# The interrupt number to call.
|
296
|
+
#
|
297
|
+
# @abstract
|
298
|
+
#
|
299
|
+
def interrupt(number)
|
300
|
+
end
|
301
|
+
|
302
|
+
#
|
303
|
+
# Generic method for generating the instruction for invoking a syscall.
|
304
|
+
#
|
305
|
+
# @abstract
|
306
|
+
#
|
307
|
+
def syscall
|
308
|
+
end
|
309
|
+
|
310
|
+
#
|
311
|
+
# Generic method for pushing onto the stack.
|
312
|
+
#
|
313
|
+
# @param [Register, Integer] value
|
314
|
+
# The value to push.
|
315
|
+
#
|
316
|
+
# @abstract
|
317
|
+
#
|
318
|
+
def stack_push(value)
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# Generic method for popping off the stack.
|
323
|
+
#
|
324
|
+
# @param [Symbol] name
|
325
|
+
# The name of the reigster.
|
326
|
+
#
|
327
|
+
# @abstract
|
328
|
+
#
|
329
|
+
def stack_pop(name)
|
330
|
+
end
|
331
|
+
|
332
|
+
#
|
333
|
+
# Generic method for clearing a register.
|
334
|
+
#
|
335
|
+
# @param [Symbol] name
|
336
|
+
# The name of the reigster.
|
337
|
+
#
|
338
|
+
# @abstract
|
339
|
+
#
|
340
|
+
def register_clear(name)
|
341
|
+
end
|
342
|
+
|
343
|
+
#
|
344
|
+
# Generic method for setting a register.
|
345
|
+
#
|
346
|
+
# @param [Symbol] name
|
347
|
+
# The name of the reigster.
|
348
|
+
#
|
349
|
+
# @param [Register, ImmediateOperand, Integer] value
|
350
|
+
# The new value for the register.
|
351
|
+
#
|
352
|
+
# @abstract
|
353
|
+
#
|
354
|
+
def register_set(name,value)
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# Generic method for saving a register.
|
359
|
+
#
|
360
|
+
# @param [Symbol] name
|
361
|
+
# The name of the reigster.
|
362
|
+
#
|
363
|
+
# @abstract
|
364
|
+
#
|
365
|
+
def register_save(name)
|
366
|
+
end
|
367
|
+
|
368
|
+
#
|
369
|
+
# Generic method for loading a register.
|
370
|
+
#
|
371
|
+
# @param [Symbol] name
|
372
|
+
# The name of the reigster.
|
373
|
+
#
|
374
|
+
# @abstract
|
375
|
+
#
|
376
|
+
def register_load(name)
|
377
|
+
end
|
378
|
+
|
379
|
+
#
|
380
|
+
# Defines a critical region, where the specified Registers
|
381
|
+
# should be saved and then reloaded.
|
382
|
+
#
|
383
|
+
# @param [Array<Symbol>] regs
|
384
|
+
# The registers to save and reload.
|
385
|
+
#
|
386
|
+
# @yield []
|
387
|
+
# The given block will be evaluated after the registers
|
388
|
+
# have been saved.
|
389
|
+
#
|
390
|
+
def critical(*regs,&block)
|
391
|
+
regs.each { |name| register_save(name) }
|
392
|
+
|
393
|
+
instance_eval(&block)
|
394
|
+
|
395
|
+
regs.reverse_each { |name| register_load(name) }
|
396
|
+
end
|
397
|
+
|
398
|
+
#
|
399
|
+
# Evaluates code within the Program.
|
400
|
+
#
|
401
|
+
# @yield []
|
402
|
+
# The code to evaluate.
|
403
|
+
#
|
404
|
+
def eval(&block)
|
405
|
+
instance_eval(&block)
|
406
|
+
end
|
407
|
+
|
408
|
+
#
|
409
|
+
# Converts the program to Assembly Source Code.
|
410
|
+
#
|
411
|
+
# @param [Symbol] syntax
|
412
|
+
# The syntax to compile the program to.
|
413
|
+
#
|
414
|
+
def to_asm(syntax=:intel)
|
415
|
+
SYNTAX[syntax].emit_program(self)
|
416
|
+
end
|
417
|
+
|
418
|
+
#
|
419
|
+
# @see #to_s
|
420
|
+
#
|
421
|
+
def to_s
|
422
|
+
to_asm
|
423
|
+
end
|
424
|
+
|
425
|
+
#
|
426
|
+
# Assembles the program.
|
427
|
+
#
|
428
|
+
# @param [String] output
|
429
|
+
# The path for the assembled program.
|
430
|
+
#
|
431
|
+
# @param [Symbol, String] syntax
|
432
|
+
# The syntax to compile the program to.
|
433
|
+
#
|
434
|
+
# @param [Symbol] format
|
435
|
+
# The format of the assembled executable. May be one of:
|
436
|
+
#
|
437
|
+
# * `:dbg` - Trace of all info passed to object format module.
|
438
|
+
# * `:bin` - Flat format binary.
|
439
|
+
# * `:dosexe` - DOS .EXE format binary.
|
440
|
+
# * `:elf` - ELF.
|
441
|
+
# * `:elf32` - ELF (32-bit).
|
442
|
+
# * `:elf64` - ELF (64-bit).
|
443
|
+
# * `:coff` - COFF (DJGPP).
|
444
|
+
# * `:macho` - Mac OS X ABI Mach-O File Format.
|
445
|
+
# * `:macho32` - Mac OS X ABI Mach-O File Format (32-bit).
|
446
|
+
# * `:macho64` - Mac OS X ABI Mach-O File Format (64-bit).
|
447
|
+
# * `:rdf` - Relocatable Dynamic Object File Format (RDOFF) v2.0.
|
448
|
+
# * `:win32` - Win32.
|
449
|
+
# * `:win64` / `:x64` - Win64.
|
450
|
+
# * `:xdf` - Extended Dynamic Object.
|
451
|
+
#
|
452
|
+
# @return [String]
|
453
|
+
# The path to the assembled program.
|
454
|
+
#
|
455
|
+
# @raise [ArgumentError]
|
456
|
+
# The given syntax was not `:intel` or `:att`.
|
457
|
+
#
|
458
|
+
def assemble(output, syntax: :intel, format: :bin)
|
459
|
+
parser = PARSERS.fetch(syntax) do
|
460
|
+
raise(ArgumentError,"unknown ASM syntax: #{syntax.inspect}")
|
461
|
+
end
|
462
|
+
|
463
|
+
source = Tempfile.new(['ronin-code-asm', '.s'])
|
464
|
+
source.write(to_asm(syntax))
|
465
|
+
source.close
|
466
|
+
|
467
|
+
YASM::Command.run(
|
468
|
+
file: source.path,
|
469
|
+
parser: parser,
|
470
|
+
target: @arch,
|
471
|
+
output_format: format,
|
472
|
+
output: output
|
473
|
+
)
|
474
|
+
|
475
|
+
return output
|
476
|
+
end
|
477
|
+
|
478
|
+
protected
|
479
|
+
|
480
|
+
# undefine the syscall method, so method_missing handles it
|
481
|
+
undef syscall
|
482
|
+
|
483
|
+
#
|
484
|
+
# Allows adding unknown instructions to the program.
|
485
|
+
#
|
486
|
+
# @param [Symbol] name
|
487
|
+
# The name of the instruction.
|
488
|
+
#
|
489
|
+
# @param [Array] arguments
|
490
|
+
# Additional operands.
|
491
|
+
#
|
492
|
+
def method_missing(name,*arguments,&block)
|
493
|
+
if (block && arguments.empty?)
|
494
|
+
label(name,&block)
|
495
|
+
elsif block.nil?
|
496
|
+
if (arguments.empty? && register?(name))
|
497
|
+
register(name)
|
498
|
+
else
|
499
|
+
instruction(name,*arguments)
|
500
|
+
end
|
501
|
+
else
|
502
|
+
super(name,*arguments,&block)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-asm - A Ruby DSL for crafting Assembly programs and shellcode.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-asm is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-asm is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-asm. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/asm/memory_operand'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Code
|
25
|
+
module ASM
|
26
|
+
#
|
27
|
+
# Represents a Register.
|
28
|
+
#
|
29
|
+
class Register < Struct.new(:name, :width, :general)
|
30
|
+
|
31
|
+
#
|
32
|
+
# Initializes a register.
|
33
|
+
#
|
34
|
+
# @param [Symbol] name
|
35
|
+
# The register name.
|
36
|
+
#
|
37
|
+
# @param [Integer] width
|
38
|
+
# The width of the register.
|
39
|
+
#
|
40
|
+
# @param [Boolean] general
|
41
|
+
# Specifies whether the register is a General Purpose Register (GPR).
|
42
|
+
#
|
43
|
+
def initialize(name,width,general=false)
|
44
|
+
super(name,width,general)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Adds an offset to the value within the register and dereferences the
|
49
|
+
# address.
|
50
|
+
#
|
51
|
+
# @param [MemoryOperand, Register, Integer] offset
|
52
|
+
# The offset to add to the value of the register.
|
53
|
+
#
|
54
|
+
# @return [MemoryOperand]
|
55
|
+
# The new Memory Operand.
|
56
|
+
#
|
57
|
+
# @raise [TypeError]
|
58
|
+
# the `offset` was not an {MemoryOperand}, {Register} or Integer.
|
59
|
+
#
|
60
|
+
def +(offset)
|
61
|
+
case offset
|
62
|
+
when MemoryOperand
|
63
|
+
MemoryOperand.new(self,offset.offset,offset.index,offset.scale)
|
64
|
+
when Register
|
65
|
+
MemoryOperand.new(self,0,offset)
|
66
|
+
when Integer
|
67
|
+
MemoryOperand.new(self,offset)
|
68
|
+
else
|
69
|
+
raise(TypeError,"offset was not an MemoryOperand, Register or Integer")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Substracts from the value within the register and dereferences the
|
75
|
+
# address.
|
76
|
+
#
|
77
|
+
# @param [Integer] offset
|
78
|
+
# The value to subtract from the value of the register.
|
79
|
+
#
|
80
|
+
# @return [MemoryOperand]
|
81
|
+
# The new Memory Operand.
|
82
|
+
#
|
83
|
+
def -(offset)
|
84
|
+
MemoryOperand.new(self,-offset)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Multiples the value within the register.
|
89
|
+
#
|
90
|
+
# @param [Integer] scale
|
91
|
+
# The scale to multiply the value within register by.
|
92
|
+
#
|
93
|
+
# @return [MemoryOperand]
|
94
|
+
# The new Memory Operand.
|
95
|
+
#
|
96
|
+
def *(scale)
|
97
|
+
MemoryOperand.new(nil,0,self,scale)
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
# The register's name.
|
103
|
+
#
|
104
|
+
def to_s
|
105
|
+
self.name.to_s
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|