pwntools 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/lib/pwn.rb +1 -0
  4. data/lib/pwnlib/abi.rb +1 -0
  5. data/lib/pwnlib/asm.rb +83 -42
  6. data/lib/pwnlib/constants/constant.rb +4 -1
  7. data/lib/pwnlib/constants/constants.rb +3 -0
  8. data/lib/pwnlib/constants/linux/amd64.rb +2 -0
  9. data/lib/pwnlib/constants/linux/i386.rb +2 -0
  10. data/lib/pwnlib/context.rb +10 -1
  11. data/lib/pwnlib/dynelf.rb +7 -2
  12. data/lib/pwnlib/elf/elf.rb +79 -6
  13. data/lib/pwnlib/errors.rb +3 -2
  14. data/lib/pwnlib/ext/array.rb +2 -1
  15. data/lib/pwnlib/ext/helper.rb +3 -2
  16. data/lib/pwnlib/ext/integer.rb +2 -1
  17. data/lib/pwnlib/ext/string.rb +3 -2
  18. data/lib/pwnlib/logger.rb +21 -1
  19. data/lib/pwnlib/memleak.rb +1 -0
  20. data/lib/pwnlib/pwn.rb +5 -1
  21. data/lib/pwnlib/reg_sort.rb +5 -0
  22. data/lib/pwnlib/runner.rb +53 -0
  23. data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +2 -0
  24. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +1 -0
  25. data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +5 -1
  26. data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +4 -0
  27. data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +2 -0
  28. data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +1 -0
  29. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +3 -1
  30. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +1 -0
  31. data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +1 -0
  32. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +3 -2
  33. data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +3 -2
  34. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +1 -0
  35. data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +1 -0
  36. data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +2 -0
  37. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +1 -0
  38. data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +1 -0
  39. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +3 -2
  40. data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
  41. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +1 -0
  42. data/lib/pwnlib/shellcraft/generators/helper.rb +11 -2
  43. data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +2 -0
  44. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +1 -0
  45. data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
  46. data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +3 -0
  47. data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +2 -0
  48. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +2 -0
  49. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +1 -0
  50. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +3 -2
  51. data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +3 -2
  52. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +1 -0
  53. data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +1 -0
  54. data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +2 -0
  55. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +1 -0
  56. data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +1 -0
  57. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +3 -2
  58. data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
  59. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +1 -0
  60. data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +5 -3
  61. data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +2 -0
  62. data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
  63. data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +2 -0
  64. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +2 -0
  65. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +1 -0
  66. data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +8 -6
  67. data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +1 -0
  68. data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +3 -0
  69. data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +1 -0
  70. data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +2 -0
  71. data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +1 -0
  72. data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +1 -0
  73. data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +1 -0
  74. data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
  75. data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +10 -10
  76. data/lib/pwnlib/shellcraft/registers.rb +5 -1
  77. data/lib/pwnlib/shellcraft/shellcraft.rb +8 -3
  78. data/lib/pwnlib/timer.rb +6 -2
  79. data/lib/pwnlib/tubes/buffer.rb +4 -1
  80. data/lib/pwnlib/tubes/process.rb +2 -0
  81. data/lib/pwnlib/tubes/serialtube.rb +3 -1
  82. data/lib/pwnlib/tubes/sock.rb +7 -1
  83. data/lib/pwnlib/tubes/tube.rb +23 -3
  84. data/lib/pwnlib/ui.rb +21 -0
  85. data/lib/pwnlib/util/cyclic.rb +2 -0
  86. data/lib/pwnlib/util/fiddling.rb +37 -5
  87. data/lib/pwnlib/util/getdents.rb +1 -0
  88. data/lib/pwnlib/util/hexdump.rb +8 -5
  89. data/lib/pwnlib/util/lists.rb +3 -0
  90. data/lib/pwnlib/util/packing.rb +5 -2
  91. data/lib/pwnlib/util/ruby.rb +1 -0
  92. data/lib/pwnlib/version.rb +2 -1
  93. data/test/abi_test.rb +1 -0
  94. data/test/asm_test.rb +75 -85
  95. data/test/constants/constant_test.rb +1 -0
  96. data/test/constants/constants_test.rb +1 -0
  97. data/test/context_test.rb +1 -0
  98. data/test/data/assembly/aarch64.s +19 -0
  99. data/test/data/assembly/amd64.s +21 -0
  100. data/test/data/assembly/arm.s +9 -0
  101. data/test/data/assembly/i386.s +21 -0
  102. data/test/data/assembly/mips.s +16 -0
  103. data/test/data/assembly/mips64.s +6 -0
  104. data/test/data/assembly/powerpc.s +18 -0
  105. data/test/data/assembly/powerpc64.s +36 -0
  106. data/test/data/assembly/sparc.s +33 -0
  107. data/test/data/assembly/sparc64.s +5 -0
  108. data/test/data/assembly/thumb.s +37 -0
  109. data/test/data/echo.rb +1 -0
  110. data/test/dynelf_test.rb +3 -1
  111. data/test/elf/elf_test.rb +18 -0
  112. data/test/ext_test.rb +1 -0
  113. data/test/files/use_pwn.rb +1 -0
  114. data/test/files/use_pwnlib.rb +1 -0
  115. data/test/full_file_test.rb +6 -0
  116. data/test/logger_test.rb +24 -3
  117. data/test/memleak_test.rb +1 -0
  118. data/test/reg_sort_test.rb +1 -0
  119. data/test/runner_test.rb +32 -0
  120. data/test/shellcraft/infloop_test.rb +1 -0
  121. data/test/shellcraft/linux/cat_test.rb +1 -0
  122. data/test/shellcraft/linux/ls_test.rb +1 -0
  123. data/test/shellcraft/linux/sh_test.rb +1 -0
  124. data/test/shellcraft/linux/sleep_test.rb +68 -0
  125. data/test/shellcraft/linux/syscalls/execve_test.rb +1 -0
  126. data/test/shellcraft/linux/syscalls/exit_test.rb +1 -0
  127. data/test/shellcraft/linux/syscalls/open_test.rb +1 -0
  128. data/test/shellcraft/linux/syscalls/syscall_test.rb +1 -0
  129. data/test/shellcraft/memcpy_test.rb +20 -5
  130. data/test/shellcraft/mov_test.rb +1 -0
  131. data/test/shellcraft/nop_test.rb +1 -0
  132. data/test/shellcraft/popad_test.rb +1 -0
  133. data/test/shellcraft/pushstr_array_test.rb +1 -0
  134. data/test/shellcraft/pushstr_test.rb +1 -0
  135. data/test/shellcraft/registers_test.rb +1 -0
  136. data/test/shellcraft/ret_test.rb +1 -0
  137. data/test/shellcraft/setregs_test.rb +9 -8
  138. data/test/shellcraft/shellcraft_test.rb +1 -0
  139. data/test/test_helper.rb +28 -0
  140. data/test/timer_test.rb +2 -1
  141. data/test/tubes/buffer_test.rb +1 -0
  142. data/test/tubes/process_test.rb +8 -2
  143. data/test/tubes/serialtube_test.rb +1 -4
  144. data/test/tubes/sock_test.rb +1 -0
  145. data/test/tubes/tube_test.rb +10 -1
  146. data/test/ui_test.rb +18 -0
  147. data/test/util/cyclic_test.rb +1 -0
  148. data/test/util/fiddling_test.rb +8 -0
  149. data/test/util/getdents_test.rb +1 -0
  150. data/test/util/hexdump_test.rb +2 -1
  151. data/test/util/lists_test.rb +1 -0
  152. data/test/util/packing_test.rb +3 -2
  153. metadata +119 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c323ddeb8d8f4ea3d4d087fc69eb53ee7594f64c2ee01dfb57ada9e2a9eb111
