pwntools 1.1.0 → 1.2.0

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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/lib/pwn.rb +1 -0
  4. data/lib/pwnlib/abi.rb +1 -0
  5. data/lib/pwnlib/asm.rb +83 -42
  6. data/lib/pwnlib/constants/constant.rb +4 -1
  7. data/lib/pwnlib/constants/constants.rb +3 -0
  8. data/lib/pwnlib/constants/linux/amd64.rb +2 -0
  9. data/lib/pwnlib/constants/linux/i386.rb +2 -0
  10. data/lib/pwnlib/context.rb +10 -1
  11. data/lib/pwnlib/dynelf.rb +7 -2
  12. data/lib/pwnlib/elf/elf.rb +79 -6
  13. data/lib/pwnlib/errors.rb +3 -2
  14. data/lib/pwnlib/ext/array.rb +2 -1
  15. data/lib/pwnlib/ext/helper.rb +3 -2
  16. data/lib/pwnlib/ext/integer.rb +2 -1
  17. data/lib/pwnlib/ext/string.rb +3 -2
  18. data/lib/pwnlib/logger.rb +21 -1
  19. data/lib/pwnlib/memleak.rb +1 -0
  20. data/lib/pwnlib/pwn.rb +5 -1
  21. data/lib/pwnlib/reg_sort.rb +5 -0
  22. data/lib/pwnlib/runner.rb +53 -0
  23. data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +2 -0
  24. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +1 -0
  25. data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +5 -1
  26. data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +4 -0
  27. data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +2 -0
  28. data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +1 -0
  29. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +3 -1
  30. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +1 -0
  31. data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +1 -0
  32. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +3 -2
  33. data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +3 -2
  34. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +1 -0
  35. data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +1 -0
  36. data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +2 -0
  37. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +1 -0
  38. data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +1 -0
  39. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +3 -2
  40. data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
  41. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +1 -0
  42. data/lib/pwnlib/shellcraft/generators/helper.rb +11 -2
  43. data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +2 -0
  44. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +1 -0
  45. data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
  46. data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +3 -0
  47. data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +2 -0
  48. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +2 -0
  49. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +1 -0
  50. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +3 -2
  51. data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +3 -2
  52. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +1 -0
  53. data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +1 -0
  54. data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +2 -0
  55. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +1 -0
  56. data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +1 -0
  57. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +3 -2
  58. data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
  59. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +1 -0
  60. data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +5 -3
  61. data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +2 -0
  62. data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
  63. data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +2 -0
  64. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +2 -0
  65. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +1 -0
  66. data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +8 -6
  67. data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +1 -0
  68. data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +3 -0
  69. data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +1 -0
  70. data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +2 -0
  71. data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +1 -0
  72. data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +1 -0
  73. data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +1 -0
  74. data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
  75. data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +10 -10
  76. data/lib/pwnlib/shellcraft/registers.rb +5 -1
  77. data/lib/pwnlib/shellcraft/shellcraft.rb +8 -3
  78. data/lib/pwnlib/timer.rb +6 -2
  79. data/lib/pwnlib/tubes/buffer.rb +4 -1
  80. data/lib/pwnlib/tubes/process.rb +2 -0
  81. data/lib/pwnlib/tubes/serialtube.rb +3 -1
  82. data/lib/pwnlib/tubes/sock.rb +7 -1
  83. data/lib/pwnlib/tubes/tube.rb +23 -3
  84. data/lib/pwnlib/ui.rb +21 -0
  85. data/lib/pwnlib/util/cyclic.rb +2 -0
  86. data/lib/pwnlib/util/fiddling.rb +37 -5
  87. data/lib/pwnlib/util/getdents.rb +1 -0
  88. data/lib/pwnlib/util/hexdump.rb +8 -5
  89. data/lib/pwnlib/util/lists.rb +3 -0
  90. data/lib/pwnlib/util/packing.rb +5 -2
  91. data/lib/pwnlib/util/ruby.rb +1 -0
  92. data/lib/pwnlib/version.rb +2 -1
  93. data/test/abi_test.rb +1 -0
  94. data/test/asm_test.rb +75 -85
  95. data/test/constants/constant_test.rb +1 -0
  96. data/test/constants/constants_test.rb +1 -0
  97. data/test/context_test.rb +1 -0
  98. data/test/data/assembly/aarch64.s +19 -0
  99. data/test/data/assembly/amd64.s +21 -0
  100. data/test/data/assembly/arm.s +9 -0
  101. data/test/data/assembly/i386.s +21 -0
  102. data/test/data/assembly/mips.s +16 -0
  103. data/test/data/assembly/mips64.s +6 -0
  104. data/test/data/assembly/powerpc.s +18 -0
  105. data/test/data/assembly/powerpc64.s +36 -0
  106. data/test/data/assembly/sparc.s +33 -0
  107. data/test/data/assembly/sparc64.s +5 -0
  108. data/test/data/assembly/thumb.s +37 -0
  109. data/test/data/echo.rb +1 -0
  110. data/test/dynelf_test.rb +3 -1
  111. data/test/elf/elf_test.rb +18 -0
  112. data/test/ext_test.rb +1 -0
  113. data/test/files/use_pwn.rb +1 -0
  114. data/test/files/use_pwnlib.rb +1 -0
  115. data/test/full_file_test.rb +6 -0
  116. data/test/logger_test.rb +24 -3
  117. data/test/memleak_test.rb +1 -0
  118. data/test/reg_sort_test.rb +1 -0
  119. data/test/runner_test.rb +32 -0
  120. data/test/shellcraft/infloop_test.rb +1 -0
  121. data/test/shellcraft/linux/cat_test.rb +1 -0
  122. data/test/shellcraft/linux/ls_test.rb +1 -0
  123. data/test/shellcraft/linux/sh_test.rb +1 -0
  124. data/test/shellcraft/linux/sleep_test.rb +68 -0
  125. data/test/shellcraft/linux/syscalls/execve_test.rb +1 -0
  126. data/test/shellcraft/linux/syscalls/exit_test.rb +1 -0
  127. data/test/shellcraft/linux/syscalls/open_test.rb +1 -0
  128. data/test/shellcraft/linux/syscalls/syscall_test.rb +1 -0
  129. data/test/shellcraft/memcpy_test.rb +20 -5
  130. data/test/shellcraft/mov_test.rb +1 -0
  131. data/test/shellcraft/nop_test.rb +1 -0
  132. data/test/shellcraft/popad_test.rb +1 -0
  133. data/test/shellcraft/pushstr_array_test.rb +1 -0
  134. data/test/shellcraft/pushstr_test.rb +1 -0
  135. data/test/shellcraft/registers_test.rb +1 -0
  136. data/test/shellcraft/ret_test.rb +1 -0
  137. data/test/shellcraft/setregs_test.rb +9 -8
  138. data/test/shellcraft/shellcraft_test.rb +1 -0
  139. data/test/test_helper.rb +28 -0
  140. data/test/timer_test.rb +2 -1
  141. data/test/tubes/buffer_test.rb +1 -0
  142. data/test/tubes/process_test.rb +8 -2
  143. data/test/tubes/serialtube_test.rb +1 -4
  144. data/test/tubes/sock_test.rb +1 -0
  145. data/test/tubes/tube_test.rb +10 -1
  146. data/test/ui_test.rb +18 -0
  147. data/test/util/cyclic_test.rb +1 -0
  148. data/test/util/fiddling_test.rb +8 -0
  149. data/test/util/getdents_test.rb +1 -0
  150. data/test/util/hexdump_test.rb +2 -1
  151. data/test/util/lists_test.rb +1 -0
  152. data/test/util/packing_test.rb +3 -2
  153. metadata +119 -59
