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
data/lib/pwnlib/pwn.rb CHANGED
@@ -1,26 +1,44 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
- # require this file would also require all things in pwnlib, but would not
4
- # pollute anything.
4
+ # require this file would also require all things in pwnlib, but would not pollute anything.
5
5
 
6
+ require 'pwnlib/asm'
6
7
  require 'pwnlib/constants/constant'
7
8
  require 'pwnlib/constants/constants'
8
9
  require 'pwnlib/context'
9
10
  require 'pwnlib/dynelf'
11
+ require 'pwnlib/elf/elf'
12
+ require 'pwnlib/errors'
13
+ require 'pwnlib/logger'
10
14
  require 'pwnlib/reg_sort'
11
-
15
+ require 'pwnlib/runner'
16
+ require 'pwnlib/shellcraft/shellcraft'
17
+ require 'pwnlib/tubes/process'
18
+ require 'pwnlib/tubes/serialtube'
19
+ require 'pwnlib/tubes/sock'
20
+ require 'pwnlib/ui'
12
21
  require 'pwnlib/util/cyclic'
13
22
  require 'pwnlib/util/fiddling'
23
+ require 'pwnlib/util/getdents'
14
24
  require 'pwnlib/util/hexdump'
25
+ require 'pwnlib/util/lists'
15
26
  require 'pwnlib/util/packing'
16
27
 
17
- # include this module in a class to use all pwnlib functions in that class
18
- # instance.
28
+ # include this module in a class to use all pwnlib functions in that class instance.
19
29
  module Pwn
30
+ include ::Pwnlib::Asm
20
31
  include ::Pwnlib::Context
32
+ include ::Pwnlib::Logger
33
+ include ::Pwnlib::Runner
34
+ include ::Pwnlib::UI
35
+ include ::Pwnlib::Util::Cyclic
36
+ include ::Pwnlib::Util::Fiddling
37
+ include ::Pwnlib::Util::HexDump
38
+ include ::Pwnlib::Util::Lists
39
+ include ::Pwnlib::Util::Packing
21
40
 
22
- include ::Pwnlib::Util::Cyclic::ClassMethods
23
- include ::Pwnlib::Util::Fiddling::ClassMethods
24
- include ::Pwnlib::Util::HexDump::ClassMethods
25
- include ::Pwnlib::Util::Packing::ClassMethods
41
+ def shellcraft
42
+ ::Pwnlib::Shellcraft::Shellcraft.instance
43
+ end
26
44
  end
@@ -1,127 +1,126 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/context'
5
+ require 'pwnlib/util/ruby'
4
6
 
5
7
  module Pwnlib
6
8
  # Do topological sort on register assignments.
7
9
  module RegSort