4
- data.tar.gz: b9177cb71feb8ecc038680cf850f0b654eea567c7f6774d8ad35cdb1b8949e93
3
+ metadata.gz: 3385a46a409e98ef0ffac19fb93c5ec19d7fde592abbb99ccdc462e186d2b922
4
+ data.tar.gz: d865773d92a7b3068005bfaa9c9d5ba5f1a8bce0e62e05eb55b340c80d2fe3dd
5
5
  SHA512:
6
- metadata.gz: 76cbcd91657ecaee5b8bfae49bfe378539937718a1eb46fbe517c8559919877a153cffb0e5beeb06dde68fe64cd7ab9e1fb30af507547771f49331e7bb90c93b
7
- data.tar.gz: 1aedd2cb397654aa4bc4e13761a1587431f7a67ecec2150c5e6393f1275dfd14801089e0de7234ddd887b2fb99c9635c9bc438ba183c6fcf8d021331269d99b5
6
+ metadata.gz: 1d529315d4e222cc89d3783f4454e804d8ce9d4c8a23a2657b29f23be14dd63e7b5f8f668aa95db944b7ec887d7fc2563f00067a93ec2b841448408e3aee141c
7
+ data.tar.gz: 1f95b2194ac3fea17ea87d030bbba0f52e638978797a7f9068e9af92c2c2d98f39d25cb90be6b73d2e7008e41fd2f913684395a68fbbb6d22115b341533fc8fd
data/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  [![GitHub stars](https://img.shields.io/github/stars/peter50216/pwntools-ruby.svg)](https://github.com/peter50216/pwntools-ruby/stargazers)
2
- [![Dependency Status](https://img.shields.io/gemnasium/peter50216/pwntools-ruby.svg)](https://gemnasium.com/peter50216/pwntools-ruby)
2
+ [![GitHub issues](https://img.shields.io/github/issues/peter50216/pwntools-ruby.svg)](https://github.com/peter50216/pwntools-ruby/issues)
3
3
  [![Build Status](https://img.shields.io/travis/peter50216/pwntools-ruby.svg)](https://travis-ci.org/peter50216/pwntools-ruby)
4
- [![Test Coverage](https://img.shields.io/codeclimate/coverage/github/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby/coverage)
5
- [![Code Climate](https://img.shields.io/codeclimate/github/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby)
4
+ [![Test Coverage](https://img.shields.io/codeclimate/coverage/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby/coverage)
5
+ [![Code Climate](https://img.shields.io/codeclimate/maintainability/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby)
6
6
  [![Inline docs](https://inch-ci.org/github/peter50216/pwntools-ruby.svg)](https://inch-ci.org/github/peter50216/pwntools-ruby)
7
7
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
8
+ [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=peter50216/pwntools-ruby)](https://dependabot.com)
9
+ [![Rawsec's CyberSecurity Inventory](https://inventory.rawsec.ml/img/badges/Rawsec-inventoried-FF5050_flat.svg)](https://inventory.rawsec.ml/)
10
+ <!-- [![Dependency Status](https://img.shields.io/gemnasium/peter50216/pwntools-ruby.svg)](https://gemnasium.com/peter50216/pwntools-ruby) -->
8
11
 
9
12
  # pwntools-ruby
10
13
 
data/lib/pwn.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  # require this file for easy exploit development, but would pollute main Object and some built-in objects. (String,
4
5
  # Integer, ...)
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/context'
4
5
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'tempfile'
4
5
 
@@ -37,41 +38,44 @@ module Pwnlib
37
38
  #
38
39
  # @raise [Pwnlib::Errors::DependencyError]
39
40
  # If libcapstone is not installed.
41
+ # @raise [Pwnlib::Errors::UnsupportedArchError]
42
+ # If disassembling of +context.arch+ is not supported.
40
43
  #
41
44
  # @example
42
45
  # context.arch = 'i386'
43
46
  # print disasm("\xb8\x5d\x00\x00\x00")
44
- # # 0: b8 5d 00 00 00 mov eax, 0x5d
47
+ # # 0: b8 5d 00 00 00 mov eax, 0x5d
45
48
  #
46
49
  # context.arch = 'amd64'
47
50
  # print disasm("\xb8\x17\x00\x00\x00")
48
51
  # # 0: b8 17 00 00 00 mov eax, 0x17
49
52
  # print disasm("jhH\xb8/bin///sPH\x89\xe71\xd21\xf6j;X\x0f\x05", vma: 0x1000)
50
- # # 1000: 6a 68 push 0x68
51
- # # 1002: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f
52
- # # 100c: 50 push rax
53
- # # 100d: 48 89 e7 mov rdi, rsp
54
- # # 1010: 31 d2 xor edx, edx
55
- # # 1012: 31 f6 xor esi, esi
56
- # # 1014: 6a 3b push 0x3b
57
- # # 1016: 58 pop rax
58
- # # 1017: 0f 05 syscall
53
+ # # 1000: 6a 68 push 0x68
54
+ # # 1002: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f
55
+ # # 100c: 50 push rax
56
+ # # 100d: 48 89 e7 mov rdi, rsp
57
+ # # 1010: 31 d2 xor edx, edx
58
+ # # 1012: 31 f6 xor esi, esi
59
+ # # 1014: 6a 3b push 0x3b
60
+ # # 1016: 58 pop rax
61
+ # # 1017: 0f 05 syscall
59
62
  def disasm(data, vma: 0)
60
63
  require_message('crabstone', install_crabstone_guide) # will raise error if require fail.
61
- cs = Crabstone::Disassembler.new(cap_arch, cap_mode)
64
+ cs = Crabstone::Disassembler.new(cs_arch, cs_mode)
62
65
  insts = cs.disasm(data, vma).map do |ins|
63
- [ins.address, ins.bytes.pack('C*'), ins.mnemonic, ins.op_str.to_s]
66
+ [ins.address, ins.bytes, ins.mnemonic.to_s, ins.op_str.to_s]
64
67
  end
65
68
  max_dlen = format('%x', insts.last.first).size + 2
66
69
  max_hlen = insts.map { |ins| ins[1].size }.max * 3
70
+ max_ilen = insts.map { |ins| ins[2].size }.max
67
71
  insts.reduce('') do |s, ins|
68
- hex_code = ins[1].bytes.map { |c| format('%02x', c) }.join(' ')
72
+ hex_code = ins[1].map { |c| format('%02x', c) }.join(' ')
69
73
  inst = if ins[3].empty?
70
74
  ins[2]
71
75
  else
72
- format('%-7s %s', ins[2], ins[3])
76
+ format("%-#{max_ilen}s %s", ins[2], ins[3])
73
77
  end
74
- s + format("%#{max_dlen}x: %-#{max_hlen}s%s\n", ins[0], hex_code, inst)
78
+ s + format("%#{max_dlen}x: %-#{max_hlen}s %s\n", ins[0], hex_code, inst)
75
79
  end
76
80
  end
77
81
 
@@ -79,10 +83,17 @@ module Pwnlib
79
83
  #
80
84
  # @param [String] code
81
85
  # The assembly code to be converted.
86
+ # @param [Integer] vma
87
+ # Virtual memory address.
82
88
  #
83
89
  # @return [String]
84
90
  # The result.
85
91
  #
92
+ # @raise [Pwnlib::Errors::DependencyError]
93
+ # If libkeystone is not installed.
94
+ # @raise [Pwnlib::Errors::UnsupportedArchError]
95
+ # If assembling of +context.arch+ is not supported.
96
+ #
86
97
  # @example
87
98
  # assembly = shellcraft.amd64.linux.sh
88
99
  # context.local(arch: 'amd64') { asm(assembly) }
@@ -93,9 +104,9 @@ module Pwnlib
93
104
  #
94
105
  # @diff
95
106
  # Not support +asm('mov eax, SYS_execve')+.
96
- def asm(code)
107
+ def asm(code, vma: 0)
97
108
  require_message('keystone_engine', install_keystone_guide)
98
- KeystoneEngine::Ks.new(ks_arch, ks_mode).asm(code)[0]
109
+ KeystoneEngine::Ks.new(ks_arch, ks_mode).asm(code, vma)[0]
99
110
  end
100
111
 
101
112
  # Builds an ELF file from executable code.
@@ -166,6 +177,7 @@ module Pwnlib
166
177
  phdr.p_filesz = phdr.p_memsz = entry + data.size
167
178
  elf = ehdr.to_binary_s + phdr.to_binary_s + data
168
179
  return elf unless to_file
180
+
169
181
  path = Dir::Tmpname.create(['pwn', '.elf']) do |temp|
170
182
  File.open(temp, 'wb', 0o750) { |f| f.write(elf) }
171
183
  end
@@ -173,32 +185,59 @@ module Pwnlib
173
185
  end
174
186
 
175
187
  ::Pwnlib::Util::Ruby.private_class_method_block do
176
- def cap_arch
177
- {
178
- 'i386' => Crabstone::ARCH_X86,
179
- 'amd64' => Crabstone::ARCH_X86
180
- }[context.arch]
188
+ def cs_arch
189
+ case context.arch
190
+ when 'aarch64' then Crabstone::ARCH_ARM64
191
+ when 'amd64', 'i386' then Crabstone::ARCH_X86
192
+ when 'arm', 'thumb' then Crabstone::ARCH_ARM
193
+ when 'mips', 'mips64' then Crabstone::ARCH_MIPS
194
+ when 'powerpc64' then Crabstone::ARCH_PPC
195
+ when 'sparc', 'sparc64' then Crabstone::ARCH_SPARC
196
+ else unsupported!("Disasm on architecture #{context.arch.inspect} is not supported yet.")
197
+ end
181
198
  end
182
199
 
183
- def cap_mode
184
- {
185
- 32 => Crabstone::MODE_32,
186
- 64 => Crabstone::MODE_64
187
- }[context.bits]
200
+ def cs_mode
201
+ case context.arch
202
+ when 'aarch64' then Crabstone::MODE_ARM
203
+ when 'amd64' then Crabstone::MODE_64
204
+ when 'arm' then Crabstone::MODE_ARM
205
+ when 'i386' then Crabstone::MODE_32
206
+ when 'mips' then Crabstone::MODE_MIPS32
207
+ when 'mips64' then Crabstone::MODE_MIPS64
208
+ when 'powerpc64' then Crabstone::MODE_64
209
+ when 'sparc' then 0 # default mode
210
+ when 'sparc64' then Crabstone::MODE_V9
211
+ when 'thumb' then Crabstone::MODE_THUMB
212
+ end | (context.endian == 'big' ? Crabstone::MODE_BIG_ENDIAN : Crabstone::MODE_LITTLE_ENDIAN)
188
213
  end
189
214
 
190
215
  def ks_arch
191
- {
192
- 'i386' => KeystoneEngine::KS_ARCH_X86,
193
- 'amd64' => KeystoneEngine::KS_ARCH_X86
194
- }[context.arch]
216
+ case context.arch
217
+ when 'aarch64' then KeystoneEngine::KS_ARCH_ARM64
218
+ when 'amd64', 'i386' then KeystoneEngine::KS_ARCH_X86
219
+ when 'arm', 'thumb' then KeystoneEngine::KS_ARCH_ARM
220
+ when 'mips', 'mips64' then KeystoneEngine::KS_ARCH_MIPS
221
+ when 'powerpc', 'powerpc64' then KeystoneEngine::KS_ARCH_PPC
222
+ when 'sparc', 'sparc64' then KeystoneEngine::KS_ARCH_SPARC
223
+ else unsupported!("Asm on architecture #{context.arch.inspect} is not supported yet.")
224
+ end
195
225
  end
196
226
 
197
227
  def ks_mode
198
- {
199
- 32 => KeystoneEngine::KS_MODE_32,
200
- 64 => KeystoneEngine::KS_MODE_64
201
- }[context.bits]
228
+ case context.arch
229
+ when 'aarch64' then 0 # default mode
230
+ when 'amd64' then KeystoneEngine::KS_MODE_64
231
+ when 'arm' then KeystoneEngine::KS_MODE_ARM
232
+ when 'i386' then KeystoneEngine::KS_MODE_32
233
+ when 'mips' then KeystoneEngine::KS_MODE_MIPS32
234
+ when 'mips64' then KeystoneEngine::KS_MODE_MIPS64
235
+ when 'powerpc' then KeystoneEngine::KS_MODE_PPC32
236
+ when 'powerpc64' then KeystoneEngine::KS_MODE_PPC64
237
+ when 'sparc' then KeystoneEngine::KS_MODE_SPARC32
238
+ when 'sparc64' then KeystoneEngine::KS_MODE_SPARC64
239
+ when 'thumb' then KeystoneEngine::KS_MODE_THUMB
240
+ end | (context.endian == 'big' ? KeystoneEngine::KS_MODE_BIG_ENDIAN : KeystoneEngine::KS_MODE_LITTLE_ENDIAN)
202
241
  end
203
242
 
204
243
  # FFI is used in keystone and capstone binding gems, this method handles when libraries not installed yet.
@@ -210,7 +249,7 @@ module Pwnlib
210
249
 
211
250
  def install_crabstone_guide
212
251
  <<-EOS
213
- #disasm dependes on capstone, which is detected not installed yet.
252
+ #disasm depends on capstone, which is detected not installed yet.
214
253
  Checkout the following link for installation guide:
215
254
 
216
255
  http://www.capstone-engine.org/documentation.html
@@ -220,7 +259,7 @@ http://www.capstone-engine.org/documentation.html
220
259
 
221
260
  def install_keystone_guide
222
261
  <<-EOS
223
- #asm dependes on keystone, which is detected not installed yet.
262
+ #asm depends on keystone, which is detected not installed yet.
224
263
  Checkout the following link for installation guide:
225
264
 
226
265
  https://github.com/keystone-engine/keystone/tree/master/docs
@@ -264,7 +303,7 @@ https://github.com/keystone-engine/keystone/tree/master/docs
264
303
  header.p_offset = 0
265
304
  header.p_vaddr = vma
266
305
  header.p_paddr = vma
267
- header.p_flags = 4 | 1 # r-x
306
+ header.p_flags = 4 | 2 | 1 # rwx
268
307
  header.p_align = arch_align
269
308
  header
270
309
  end
@@ -299,10 +338,8 @@ https://github.com/keystone-engine/keystone/tree/master/docs
299
338
 
300
339
  def e_machine
301
340
  const = ARCH_EM[context.arch.to_sym]
302
- if const.nil?
303
- raise ::Pwnlib::Errors::UnsupportedArchError,
304
- "Unknown machine type of architecture #{context.arch.inspect}."
305
- end
341
+ unsupported!("Unknown machine type of architecture #{context.arch.inspect}.") if const.nil?
342
+
306
343
  ::ELFTools::Constants::EM.const_get("EM_#{const}")
307
344
  end
308
345
 
@@ -310,6 +347,10 @@ https://github.com/keystone-engine/keystone/tree/master/docs
310
347
  context.endian.to_sym
311
348
  end
312
349
 
350
+ def unsupported!(msg)
351
+ raise ::Pwnlib::Errors::UnsupportedArchError, msg
352
+ end
353
+
313
354
  include ::Pwnlib::Context
314
355
  end
315
356
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pwnlib/util/fiddling'
4
5
 
@@ -27,9 +28,11 @@ module Pwnlib
27
28
  end
28
29
 
29
30
  # We don't need to fall back to super for this, so just disable the lint.
30
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
31
+ # rubocop:disable Style/MethodMissingSuper
32
+ def method_missing(method, *args, &block)
31
33
  @val.__send__(method, *args, &block)
32
34
  end
35
+ # rubocop:enable Style/MethodMissingSuper
33
36
 
34
37
  def respond_to_missing?(method, include_all)
35
38
  @val.respond_to?(method, include_all)
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'dentaku'
4
5
 
@@ -50,6 +51,7 @@ module Pwnlib
50
51
  # # Pwnlib::Errors::ConstantNotFoundError: Undefined constant(s): meow
51
52
  def eval(str)
52
53
  return str unless str.instance_of?(String)
54
+
53
55
  begin
54
56
  val = calculator.evaluate!(str.strip).to_i
55
57
  rescue Dentaku::UnboundVariableError => e
@@ -94,6 +96,7 @@ module Pwnlib
94
96
  def load_constants((os, arch))
95
97
  filename = File.join(__dir__, os, "#{arch}.rb")
96
98
  return {} unless File.exist?(filename)
99
+
97
100
  builder = ConstantBuilder.new
98
101
  builder.instance_eval(IO.read(filename))
99
102
  builder.tbl
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  const :__NR_read, 0
2
4
  const :__NR_write, 1
3
5
  const :__NR_open, 2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  const :__NR_exit, 1
2
4
  const :__NR_fork, 2
3
5
  const :__NR_read, 3
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'logger'
4
5
 
@@ -96,6 +97,7 @@ module Pwnlib
96
97
  def update(**kwargs)
97
98
  kwargs.each do |k, v|
98
99
  next if v.nil?
100
+
99
101
  public_send("#{k}=", v)
100
102
  end
101
103
  self
@@ -123,6 +125,7 @@ module Pwnlib
123
125
  # # little
124
126
  def local(**kwargs)
125
127
  raise ArgumentError, "Need a block for #{self.class}##{__callee__}" unless block_given?
128
+
126
129
  # XXX(Darkpi):
127
130
  # improve performance for this if this is too slow, since we use this in many places that has argument
128
131
  # endian / signed / ...
@@ -183,6 +186,7 @@ module Pwnlib
183
186
  arch = arch.to_s.downcase.gsub(/[[:punct:]]/, '')
184
187
  defaults = ARCHS[arch]
185
188
  raise ArgumentError, "arch must be one of #{ARCHS.keys.sort.inspect}" unless defaults
189
+
186
190
  defaults.each { |k, v| @attrs[k] = v }
187
191
  @attrs[:arch] = arch
188
192
  end
@@ -192,7 +196,8 @@ module Pwnlib
192
196
  # @param [Integer] bits
193
197
  # The word size.
194
198
  def bits=(bits)
195
- raise ArgumentError, "bits must be > 0 (#{bits} given)" unless bits > 0
199
+ raise ArgumentError, "bits must be > 0 (#{bits} given)" unless bits.positive?
200
+
196
201
  @attrs[:bits] = bits
197
202
  end
198
203
 
@@ -219,6 +224,7 @@ module Pwnlib
219
224
  def endian=(endian)
220
225
  endian = ENDIANNESSES[endian.to_s.downcase]
221
226
  raise ArgumentError, "endian must be one of #{ENDIANNESSES.sort.inspect}" if endian.nil?
227
+
222
228
  @attrs[:endian] = endian
223
229
  end
224
230
 
@@ -239,6 +245,7 @@ module Pwnlib
239
245
  log_level = value
240
246
  end
241
247
  raise ArgumentError, "log_level must be an integer or one of #{LOG_LEVELS.inspect}" unless log_level
248
+
242
249
  @attrs[:log_level] = log_level
243
250
  end
244
251
 
@@ -252,6 +259,7 @@ module Pwnlib
252
259
  def os=(os)
253
260
  os = os.to_s.downcase
254
261
  raise ArgumentError, "os must be one of #{OSES.sort.inspect}" unless OSES.include?(os)
262
+
255
263
  @attrs[:os] = os
256
264
  end
257
265
 
@@ -275,6 +283,7 @@ module Pwnlib
275
283
  signed = value
276
284
  end
277
285
  raise ArgumentError, "signed must be boolean or one of #{SIGNEDNESSES.keys.sort.inspect}" if signed.nil?
286
+
278
287
  @attrs[:signed] = signed
279
288
  end
280
289
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'elftools'
4
5
 
@@ -80,6 +81,7 @@ module Pwnlib
80
81
  def build_id
81
82
  build_id_offsets.each do |offset|
82
83
  next unless @leak.n(@libbase + offset + 12, 4) == "GNU\x00"
84
+
83
85
  return @leak.n(@libbase + offset + 16, 20).unpack('H*').first
84
86
  end
85
87
  nil
@@ -105,6 +107,7 @@ module Pwnlib
105
107
  ptr &= PAGE_MASK
106
108
  loop do
107
109
  return @base = ptr if @leak.n(ptr, 4) == "\x7fELF"
110
+
108
111
  ptr -= PAGE_SIZE
109
112
  end
110
113
  end
@@ -116,6 +119,7 @@ module Pwnlib
116
119
  loop do
117
120
  ptype = @leak.d(e_phoff)
118
121
  break if ptype == ELFTools::Constants::PT::PT_DYNAMIC
122
+
119
123
  e_phoff += phdr_size
120
124
  end
121
125
  offset = { 32 => 8, 64 => 16 }[@elfclass]
@@ -134,6 +138,7 @@ module Pwnlib
134
138
  d_addr = unpack(tmp[@elfword, @elfword])
135
139
  break if d_tag.zero?
136
140
  return d_addr if tag == d_tag
141
+
137
142
  ptr += dyn_size
138
143
  end
139
144
  nil
@@ -157,8 +162,8 @@ module Pwnlib
157
162
  def build_id_offsets
158
163
  {
159
164
  i386: [0x174],
160
- arm: [0x174],
161
- thumb: [0x174],
165
+ arm: [0x174],
166
+ thumb: [0x174],
162
167
  aarch64: [0x238],
163
168
  amd64: [0x270, 0x174],
164
169
  powerpc: [0x174],
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ostruct'
2
4
 
3
5
  require 'elftools'
6
+ require 'one_gadget/one_gadget'
4
7
  require 'rainbow'
5
8
 
6
9
  require 'pwnlib/logger'
@@ -40,14 +43,15 @@ module Pwnlib
40
43
  # # PIE: PIE enabled
41
44
  # #=> #<Pwnlib::ELF::ELF:0x00559bd670dcb8>
42
45
  def initialize(path, checksec: true)
43
- path = File.realpath(path)
46
+ @path = File.realpath(path)
44
47
  @elf_file = ELFTools::ELFFile.new(File.open(path, 'rb'))
45
48
  load_got
46
49
  load_plt
47
50
  load_symbols
48
51
  @address = base_address
49
52
  @load_addr = @address
50
- show_info(path) if checksec
53
+ @one_gadgets = nil
54
+ show_info(@path) if checksec
51
55
  end
52
56
 
53
57
  # Set the base address.
@@ -56,6 +60,7 @@ module Pwnlib
56
60
  # got
57
61
  # plt
58
62
  # symbols
63
+ # one_gadgets
59
64
  #
60
65
  # @param [Integer] val
61
66
  # Address to be changed to.
@@ -68,6 +73,7 @@ module Pwnlib
68
73
  [@got, @plt, @symbols].compact.each do |tbl|
69
74
  tbl.each_pair { |k, _| tbl[k] += val - old }
70
75
  end
76
+ @one_gadgets&.map! { |off| off + val - old }
71
77
  end
72
78
 
73
79
  # Return the protection information, wrapper with color codes.
@@ -82,7 +88,7 @@ module Pwnlib
82
88
  none: Rainbow('No RELRO').red
83
89
  }[relro],
84
90
  'Stack:'.ljust(10) + {
85
- true => Rainbow('Canary found').green,
91
+ true => Rainbow('Canary found').green,
86
92
  false => Rainbow('No canary found').red
87
93
  }[canary?],
88
94
  'NX:'.ljust(10) + {
@@ -102,10 +108,13 @@ module Pwnlib
102
108
  def relro
103
109
  return :none unless @elf_file.segment_by_type(:gnu_relro)
104
110
  return :full if dynamic_tag(:bind_now)
111
+
105
112
  flags = dynamic_tag(:flags)
106
113
  return :full if flags && (flags.value & ::ELFTools::Constants::DF_BIND_NOW) != 0
114
+
107
115
  flags1 = dynamic_tag(:flags_1)
108
116
  return :full if flags1 && (flags1.value & ::ELFTools::Constants::DF_1_NOW) != 0
117
+
109
118
  :partial
110
119
  end
111
120
 
@@ -159,6 +168,7 @@ module Pwnlib
159
168
  # #=> true
160
169
  def search(needle)
161
170
  return enum_for(:search, needle) unless block_given?
171
+
162
172
  load_address_fixup = @address - @load_addr
163
173
  stream = @elf_file.stream
164
174
  @elf_file.each_segments do |seg|
@@ -173,6 +183,7 @@ module Pwnlib
173
183
  loop do
174
184
  offset = data.index(needle, offset)
175
185
  break if offset.nil?
186
+
176
187
  yield (addr + offset + load_address_fixup)
177
188
  offset += 1
178
189
  end
@@ -181,6 +192,58 @@ module Pwnlib
181
192
  end
182
193
  alias find search
183
194
 
195
+ # Returns one-gadgets of glibc.
196
+ #
197
+ # @return [Array<Integer>]
198
+ # Returns array of one-gadgets, see examples.
199
+ #
200
+ # @example
201
+ # ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6').one_gadgets[0]
202
+ # #=> 324293 # 0x4f2c5
203
+ #
204
+ # @example
205
+ # libc = ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6')
206
+ # libc.one_gadgets[1]
207
+ # #=> 324386 # 0x4f322
208
+ #
209
+ # libc.address = 0x7fff7fff0000
210
+ # libc.one_gadgets[1]
211
+ # #=> 140735341130530 # 0x7fff8003f322
212
+ #
213
+ # @example
214
+ # libc = ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6')
215
+ # context.log_level = :debug
216
+ # libc.one_gadgets[0]
217
+ # # [DEBUG] 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
218
+ # # constraints:
219
+ # # rcx == NULL
220
+ # #=> 324293
221
+ def one_gadgets
222
+ return @one_gadgets if @one_gadgets
223
+
224
+ gadgets = OneGadget.gadgets(file: @path, details: true, level: 1)
225
+ @one_gadgets = gadgets.map { |g| g.offset + address }
226
+ @one_gadgets.instance_variable_set(:@gadgets, gadgets)
227
+
228
+ class << @one_gadgets
229
+ def [](idx)
230
+ super.tap { log.debug(@gadgets[idx].inspect) }
231
+ end
232
+
233
+ def first
234
+ self[0]
235
+ end
236
+
237
+ def last
238
+ self[-1]
239
+ end
240
+
241
+ include ::Pwnlib::Logger
242
+ end
243
+
244
+ @one_gadgets
245
+ end
246
+
184
247
  private
185
248
 
186
249
  def show_info(path)
@@ -192,7 +255,8 @@ module Pwnlib
192
255
  # @return [ELFTools::Dynamic::Tag, nil]
193
256
  def dynamic_tag(type)
194
257
  dynamic = @elf_file.segment_by_type(:dynamic) || @elf_file.section_by_name('.dynamic')
195
- return nil if dynamic.nil? # No dynamic present, might be static-linked.
258
+ return nil if dynamic.nil? # No dynamic table presents, might be statically linked.
259
+
196
260
  dynamic.tag_by_type(type)
197
261
  end
198
262
 
@@ -202,9 +266,11 @@ module Pwnlib
202
266
  sections_by_types(%i(rel rela)).each do |rel_sec|
203
267
  symtab = @elf_file.section_at(rel_sec.header.sh_link)
204
268
  next unless symtab.respond_to?(:symbol_at)
269
+
205
270
  rel_sec.relocations.each do |rel|
206
271
  symbol = symtab.symbol_at(rel.symbol_index)
207
272
  next if symbol.nil? # Unusual case.
273
+
208
274
  @got[symbol.name] = rel.header.r_offset.to_i
209
275
  end
210
276
  end
@@ -220,15 +286,19 @@ module Pwnlib
220
286
  @plt = nil
221
287
  plt_sec = @elf_file.section_by_name('.plt')
222
288
  return log.warn('No PLT section found, PLT not loaded') if plt_sec.nil?
289
+
223
290
  rel_sec = @elf_file.section_by_name('.rel.plt') || @elf_file.section_by_name('.rela.plt')
224
291
  return log.warn('No REL.PLT section found, PLT not loaded') if rel_sec.nil?
292
+
225
293
  symtab = @elf_file.section_at(rel_sec.header.sh_link)
226
294
  return unless symtab.respond_to?(:symbol_at) # unusual case
295
+
227
296
  @plt = OpenStruct.new
228
297
  address = plt_sec.header.sh_addr.to_i + PLT_OFFSET
229
298
  rel_sec.relocations.each do |rel|
230
299
  symbol = symtab.symbol_at(rel.symbol_index)
231
300
  next if symbol.nil? # unusual case
301
+
232
302
  @plt[symbol.name] = address
233
303
  address += PLT_OFFSET
234
304
  end
@@ -239,11 +309,13 @@ module Pwnlib
239
309
  @symbols = OpenStruct.new
240
310
  @elf_file.each_sections do |section|
241
311
  next unless section.respond_to?(:symbols)
312
+
242
313
  section.each_symbols do |symbol|
243
- # Don't care symbols without name.
314
+ # Don't care symbols without a name.
244
315
  next if symbol.name.empty?
245
316
  next if symbol.header.st_value.zero?
246
- # TODO(david942j): handle symbols with same name.
317
+
318
+ # TODO(david942j): handle symbols with the same name.
247
319
  @symbols[symbol.name] = symbol.header.st_value.to_i
248
320
  end
249
321
  end
@@ -255,6 +327,7 @@ module Pwnlib
255
327
 
256
328
  def base_address
257
329
  return 0 if pie?
330
+
258
331
  # Find the min of PT_LOAD's p_vaddr
259
332
  @elf_file.segments_by_type(:load)
260
333
  .map { |seg| seg.header.p_vaddr }