@@ -0,0 +1,21 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'pwnlib/logger'
5
+
6
+ module Pwnlib
7
+ # This module collects utilities that need user interactions.
8
+ module UI
9
+ module_function
10
+
11
+ # Waits for user input.
12
+ #
13
+ # @return [void]
14
+ def pause
15
+ log.info('Paused (press enter to continue)')
16
+ $stdin.gets
17
+ end
18
+
19
+ include ::Pwnlib::Logger
20
+ end
21
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Pwnlib
4
5
  module Util
@@ -42,6 +43,7 @@ module Pwnlib
42
43
  # The result sequence.
43
44
  def de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
44
45
  return to_enum(__method__, alphabet: alphabet, n: n) { alphabet.size**n } unless block_given?
46
+
45
47
  k = alphabet.size
46
48
  a = [0] * (k * n)
47
49
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/context'
4
5
 
@@ -56,7 +57,7 @@ module Pwnlib
56
57
  # hex(-10) #=> '-0xa'
57
58
  # hex(0xfaceb00cdeadbeef) #=> '0xfaceb00cdeadbeef'
58
59
  def hex(n)
59
- (n < 0 ? '-' : '') + format('0x%x', n.abs)
60
+ (n.negative? ? '-' : '') + format('0x%x', n.abs)
60
61
  end
