pwntools 0.1.0 → 1.0.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -11
  3. data/Rakefile +5 -1
  4. data/lib/pwn.rb +9 -7
  5. data/lib/pwnlib/abi.rb +60 -0
  6. data/lib/pwnlib/asm.rb +146 -0
  7. data/lib/pwnlib/constants/constant.rb +16 -2
  8. data/lib/pwnlib/constants/constants.rb +35 -19
  9. data/lib/pwnlib/constants/linux/amd64.rb +30 -1
  10. data/lib/pwnlib/context.rb +25 -17
  11. data/lib/pwnlib/dynelf.rb +117 -54
  12. data/lib/pwnlib/elf/elf.rb +267 -0
  13. data/lib/pwnlib/ext/helper.rb +4 -4
  14. data/lib/pwnlib/logger.rb +87 -0
  15. data/lib/pwnlib/memleak.rb +58 -29
  16. data/lib/pwnlib/pwn.rb +19 -8
  17. data/lib/pwnlib/reg_sort.rb +102 -108
  18. data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +14 -0
  19. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +17 -0
  20. data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +31 -0
  21. data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +127 -0
  22. data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +16 -0
  23. data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +27 -0
  24. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +64 -0
  25. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +19 -0
  26. data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +32 -0
  27. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +19 -0
  28. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +21 -0
  29. data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +14 -0
  30. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +19 -0
  31. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +19 -0
  32. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +21 -0
  33. data/lib/pwnlib/shellcraft/generators/helper.rb +106 -0
  34. data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +14 -0
  35. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +17 -0
  36. data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +90 -0
  37. data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +16 -0
  38. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +39 -0
  39. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +19 -0
  40. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +19 -0
  41. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +19 -0
  42. data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +14 -0
  43. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +19 -0
  44. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +19 -0
  45. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +19 -0
  46. data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +26 -0
  47. data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +22 -0
  48. data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +15 -0
  49. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +15 -0
  50. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +85 -0
  51. data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +82 -0
  52. data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +69 -0
  53. data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +14 -0
  54. data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +66 -0
  55. data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +52 -0
  56. data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
  57. data/lib/pwnlib/shellcraft/registers.rb +145 -0
  58. data/lib/pwnlib/shellcraft/shellcraft.rb +67 -0
  59. data/lib/pwnlib/timer.rb +60 -0
  60. data/lib/pwnlib/tubes/buffer.rb +96 -0
  61. data/lib/pwnlib/tubes/sock.rb +95 -0
  62. data/lib/pwnlib/tubes/tube.rb +270 -0
  63. data/lib/pwnlib/util/cyclic.rb +95 -94
  64. data/lib/pwnlib/util/fiddling.rb +256 -220
  65. data/lib/pwnlib/util/getdents.rb +83 -0
  66. data/lib/pwnlib/util/hexdump.rb +109 -108
  67. data/lib/pwnlib/util/lists.rb +55 -0
  68. data/lib/pwnlib/util/packing.rb +226 -228
  69. data/lib/pwnlib/util/ruby.rb +18 -0
  70. data/lib/pwnlib/version.rb +2 -1
  71. data/test/abi_test.rb +21 -0
  72. data/test/asm_test.rb +104 -0
  73. data/test/constants/constant_test.rb +1 -0
  74. data/test/constants/constants_test.rb +4 -2
  75. data/test/context_test.rb +1 -0
  76. data/test/data/echo.rb +20 -0
  77. data/test/data/elfs/Makefile +22 -0
  78. data/test/data/elfs/amd64.frelro.elf +0 -0
  79. data/test/data/elfs/amd64.frelro.pie.elf +0 -0
  80. data/test/data/elfs/amd64.nrelro.elf +0 -0
  81. data/test/data/elfs/amd64.prelro.elf +0 -0
  82. data/test/data/elfs/i386.frelro.pie.elf +0 -0
  83. data/test/data/elfs/i386.prelro.elf +0 -0
  84. data/test/data/elfs/source.cpp +19 -0
  85. data/test/data/flag +1 -0
  86. data/test/data/lib32/ld.so.2 +0 -0
  87. data/test/data/lib32/libc.so.6 +0 -0
  88. data/test/data/lib64/ld.so.2 +0 -0
  89. data/test/data/lib64/libc.so.6 +0 -0
  90. data/test/dynelf_test.rb +59 -24
  91. data/test/elf/elf_test.rb +120 -0
  92. data/test/ext_test.rb +3 -2
  93. data/test/files/use_pwnlib.rb +1 -1
  94. data/test/logger_test.rb +61 -0
  95. data/test/memleak_test.rb +4 -33
  96. data/test/reg_sort_test.rb +3 -1
  97. data/test/shellcraft/infloop_test.rb +26 -0
  98. data/test/shellcraft/linux/ls_test.rb +108 -0
  99. data/test/shellcraft/linux/sh_test.rb +119 -0
  100. data/test/shellcraft/linux/syscalls/execve_test.rb +136 -0
  101. data/test/shellcraft/linux/syscalls/syscall_test.rb +83 -0
  102. data/test/shellcraft/memcpy_test.rb +35 -0
  103. data/test/shellcraft/mov_test.rb +98 -0
  104. data/test/shellcraft/nop_test.rb +26 -0
  105. data/test/shellcraft/popad_test.rb +29 -0
  106. data/test/shellcraft/pushstr_array_test.rb +91 -0
  107. data/test/shellcraft/pushstr_test.rb +108 -0
  108. data/test/shellcraft/registers_test.rb +32 -0
  109. data/test/shellcraft/ret_test.rb +30 -0
  110. data/test/shellcraft/setregs_test.rb +62 -0
  111. data/test/shellcraft/shellcraft_test.rb +28 -0
  112. data/test/test_helper.rb +12 -1
  113. data/test/timer_test.rb +23 -0
  114. data/test/tubes/buffer_test.rb +45 -0
  115. data/test/tubes/sock_test.rb +68 -0
  116. data/test/tubes/tube_test.rb +241 -0
  117. data/test/util/cyclic_test.rb +2 -1
  118. data/test/util/fiddling_test.rb +2 -1
  119. data/test/util/getdents_test.rb +32 -0
  120. data/test/util/hexdump_test.rb +7 -9
  121. data/test/util/lists_test.rb +21 -0
  122. data/test/util/packing_test.rb +4 -3
  123. metadata +215 -25