8
- # @note Do not create and call instance method here. Instead, call module method on {RegSort}.
9
- module ClassMethods
10
- # Sorts register dependencies.
11
- #
12
- # Given a dictionary of registers to desired register contents,
13
- # return the optimal order in which to set the registers to
14
- # those contents.
15
- #
16
- # The implementation assumes that it is possible to move from
17
- # any register to any other register.
18
- #
19
- # @param [Hash<Symbol, String => Object>] in_out
20
- # Dictionary of desired register states.
21
- # Keys are registers, values are either registers or any other value.
22
- # @param [Array<String>] all_regs
23
- # List of all possible registers.
24
- # Used to determine which values in +in_out+ are registers, versus
25
- # regular values.
26
- # @option [Boolean] randomize
27
- # Randomize as much as possible about the order or registers.
28
- #
29
- # @return [Array]
30
- # Array of instructions, see examples for more details.
31
- #
32
- # @example
33
- # regs = %w(a b c d x y z)
34
- # regsort({a: 1, b: 2}, regs)
35
- # => [['mov', 'a', 1], ['mov', 'b', 2]]
36
- # regsort({a: 'b', b: 'a'}, regs)
37
- # => [['xchg', 'a', 'b']]
38
- # regsort({a: 1, b: 'a'}, regs)
39
- # => [['mov', 'b', 'a'], ['mov', 'a', 1]]
40
- # regsort({a: 'b', b: 'a', c: 3}, regs)
41
- # => [['mov', 'c', 3], ['xchg', 'a', 'b']]
42
- # regsort({a: 'b', b: 'a', c: 'b'}, regs)
43
- # => [['mov', 'c', 'b'], ['xchg', 'a', 'b']]
44
- # regsort({a: 'b', b: 'c', c: 'a', x: '1', y: 'z', z: 'c'}, regs)
45
- # => [['mov', 'x', '1'],
46
- # ['mov', 'y', 'z'],
47
- # ['mov', 'z', 'c'],
48
- # ['xchg', 'a', 'b'],
49
- # ['xchg', 'b', 'c']]
10
+ module_function
11
+
12
+ # Sorts register dependencies.
13
+ #
14
+ # Given a dictionary of registers to desired register contents, return the optimal order in which to set the
15
+ # registers to those contents.
16
+ #
17
+ # The implementation assumes that it is possible to move from any register to any other register.
18
+ #
19
+ # @param [Hash<Symbol, String => Object>] in_out
20
+ # Dictionary of desired register states.
21
+ # Keys are registers, values are either registers or any other value.
22
+ # @param [Array<String>] all_regs
23
+ # List of all possible registers.
24
+ # Used to determine which values in +in_out+ are registers, versus regular values.
25
+ # @param [Boolean] randomize
26
+ # Randomize as much as possible about the order or registers.
27
+ #
28
+ # @return [Array]
29
+ # Array of instructions, see examples for more details.
30
+ #
31
+ # @diff We don't support +tmp+/+xchg+ options because there's no such usage at all.
32
+ #
33
+ # @example
34
+ # regs = %w(a b c d x y z)
35
+ # regsort({a: 1, b: 2}, regs)
36
+ # #=> [['mov', 'a', 1], ['mov', 'b', 2]]
37
+ # regsort({a: 'b', b: 'a'}, regs)
38
+ # #=> [['xchg', 'a', 'b']]
39
+ # regsort({a: 1, b: 'a'}, regs)
40
+ # #=> [['mov', 'b', 'a'], ['mov', 'a', 1]]
41
+ # regsort({a: 'b', b: 'a', c: 3}, regs)
42
+ # #=> [['mov', 'c', 3], ['xchg', 'a', 'b']]
43
+ # regsort({a: 'b', b: 'a', c: 'b'}, regs)
44
+ # #=> [['mov', 'c', 'b'], ['xchg', 'a', 'b']]
45
+ # regsort({a: 'b', b: 'c', c: 'a', x: '1', y: 'z', z: 'c'}, regs)
46
+ # #=> [['mov', 'x', '1'],
47
+ # ['mov', 'y', 'z'],
48
+ # ['mov', 'z', 'c'],
49
+ # ['xchg', 'a', 'b'],
50
+ # ['xchg', 'b', 'c']]
51
+ def regsort(in_out, all_regs, randomize: nil)
52
+ # randomize = context.randomize if randomize.nil?
53
+
54
+ # TODO(david942j): stringify_keys
55
+ in_out = in_out.transform_keys(&:to_s)
56
+ # Drop all registers which will be set to themselves.
57
+ # Ex. {eax: 'eax'}
58
+ in_out.reject! { |k, v| k == v }
59
+
60
+ # Check input
61
+ if (in_out.keys - all_regs).any?
62
+ raise ArgumentError, format('Unknown register! Know: %p. Got: %p', all_regs, in_out)
63
+ end
64
+
65
+ # Collapse constant values.
50
66
  #
