pwntools 0.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +96 -15
  3. data/Rakefile +8 -2
  4. data/lib/pwn.rb +10 -7
  5. data/lib/pwnlib/abi.rb +61 -0
  6. data/lib/pwnlib/asm.rb +357 -0
  7. data/lib/pwnlib/constants/constant.rb +19 -3
  8. data/lib/pwnlib/constants/constants.rb +46 -20
  9. data/lib/pwnlib/constants/linux/amd64.rb +32 -1
  10. data/lib/pwnlib/constants/linux/i386.rb +2 -0
  11. data/lib/pwnlib/context.rb +128 -27
  12. data/lib/pwnlib/dynelf.rb +122 -54
  13. data/lib/pwnlib/elf/elf.rb +340 -0
  14. data/lib/pwnlib/errors.rb +31 -0
  15. data/lib/pwnlib/ext/array.rb +2 -1
  16. data/lib/pwnlib/ext/helper.rb +6 -5
  17. data/lib/pwnlib/ext/integer.rb +2 -1
  18. data/lib/pwnlib/ext/string.rb +3 -2
  19. data/lib/pwnlib/logger.rb +245 -0
  20. data/lib/pwnlib/memleak.rb +59 -29
  21. data/lib/pwnlib/pwn.rb +27 -9
  22. data/lib/pwnlib/reg_sort.rb +109 -110
  23. data/lib/pwnlib/runner.rb +53 -0
  24. data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +16 -0
  25. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +24 -0
  26. data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +35 -0
  27. data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +131 -0
  28. data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +18 -0
  29. data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +28 -0
  30. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +66 -0
  31. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +24 -0
  32. data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +33 -0
  33. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +24 -0
  34. data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +24 -0
  35. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +24 -0
  36. data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +24 -0
  37. data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +16 -0
  38. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +24 -0
  39. data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +24 -0
  40. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +24 -0
  41. data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
  42. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +24 -0
  43. data/lib/pwnlib/shellcraft/generators/helper.rb +115 -0
  44. data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +16 -0
  45. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +24 -0
  46. data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
  47. data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +93 -0
  48. data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +18 -0
  49. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +41 -0
  50. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +24 -0
  51. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +24 -0
  52. data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +24 -0
  53. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +24 -0
  54. data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +24 -0
  55. data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +16 -0
  56. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +24 -0
  57. data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +24 -0
  58. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +24 -0
  59. data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
  60. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +24 -0
  61. data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +29 -0
  62. data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +24 -0
  63. data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
  64. data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +17 -0
  65. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +17 -0
  66. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +86 -0
  67. data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +84 -0
  68. data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +54 -0
  69. data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +72 -0
  70. data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +34 -0
  71. data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +16 -0
  72. data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +67 -0
  73. data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +47 -0
  74. data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +53 -0
  75. data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
  76. data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
  77. data/lib/pwnlib/shellcraft/registers.rb +148 -0
  78. data/lib/pwnlib/shellcraft/shellcraft.rb +73 -0
  79. data/lib/pwnlib/timer.rb +67 -0
  80. data/lib/pwnlib/tubes/buffer.rb +99 -0
  81. data/lib/pwnlib/tubes/process.rb +155 -0
  82. data/lib/pwnlib/tubes/serialtube.rb +114 -0
  83. data/lib/pwnlib/tubes/sock.rb +101 -0
  84. data/lib/pwnlib/tubes/tube.rb +442 -0
  85. data/lib/pwnlib/ui.rb +21 -0
  86. data/lib/pwnlib/util/cyclic.rb +97 -94
  87. data/lib/pwnlib/util/fiddling.rb +288 -220
  88. data/lib/pwnlib/util/getdents.rb +85 -0
  89. data/lib/pwnlib/util/hexdump.rb +116 -112
  90. data/lib/pwnlib/util/lists.rb +58 -0
  91. data/lib/pwnlib/util/packing.rb +223 -228
  92. data/lib/pwnlib/util/ruby.rb +19 -0
  93. data/lib/pwnlib/version.rb +3 -1
  94. data/test/abi_test.rb +22 -0
  95. data/test/asm_test.rb +177 -0
  96. data/test/constants/constant_test.rb +2 -0
  97. data/test/constants/constants_test.rb +5 -2
  98. data/test/context_test.rb +14 -3
  99. data/test/data/assembly/aarch64.s +19 -0
  100. data/test/data/assembly/amd64.s +21 -0
  101. data/test/data/assembly/arm.s +9 -0
  102. data/test/data/assembly/i386.s +21 -0
  103. data/test/data/assembly/mips.s +16 -0
  104. data/test/data/assembly/mips64.s +6 -0
  105. data/test/data/assembly/powerpc.s +18 -0
  106. data/test/data/assembly/powerpc64.s +36 -0
  107. data/test/data/assembly/sparc.s +33 -0
  108. data/test/data/assembly/sparc64.s +5 -0
  109. data/test/data/assembly/thumb.s +37 -0
  110. data/test/data/echo.rb +16 -0
  111. data/test/data/elfs/Makefile +24 -0
  112. data/test/data/elfs/amd64.frelro.elf +0 -0
  113. data/test/data/elfs/amd64.frelro.pie.elf +0 -0
  114. data/test/data/elfs/amd64.nrelro.elf +0 -0
  115. data/test/data/elfs/amd64.prelro.elf +0 -0
  116. data/test/data/elfs/amd64.static.elf +0 -0
  117. data/test/data/elfs/i386.frelro.pie.elf +0 -0
  118. data/test/data/elfs/i386.prelro.elf +0 -0
  119. data/test/data/elfs/source.cpp +19 -0
  120. data/test/data/flag +1 -0
  121. data/test/data/lib32/ld.so.2 +0 -0
  122. data/test/data/lib32/libc.so.6 +0 -0
  123. data/test/data/lib64/ld.so.2 +0 -0
  124. data/test/data/lib64/libc.so.6 +0 -0
  125. data/test/dynelf_test.rb +62 -25
  126. data/test/elf/elf_test.rb +147 -0
  127. data/test/ext_test.rb +4 -2
  128. data/test/files/use_pwn.rb +3 -6
  129. data/test/files/use_pwnlib.rb +2 -1
  130. data/test/full_file_test.rb +6 -0
  131. data/test/logger_test.rb +120 -0
  132. data/test/memleak_test.rb +5 -33
  133. data/test/reg_sort_test.rb +4 -1
  134. data/test/runner_test.rb +32 -0
  135. data/test/shellcraft/infloop_test.rb +27 -0
  136. data/test/shellcraft/linux/cat_test.rb +87 -0
  137. data/test/shellcraft/linux/ls_test.rb +109 -0
  138. data/test/shellcraft/linux/sh_test.rb +120 -0
  139. data/test/shellcraft/linux/sleep_test.rb +68 -0
  140. data/test/shellcraft/linux/syscalls/execve_test.rb +137 -0
  141. data/test/shellcraft/linux/syscalls/exit_test.rb +57 -0
  142. data/test/shellcraft/linux/syscalls/open_test.rb +87 -0
  143. data/test/shellcraft/linux/syscalls/syscall_test.rb +84 -0
  144. data/test/shellcraft/memcpy_test.rb +50 -0
  145. data/test/shellcraft/mov_test.rb +99 -0
  146. data/test/shellcraft/nop_test.rb +27 -0
  147. data/test/shellcraft/popad_test.rb +30 -0
  148. data/test/shellcraft/pushstr_array_test.rb +92 -0
  149. data/test/shellcraft/pushstr_test.rb +109 -0
  150. data/test/shellcraft/registers_test.rb +33 -0
  151. data/test/shellcraft/ret_test.rb +31 -0
  152. data/test/shellcraft/setregs_test.rb +63 -0
  153. data/test/shellcraft/shellcraft_test.rb +30 -0
  154. data/test/test_helper.rb +61 -2
  155. data/test/timer_test.rb +42 -0
  156. data/test/tubes/buffer_test.rb +46 -0
  157. data/test/tubes/process_test.rb +105 -0
  158. data/test/tubes/serialtube_test.rb +162 -0
  159. data/test/tubes/sock_test.rb +68 -0
  160. data/test/tubes/tube_test.rb +320 -0
  161. data/test/ui_test.rb +18 -0
  162. data/test/util/cyclic_test.rb +3 -1
  163. data/test/util/fiddling_test.rb +12 -3
  164. data/test/util/getdents_test.rb +33 -0
  165. data/test/util/hexdump_test.rb +9 -10
  166. data/test/util/lists_test.rb +22 -0
  167. data/test/util/packing_test.rb +5 -3
  168. metadata +357 -37