@@ -0,0 +1,32 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/shellcraft/registers'
6
+
7
+ class RegistersTest < MiniTest::Test
8
+ include ::Pwnlib::Shellcraft::Registers
9
+
10
+ def test_get_register
11
+ assert_instance_of(::Pwnlib::Shellcraft::Registers::Register, get_register('rdi'))
12
+ assert_nil(get_register('meow'))
13
+ end
14
+
15
+ def test_regtsiter?
16
+ assert_equal(true, register?(:ax))
17
+ assert_equal(true, register?('ax'))
18
+ assert_equal(true, register?('r8'))
19
+ assert_equal(true, register?('r15b'))
20
+ assert_equal(true, register?('r15w'))
21
+ assert_equal(false, register?('xdd'))
22
+ assert_equal(false, register?(''))
23
+ end
24
+
25
+ def test_register
26
+ assert_equal(8, get_register('rdi').bytes)
27
+ assert_equal(16, get_register('ax').bits)
28
+ assert_equal('r10d', get_register('r10d').name)
29
+ assert_equal('r10d', get_register('r10d').to_s)
30
+ assert_equal('Register(r10d)', get_register('r10d').inspect)
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/context'
6
+ require 'pwnlib/shellcraft/shellcraft'
7
+
8
+ class RetTest < MiniTest::Test
9
+ include ::Pwnlib::Context
10
+
11
+ def setup
12
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
13
+ end
14
+
15
+ def test_amd64
16
+ context.local(arch: 'amd64') do
17
+ assert_equal(" ret\n", @shellcraft.ret)
18
+ assert_equal(" mov rax, rdi\n ret\n", @shellcraft.ret(:rdi))
19
+ assert_equal(" xor eax, eax /* 0 */\n ret\n", @shellcraft.ret(0))
20
+ assert_equal(<<-'EOS', @shellcraft.ret(0x100000000))
21
+ mov rax, 0x101010201010101
22
+ push rax
23
+ mov rax, 0x101010301010101
24
+ xor [rsp], rax /* 0x100000000 == 0x101010201010101 ^ 0x101010301010101 */
25
+ pop rax
26
+ ret
27
+ EOS
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/context'
6
+ require 'pwnlib/shellcraft/shellcraft'
7
+
8
+ class SetregsTest < MiniTest::Test
9
+ include ::Pwnlib::Context
10
+
11
+ def setup
12
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
13
+ end
14
+
15
+ def test_amd64
16
+ context.local(arch: 'amd64') do
17
+ assert_equal(<<-'EOS', @shellcraft.setregs(rax: 1, rbx: 'rax'))
18
+ mov rbx, rax
19
+ push 1
20
+ pop rax
21
+ EOS
22
+ assert_equal(<<-'EOS', @shellcraft.setregs(rax: 'SYS_write', rbx: 'rax'))
23
+ mov rbx, rax
24
+ push 1 /* (SYS_write) */
25
+ pop rax
26
+ EOS
27
+ assert_equal(<<-'EOS', @shellcraft.setregs(rax: 'rbx', rbx: 'rax', rcx: 'rbx'))
28
+ mov rcx, rbx
29
+ xchg rax, rbx
30
+ EOS
31
+ assert_equal(<<-'EOS', @shellcraft.setregs(rax: 1, rdx: 0))
32
+ push 1
33
+ pop rax
34
+ cdq /* rdx=0 */
35
+ EOS
36
+ # issue #50
37
+ assert_equal(<<-'EOS', @shellcraft.setregs(rdi: :rax, rsi: :rdi))
38
+ mov rsi, rdi
39
+ mov rdi, rax
40
+ EOS
41
+ end
42
+ end
43
+
44
+ def test_i386
45
+ context.local(arch: 'i386') do
46
+ assert_equal(<<-EOS, @shellcraft.setregs(eax: 1, ebx: 'eax'))
47
+ mov ebx, eax
48
+ push 1
49
+ pop eax
50
+ EOS
51
+ assert_equal(<<-EOS, @shellcraft.setregs(eax: 'ebx', ebx: 'eax', ecx: 'ebx'))
52
+ mov ecx, ebx
53
+ xchg eax, ebx
54
+ EOS
55
+ assert_equal(<<-'EOS', @shellcraft.setregs(eax: 1, edx: 0))
56
+ push 1
57
+ pop eax
58
+ cdq /* edx=0 */
59
+ EOS
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/context'
6
+ require 'pwnlib/shellcraft/shellcraft'
7
+
8
+ class ShellcraftTest < MiniTest::Test
9
+ include ::Pwnlib::Context
10
+
11
+ def setup
12
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
13
+ end
14
+
15
+ def test_respond
16
+ context.local(arch: 'amd64') do
17
+ # Check respond_to_missing? is well defined
18
+ assert @shellcraft.respond_to?(:mov)
19
+ assert @shellcraft.method(:sh)
20
+ end
21
+ refute @shellcraft.respond_to?(:linux)
22
+ assert_raises(NoMethodError) { @shellcraft.meow }
23
+
24
+ context.local(arch: 'arm') do
25
+ refute @shellcraft.respond_to?(:mov)
26
+ end
27
+ end
28
+ end
@@ -1,6 +1,7 @@
1
1
  require 'codeclimate-test-reporter'
