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.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/pwn.rb +1 -0
- data/lib/pwnlib/abi.rb +1 -0
- data/lib/pwnlib/asm.rb +83 -42
- data/lib/pwnlib/constants/constant.rb +4 -1
- data/lib/pwnlib/constants/constants.rb +3 -0
- data/lib/pwnlib/constants/linux/amd64.rb +2 -0
- data/lib/pwnlib/constants/linux/i386.rb +2 -0
- data/lib/pwnlib/context.rb +10 -1
- data/lib/pwnlib/dynelf.rb +7 -2
- data/lib/pwnlib/elf/elf.rb +79 -6
- data/lib/pwnlib/errors.rb +3 -2
- data/lib/pwnlib/ext/array.rb +2 -1
- data/lib/pwnlib/ext/helper.rb +3 -2
- data/lib/pwnlib/ext/integer.rb +2 -1
- data/lib/pwnlib/ext/string.rb +3 -2
- data/lib/pwnlib/logger.rb +21 -1
- data/lib/pwnlib/memleak.rb +1 -0
- data/lib/pwnlib/pwn.rb +5 -1
- data/lib/pwnlib/reg_sort.rb +5 -0
- data/lib/pwnlib/runner.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +5 -1
- data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +4 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +3 -1
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/helper.rb +11 -2
- data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +3 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +3 -2
- data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +5 -3
- data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +8 -6
- data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +3 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +2 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +1 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +10 -10
- data/lib/pwnlib/shellcraft/registers.rb +5 -1
- data/lib/pwnlib/shellcraft/shellcraft.rb +8 -3
- data/lib/pwnlib/timer.rb +6 -2
- data/lib/pwnlib/tubes/buffer.rb +4 -1
- data/lib/pwnlib/tubes/process.rb +2 -0
- data/lib/pwnlib/tubes/serialtube.rb +3 -1
- data/lib/pwnlib/tubes/sock.rb +7 -1
- data/lib/pwnlib/tubes/tube.rb +23 -3
- data/lib/pwnlib/ui.rb +21 -0
- data/lib/pwnlib/util/cyclic.rb +2 -0
- data/lib/pwnlib/util/fiddling.rb +37 -5
- data/lib/pwnlib/util/getdents.rb +1 -0
- data/lib/pwnlib/util/hexdump.rb +8 -5
- data/lib/pwnlib/util/lists.rb +3 -0
- data/lib/pwnlib/util/packing.rb +5 -2
- data/lib/pwnlib/util/ruby.rb +1 -0
- data/lib/pwnlib/version.rb +2 -1
- data/test/abi_test.rb +1 -0
- data/test/asm_test.rb +75 -85
- data/test/constants/constant_test.rb +1 -0
- data/test/constants/constants_test.rb +1 -0
- data/test/context_test.rb +1 -0
- data/test/data/assembly/aarch64.s +19 -0
- data/test/data/assembly/amd64.s +21 -0
- data/test/data/assembly/arm.s +9 -0
- data/test/data/assembly/i386.s +21 -0
- data/test/data/assembly/mips.s +16 -0
- data/test/data/assembly/mips64.s +6 -0
- data/test/data/assembly/powerpc.s +18 -0
- data/test/data/assembly/powerpc64.s +36 -0
- data/test/data/assembly/sparc.s +33 -0
- data/test/data/assembly/sparc64.s +5 -0
- data/test/data/assembly/thumb.s +37 -0
- data/test/data/echo.rb +1 -0
- data/test/dynelf_test.rb +3 -1
- data/test/elf/elf_test.rb +18 -0
- data/test/ext_test.rb +1 -0
- data/test/files/use_pwn.rb +1 -0
- data/test/files/use_pwnlib.rb +1 -0
- data/test/full_file_test.rb +6 -0
- data/test/logger_test.rb +24 -3
- data/test/memleak_test.rb +1 -0
- data/test/reg_sort_test.rb +1 -0
- data/test/runner_test.rb +32 -0
- data/test/shellcraft/infloop_test.rb +1 -0
- data/test/shellcraft/linux/cat_test.rb +1 -0
- data/test/shellcraft/linux/ls_test.rb +1 -0
- data/test/shellcraft/linux/sh_test.rb +1 -0
- data/test/shellcraft/linux/sleep_test.rb +68 -0
- data/test/shellcraft/linux/syscalls/execve_test.rb +1 -0
- data/test/shellcraft/linux/syscalls/exit_test.rb +1 -0
- data/test/shellcraft/linux/syscalls/open_test.rb +1 -0
- data/test/shellcraft/linux/syscalls/syscall_test.rb +1 -0
- data/test/shellcraft/memcpy_test.rb +20 -5
- data/test/shellcraft/mov_test.rb +1 -0
- data/test/shellcraft/nop_test.rb +1 -0
- data/test/shellcraft/popad_test.rb +1 -0
- data/test/shellcraft/pushstr_array_test.rb +1 -0
- data/test/shellcraft/pushstr_test.rb +1 -0
- data/test/shellcraft/registers_test.rb +1 -0
- data/test/shellcraft/ret_test.rb +1 -0
- data/test/shellcraft/setregs_test.rb +9 -8
- data/test/shellcraft/shellcraft_test.rb +1 -0
- data/test/test_helper.rb +28 -0
- data/test/timer_test.rb +2 -1
- data/test/tubes/buffer_test.rb +1 -0
- data/test/tubes/process_test.rb +8 -2
- data/test/tubes/serialtube_test.rb +1 -4
- data/test/tubes/sock_test.rb +1 -0
- data/test/tubes/tube_test.rb +10 -1
- data/test/ui_test.rb +18 -0
- data/test/util/cyclic_test.rb +1 -0
- data/test/util/fiddling_test.rb +8 -0
- data/test/util/getdents_test.rb +1 -0
- data/test/util/hexdump_test.rb +2 -1
- data/test/util/lists_test.rb +1 -0
- data/test/util/packing_test.rb +3 -2
- metadata +119 -59
data/lib/pwnlib/ui.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'pwnlib/logger'
|
|
5
|
+
|
|
6
|
+
module Pwnlib
|
|
7
|
+
# This module collects utilities that need user interactions.
|
|
8
|
+
module UI
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Waits for user input.
|
|
12
|
+
#
|
|
13
|
+
# @return [void]
|
|
14
|
+
def pause
|
|
15
|
+
log.info('Paused (press enter to continue)')
|
|
16
|
+
$stdin.gets
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
include ::Pwnlib::Logger
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/pwnlib/util/cyclic.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Pwnlib
|
|
4
5
|
module Util
|
|
@@ -42,6 +43,7 @@ module Pwnlib
|
|
|
42
43
|
# The result sequence.
|
|
43
44
|
def de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
|
44
45
|
return to_enum(__method__, alphabet: alphabet, n: n) { alphabet.size**n } unless block_given?
|
|
46
|
+
|
|
45
47
|
k = alphabet.size
|
|
46
48
|
a = [0] * (k * n)
|
|
47
49
|
|
data/lib/pwnlib/util/fiddling.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'pwnlib/context'
|
|
4
5
|
|
|
@@ -56,7 +57,7 @@ module Pwnlib
|
|
|
56
57
|
# hex(-10) #=> '-0xa'
|
|
57
58
|
# hex(0xfaceb00cdeadbeef) #=> '0xfaceb00cdeadbeef'
|
|
58
59
|
def hex(n)
|
|
59
|
-
(n
|
|
60
|
+
(n.negative? ? '-' : '') + format('0x%x', n.abs)
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# URL-encodes a string.
|
|
@@ -92,7 +93,7 @@ module Pwnlib
|
|
|
92
93
|
# urldecode('%qw%er%ty') #=> raise ArgumentError
|
|
93
94
|
# urldecode('%qw%er%ty', true) #=> '%qw%er%ty'
|
|
94
95
|
def urldecode(s, ignore_invalid = false)
|
|
95
|
-
res = ''
|
|
96
|
+
res = +''
|
|
96
97
|
n = 0
|
|
97
98
|
while n < s.size
|
|
98
99
|
if s[n] != '%'
|
|
@@ -139,12 +140,13 @@ module Pwnlib
|
|
|
139
140
|
is_little = context.endian == 'little'
|
|
140
141
|
case s
|
|
141
142
|
when String
|
|
142
|
-
v = 'B*'
|
|
143
|
+
v = +'B*'
|
|
143
144
|
v.downcase! if is_little
|
|
144
145
|
s.unpack(v)[0].chars.map { |ch| ch == '1' ? one : zero }
|
|
145
146
|
when Integer
|
|
146
147
|
# TODO(Darkpi): What should we do to negative number?
|
|
147
148
|
raise ArgumentError, 's must be non-negative' unless s >= 0
|
|
149
|
+
|
|
148
150
|
r = s.to_s(2).chars.map { |ch| ch == '1' ? one : zero }
|
|
149
151
|
r.unshift(zero) until (r.size % 8).zero?
|
|
150
152
|
is_little ? r.reverse : r
|
|
@@ -265,6 +267,35 @@ module Pwnlib
|
|
|
265
267
|
s.unpack('m0')[0]
|
|
266
268
|
end
|
|
267
269
|
|
|
270
|
+
# Xor two strings.
|
|
271
|
+
# If two strings have different length, the shorter one will be repeated until has the same length as another
|
|
272
|
+
# one.
|
|
273
|
+
#
|
|
274
|
+
# @param [String] s1
|
|
275
|
+
# First string.
|
|
276
|
+
# @param [String] s2
|
|
277
|
+
# Second string.
|
|
278
|
+
#
|
|
279
|
+
# @return [String]
|
|
280
|
+
# The xor-ed result.
|
|
281
|
+
#
|
|
282
|
+
# @example
|
|
283
|
+
# xor("\xE8\xE1\xF0\xF0\xF9", "\x80")
|
|
284
|
+
# => 'happy'
|
|
285
|
+
#
|
|
286
|
+
# xor("\x80", "\xE8\xE1\xF0\xF0\xF9")
|
|
287
|
+
# => 'happy'
|
|
288
|
+
#
|
|
289
|
+
# xor('plaintext', 'thekey')
|
|
290
|
+
# => "\x04\x04\x04\x02\v\r\x11\x10\x11"
|
|
291
|
+
#
|
|
292
|
+
# xor('217', "\x00" * 10)
|
|
293
|
+
# => '2172172172'
|
|
294
|
+
def xor(s1, s2)
|
|
295
|
+
s1, s2 = s2, s1 if s1.size < s2.size
|
|
296
|
+
s1.bytes.zip(''.ljust(s1.size, s2).bytes).map { |a, b| a ^ b }.pack('C*')
|
|
297
|
+
end
|
|
298
|
+
|
|
268
299
|
# Find two strings that will xor into a given string, while only using a given alphabet.
|
|
269
300
|
#
|
|
270
301
|
# @param [String, Integer] data
|
|
@@ -280,12 +311,13 @@ module Pwnlib
|
|
|
280
311
|
def xor_pair(data, avoid: "\x00\n")
|
|
281
312
|
data = pack(data) if data.is_a?(Integer)
|
|
282
313
|
alphabet = 256.times.reject { |c| avoid.include?(c.chr) }
|
|
283
|
-
res1 = ''
|
|
284
|
-
res2 = ''
|
|
314
|
+
res1 = +''
|
|
315
|
+
res2 = +''
|
|
285
316
|
data.bytes.each do |c1|
|
|
286
317
|
# alphabet.shuffle! if context.randomize
|
|
287
318
|
c2 = alphabet.find { |c| alphabet.include?(c1 ^ c) }
|
|
288
319
|
return nil if c2.nil?
|
|
320
|
+
|
|
289
321
|
res1 << c2.chr
|
|
290
322
|
res2 << (c1 ^ c2).chr
|
|
291
323
|
end
|
data/lib/pwnlib/util/getdents.rb
CHANGED
data/lib/pwnlib/util/hexdump.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'rainbow'
|
|
4
5
|
|
|
@@ -18,7 +19,7 @@ module Pwnlib
|
|
|
18
19
|
# hexdump('217')
|
|
19
20
|
# #=> "00000000 32 31 37 │217│\n00000003"
|
|
20
21
|
module HexDump
|
|
21
|
-
MARKER = "\u2502"
|
|
22
|
+
MARKER = "\u2502"
|
|
22
23
|
HIGHLIGHT_STYLE = ->(s) { Rainbow(s).bg(:red) }
|
|
23
24
|
DEFAULT_STYLE = {
|
|
24
25
|
0x00 => ->(s) { Rainbow(s).red },
|
|
@@ -67,7 +68,8 @@ module Pwnlib
|
|
|
67
68
|
style = DEFAULT_STYLE.merge(style)
|
|
68
69
|
highlight.bytes.each { |b| style[b] = HIGHLIGHT_STYLE }
|
|
69
70
|
(0..255).each do |b|
|
|
70
|
-
next if style.
|
|
71
|
+
next if style.key?(b)
|
|
72
|
+
|
|
71
73
|
style[b] = (b.chr =~ /[[:print:]]/ ? style[:printable] : style[:unprintable])
|
|
72
74
|
end
|
|
73
75
|
|
|
@@ -83,12 +85,13 @@ module Pwnlib
|
|
|
83
85
|
|
|
84
86
|
byte_index = offset
|
|
85
87
|
skipping = false
|
|
86
|
-
last_chunk = ''
|
|
88
|
+
last_chunk = +''
|
|
87
89
|
|
|
88
90
|
loop do
|
|
89
91
|
# We assume that chunk is in ASCII-8BIT encoding.
|
|
90
92
|
chunk = io.read(width)
|
|
91
93
|
break unless chunk
|
|
94
|
+
|
|
92
95
|
chunk_bytes = chunk.bytes
|
|
93
96
|
start_byte_index = byte_index
|
|
94
97
|
byte_index += chunk_bytes.size
|
|
@@ -102,8 +105,8 @@ module Pwnlib
|
|
|
102
105
|
skipping = false
|
|
103
106
|
last_chunk = chunk
|
|
104
107
|
|
|
105
|
-
hex_bytes = ''
|
|
106
|
-
printable = ''
|
|
108
|
+
hex_bytes = +''
|
|
109
|
+
printable = +''
|
|
107
110
|
chunk_bytes.each_with_index do |b, i|
|
|
108
111
|
left_hex, right_char = styled_bytes[b]
|
|
109
112
|
hex_bytes << left_hex
|
data/lib/pwnlib/util/lists.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'pwnlib/context'
|
|
4
5
|
|
|
@@ -37,6 +38,7 @@ module Pwnlib
|
|
|
37
38
|
unless %i(ignore drop fill).include?(underfull_action)
|
|
38
39
|
raise ArgumentError, 'underfull_action expect to be one of :ignore, :drop, and :fill'
|
|
39
40
|
end
|
|
41
|
+
|
|
40
42
|
sliced = str.chars.each_slice(n).map(&:join)
|
|
41
43
|
case underfull_action
|
|
42
44
|
when :drop
|
|
@@ -45,6 +47,7 @@ module Pwnlib
|
|
|
45
47
|
remain = n - sliced.last.size
|
|
46
48
|
fill_value = fill_value.to_s
|
|
47
49
|
raise ArgumentError, 'fill_value must be a character' unless fill_value.size == 1
|
|
50
|
+
|
|
48
51
|
sliced.last.concat(fill_value * remain)
|
|
49
52
|
end
|
|
50
53
|
sliced
|
data/lib/pwnlib/util/packing.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'pwnlib/context'
|
|
4
5
|
|
|
@@ -71,7 +72,8 @@ module Pwnlib
|
|
|
71
72
|
end
|
|
72
73
|
else
|
|
73
74
|
if is_all
|
|
74
|
-
raise ArgumentError, "Can't pack negative number with bits='all' and signed=false" if number
|
|
75
|
+
raise ArgumentError, "Can't pack negative number with bits='all' and signed=false" if number.negative?
|
|
76
|
+
|
|
75
77
|
bits = number.zero? ? 8 : ((number.bit_length - 1) | 7) + 1
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -197,6 +199,7 @@ module Pwnlib
|
|
|
197
199
|
bytes = bits / 8
|
|
198
200
|
|
|
199
201
|
raise ArgumentError, "data.size=#{data.size} must be a multiple of bytes=#{bytes}" if data.size % bytes != 0
|
|
202
|
+
|
|
200
203
|
ret = []
|
|
201
204
|
(data.size / bytes).times do |idx|
|
|
202
205
|
x1 = idx * bytes
|
|
@@ -259,7 +262,7 @@ module Pwnlib
|
|
|
259
262
|
v = case it
|
|
260
263
|
when Array then flat(*it, **kwargs, &preprocessor)
|
|
261
264
|
when Integer then p[it]
|
|
262
|
-
when String then it.force_encoding('ASCII-8BIT')
|
|
265
|
+
when String then it.dup.force_encoding('ASCII-8BIT') # dup in case 'it' is frozen
|
|
263
266
|
else
|
|
264
267
|
raise ArgumentError, "flat does not support values of type #{it.class}"
|
|
265
268
|
end
|
data/lib/pwnlib/util/ruby.rb
CHANGED
data/lib/pwnlib/version.rb
CHANGED
data/test/abi_test.rb
CHANGED
data/test/asm_test.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# encoding: ASCII-8BIT
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'test_helper'
|
|
4
5
|
|
|
5
6
|
require 'pwnlib/asm'
|
|
6
7
|
require 'pwnlib/shellcraft/shellcraft'
|
|
8
|
+
require 'pwnlib/tubes/process'
|
|
7
9
|
|
|
8
10
|
class AsmTest < MiniTest::Test
|
|
9
11
|
include ::Pwnlib::Context
|
|
@@ -13,98 +15,88 @@ class AsmTest < MiniTest::Test
|
|
|
13
15
|
@shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
def parse_sfile(filename)
|
|
19
|
+
File.read(filename).split("\n\n").each do |it|
|
|
20
|
+
lines = it.lines
|
|
21
|
+
metadata = {}
|
|
22
|
+
# First line of +lines+ might be the extra context setting
|
|
23
|
+
if lines.first.start_with?('# context: ')
|
|
24
|
+
# "# context: arch: a, endian: big"
|
|
25
|
+
# => { arch: 'a', endian: 'big' }
|
|
26
|
+
metadata = lines.shift.slice(11..-1)
|
|
27
|
+
.split(',').map { |c| c.split(':', 2).map(&:strip) }
|
|
28
|
+
.map { |k, v| [k.to_sym, v] }.to_h
|
|
29
|
+
end
|
|
30
|
+
comment, output = lines.partition { |l| l =~ /^\s*[;#]/ }.map(&:join)
|
|
31
|
+
next if output.empty?
|
|
32
|
+
|
|
33
|
+
output << "\n" unless output.end_with?("\n")
|
|
34
|
+
tests = output.lines.map do |l|
|
|
35
|
+
vma, hex_code, _dummy, inst = l.scan(/^\s*(\w+):\s{3}(([\da-f]{2}\s)+)\s+(.*)$/).first
|
|
36
|
+
[vma.to_i(16), hex_code.split.join, inst.strip]
|
|
37
|
+
end
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
vma = tests.first.first
|
|
40
|
+
bytes = [tests.map { |l| l[1] }.join].pack('H*')
|
|
41
|
+
insts = tests.map(&:last)
|
|
42
|
+
yield(bytes, vma, insts, output, comment, **metadata)
|
|
43
|
+
end
|
|
22
44
|
end
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
# All tests of asm can be found under test/data/assembly/<arch>.s.
|
|
47
|
+
%w[aarch64 amd64 arm i386 mips mips64 powerpc powerpc64 sparc sparc64 thumb].each do |arch|
|
|
48
|
+
file = File.join(__dir__, 'data', 'assembly', arch + '.s')
|
|
49
|
+
# Defining methods dynamically makes proper error message shown when tests failed.
|
|
50
|
+
__send__(:define_method, "test_asm_#{arch}") do
|
|
51
|
+
skip_windows
|
|
52
|
+
|
|
53
|
+
context.local(arch: arch) do
|
|
54
|
+
parse_sfile(file) do |bytes, vma, insts, _output, comment, **ctx|
|
|
55
|
+
next if comment.include?('!skip asm')
|
|
56
|
+
|
|
57
|
+
context.local(**ctx) do
|
|
58
|
+
assert_equal(bytes, Asm.asm(insts.join("\n"), vma: vma))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
33
62
|
end
|
|
34
63
|
end
|
|
35
64
|
|
|
36
|
-
def
|
|
65
|
+
def test_asm_unsupported
|
|
37
66
|
skip_windows
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assert_equal("jhH\xb8/bin///sPj;XH\x89\xe71\xf6\x99\x0f\x05", Asm.asm(@shellcraft.sh))
|
|
42
|
-
assert_equal("j\x01\xfe\x0c$H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8\xfe\xfe\xfe\xfe\xfe\xfe\x0b\xfeH1\x04$",
|
|
43
|
-
Asm.asm(@shellcraft.pushstr("\xff\xff\xff\xff\xff\xff\x0a\xff")))
|
|
67
|
+
|
|
68
|
+
err = context.local(arch: :vax) do
|
|
69
|
+
assert_raises(::Pwnlib::Errors::UnsupportedArchError) { Asm.asm('') }
|
|
44
70
|
end
|
|
71
|
+
assert_equal('Asm on architecture "vax" is not supported yet.', err.message)
|
|
45
72
|
end
|
|
46
73
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
19: 68 2f 2f 2f 73 push 0x732f2f2f
|
|
64
|
-
1e: 68 2f 62 69 6e push 0x6e69622f
|
|
65
|
-
23: 6a 0b push 0xb
|
|
66
|
-
25: 58 pop eax
|
|
67
|
-
26: 89 e3 mov ebx, esp
|
|
68
|
-
28: 89 d1 mov ecx, edx
|
|
69
|
-
2a: 99 cdq
|
|
70
|
-
2b: cd 80 int 0x80
|
|
71
|
-
EOS
|
|
72
|
-
assert_equal(<<-EOS, Asm.disasm("\xb8\x5d\x00\x00\x00"))
|
|
73
|
-
0: b8 5d 00 00 00 mov eax, 0x5d
|
|
74
|
-
EOS
|
|
74
|
+
# All tests of disasm can be found under test/data/assembly/<arch>.s.
|
|
75
|
+
%w[aarch64 amd64 arm i386 mips mips64 powerpc64 sparc sparc64 thumb].each do |arch|
|
|
76
|
+
file = File.join(__dir__, 'data', 'assembly', arch + '.s')
|
|
77
|
+
# Defining methods dynamically makes proper error message shown when tests failed.
|
|
78
|
+
__send__(:define_method, "test_disasm_#{arch}") do
|
|
79
|
+
skip_windows
|
|
80
|
+
|
|
81
|
+
context.local(arch: arch) do
|
|
82
|
+
parse_sfile(file) do |bytes, vma, _insts, output, comment, **ctx|
|
|
83
|
+
next if comment.include?('!skip disasm')
|
|
84
|
+
|
|
85
|
+
context.local(**ctx) do
|
|
86
|
+
assert_equal(output, Asm.disasm(bytes, vma: vma))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
75
90
|
end
|
|
76
91
|
end
|
|
77
92
|
|
|
78
|
-
def
|
|
93
|
+
def test_disasm_unsupported
|
|
79
94
|
skip_windows
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
assert_equal(<<-EOS, str)
|
|
85
|
-
fff: 68 72 69 01 01 push 0x1016972
|
|
86
|
-
1004: 81 34 24 01 01 01 01 xor dword ptr [rsp], 0x1010101
|
|
87
|
-
100b: 31 d2 xor edx, edx
|
|
88
|
-
100d: 52 push rdx
|
|
89
|
-
100e: 6a 08 push 8
|
|
90
|
-
1010: 5a pop rdx
|
|
91
|
-
1011: 48 01 e2 add rdx, rsp
|
|
92
|
-
1014: 52 push rdx
|
|
93
|
-
1015: 48 89 e2 mov rdx, rsp
|
|
94
|
-
1018: 6a 68 push 0x68
|
|
95
|
-
101a: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f
|
|
96
|
-
1024: 50 push rax
|
|
97
|
-
1025: 6a 3b push 0x3b
|
|
98
|
-
1027: 58 pop rax
|
|
99
|
-
1028: 48 89 e7 mov rdi, rsp
|
|
100
|
-
102b: 48 89 d6 mov rsi, rdx
|
|
101
|
-
102e: 99 cdq
|
|
102
|
-
102f: 0f 05 syscall
|
|
103
|
-
EOS
|
|
104
|
-
assert_equal(<<-EOS, Asm.disasm("\xb8\x17\x00\x00\x00"))
|
|
105
|
-
0: b8 17 00 00 00 mov eax, 0x17
|
|
106
|
-
EOS
|
|
95
|
+
|
|
96
|
+
err = context.local(arch: :vax) do
|
|
97
|
+
assert_raises(::Pwnlib::Errors::UnsupportedArchError) { Asm.disasm('') }
|
|
107
98
|
end
|
|
99
|
+
assert_equal('Disasm on architecture "vax" is not supported yet.', err.message)
|
|
108
100
|
end
|
|
109
101
|
|
|
110
102
|
# To ensure coverage
|
|
@@ -115,8 +107,8 @@ class AsmTest < MiniTest::Test
|
|
|
115
107
|
assert_match(/meow/, err.message)
|
|
116
108
|
end
|
|
117
109
|
|
|
118
|
-
def make_elf_file(
|
|
119
|
-
elf = Asm.make_elf(
|
|
110
|
+
def make_elf_file(data, **kwargs)
|
|
111
|
+
elf = Asm.make_elf(data, **kwargs)
|
|
120
112
|
stream = StringIO.new(elf)
|
|
121
113
|
[elf, ::ELFTools::ELFFile.new(stream)]
|
|
122
114
|
end
|
|
@@ -167,19 +159,17 @@ class AsmTest < MiniTest::Test
|
|
|
167
159
|
# this test can be removed after method +run_shellcode+ being implemented
|
|
168
160
|
def test_make_elf_and_run
|
|
169
161
|
# run the ELF we created to make sure it works.
|
|
170
|
-
linux_only
|
|
162
|
+
linux_only 'ELF can only be executed on Linux'
|
|
171
163
|
|
|
172
164
|
# test supported architecture
|
|
173
165
|
{
|
|
174
|
-
i386: /08048000-08049000
|
|
175
|
-
amd64: /00400000-00401000
|
|
166
|
+
i386: /08048000-08049000 rwxp/,
|
|
167
|
+
amd64: /00400000-00401000 rwxp/
|
|
176
168
|
}.each do |arch, regexp|
|
|
177
169
|
context.local(arch: arch) do
|
|
178
170
|
data = Asm.asm(@shellcraft.cat('/proc/self/maps') + @shellcraft.syscall('SYS_exit', 0))
|
|
179
171
|
Asm.make_elf(data) do |path|
|
|
180
|
-
|
|
181
|
-
assert_match(regexp, o.gets)
|
|
182
|
-
end
|
|
172
|
+
assert_match(regexp, ::Pwnlib::Tubes::Process.new(path).gets)
|
|
183
173
|
end
|
|
184
174
|
end
|
|
185
175
|
end
|