51
- # @note
52
- # Different from python-pwntools, we don't support +tmp+/+xchg+ options
53
- # because there's no such usage at all.
54
- def regsort(in_out, all_regs, randomize: nil)
55
- # randomize = context.randomize if randomize.nil?
56
-
57
- # TODO(david942j): stringify_keys
58
- in_out = in_out.map { |k, v| [k.to_s, v] }.to_h
59
- # Drop all registers which will be set to themselves.
60
- # Ex. {eax: 'eax'}
61
- in_out.reject! { |k, v| k == v }
62
-
63
- # Check input
64
- if (in_out.keys - all_regs).any?
65
- raise ArgumentError, format('Unknown register! Know: %p. Got: %p', all_regs, in_out)
66
- end
67
+ # Ex. {eax: 1, ebx: 1} can be collapsed to {eax: 1, ebx: 'eax'}.
68
+ # +post_mov+ are collapsed registers, set their values in the end.
69
+ post_mov = in_out.group_by { |_, v| v }.each_value.with_object({}) do |list, hash|
70
+ list.sort!
71
+ first_reg, val = list.shift
72
+ # Special case for val.zero? because zeroify registers is cheaper than mov.
73
+ next if list.empty? || all_regs.include?(val) || val.zero?
67
74
 
68
- # Collapse constant values
69
- #
70
- # Ex. {eax: 1, ebx: 1} can be collapsed to {eax: 1, ebx: 'eax'}.
71
- # +post_mov+ are collapsed registers, set their values in the end.
72
- post_mov = in_out.group_by { |_, v| v }.each_value.with_object({}) do |list, hash|
73
- list.sort!
74
- first_reg, val = list.shift
75
- # Special case for val.zero? because zeroify registers cost cheaper than mov.
76
- next if list.empty? || all_regs.include?(val) || val.zero?
77
- list.each do |reg, _|
78
- hash[reg] = first_reg
79
- in_out.delete(reg)
80
- end
75
+ list.each do |(reg, _)|
76
+ hash[reg] = first_reg
77
+ in_out.delete(reg)
81
78
  end
79
+ end
82
80
 
83
- graph = in_out.dup
84
- result = []
85
-
86
- # Let's do the topological sort.
87
- # so sad ruby 2.1 doesn't have +itself+...
88
- deg = graph.values.group_by { |i| i }.map { |k, v| [k, v.size] }.to_h
89
- graph.each_key { |k| deg[k] ||= 0 }
90
-
91
- until deg.empty?
92
- min_deg = deg.min_by { |_, v| v }[1]
93
- break unless min_deg.zero? # remain are all cycles
94
- min_pivs = deg.select { |_, v| v == min_deg }
95
- piv = randomize ? min_pivs.sample : min_pivs.first
96
- dst = piv.first
97
- deg.delete(dst)
98
- next unless graph.key?(dst) # Reach an end node.
99
- deg[graph[dst]] -= 1
100
- result << ['mov', dst, graph[dst]]
101
- graph.delete(dst)
102
- end
81
+ graph = in_out.dup
82
+ result = []
103
83
 
104
- # Remain must be cycles.
105
- graph.each_key do |reg|
106
- cycle = check_cycle(reg, graph)
107
- cycle.each_cons(2) do |d, s|
108
- result << ['xchg', d, s]
109
- end
110
- cycle.each { |r| graph.delete(r) }
111
- end
84
+ # Let's do the topological sort.
85
+ # so sad ruby 2.1 doesn't have +itself+...
86
+ deg = graph.values.group_by { |i| i }.transform_values(&:size)
87
+ graph.each_key { |k| deg[k] ||= 0 }
112
88
 
113
- # Now assign those collapsed registers.
114
- post_mov.sort.each do |dreg, sreg|
115
- result << ['mov', dreg, sreg]
89
+ until deg.empty?
90
+ min_deg = deg.min_by { |_, v| v }[1]
91
+ break unless min_deg.zero? # remain are all cycles
92
+
93
+ min_pivs = deg.select { |_, v| v == min_deg }
94
+ piv = randomize ? min_pivs.sample : min_pivs.first
95
+ dst = piv.first
96
+ deg.delete(dst)
97
+ next unless graph.key?(dst) # Reach an end node.
98
+
99
+ deg[graph[dst]] -= 1
100
+ result << ['mov', dst, graph[dst]]
101
+ graph.delete(dst)
102
+ end
103
+
104
+ # Remain must be cycles.
105
+ graph.each_key do |reg|
106
+ cycle = check_cycle(reg, graph)
107
+ cycle.each_cons(2) do |d, s|
108
+ result << ['xchg', d, s]
116
109
  end