2
-
2
+ require 'rainbow'
3
3
  require 'simplecov'
4
+
4
5
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
5
6
  [SimpleCov::Formatter::HTMLFormatter, CodeClimate::TestReporter::Formatter]
6
7
  )
@@ -11,3 +12,13 @@ end
11
12
  require 'minitest/autorun'
12
13
  require 'minitest/unit'
13
14
  require 'minitest/hell'
15
+
16
+ module MiniTest
17
+ class Test
18
+ def before_setup
19
+ super
20
+ # Default to disable coloring for easier testing.
21
+ Rainbow.enabled = false
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/timer'
6
+
7
+ class TimerTest < MiniTest::Test
8
+ include ::Pwnlib
9
+
10
+ def test_countdown
11
+ t = Timer.new
12
+ refute(t.started?)
13
+ refute(t.active?)
14
+ assert_equal('DARKHH QQ', t.countdown(0.1) { 'DARKHH QQ' })
15
+ exception = assert_raises(RuntimeError) { t.countdown(0.1) { t.countdown(0.1) {} } }
16
+ assert_equal('Nested countdown not permitted', exception.message)
17
+ t.timeout = 0.514
18
+ exception = assert_raises(RuntimeError) { t.countdown(0.1) { t.timeout = :forever } }
19
+ assert_equal("Can't change timeout when countdown", exception.message)
20
+ t.countdown(0.1) { assert(t.started?) }
21
+ t.countdown(0.1) { assert(t.active?) }
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/tubes/buffer'
6
+
7
+ class BufferTest < MiniTest::Test
8
+ def test_add
9
+ b = ::Pwnlib::Tubes::Buffer.new
10
+ b.add('A' * 10)
11
+ b.add('B' * 10)
12
+ assert_equal(20, b.size)
13
+ assert_equal('A', b.get(1))
14
+ assert_equal(19, b.size)
15
+ assert_equal(false, b.empty?)
16
+ assert_equal('AAAAAAAAABBBBBBBBBB', b.get(9999))
17
+ assert_equal(0, b.size)
18
+ assert_equal(true, b.empty?)
19
+ assert_equal('', b.get(1))
20
+ end
21
+
22
+ def test_unget
23
+ b = ::Pwnlib::Tubes::Buffer.new
24
+ b.add('hello')
25
+ b.add('world')
26
+ assert_equal('hello', b.get(5))
27
+ b.unget('goodbye')
28
+ assert_equal('goodbyeworld', b.get)
29
+ end
30
+
31
+ def test_buffer
32
+ b = ::Pwnlib::Tubes::Buffer.new
33
+ b2 = ::Pwnlib::Tubes::Buffer.new
34
+ b.add('hello')
35
+ b2.add('world')
36
+ b.add(b2)
37
+ assert_equal('hello', b.get(5))
38
+ assert_equal(false, b2.empty?)
39
+ assert_equal('world', b2.get)
40
+ b2.add('goodbye')
41
+ b.unget(b2)
42
+ assert_equal('goodbyeworld', b.get)
43
+ assert_equal('goodbye', b2.get)
44
+ end
45
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'open3'
4
+
5
+ require 'test_helper'
6
+
7
+ require 'pwnlib/tubes/sock'
8
+
9
+ class SockTest < MiniTest::Test
10
+ include ::Pwnlib::Tubes
11
+ ECHO_FILE = File.expand_path('../data/echo.rb', __dir__)
12
+ BIND_PORT = 31_337
13
+
14
+ def popen_echo(data)
15
+ Open3.popen2("bundle exec ruby #{ECHO_FILE} #{BIND_PORT}") do |_i, o, _t|
16
+ o.gets
17
+ s = Sock.new('localhost', BIND_PORT)
18
+ yield s, data, o
19
+ end
20
+ end
21
+
22
+ def test_io
23
+ popen_echo('DARKHH') do |s, data, _o|
24
+ s.io.puts(data)
25
+ rs, = IO.select([s.io])
26
+ refute_nil(rs)
27
+ assert_equal(data, s.io.readpartial(data.size))
28
+ end
29
+ end
30
+
31
+ def test_sock
32
+ popen_echo('DARKHH') do |s, data, o|
33
+ s.puts(data)
34
+ assert_equal(data + "\n", s.gets)
35
+ o.gets
36
+ s.puts(514)
37
+ sleep(0.1) # Wait for connection reset
38
+ assert_raises(EOFError) { s.puts(514) }
39
+ assert_raises(EOFError) { s.puts(514) }
40
+ assert_raises(EOFError) { s.recv }
41
+ assert_raises(EOFError) { s.recv }
42
+ end
43
+ end
44
+
45
+ def test_close
46
+ popen_echo('DARKHH') do |s, _data, _o|
47
+ s.close
48
+ assert_raises(EOFError) { s.puts(514) }
49
+ assert_raises(EOFError) { s.puts(514) }
50
+ assert_raises(EOFError) { s.recv }
51
+ assert_raises(EOFError) { s.recv }
52
+ assert_raises(ArgumentError) { s.close(:hh) }
53
+ end
54
+
55
+ popen_echo('DARKHH') do |s, _data, _o|
56
+ 3.times { s.close(:read) }
57
+ 3.times { s.close(:recv) }
58
+ 3.times { s.close(:send) }
59
+ 3.times { s.close(:write) }
60
+ assert_raises(EOFError) { s.puts(514) }
61
+ assert_raises(EOFError) { s.puts(514) }
62
+ assert_raises(EOFError) { s.recv }
63
+ assert_raises(EOFError) { s.recv }
64
+ 3.times { s.close }
65
+ assert_raises(ArgumentError) { s.close(:shik) }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,241 @@
1
+ # encoding: UTF-8
2
+
3
+ # This test use UTF-8 encoding for strings since the output for hexdump contains lots of UTF-8 characters.
4
+
5
+ require 'test_helper'
6
+
7
+ require 'pwnlib/context'
8
+ require 'pwnlib/logger'
9
+ require 'pwnlib/tubes/tube'
10
+
11
+ class TubeTest < MiniTest::Test
12
+ include ::Pwnlib::Context
13
+ include ::Pwnlib::Tubes
14
+
15
+ def setup
16
+ @old_log = ::Pwnlib::Logger.log.dup
17
+ @log = ::Pwnlib::Logger::LoggerType.new
18
+
19
+ class << @log
20
+ def clear
21
+ @logdev = StringIO.new
22
+ end
23
+
24
+ def string
25
+ @logdev.string
26
+ end
27
+ end
28
+
29
+ ::Pwnlib::Logger.instance_variable_set(:@log, @log)
30
+ end
31
+
32
+ def hello_tube
33
+ t = Tube.new
34
+
35
+ class << t
36
+ def buf
37
+ @buf ||= ''
38
+ end
39
+
40
+ private
41
+
42
+ def recv_raw(_n)
43
+ 'Hello, world'
44
+ end
45
+
46
+ def timeout_raw=(timeout)
47
+ @timeout = timeout == :forever ? nil : timeout
48
+ end
49
+
50
+ def send_raw(data)
51
+ buf << data
52
+ end
53
+ end
54
+
55
+ t
56
+ end
57
+
58
+ def basic_tube
59
+ t = Tube.new
60
+
61
+ class << t
62
+ def recv_raw(_n)
63
+ raise EOFError if io.eof?
64
+ io.read
65
+ end
66
+
67
+ def send_raw(data)
68
+ io.write(data)
69
+ end
70
+
71
+ def timeout_raw=(timeout)
72
+ @timeout = timeout == :forever ? nil : timeout
73
+ end
74
+
75
+ def io
76
+ @fakeio ||= Tempfile.new('pwntools_ruby_test')
77
+ end
78
+ end
79
+
80
+ t
81
+ end
82
+
83
+ def test_recv
84
+ t = hello_tube
85
+ assert_equal('Hello, world', t.recv)
86
+ assert_equal('Hello, world', t.recv)
87
+ t.unrecv('Woohoo')
88
+ assert_equal('Woohoo', t.recv)
89
+ assert_equal('Hello, world', t.recv)
90
+ assert_equal('H', t.recvn(1))
91
+ assert_equal('ello, w', t.recvn(7))
92
+ assert_equal('orldH', t.recvn(5))
93
+ assert_equal('ello, world', t.recv)
94
+
95
+ context.local(log_level: 'debug') do
96
+ @log.clear
97
+ t = hello_tube
98
+ assert_equal('Hello, world', t.recv)
99
+ assert_equal(<<-'EOS', @log.string.encode('UTF-8'))
100
+ [DEBUG] Received 0xc bytes:
101
+ 00000000 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 │Hell│o, w│orld│
102
+ 0000000c
103
+ EOS
104
+ end
105
+ end
106
+
107
+ def test_recvuntil
108
+ t = hello_tube
109
+ assert_equal('Hello, ', t.recvuntil(' '))
110
+ assert_equal('worldHello, ', t.recvuntil(' '))
111
+ t.unrecv('Hello, world!')
112
+ assert_equal('Hello,', t.recvuntil(' wor', drop: true))
113
+ assert_equal('', t.recvuntil('DARKHH', drop: true, timeout: 0.1))
114
+
115
+ t = basic_tube
116
+ t.unrecv('meow')
117
+ assert_equal('', t.recvuntil('DARKHH'))
118
+ assert_equal('meow', t.recv)
119
+ end
120
+
121
+ def test_recvline
122
+ t = Tube.new
123
+ t.unrecv("Foo\nBar\r\nBaz\n")
124
+ assert_equal("Foo\n", t.recvline)
125
+ assert_equal("Bar\r\n", t.recvline)
126
+ assert_equal('Baz', t.recvline(drop: true))
127
+ context.local(newline: "\r\n") do
128
+ t = Tube.new
129
+ t.unrecv("Foo\nBar\r\nBaz\n")
130
+ assert_equal("Foo\nBar", t.recvline(drop: true))
131
+ end
132
+
133
+ t = basic_tube
134
+ t.unrecv('Hello, world')
135
+ assert_equal('', t.recvline)
136
+ assert_equal('Hello, world', t.recv)
137
+ end
138
+
139
+ def test_recvpred
140
+ t = hello_tube
141
+ r = /H.*w/
142
+ 10.times { assert_match(r, t.recvpred { |data| data =~ r }) }
143
+ r = /H.*W/
144
+ assert_match('', t.recvpred(timeout: 0.01) { |data| data =~ r })
145
+ t = basic_tube
146
+ t.unrecv('darkhh')
147
+ assert_match('', t.recvpred { |data| data =~ r })
148
+ end
149
+
150
+ def test_recvregex
151
+ t = hello_tube
152
+ r = /[aeiou]/
153
+ 5.times { assert_match(r, t.recvregex(r)) }
154
+ r = /[wl][aeiou]/
155
+ 5.times { assert_match(r, t.recvregex(r)) }
156
+ end
157
+
158
+ def test_send
159
+ t = hello_tube
160
+ t.write('DARKHH')
161
+ assert_equal('DARKHH', t.buf)
162
+ t.write(' QQ')
163
+ assert_equal('DARKHH QQ', t.buf)
164
+ t.write(333)
165
+ assert_equal('DARKHH QQ333', t.buf)
166
+
167
+ context.local(log_level: 'debug') do
168
+ @log.clear
169
+ data = (0..40).map(&:chr).join
170
+ t = hello_tube
171
+ t.write(data)
172
+ assert_equal(data, t.buf)
173
+ assert_equal(<<-'EOS', @log.string.encode('UTF-8'))
174
+ [DEBUG] Sent 0x29 bytes:
175
+ 00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f │····│····│····│····│
176
+ 00000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f │····│····│····│····│
177
+ 00000020 20 21 22 23 24 25 26 27 28 │ !"#│$%&'│(│
178
+ 00000029
179
+ EOS
180
+
181
+ @log.clear
182
+ t.puts('meow')
183
+ assert_equal(<<-'EOS', @log.string.encode('UTF-8'))
184
+ [DEBUG] Sent 0x5 bytes:
185
+ 00000000 6d 65 6f 77 0a │meow│·│
186
+ 00000005
187
+ EOS
188
+ end
189
+ end
190
+
191
+ def test_sendline
192
+ t = hello_tube
193
+ t.write('DARKHH')
194
+ assert_equal('DARKHH', t.buf)
195
+ t.puts(' QQ')
196
+ assert_equal("DARKHH QQ\n", t.buf)
197
+ end
198
+
199
+ FLAG_FILE = File.expand_path('../data/flag', __dir__)
200
+ def test_interact_send
201
+ save_stdin = $stdin.dup
202
+ $stdin = File.new(FLAG_FILE, File::RDONLY)
203
+ @log.clear
204
+ begin
205
+ t = basic_tube
206
+ t.interact
207
+ rescue EOFError
208
+ t.io.rewind
209
+ assert_equal(IO.binread(FLAG_FILE), t.io.read)
210
+ end
211
+ assert_equal("[INFO] Switching to interactive mode\n", @log.string)
212
+ $stdin.close
213
+ t.io.close
214
+ $stdin = save_stdin
215
+ end
216
+
217
+ def test_interact_recv
218
+ save_stdin = $stdin.dup
219
+ save_stdout = $stdout.dup
220
+ $stdin = UDPSocket.new
221
+ $stdout = Tempfile.new('pwntools_ruby_test')
222
+ @log.clear
223
+ begin
224
+ t = basic_tube
225
+ t.instance_variable_set(:@fakeio, File.new(FLAG_FILE, File::RDONLY))
226
+ t.interact
227
+ rescue EOFError
228
+ $stdout.rewind
229
+ assert_equal(IO.binread(FLAG_FILE), $stdout.read)
230
+ end
231
+ assert_equal("[INFO] Switching to interactive mode\n", @log.string)
232
+ $stdout.close
233
+ t.io.close
234
+ $stdin = save_stdin
235
+ $stdout = save_stdout
236
+ end
237
+
238
+ def teardown
239
+ ::Pwnlib::Logger.instance_variable_set(:@log, @old_log)
240
+ end
241
+ end