pwntools 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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