61
62
 
62
63
  # URL-encodes a string.
@@ -92,7 +93,7 @@ module Pwnlib
92
93
  # urldecode('%qw%er%ty') #=> raise ArgumentError
93
94
  # urldecode('%qw%er%ty', true) #=> '%qw%er%ty'
94
95
  def urldecode(s, ignore_invalid = false)
95
- res = ''
96
+ res = +''
96
97
  n = 0
97
98
  while n < s.size
98
99
  if s[n] != '%'
@@ -139,12 +140,13 @@ module Pwnlib
139
140
  is_little = context.endian == 'little'
140
141
  case s
141
142
  when String
142
- v = 'B*'
143
+ v = +'B*'
143
144
  v.downcase! if is_little
144
145
  s.unpack(v)[0].chars.map { |ch| ch == '1' ? one : zero }
145
146
  when Integer
146
147
  # TODO(Darkpi): What should we do to negative number?
147
148
  raise ArgumentError, 's must be non-negative' unless s >= 0
149
+
148
150
  r = s.to_s(2).chars.map { |ch| ch == '1' ? one : zero }
149
151
  r.unshift(zero) until (r.size % 8).zero?
150
152
  is_little ? r.reverse : r
@@ -265,6 +267,35 @@ module Pwnlib
265
267
  s.unpack('m0')[0]
266
268
  end
267
269
 
270
+ # Xor two strings.
271
+ # If two strings have different length, the shorter one will be repeated until has the same length as another
272
+ # one.
273
+ #
274
+ # @param [String] s1
275
+ # First string.
276
+ # @param [String] s2
277
+ # Second string.
278
+ #
279
+ # @return [String]
280
+ # The xor-ed result.
281
+ #
282
+ # @example
283
+ # xor("\xE8\xE1\xF0\xF0\xF9", "\x80")
284
+ # => 'happy'
285
+ #
286
+ # xor("\x80", "\xE8\xE1\xF0\xF0\xF9")
287
+ # => 'happy'
288
+ #
289
+ # xor('plaintext', 'thekey')
290
+ # => "\x04\x04\x04\x02\v\r\x11\x10\x11"
291
+ #
292
+ # xor('217', "\x00" * 10)
293
+ # => '2172172172'
294
+ def xor(s1, s2)
295
+ s1, s2 = s2, s1 if s1.size < s2.size
296
+ s1.bytes.zip(''.ljust(s1.size, s2).bytes).map { |a, b| a ^ b }.pack('C*')
297
+ end
298
+
268
299
  # Find two strings that will xor into a given string, while only using a given alphabet.
269
300
  #
270
301
  # @param [String, Integer] data
@@ -280,12 +311,13 @@ module Pwnlib
280
311
  def xor_pair(data, avoid: "\x00\n")