@@ -0,0 +1,33 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/shellcraft/registers'
7
+
8
+ class RegistersTest < MiniTest::Test
9
+ include ::Pwnlib::Shellcraft::Registers
10
+
11
+ def test_get_register
12
+ assert_instance_of(::Pwnlib::Shellcraft::Registers::Register, get_register('rdi'))
13
+ assert_nil(get_register('meow'))
14
+ end
15
+
16
+ def test_regtsiter?
17
+ assert_equal(true, register?(:ax))
18
+ assert_equal(true, register?('ax'))
19
+ assert_equal(true, register?('r8'))
20
+ assert_equal(true, register?('r15b'))
21
+ assert_equal(true, register?('r15w'))
22
+ assert_equal(false, register?('xdd'))
23
+ assert_equal(false, register?(''))
24
+ end
25
+
26
+ def test_register
27
+ assert_equal(8, get_register('rdi').bytes)
28
+ assert_equal(16, get_register('ax').bits)
29
+ assert_equal('r10d', get_register('r10d').name)
30
+ assert_equal('r10d', get_register('r10d').to_s)
31
+ assert_equal('Register(r10d)', get_register('r10d').inspect)
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/context'
7
+ require 'pwnlib/shellcraft/shellcraft'
8
+
9
+ class RetTest < MiniTest::Test
10
+ include ::Pwnlib::Context
11
+
12
+ def setup
13
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
14
+ end
15
+
16
+ def test_amd64
17
+ context.local(arch: 'amd64') do
18
+ assert_equal(" ret\n", @shellcraft.ret)
19
+ assert_equal(" mov rax, rdi\n ret\n", @shellcraft.ret(:rdi))
20
+ assert_equal(" xor eax, eax /* 0 */\n ret\n", @shellcraft.ret(0))
21
+ assert_equal(<<-'EOS', @shellcraft.ret(0x100000000))
22
+ mov rax, 0x101010201010101
23
+ push rax
24
+ mov rax, 0x101010301010101
25
+ xor [rsp], rax /* 0x100000000 == 0x101010201010101 ^ 0x101010301010101 */
26
+ pop rax
27
+ ret
28
+ EOS
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/context'
7
+ require 'pwnlib/shellcraft/shellcraft'
8
+
9
+ class SetregsTest < MiniTest::Test
10
+ include ::Pwnlib::Context
11
+
12
+ def setup
13
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
14
+ end
15
+
16
+ def test_amd64
17
+ context.local(arch: 'amd64') do
18
+ assert_equal(<<-'EOS', @shellcraft.setregs({ rax: 1, rbx: 'rax' }))
19
+ mov rbx, rax
20
+ push 1
21
+ pop rax
22
+ EOS
23
+ assert_equal(<<-'EOS', @shellcraft.setregs({ rax: 'SYS_write', rbx: 'rax' }))
24
+ mov rbx, rax
25
+ push 1 /* (SYS_write) */
26
+ pop rax
27
+ EOS
28
+ assert_equal(<<-'EOS', @shellcraft.setregs({ rax: 'rbx', rbx: 'rax', rcx: 'rbx' }))
29
+ mov rcx, rbx
30
+ xchg rax, rbx
31
+ EOS
32
+ assert_equal(<<-'EOS', @shellcraft.setregs({ rax: 1, rdx: 0 }))
33
+ push 1
34
+ pop rax
35
+ cdq /* rdx=0 */
36
+ EOS
37
+ # issue #50
38
+ assert_equal(<<-'EOS', @shellcraft.setregs({ rdi: :rax, rsi: :rdi }))
39
+ mov rsi, rdi
40
+ mov rdi, rax
41
+ EOS
42
+ end
43
+ end
44
+
45
+ def test_i386
46
+ context.local(arch: 'i386') do
47
+ assert_equal(<<-EOS, @shellcraft.setregs({ eax: 1, ebx: 'eax' }))
48
+ mov ebx, eax
49
+ push 1
50
+ pop eax
51
+ EOS
52
+ assert_equal(<<-EOS, @shellcraft.setregs({ eax: 'ebx', ebx: 'eax', ecx: 'ebx' }))
53
+ mov ecx, ebx
54
+ xchg eax, ebx
55
+ EOS
56
+ assert_equal(<<-'EOS', @shellcraft.setregs({ eax: 1, edx: 0 }))
57
+ push 1
58
+ pop eax
59
+ cdq /* edx=0 */
60
+ EOS
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/context'
7
+ require 'pwnlib/shellcraft/shellcraft'
8
+
9
+ class ShellcraftTest < MiniTest::Test
10
+ include ::Pwnlib::Context
11
+
12
+ def setup
13
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
14
+ end
15
+
16
+ def test_respond
17
+ context.local(arch: 'amd64') do
18
+ # Check respond_to_missing? is well defined
19
+ assert(@shellcraft.respond_to?(:mov))
20
+ assert(@shellcraft.method(:sh))
21
+ end
22
+ refute(@shellcraft.respond_to?(:linux))
23
+ assert_raises(NoMethodError) { @shellcraft.meow }
24
+
25
+ context.local(arch: 'arm') do
26
+ err = assert_raises(::Pwnlib::Errors::UnsupportedArchError) { @shellcraft.respond_to?(:mov) }
27
+ assert_equal("Can't use shellcraft under architecture \"arm\".", err.message)
28
+ end
29
+ end
30
+ end
data/test/test_helper.rb CHANGED
@@ -1,8 +1,11 @@
1
- require 'codeclimate-test-reporter'
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'rainbow'
3
4
  require 'simplecov'