110
+ cycle.each { |r| graph.delete(r) }
111
+ end
117
112
 
118
- result
113
+ # Now assign those collapsed registers.
114
+ post_mov.sort.each do |dreg, sreg|
115
+ result << ['mov', dreg, sreg]
119
116
  end
120
117
 
121
- private
118
+ result
119
+ end
122
120
 
123
- # Walk down the assignment list of a register,
124
- # return the path walked if it is encountered again.
121
+ Pwnlib::Util::Ruby.private_class_method_block do
122
+ # Walk down the assignment list of a register, return the path walked if it is encountered again.
123
+ #
125
124
  # @example
126
125
  # check_cycle('a', {'a' => 1}) #=> []
127
126
  # check_cycle('a', {'a' => 'a'}) #=> ['a']
@@ -132,16 +131,16 @@ module Pwnlib
132
131
  check_cycle_(reg, assignments, [])
133
132
  end
134
133
 
135
- def check_cycle_(reg, assignments, path) # :nodoc:
134
+ def check_cycle_(reg, assignments, path)
136
135
  target = assignments[reg]
137
136
  path << reg
138
- # No cycle, some other value (e.g. 1)
137
+ # No cycle, some other value (e.g. 1).
139
138
  return [] unless assignments.key?(target)
140
- # Found a cycle
139
+ # Found a cycle.
141
140
  return target == path.first ? path : [] if path.include?(target)
141
+
142
142
  check_cycle_(target, assignments, path)
143
143
  end
144
144
  end
145
- extend ClassMethods
146
145
  end
147
146
  end