281
312
  data = pack(data) if data.is_a?(Integer)
282
313
  alphabet = 256.times.reject { |c| avoid.include?(c.chr) }
283
- res1 = ''
284
- res2 = ''
314
+ res1 = +''
315
+ res2 = +''
285
316
  data.bytes.each do |c1|
286
317
  # alphabet.shuffle! if context.randomize
287
318
  c2 = alphabet.find { |c| alphabet.include?(c1 ^ c) }
288
319
  return nil if c2.nil?
320
+
289
321
  res1 << c2.chr
290
322
  res2 << (c1 ^ c2).chr
291
323
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bindata'
4
5
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rainbow'
4
5
 
@@ -18,7 +19,7 @@ module Pwnlib
18
19
  # hexdump('217')
19
20
  # #=> "00000000 32 31 37 │217│\n00000003"
20
21
  module HexDump
21
- MARKER = "\u2502".freeze
22
+ MARKER = "\u2502"
22
23
  HIGHLIGHT_STYLE = ->(s) { Rainbow(s).bg(:red) }
23
24
  DEFAULT_STYLE = {
24
25
  0x00 => ->(s) { Rainbow(s).red },
@@ -67,7 +68,8 @@ module Pwnlib
67
68
  style = DEFAULT_STYLE.merge(style)
68
69
  highlight.bytes.each { |b| style[b] = HIGHLIGHT_STYLE }
69
70
  (0..255).each do |b|
70
- next if style.include?(b)
71
+ next if style.key?(b)
72
+
71
73
  style[b] = (b.chr =~ /[[:print:]]/ ? style[:printable] : style[:unprintable])
72
74
  end
73
75
 
@@ -83,12 +85,13 @@ module Pwnlib
83
85
 
84
86
  byte_index = offset
85
87
  skipping = false
86
- last_chunk = ''
88
+ last_chunk = +''
87
89
 
88
90
  loop do
89
91
  # We assume that chunk is in ASCII-8BIT encoding.
90
92
  chunk = io.read(width)
91
93
  break unless chunk
94
+
92
95
  chunk_bytes = chunk.bytes
93
96
  start_byte_index = byte_index
94
97
  byte_index += chunk_bytes.size
@@ -102,8 +105,8 @@ module Pwnlib
102
105
  skipping = false
103
106
  last_chunk = chunk
104
107
 
105
- hex_bytes = ''
106
- printable = ''
108
+ hex_bytes = +''
109
+ printable = +''
107
110
  chunk_bytes.each_with_index do |b, i|
108
111
  left_hex, right_char = styled_bytes[b]
109
112
  hex_bytes << left_hex
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/context'
4
5
 
@@ -37,6 +38,7 @@ module Pwnlib
37
38
  unless %i(ignore drop fill).include?(underfull_action)
38
39
  raise ArgumentError, 'underfull_action expect to be one of :ignore, :drop, and :fill'
39
40
  end
41
+
40
42
  sliced = str.chars.each_slice(n).map(&:join)
41
43
  case underfull_action
42
44
  when :drop
@@ -45,6 +47,7 @@ module Pwnlib
45
47
  remain = n - sliced.last.size
46
48
  fill_value = fill_value.to_s
47
49
  raise ArgumentError, 'fill_value must be a character' unless fill_value.size == 1
50
+
48
51
  sliced.last.concat(fill_value * remain)
49
52
  end
50
53
  sliced
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/context'
4
5
 
@@ -71,7 +72,8 @@ module Pwnlib
71
72
  end
72
73
  else
73
74
  if is_all
74
- raise ArgumentError, "Can't pack negative number with bits='all' and signed=false" if number < 0
75
+ raise ArgumentError, "Can't pack negative number with bits='all' and signed=false" if number.negative?
76
+
75
77
  bits = number.zero? ? 8 : ((number.bit_length - 1) | 7) + 1
76
78
  end
77
79
 
@@ -197,6 +199,7 @@ module Pwnlib
197
199
  bytes = bits / 8
198
200
 
199
201
  raise ArgumentError, "data.size=#{data.size} must be a multiple of bytes=#{bytes}" if data.size % bytes != 0
202
+
200
203
  ret = []
201
204
  (data.size / bytes).times do |idx|
202
205
  x1 = idx * bytes
@@ -259,7 +262,7 @@ module Pwnlib
259
262
  v = case it
260
263
  when Array then flat(*it, **kwargs, &preprocessor)
261
264
  when Integer then p[it]
262
- when String then it.force_encoding('ASCII-8BIT')
265
+ when String then it.dup.force_encoding('ASCII-8BIT') # dup in case 'it' is frozen
263
266
  else
264
267
  raise ArgumentError, "flat does not support values of type #{it.class}"
265
268
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Pwnlib
4
5
  module Util
@@ -1,6 +1,7 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Pwnlib
4
5
  # version of pwntools-ruby
5
- VERSION = '1.1.0'.freeze
6
+ VERSION = '1.2.0'
6
7
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'test_helper'
4
5
 
@@ -1,9 +1,11 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'test_helper'
4
5
 
5
6
  require 'pwnlib/asm'
6
7
  require 'pwnlib/shellcraft/shellcraft'
8
+ require 'pwnlib/tubes/process'
7
9
 
8
10
  class AsmTest < MiniTest::Test
9
11
  include ::Pwnlib::Context
@@ -13,98 +15,88 @@ class AsmTest < MiniTest::Test
13
15
  @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
14
16
  end
15
17
 
16
- def skip_windows
17
- skip 'Not test asm/disasm on Windows' if TTY::Platform.new.windows?
18
- end
18
+ def parse_sfile(filename)
19
+ File.read(filename).split("\n\n").each do |it|
20
+ lines = it.lines
21
+ metadata = {}
22
+ # First line of +lines+ might be the extra context setting
23
+ if lines.first.start_with?('# context: ')
24
+ # "# context: arch: a, endian: big"
25
+ # => { arch: 'a', endian: 'big' }
26
+ metadata = lines.shift.slice(11..-1)
27
+ .split(',').map { |c| c.split(':', 2).map(&:strip) }
28
+ .map { |k, v| [k.to_sym, v] }.to_h
29
+ end
30
+ comment, output = lines.partition { |l| l =~ /^\s*[;#]/ }.map(&:join)
31
+ next if output.empty?
32
+
33
+ output << "\n" unless output.end_with?("\n")
34
+ tests = output.lines.map do |l|
35
+ vma, hex_code, _dummy, inst = l.scan(/^\s*(\w+):\s{3}(([\da-f]{2}\s)+)\s+(.*)$/).first
36
+ [vma.to_i(16), hex_code.split.join, inst.strip]
37
+ end
19
38
 
20
- def linux_only
21
- skip 'ELF can only be executed on Linux' unless TTY::Platform.new.linux?
39
+ vma = tests.first.first
40
+ bytes = [tests.map { |l| l[1] }.join].pack('H*')
41
+ insts = tests.map(&:last)
42
+ yield(bytes, vma, insts, output, comment, **metadata)
43
+ end
22
44
  end
23
45
 
24
- def test_i386_asm
25
- skip_windows
26
- context.local(arch: 'i386') do
27
- assert_equal("\x90", Asm.asm('nop'))
28
- assert_equal("\xeb\xfe", Asm.asm(@shellcraft.infloop))
29
- assert_equal("jhh///sh/binj\x0bX\x89\xe31\xc9\x99\xcd\x80", Asm.asm(@shellcraft.sh))
30
- # issue #51
31
- assert_equal("j\x01\xfe\x0c$h\x01\x01\x01\x01\x814$\xf2\xf3\x0b\xfe",
32
- Asm.asm(@shellcraft.pushstr("\xf3\xf2\x0a\xff")))
46
+ # All tests of asm can be found under test/data/assembly/<arch>.s.
47
+ %w[aarch64 amd64 arm i386 mips mips64 powerpc powerpc64 sparc sparc64 thumb].each do |arch|
48
+ file = File.join(__dir__, 'data', 'assembly', arch + '.s')
49
+ # Defining methods dynamically makes proper error message shown when tests failed.
50
+ __send__(:define_method, "test_asm_#{arch}") do
51
+ skip_windows
52
+
53
+ context.local(arch: arch) do
54
+ parse_sfile(file) do |bytes, vma, insts, _output, comment, **ctx|
55
+ next if comment.include?('!skip asm')
56
+
57
+ context.local(**ctx) do
58
+ assert_equal(bytes, Asm.asm(insts.join("\n"), vma: vma))
59
+ end
60
+ end
61
+ end
33
62
  end
34
63
  end
35
64
 
36
- def test_amd64_asm
65
+ def test_asm_unsupported
37
66
  skip_windows
38
- context.local(arch: 'amd64') do
39
- assert_equal("\x90", Asm.asm('nop'))
40
- assert_equal("\xeb\xfe", Asm.asm(@shellcraft.infloop))
41
- assert_equal("jhH\xb8/bin///sPj;XH\x89\xe71\xf6\x99\x0f\x05", Asm.asm(@shellcraft.sh))
42
- assert_equal("j\x01\xfe\x0c$H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8\xfe\xfe\xfe\xfe\xfe\xfe\x0b\xfeH1\x04$",
43
- Asm.asm(@shellcraft.pushstr("\xff\xff\xff\xff\xff\xff\x0a\xff")))
67
+
68
+ err = context.local(arch: :vax) do
69
+ assert_raises(::Pwnlib::Errors::UnsupportedArchError) { Asm.asm('') }
44
70
  end
71
+ assert_equal('Asm on architecture "vax" is not supported yet.', err.message)
45
72
  end
46
73
 
47
- def test_i386_disasm
48
- skip_windows
49
- context.local(arch: 'i386') do
50
- str = Asm.disasm("h\x01\x01\x01\x01\x814$ri\x01\x011\xd2"\
51
- "Rj\x04Z\x01\xe2R\x89\xe2jhh///sh/binj\x0bX\x89\xe3\x89\xd1\x99\xcd\x80")
52
- assert_equal(<<-EOS, str)
53
- 0: 68 01 01 01 01 push 0x1010101
54
- 5: 81 34 24 72 69 01 01 xor dword ptr [esp], 0x1016972
55
- c: 31 d2 xor edx, edx
56
- e: 52 push edx
57
- f: 6a 04 push 4
58
- 11: 5a pop edx
59
- 12: 01 e2 add edx, esp
60
- 14: 52 push edx
61
- 15: 89 e2 mov edx, esp
62
- 17: 6a 68 push 0x68
63
- 19: 68 2f 2f 2f 73 push 0x732f2f2f
64
- 1e: 68 2f 62 69 6e push 0x6e69622f
65
- 23: 6a 0b push 0xb
66
- 25: 58 pop eax
67
- 26: 89 e3 mov ebx, esp
68
- 28: 89 d1 mov ecx, edx
69
- 2a: 99 cdq
70
- 2b: cd 80 int 0x80
71
- EOS
72
- assert_equal(<<-EOS, Asm.disasm("\xb8\x5d\x00\x00\x00"))
73
- 0: b8 5d 00 00 00 mov eax, 0x5d
74
- EOS
74
+ # All tests of disasm can be found under test/data/assembly/<arch>.s.
75
+ %w[aarch64 amd64 arm i386 mips mips64 powerpc64 sparc sparc64 thumb].each do |arch|
76
+ file = File.join(__dir__, 'data', 'assembly', arch + '.s')
77
+ # Defining methods dynamically makes proper error message shown when tests failed.
78
+ __send__(:define_method, "test_disasm_#{arch}") do
79
+ skip_windows
80
+
81
+ context.local(arch: arch) do
82
+ parse_sfile(file) do |bytes, vma, _insts, output, comment, **ctx|
83
+ next if comment.include?('!skip disasm')
84
+
85
+ context.local(**ctx) do
86
+ assert_equal(output, Asm.disasm(bytes, vma: vma))
87
+ end
88
+ end
89
+ end
75
90
  end
76
91
  end
77
92
 
78
- def test_amd64_disasm
93
+ def test_disasm_unsupported
79
94
  skip_windows
80
- context.local(arch: 'amd64') do
81
- str = Asm.disasm("hri\x01\x01\x814$\x01\x01\x01\x011\xd2" \
82
- "Rj\x08ZH\x01\xe2RH\x89\xe2jhH\xb8/bin///sPj;XH\x89\xe7H\x89\xd6\x99\x0f\x05", vma: 0xfff)
83
-
84
- assert_equal(<<-EOS, str)
85
- fff: 68 72 69 01 01 push 0x1016972
86
- 1004: 81 34 24 01 01 01 01 xor dword ptr [rsp], 0x1010101
87
- 100b: 31 d2 xor edx, edx
88
- 100d: 52 push rdx
89
- 100e: 6a 08 push 8
90
- 1010: 5a pop rdx
91
- 1011: 48 01 e2 add rdx, rsp
92
- 1014: 52 push rdx
93
- 1015: 48 89 e2 mov rdx, rsp
94
- 1018: 6a 68 push 0x68
95
- 101a: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f
96
- 1024: 50 push rax
97
- 1025: 6a 3b push 0x3b
98
- 1027: 58 pop rax
99
- 1028: 48 89 e7 mov rdi, rsp
100
- 102b: 48 89 d6 mov rsi, rdx
101
- 102e: 99 cdq
102
- 102f: 0f 05 syscall
103
- EOS
104
- assert_equal(<<-EOS, Asm.disasm("\xb8\x17\x00\x00\x00"))
105
- 0: b8 17 00 00 00 mov eax, 0x17
106
- EOS
95
+
96
+ err = context.local(arch: :vax) do
97
+ assert_raises(::Pwnlib::Errors::UnsupportedArchError) { Asm.disasm('') }
107
98
  end
99
+ assert_equal('Disasm on architecture "vax" is not supported yet.', err.message)
108
100
  end
109
101
 
110
102
  # To ensure coverage
@@ -115,8 +107,8 @@ class AsmTest < MiniTest::Test
115
107
  assert_match(/meow/, err.message)
116
108
  end
117
109
 
118
- def make_elf_file(*args)
119
- elf = Asm.make_elf(*args)
110
+ def make_elf_file(data, **kwargs)
111
+ elf = Asm.make_elf(data, **kwargs)
120
112
  stream = StringIO.new(elf)
121
113
  [elf, ::ELFTools::ELFFile.new(stream)]
122
114
  end
@@ -167,19 +159,17 @@ class AsmTest < MiniTest::Test
167
159
  # this test can be removed after method +run_shellcode+ being implemented
168
160
  def test_make_elf_and_run
169
161
  # run the ELF we created to make sure it works.
170
- linux_only
162
+ linux_only 'ELF can only be executed on Linux'
171
163
 
172
164
  # test supported architecture
173
165
  {
174
- i386: /08048000-08049000 r-xp/,
175
- amd64: /00400000-00401000 r-xp/
166
+ i386: /08048000-08049000 rwxp/,
167
+ amd64: /00400000-00401000 rwxp/
176
168
  }.each do |arch, regexp|
177
169
  context.local(arch: arch) do
178
170
  data = Asm.asm(@shellcraft.cat('/proc/self/maps') + @shellcraft.syscall('SYS_exit', 0))
179
171
  Asm.make_elf(data) do |path|
180
- Open3.popen2(path) do |_i, o, _t|
181
- assert_match(regexp, o.gets)
182
- end
172
+ assert_match(regexp, ::Pwnlib::Tubes::Process.new(path).gets)
183
173
  end
184
174
  end
185
175
  end