5
+ require 'tty/platform'
6
+
4
7
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
5
- [SimpleCov::Formatter::HTMLFormatter, CodeClimate::TestReporter::Formatter]
8
+ [SimpleCov::Formatter::HTMLFormatter]
6
9
  )
7
10
  SimpleCov.start do
8
11
  add_filter '/test/'
@@ -11,3 +14,59 @@ end
11
14
  require 'minitest/autorun'
12
15
  require 'minitest/unit'
13
16
  require 'minitest/hell'
17
+
18
+ module MiniTest
19
+ class Test
20
+ def before_setup
21
+ super
22
+ # Default to disable coloring for easier testing.
23
+ Rainbow.enabled = false
24
+ end
25
+
26
+ def linux_only(msg = 'Only tested on Linux')
27
+ skip msg unless TTY::Platform.new.linux?
28
+ end
29
+
30
+ def skip_windows(msg = 'Skip on Windows')
31
+ skip msg if TTY::Platform.new.windows?
32
+ end
33
+
34
+ # Methods for hooking logger,
35
+ # require 'pwnlib/logger' before using these methods.
36
+
37
+ def log_null(&block)
38
+ File.open(File::NULL, 'w') { |f| log_hook(f, &block) }
39
+ end
40
+
41
+ def log_stdout(&block)
42
+ log_hook($stdout, &block)
43
+ end
44
+
45
+ def log_hook(obj)
46
+ old = ::Pwnlib::Logger.log.instance_variable_get(:@logdev)
47
+ ::Pwnlib::Logger.log.instance_variable_set(:@logdev, obj)
48
+ begin
49
+ yield
50
+ ensure
51
+ ::Pwnlib::Logger.log.instance_variable_set(:@logdev, old)
52
+ end
53
+ end
54
+
55
+ # Hooks +$stdin+.
56
+ #
57
+ # @param [IO] io
58
+ # IO object to be used as +$stdin+.
59
+ #
60
+ # @return [Object]
61
+ # Returns what the block returned.
62
+ def hook_stdin(io)
63
+ org_stdin = $stdin
64
+ $stdin = io
65
+ begin
66
+ yield
67
+ ensure
68
+ $stdin = org_stdin
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/timer'
7
+
8
+ class TimerTest < MiniTest::Test
9
+ include ::Pwnlib
10
+
11
+ def test_countdown
12
+ t = Timer.new
13
+ refute(t.started?)
14
+ refute(t.active?)
15
+ assert_equal('DARKHH QQ', t.countdown(0.1) { 'DARKHH QQ' })
16
+
17
+ exception = assert_raises(RuntimeError) { t.countdown(0.1) { t.countdown(0.1) {} } }
18
+ assert_equal('Nested countdown not permitted', exception.message)
19
+
20
+ t.timeout = 0.514
21
+ exception = assert_raises(RuntimeError) do
22
+ t.countdown(0.1) { t.timeout = :forever }
23
+ end
24
+ assert_equal("Can't change timeout when countdown", exception.message)
25
+
26
+ t.countdown(0.1) { assert(t.started?) }
27
+ t.countdown(0.1) { assert(t.active?) }
28
+
29
+ assert_raises(::Pwnlib::Errors::TimeoutError) do
30
+ t.countdown(0.1) { sleep(0.2) }
31
+ end
32
+ end
33
+
34
+ # Check #115 fixed
35
+ def test_nested_countdown_false_positve_115
36
+ t = Timer.new
37
+ assert_raises(::Pwnlib::Errors::TimeoutError) do
38
+ t.countdown(0.01) { sleep(1) }
39
+ end
40
+ t.countdown(0.01) {}
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+
6
+ require 'pwnlib/tubes/buffer'
7
+
8
+ class BufferTest < MiniTest::Test
9
+ def test_add
10
+ b = ::Pwnlib::Tubes::Buffer.new
11
+ b.add('A' * 10)
12
+ b.add('B' * 10)
13
+ assert_equal(20, b.size)
14
+ assert_equal('A', b.get(1))
15
+ assert_equal(19, b.size)
16
+ assert_equal(false, b.empty?)
17
+ assert_equal('AAAAAAAAABBBBBBBBBB', b.get(9999))
18
+ assert_equal(0, b.size)
19
+ assert_equal(true, b.empty?)
20
+ assert_equal('', b.get(1))
21
+ end
22
+
23
+ def test_unget
24
+ b = ::Pwnlib::Tubes::Buffer.new
25
+ b.add('hello')
26
+ b.add('world')
27
+ assert_equal('hello', b.get(5))
28
+ b.unget('goodbye')
29
+ assert_equal('goodbyeworld', b.get)
30
+ end
31
+
32
+ def test_buffer
33
+ b = ::Pwnlib::Tubes::Buffer.new
34
+ b2 = ::Pwnlib::Tubes::Buffer.new
35
+ b.add('hello')
36
+ b2.add('world')
37
+ b.add(b2)
38
+ assert_equal('hello', b.get(5))
39
+ assert_equal(false, b2.empty?)
40
+ assert_equal('world', b2.get)
41
+ b2.add('goodbye')
42
+ b.unget(b2)
43
+ assert_equal('goodbyeworld', b.get)
44
+ assert_equal('goodbye', b2.get)
45
+ end
46
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'socket'
5
+ require 'tty-platform'
6
+
7
+ require 'test_helper'
8
+
9
+ require 'pwnlib/context'
10
+ require 'pwnlib/errors'
11
+ require 'pwnlib/tubes/process'
12
+
13
+ class ProcessTest < MiniTest::Test
14
+ include ::Pwnlib::Context
15
+
16
+ def setup
17
+ skip_windows
18
+ end
19
+
20
+ def test_io
21
+ cat = ::Pwnlib::Tubes::Process.new('cat')
22
+ cat.puts('HAHA')
23
+ assert_equal("HAHA\n", cat.gets)
24
+ assert_raises(::Pwnlib::Errors::TimeoutError) { cat.gets(timeout: 0.1) }
25
+ cat.puts('HAHA2')
26
+ assert_equal("HAHA2\n", cat.gets)
27
+ end
28
+
29
+ def test_env
30
+ data = ::Pwnlib::Tubes::Process.new('env').read
31
+ assert_match('PATH=', data)
32
+ data = ::Pwnlib::Tubes::Process.new('env', env: { 'FOO' => 'BAR' }).read
33
+ assert_equal("FOO=BAR\n", data)
34
+ end
35
+
36
+ def test_aslr
37
+ linux_only
38
+
39
+ map1 = ::Pwnlib::Tubes::Process.new('cat /proc/self/maps', aslr: false).read
40
+ map2 = ::Pwnlib::Tubes::Process.new(['cat', '/proc/self/maps'], aslr: false).read
41
+ assert_match('/bin/cat', map1) # make sure it read something
42
+ assert_equal(map1, map2)
43
+ end
44
+
45
+ def test_eof
46
+ ls = ::Pwnlib::Tubes::Process.new(['ls', '-la'])
47
+ assert_match(/total/, ls.gets)
48
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { loop { ls.write('anything') } }
49
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { loop { ls.gets } }
50
+ end
51
+
52
+ def test_shutdown
53
+ cat = ::Pwnlib::Tubes::Process.new('cat')
54
+ assert_raises(::Pwnlib::Errors::TimeoutError) { cat.recvn(1, timeout: 0.1) }
55
+ cat.shutdown(:write) # This should cause `cat` dead
56
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { cat.recvn(1, timeout: 0.1) }
57
+ cat.shutdown
58
+
59
+ cat = ::Pwnlib::Tubes::Process.new('cat')
60
+ cat.shutdown(:read)
61
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { cat.recvn(1) }
62
+ cat.shutdown
63
+ assert_raises(ArgumentError) { cat.shutdown(:zz) }
64
+ end
65
+
66
+ def test_kill
67
+ cat = ::Pwnlib::Tubes::Process.new('cat')
68
+ cat.kill
69
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { cat.recvn(1) }
70
+ end
71
+
72
+ def test_tty
73
+ tty_test = proc do |*args, raw: true|
74
+ in_, out = args.map { |v| v ? :pty : :pipe }
75
+ process = ::Pwnlib::Tubes::Process.new('ruby -e "p [STDIN.tty?, STDOUT.tty?]"',
76
+ in: in_, out: out, raw: raw)
77
+ process.gets
78
+ end
79
+ assert_equal("[false, false]\n", tty_test.call(false, false))
80
+ assert_equal("[true, false]\n", tty_test.call(true, false))
81
+ assert_equal("[false, true]\n", tty_test.call(false, true))
82
+ assert_equal("[false, true]\r\n", tty_test.call(false, true, raw: false))
83
+
84
+ cat = ::Pwnlib::Tubes::Process.new('cat', in: :pty, out: :pty, raw: false)
85
+ cat.puts('Hi')
86
+ # In cooked mode, tty should echo the input, so we can gets twice.
87
+ assert_equal("Hi\r\n", cat.gets)
88
+ assert_equal("Hi\r\n", cat.gets)
89
+ class << cat
90
+ # hook shutdown to silence the +cat: -: Input/output error+ message.
91
+ def shutdown; end
92
+ end
93
+ cat.close
94
+ end
95
+
96
+ def test_interact
97
+ ls = ::Pwnlib::Tubes::Process.new('ls Gemfile*')
98
+ saved = $stdin
99
+ # prevents stdin being closed
100
+ $stdin = UDPSocket.new
101
+ assert_output("Gemfile\nGemfile.lock\n") { context.local(log_level: :fatal) { ls.interact } }
102
+ $stdin.close
103
+ $stdin = saved
104
+ end
105
+ end
@@ -0,0 +1,162 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'open3'
5
+
6
+ require 'test_helper'
7
+
8
+ require 'pwnlib/tubes/serialtube'
9
+
10
+ module Pwnlib
11
+ module Tubes
12
+ class SerialTube
13
+ def break_encapsulation
14
+ @conn.close
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ class SerialTest < MiniTest::Test
21
+ include ::Pwnlib::Tubes
22
+
23
+ def open_pair
24
+ Open3.popen3('socat -d -d pty,raw,echo=0 pty,raw,echo=0') do |_i, _o, stderr, thread|
25
+ devs = []
26
+ 2.times do
27
+ devs << stderr.readline.chomp.split.last
28
+ # First pattern matches Linux, second is macOS
29
+ raise IOError, 'Could not create serial crosslink' if devs.last !~ %r{^(/dev/pts/[0-9]+|/dev/ttys[0-9]+)$}
30
+ end
31
+ # To ensure socat have finished setup
32
+ stderr.gets('starting data transfer loop')
33
+
34
+ serial = SerialTube.new devs[1], convert_newlines: false
35
+
36
+ begin
37
+ File.open devs[0], 'r+' do |file|
38
+ file.set_encoding 'default'.encoding
39
+ yield file, serial, thread
40
+ end
41
+ ensure
42
+ ::Process.kill('SIGTERM', thread.pid) if thread.alive?
43
+ end
44
+ end
45
+ end
46
+
47
+ def random_string(length)
48
+ Random.rand(36**length).to_s(36).rjust(length, '0')
49
+ end
50
+
51
+ def test_raise
52
+ skip_windows
53
+ open_pair do |_file, serial, thread|
54
+ ::Process.kill('SIGTERM', thread.pid)
55
+ # ensure the process has been killed
56
+ thread.value
57
+ assert_raises(Pwnlib::Errors::EndOfTubeError) { serial.puts('a') }
58
+ end
59
+ open_pair do |_file, serial|
60
+ serial.break_encapsulation
61
+ assert_raises(Pwnlib::Errors::EndOfTubeError) { serial.recv(1, timeout: 2) }
62
+ end
63
+ end
64
+
65
+ def test_recv
66
+ skip_windows
67
+ open_pair do |file, serial|
68
+ # recv, recvline
69
+ rs = random_string 24
70
+ file.puts rs
71
+ result = serial.recv 8, timeout: 1
72
+
73
+ assert_equal(rs[0...8], result)
74
+ result = serial.recv 8
75
+ assert_equal(rs[8...16], result)
76
+ result = serial.recvline.chomp
77
+ assert_equal(rs[16..-1], result)
78
+
79
+ assert_raises(Pwnlib::Errors::TimeoutError) { serial.recv(1, timeout: 0.2) }
80
+
81
+ # recvpred
82
+ rs = random_string 12
83
+ file.print rs
84
+ result = serial.recvpred do |data|
85
+ data[-6..-1] == rs[-6..-1]
86
+ end
87
+ assert_equal rs, result
88
+
89
+ assert_raises(Pwnlib::Errors::TimeoutError) { serial.recv(1, timeout: 0.2) }
90
+
91
+ # recvn
92
+ rs = random_string 6
93
+ file.print rs
94
+ result = ''
95
+ assert_raises(Pwnlib::Errors::TimeoutError) do
96
+ result = serial.recvn 120, timeout: 1
97
+ end
98
+ assert_empty result
99
+ file.print rs
100
+ result = serial.recvn 12
101
+ assert_equal rs * 2, result
102
+
103
+ assert_raises(Pwnlib::Errors::TimeoutError) { serial.recv(1, timeout: 0.2) }
104
+
105
+ # recvuntil
106
+ rs = random_string 12
107
+ file.print "#{rs}|"
108
+ result = serial.recvuntil('|').chomp('|')
109
+ assert_equal rs, result
110
+
111
+ assert_raises(Pwnlib::Errors::TimeoutError) { serial.recv(1, timeout: 0.2) }
112
+
113
+ # gets
114
+ rs = random_string 24
115
+ file.puts rs
116
+ result = serial.gets 12
117
+ assert_equal rs[0...12], result
118
+ result = serial.gets.chomp
119
+ assert_equal rs[12..-1], result
120
+
121
+ assert_raises(Pwnlib::Errors::TimeoutError) { serial.recv(1, timeout: 0.2) }
122
+ end
123
+ end
124
+
125
+ def test_send
126
+ skip_windows
127
+ open_pair do |file, serial|
128
+ # send, sendline
129
+ rs = random_string 24
130
+ # rubocop:disable Style/Send
131
+ # Justification: This isn't Object#send, false positive.
132
+ serial.send rs[0...12]
133
+ # rubocop:enable Style/Send
134
+ serial.sendline rs[12...24]
135
+ result = file.readline.chomp
136
+ assert_equal rs, result
137
+
138
+ # puts
139
+ r1 = random_string 4
140
+ r2 = random_string 4
141
+ r3 = random_string 4
142
+ serial.puts r1, r2, r3
143
+ result = ''
144
+ 3.times do
145
+ result += file.readline.chomp
146
+ end
147
+ assert_equal r1 + r2 + r3, result
148
+ end
149
+ end
150
+
151
+ def test_close
152
+ skip_windows
153
+ open_pair do |_file, serial|
154
+ serial.close
155
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { serial.puts(514) }
156
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { serial.puts(514) }
157
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { serial.recv }
158
+ assert_raises(::Pwnlib::Errors::EndOfTubeError) { serial.recv }
159
+ assert_raises(ArgumentError) { serial.close(:hh) }
160
+ end
161
+ end
162
+ end