@@ -0,0 +1,53 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'fileutils'
5
+
6
+ require 'pwnlib/asm'
7
+ require 'pwnlib/tubes/process'
8
+
9
+ module Pwnlib
10
+ # This module collects the methods for executing codes, e.g., assembly code, assembled machine code, etc.
11
+ module Runner
12
+ module_function
13
+
14
+ # Given an assembly listing, assemble and execute it.
15
+ #
16
+ # @param [String] assembly
17
+ # Assembly code.
18
+ #
19
+ # @return [Pwnlib::Tubes::Process]
20
+ # The tube for interacting.
21
+ #
22
+ # @see Runner.run_shellcode
23
+ def run_assembly(assembly)
24
+ run_shellcode(::Pwnlib::Asm.asm(assembly))
25
+ end
26
+
27
+ # Given assembled machine code bytes, execute them.
28
+ #
29
+ # @param [String] bytes
30
+ # Assembled code.
31
+ #
32
+ # @return [Pwnlib::Tubes::Process]
33
+ # The tube for interacting.
34
+ #
35
+ # @example
36
+ # r = run_shellcode(asm(shellcraft.cat('/etc/passwd')))
37
+ # r.interact
38
+ # # [INFO] Switching to interactive mode
39
+ # # root:x:0:0:root:/root:/bin/bash
40
+ # # daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
41
+ # # bin:x:2:2:bin:/bin:/usr/sbin/nologin
42
+ # # sys:x:3:3:sys:/dev:/usr/sbin/nologin
43
+ # # sync:x:4:65534:sync:/bin:/bin/sync
44
+ # # games:x:5:60:games:/usr/games:/usr/sbin/nologin
45
+ # # [INFO] Got EOF in interactive mode
46
+ # #=> true
47
+ def run_shellcode(bytes)
48
+ file = ::Pwnlib::Asm.make_elf(bytes, to_file: true)
49
+ at_exit { FileUtils.rm_f(file) if File.exist?(file) }
50
+ ::Pwnlib::Tubes::Process.new(file)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pwnlib/shellcraft/generators/helper'
4
+
5
+ module Pwnlib
6
+ module Shellcraft
7
+ module Generators
8
+ module Amd64
9
+ # For non os-related methods.
10
+ module Common
11
+ extend ::Pwnlib::Shellcraft::Generators::Helper
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'pwnlib/shellcraft/generators/amd64/common/common'
5
+ require 'pwnlib/shellcraft/generators/x86/common/infloop'
6
+
7
+ module Pwnlib
8
+ module Shellcraft
9
+ module Generators
10
+ module Amd64
11
+ module Common
12
+ # @overload infloop
13
+ #
14
+ # @see Generators::X86::Common#infloop
15
+ def infloop(*args)
16
+ context.local(arch: :amd64) do
17
+ cat X86::Common.infloop(*args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'pwnlib/shellcraft/generators/amd64/common/common'
5
+ require 'pwnlib/shellcraft/generators/amd64/common/setregs'
6
+
7
+ module Pwnlib
8
+ module Shellcraft
9
+ module Generators
10
+ module Amd64
11
+ module Common
12
+ # Like +memcpy+ in glibc.
13
+ #
14
+ # Copy +n+ bytes from +src+ to +dst+.
15
+ #
16
+ # @param [String, Symbol, Integer] dst
17
+ # Destination.
18
+ # @param [String, Symbol, Integer] src
19
+ # Source to be copied.
20
+ # @param [Integer] n
21
+ # The number of bytes to be copied.
22
+ #
23
+ # @example
24
+ # shellcraft.memcpy('rax', 'rbx', 0x1000)
25
+ def memcpy(dst, src, n)
26
+ cat "/* memcpy(#{pretty(dst)}, #{pretty(src)}, #{pretty(n)}) */"
27
+ cat 'cld'
28
+ cat Common.setregs({ rdi: dst, rsi: src, rcx: n })
29
+ cat 'rep movsb'
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'pwnlib/shellcraft/generators/amd64/common/common'
5
+
6
+ module Pwnlib
7
+ module Shellcraft
8
+ module Generators
9
+ module Amd64
10
+ module Common
11
+ # Move +src+ into +dst+ without newlines and null bytes.
12
+ #
13
+ # @param [String, Symbol] dst
14
+ # Register's name.
15
+ # @param [String, Symbol, Integer] src
16
+ # Register's name or immediate value.
17
+ # @param [Boolean] stack_allowed
18
+ # If equals to +false+, generated assembly code would not use stack-related operations.
19
+ # But beware of without stack-related operations the generated code length is longer.
20
+ #
21
+ # @example
22
+ # context.arch = 'amd64'
23
+ # shellcraft.mov('rdi', 'ax')
24
+ # #=> " movzx edi, ax\n"
25
+ # @example
26
+ # context.arch = 'amd64'
27
+ # puts shellcraft.mov('rax', 10)
28
+ # # push 9 /* mov eax, '\n' */
29
+ # # pop rax
30
+ # # inc eax
31
+ # #=> nil
32
+ # @example
33
+ # context.arch = 'amd64'
34
+ # puts shellcraft.mov('rax', 10, stack_allowed: false)
35
+ # # mov eax, 0x1010101
36
+ # # xor eax, 0x101010b /* 0xa == 0x1010101 ^ 0x101010b */
37
+ # #=> nil
38
+ def mov(dst, src, stack_allowed: true)
39
+ raise ArgumentError, "#{dst} is not a register" unless register?(dst)
40
+
41
+ dst = get_register(dst)
42
+ if register?(src)
43
+ src = get_register(src)
44
+ if dst.size < src.size && !dst.bigger.include?(src.name)
45
+ raise ArgumentError, "cannot mov #{dst}, #{src}: dst is smaller than src"
46
+ end
47
+
48
+ # Downgrade our register choice if possible.
49
+ # Opcodes for operating on 32-bit registers are always (?) shorter.
50
+ dst = get_register(dst.native32) if dst.size == 64 && src.size <= 32
51
+ else
52
+ context.local(arch: 'amd64') { src = evaluate(src) }
53
+ raise ArgumentError, format('cannot mov %s, %d: dst is smaller than src', dst, src) unless dst.fits(src)
54
+
55
+ orig_dst = dst
56
+ dst = get_register(dst.native32) if dst.size == 64 && bits_required(src) <= 32
57
+
58
+ # Calculate the packed version.
59
+ srcp = pack(src & ((1 << dst.size) - 1), bits: dst.size)
60
+
61
+ # Calculate the unsigned and signed versions.
62
+ srcu = unpack(srcp, bits: dst.size, signed: false)
63
+ # N.B.: We may have downsized the register for e.g. mov('rax', 0xffffffff)
64
+ # In this case, srcp is now a 4-byte packed value, which will expand to "-1", which isn't correct.
65
+ srcs = orig_dst.size == dst.size ? unpack(srcp, bits: dst.size, signed: true) : src
66
+ end
67
+ if register?(src)
68
+ if src == dst || dst.bigger.include?(src.name)
69
+ cat "/* moving #{src} into #{dst}, but this is a no-op */"
70
+ elsif dst.size > src.size
71
+ cat "movzx #{dst}, #{src}"
72
+ else
73
+ cat "mov #{dst}, #{src}"
74
+ end
75
+ elsif src.is_a?(Numeric) # Constant or immi
76
+ xor = ->(reg) { "xor #{reg.xor}, #{reg.xor}" }
77
+ if src.zero?
78
+ # Special case for zeroes.
79
+ # XORing the 32-bit register clears the high 32 bits as well.
80
+ cat "xor #{dst}, #{dst} /* #{src} */"
81
+ elsif stack_allowed && [32, 64].include?(dst.size) && src == 10
82
+ cat "push 9 /* mov #{dst}, '\\n' */"
83
+ cat "pop #{dst.native64}"
84
+ cat "inc #{dst}"
85
+ elsif stack_allowed && [32, 64].include?(dst.size) && (-2**7 <= srcs && srcs < 2**7) && okay(srcp[0])
86
+ # It's smaller to PUSH and POP small sign-extended values than to directly move them into various
87
+ # registers.
88
+ #
89
+ # 6aff58 push -1; pop rax
90
+ # 48c7c0ffffffff mov rax, -1
91
+ cat "push #{pretty(src)}"
92
+ cat "pop #{dst.native64}"
93
+ elsif okay(srcp)
94
+ # Easy case. This implies that the register size and value are the same.
95
+ cat "mov #{dst}, #{pretty(src)}"
96
+ elsif srcu < 2**8 && okay(srcp[0]) && dst.sizes.include?(8) # Move 8-bit value into register.
97
+ cat xor[dst]
98
+ cat "mov #{dst.sizes[8]}, #{pretty(src)}"
99
+ elsif srcu == srcu & 0xff00 && okay(srcp[1]) && dst.ff00
100
+ # Target value is a 16-bit value with no data in the low 8 bits, we can use the 'AH' style register.
101
+ cat xor[dst]
102
+ cat "mov #{dst.ff00}, #{pretty(src)} >> 8"
103
+ elsif srcu < 2**16 && okay(srcp[0, 2]) # Target value is a 16-bit value, use a 16-bit mov.
104
+ cat xor[dst]
105
+ cat "mov #{dst.sizes[16]}, #{pretty(src)}"
106
+ else # All else has failed. Use some XOR magic to move things around.
107
+ a, b = xor_pair(srcp, avoid: "\x00\n")
108
+ a = hex(unpack(a, bits: dst.size))
109
+ b = hex(unpack(b, bits: dst.size))
110
+ if dst.size != 64
111
+ # There's no XOR REG, IMM64 but we can take the easy route for smaller registers.
112
+ cat "mov #{dst}, #{a}"
113
+ cat "xor #{dst}, #{b} /* #{hex(src)} == #{a} ^ #{b} */"
114
+ elsif stack_allowed
115
+ # However, we can PUSH IMM64 and then perform the XOR that way at the top of the stack.
116
+ cat "mov #{dst}, #{a}"
117
+ cat "push #{dst}"
118
+ cat "mov #{dst}, #{b}"
119
+ cat "xor [rsp], #{dst} /* #{hex(src)} == #{a} ^ #{b} */"
120
+ cat "pop #{dst}"
121
+ else
122
+ raise ArgumentError, "Cannot put #{pretty(src)} into '#{dst}' without using stack."
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end