pwntools 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +88 -11
- data/Rakefile +5 -1
- data/lib/pwn.rb +9 -7
- data/lib/pwnlib/abi.rb +60 -0
- data/lib/pwnlib/asm.rb +146 -0
- data/lib/pwnlib/constants/constant.rb +16 -2
- data/lib/pwnlib/constants/constants.rb +35 -19
- data/lib/pwnlib/constants/linux/amd64.rb +30 -1
- data/lib/pwnlib/context.rb +25 -17
- data/lib/pwnlib/dynelf.rb +117 -54
- data/lib/pwnlib/elf/elf.rb +267 -0
- data/lib/pwnlib/ext/helper.rb +4 -4
- data/lib/pwnlib/logger.rb +87 -0
- data/lib/pwnlib/memleak.rb +58 -29
- data/lib/pwnlib/pwn.rb +19 -8
- data/lib/pwnlib/reg_sort.rb +102 -108
- data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +14 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +31 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +127 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +27 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +64 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +32 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +21 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +14 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +21 -0
- data/lib/pwnlib/shellcraft/generators/helper.rb +106 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +14 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +90 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +39 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +14 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +19 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +26 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +22 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +15 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +15 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +85 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +82 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +69 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +14 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +66 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +52 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
- data/lib/pwnlib/shellcraft/registers.rb +145 -0
- data/lib/pwnlib/shellcraft/shellcraft.rb +67 -0
- data/lib/pwnlib/timer.rb +60 -0
- data/lib/pwnlib/tubes/buffer.rb +96 -0
- data/lib/pwnlib/tubes/sock.rb +95 -0
- data/lib/pwnlib/tubes/tube.rb +270 -0
- data/lib/pwnlib/util/cyclic.rb +95 -94
- data/lib/pwnlib/util/fiddling.rb +256 -220
- data/lib/pwnlib/util/getdents.rb +83 -0
- data/lib/pwnlib/util/hexdump.rb +109 -108
- data/lib/pwnlib/util/lists.rb +55 -0
- data/lib/pwnlib/util/packing.rb +226 -228
- data/lib/pwnlib/util/ruby.rb +18 -0
- data/lib/pwnlib/version.rb +2 -1
- data/test/abi_test.rb +21 -0
- data/test/asm_test.rb +104 -0
- data/test/constants/constant_test.rb +1 -0
- data/test/constants/constants_test.rb +4 -2
- data/test/context_test.rb +1 -0
- data/test/data/echo.rb +20 -0
- data/test/data/elfs/Makefile +22 -0
- data/test/data/elfs/amd64.frelro.elf +0 -0
- data/test/data/elfs/amd64.frelro.pie.elf +0 -0
- data/test/data/elfs/amd64.nrelro.elf +0 -0
- data/test/data/elfs/amd64.prelro.elf +0 -0
- data/test/data/elfs/i386.frelro.pie.elf +0 -0
- data/test/data/elfs/i386.prelro.elf +0 -0
- data/test/data/elfs/source.cpp +19 -0
- data/test/data/flag +1 -0
- data/test/data/lib32/ld.so.2 +0 -0
- data/test/data/lib32/libc.so.6 +0 -0
- data/test/data/lib64/ld.so.2 +0 -0
- data/test/data/lib64/libc.so.6 +0 -0
- data/test/dynelf_test.rb +59 -24
- data/test/elf/elf_test.rb +120 -0
- data/test/ext_test.rb +3 -2
- data/test/files/use_pwnlib.rb +1 -1
- data/test/logger_test.rb +61 -0
- data/test/memleak_test.rb +4 -33
- data/test/reg_sort_test.rb +3 -1
- data/test/shellcraft/infloop_test.rb +26 -0
- data/test/shellcraft/linux/ls_test.rb +108 -0
- data/test/shellcraft/linux/sh_test.rb +119 -0
- data/test/shellcraft/linux/syscalls/execve_test.rb +136 -0
- data/test/shellcraft/linux/syscalls/syscall_test.rb +83 -0
- data/test/shellcraft/memcpy_test.rb +35 -0
- data/test/shellcraft/mov_test.rb +98 -0
- data/test/shellcraft/nop_test.rb +26 -0
- data/test/shellcraft/popad_test.rb +29 -0
- data/test/shellcraft/pushstr_array_test.rb +91 -0
- data/test/shellcraft/pushstr_test.rb +108 -0
- data/test/shellcraft/registers_test.rb +32 -0
- data/test/shellcraft/ret_test.rb +30 -0
- data/test/shellcraft/setregs_test.rb +62 -0
- data/test/shellcraft/shellcraft_test.rb +28 -0
- data/test/test_helper.rb +12 -1
- data/test/timer_test.rb +23 -0
- data/test/tubes/buffer_test.rb +45 -0
- data/test/tubes/sock_test.rb +68 -0
- data/test/tubes/tube_test.rb +241 -0
- data/test/util/cyclic_test.rb +2 -1
- data/test/util/fiddling_test.rb +2 -1
- data/test/util/getdents_test.rb +32 -0
- data/test/util/hexdump_test.rb +7 -9
- data/test/util/lists_test.rb +21 -0
- data/test/util/packing_test.rb +4 -3
- metadata +215 -25
@@ -0,0 +1,270 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'pwnlib/context'
|
4
|
+
require 'pwnlib/logger'
|
5
|
+
require 'pwnlib/timer'
|
6
|
+
require 'pwnlib/tubes/buffer'
|
7
|
+
require 'pwnlib/util/hexdump'
|
8
|
+
|
9
|
+
module Pwnlib
|
10
|
+
module Tubes
|
11
|
+
# Things common to all tubes (sockets, tty, ...)
|
12
|
+
# @!macro [new] timeout_definition
|
13
|
+
# @param [Float] timeout
|
14
|
+
# Any positive floating number, indicates timeout in seconds.
|
15
|
+
# Using +context.timeout+ if +timeout+ equals to +nil+.
|
16
|
+
class Tube
|
17
|
+
BUFSIZE = 4096
|
18
|
+
|
19
|
+
# Instantiate a {Pwnlib::Tubes::Tube} object.
|
20
|
+
#
|
21
|
+
# @!macro timeout_definition
|
22
|
+
def initialize(timeout: nil)
|
23
|
+
@timer = Timer.new(timeout)
|
24
|
+
@buffer = Buffer.new
|
25
|
+
end
|
26
|
+
|
27
|
+
# Receives up to +num_bytes+ bytes of data from the tube, and returns as soon as any quantity
|
28
|
+
# of data is available.
|
29
|
+
#
|
30
|
+
# @param [Integer] num_bytes
|
31
|
+
# The maximum number of bytes to receive.
|
32
|
+
# @!macro timeout_definition
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
# A string contains bytes received from the tube, or +''+ if a timeout occurred while
|
36
|
+
# waiting.
|
37
|
+
def recv(num_bytes = nil, timeout: nil)
|
38
|
+
return '' if @buffer.empty? && !fillbuffer(timeout: timeout)
|
39
|
+
@buffer.get(num_bytes)
|
40
|
+
end
|
41
|
+
alias read recv
|
42
|
+
|
43
|
+
# Puts the specified data back at the beginning of the receive buffer.
|
44
|
+
#
|
45
|
+
# @param [String] data
|
46
|
+
# A string to put back.
|
47
|
+
def unrecv(data)
|
48
|
+
@buffer.unget(data)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Receives one byte at a time from the tube, until the predicate evaluates to +true+.
|
52
|
+
#
|
53
|
+
# @!macro timeout_definition
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
# A string contains bytes received from the tube, or +''+ if a timeout occurred while
|
57
|
+
# waiting.
|
58
|
+
#
|
59
|
+
# @yield
|
60
|
+
# A predicate to evaluate whether the data satisfy the condition.
|
61
|
+
#
|
62
|
+
# @yieldparam [String] data
|
63
|
+
# A string data to be validated by the predicate.
|
64
|
+
#
|
65
|
+
# @yieldreturn [Boolean]
|
66
|
+
# Whether the data satisfy the condition.
|
67
|
+
#
|
68
|
+
# @raise [ArgumentError]
|
69
|
+
# If the block is not given.
|
70
|
+
def recvpred(timeout: nil)
|
71
|
+
raise ArgumentError, 'Need a block for recvpred' unless block_given?
|
72
|
+
@timer.countdown(timeout) do
|
73
|
+
data = ''
|
74
|
+
begin
|
75
|
+
until yield(data)
|
76
|
+
return '' unless @timer.active?
|
77
|
+
|
78
|
+
begin
|
79
|
+
# TODO(Darkpi): Some form of binary search to speed up?
|
80
|
+
c = recv(1)
|
81
|
+
rescue
|
82
|
+
return ''
|
83
|
+
end
|
84
|
+
|
85
|
+
return '' if c.empty?
|
86
|
+
data << c
|
87
|
+
end
|
88
|
+
data.slice!(0..-1)
|
89
|
+
ensure
|
90
|
+
unrecv(data)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Receives exactly +num_bytes+ bytes.
|
96
|
+
# If the request is not satisfied before +timeout+ seconds pass, all data is buffered and an
|
97
|
+
# empty string +''+ is returned.
|
98
|
+
#
|
99
|
+
# @param [Integer] num_bytes
|
100
|
+
# The number of bytes to receive.
|
101
|
+
# @!macro timeout_definition
|
102
|
+
#
|
103
|
+
# @return [String]
|
104
|
+
# A string contains bytes received from the tube, or +''+ if a timeout occurred while
|
105
|
+
# waiting.
|
106
|
+
def recvn(num_bytes, timeout: nil)
|
107
|
+
@timer.countdown(timeout) do
|
108
|
+
fillbuffer while @timer.active? && @buffer.size < num_bytes
|
109
|
+
@buffer.size >= num_bytes ? @buffer.get(num_bytes) : ''
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Receive data until one of +delims+ is encountered. If the request is not satisfied before
|
114
|
+
# +timeout+ seconds pass, all data is buffered and an empty string is returned.
|
115
|
+
#
|
116
|
+
# @param [Array<String>] delims
|
117
|
+
# String of delimiters characters, or list of delimiter strings.
|
118
|
+
# @param [Boalean] drop
|
119
|
+
# Whether drop the ending.
|
120
|
+
# @!macro timeout_definition
|
121
|
+
#
|
122
|
+
# @return [String]
|
123
|
+
# A string contains bytes, which ends string in +delims+, received from the tube.
|
124
|
+
#
|
125
|
+
# @diff We return the string that ends the earliest, rather then starts the earliest,
|
126
|
+
# since the latter can't be done greedly. Still, it would be bad to call this
|
127
|
+
# for case with ambiguity.
|
128
|
+
def recvuntil(delims, drop: false, timeout: nil)
|
129
|
+
delims = Array(delims)
|
130
|
+
max_len = delims.map(&:size).max
|
131
|
+
@timer.countdown(timeout) do
|
132
|
+
data = Buffer.new
|
133
|
+
matching = ''
|
134
|
+
begin
|
135
|
+
while @timer.active?
|
136
|
+
begin
|
137
|
+
s = recv(1)
|
138
|
+
rescue # TODO(Darkpi): QQ
|
139
|
+
return ''
|
140
|
+
end
|
141
|
+
|
142
|
+
return '' if s.empty?
|
143
|
+
matching << s
|
144
|
+
|
145
|
+
sidx = matching.size
|
146
|
+
match_len = 0
|
147
|
+
delims.each do |d|
|
148
|
+
idx = matching.index(d)
|
149
|
+
next unless idx
|
150
|
+
if idx + d.size <= sidx + match_len
|
151
|
+
sidx = idx
|
152
|
+
match_len = d.size
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
if sidx < matching.size
|
157
|
+
r = data.get + matching.slice!(0, sidx + match_len)
|
158
|
+
r.slice!(-match_len..-1) if drop
|
159
|
+
return r
|
160
|
+
end
|
161
|
+
|
162
|
+
data << matching.slice!(0...-max_len) if matching.size > max_len
|
163
|
+
end
|
164
|
+
''
|
165
|
+
ensure
|
166
|
+
unrecv(matching)
|
167
|
+
unrecv(data)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Receive a single line from the tube.
|
173
|
+
# A "line" is any sequence of bytes terminated by the byte sequence set in +context.newline+,
|
174
|
+
# which defaults to +"\n"+.
|
175
|
+
#
|
176
|
+
# @param [Boolean] drop
|
177
|
+
# Whether drop the line ending.
|
178
|
+
# @!macro timeout_definition
|
179
|
+
#
|
180
|
+
# @return [String]
|
181
|
+
# All bytes received over the tube until the first newline is received.
|
182
|
+
# Optionally retains the ending.
|
183
|
+
def recvline(drop: false, timeout: nil)
|
184
|
+
recvuntil(context.newline, drop: drop, timeout: timeout)
|
185
|
+
end
|
186
|
+
alias gets recvline
|
187
|
+
|
188
|
+
# Wrapper around +recvpred+, which will return when a regex matches the string in the buffer.
|
189
|
+
#
|
190
|
+
# @param [Regexp] regex
|
191
|
+
# A regex to match.
|
192
|
+
# @!macro timeout_definition
|
193
|
+
#
|
194
|
+
# @return [String]
|
195
|
+
# A string contains bytes received from the tube, or +''+ if a timeout occurred while
|
196
|
+
# waiting.
|
197
|
+
def recvregex(regex, timeout: nil)
|
198
|
+
recvpred(timeout: timeout) { |data| data =~ regex }
|
199
|
+
end
|
200
|
+
|
201
|
+
# Sends data
|
202
|
+
#
|
203
|
+
# @param [String] data
|
204
|
+
# The +data+ string to send.
|
205
|
+
def send(data)
|
206
|
+
data = data.to_s
|
207
|
+
log.debug(format('Sent %#x bytes:', data.size))
|
208
|
+
log.indented(hexdump(data), level: DEBUG)
|
209
|
+
send_raw(data)
|
210
|
+
end
|
211
|
+
alias write send
|
212
|
+
|
213
|
+
# Sends data with +context.newline+.
|
214
|
+
#
|
215
|
+
# @param [String] data
|
216
|
+
# The +data+ string to send.
|
217
|
+
def sendline(data)
|
218
|
+
# Logged by +write+, not +send_raw+
|
219
|
+
write(data.to_s + context.newline)
|
220
|
+
end
|
221
|
+
alias puts sendline
|
222
|
+
|
223
|
+
# Does simultaneous reading and writing to the tube. In principle this just connects the tube
|
224
|
+
# to standard in and standard out.
|
225
|
+
def interact
|
226
|
+
log.info('Switching to interactive mode')
|
227
|
+
$stdout.write(@buffer.get)
|
228
|
+
until io.closed?
|
229
|
+
rs, = IO.select([$stdin, io])
|
230
|
+
if rs.include?($stdin)
|
231
|
+
s = $stdin.readpartial(BUFSIZE)
|
232
|
+
write(s)
|
233
|
+
end
|
234
|
+
if rs.include?(io)
|
235
|
+
s = recv
|
236
|
+
$stdout.write(s)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def fillbuffer(timeout: nil)
|
244
|
+
data = @timer.countdown(timeout) do
|
245
|
+
self.timeout_raw = @timer.timeout
|
246
|
+
recv_raw(BUFSIZE)
|
247
|
+
end
|
248
|
+
if data
|
249
|
+
@buffer << data
|
250
|
+
log.debug(format('Received %#x bytes:', data.size))
|
251
|
+
log.indented(hexdump(data), level: DEBUG)
|
252
|
+
end
|
253
|
+
data
|
254
|
+
end
|
255
|
+
|
256
|
+
def send_raw(_data); raise NotImplementedError, 'Not implemented'
|
257
|
+
end
|
258
|
+
|
259
|
+
def recv_raw(_size); raise NotImplementedError, 'Not implemented'
|
260
|
+
end
|
261
|
+
|
262
|
+
def timeout_raw=(_timeout); raise NotImplementedError, 'Not implemented'
|
263
|
+
end
|
264
|
+
|
265
|
+
include ::Pwnlib::Context
|
266
|
+
include ::Pwnlib::Logger
|
267
|
+
include ::Pwnlib::Util::HexDump
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
data/lib/pwnlib/util/cyclic.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Pwnlib
|
4
4
|
module Util
|
5
5
|
# Generate string with easy-to-find pattern.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# @example Call by specifying full module path.
|
8
8
|
# require 'pwnlib/util/cyclic'
|
9
9
|
# Pwnlib::Util::Cyclic.cyclic_find(Pwnlib::Util::Cyclic.cyclic(200)[123, 4]) #=> 123
|
@@ -11,110 +11,111 @@ module Pwnlib
|
|
11
11
|
# require 'pwn'
|
12
12
|
# cyclic_find(cyclic(200)[123, 4]) #=> 123
|
13
13
|
module Cyclic
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
ASCII_LOWERCASE = ('a'..'z').to_a.join
|
18
|
-
private_constant :ASCII_LOWERCASE
|
14
|
+
# TODO(Darkpi): Should we put this constant in some 'String' module?
|
15
|
+
ASCII_LOWERCASE = ('a'..'z').to_a.join
|
16
|
+
private_constant :ASCII_LOWERCASE
|
19
17
|
|
20
|
-
|
21
|
-
# This is implemented using a De Bruijn Sequence over the given +alphabet+.
|
22
|
-
# Returns an Enumerator if no block given.
|
23
|
-
#
|
24
|
-
# @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
25
|
-
# @param [String, Array] alphabet
|
26
|
-
# Alphabet to be used.
|
27
|
-
# @param [Integer] n
|
28
|
-
# Length of substring that should be unique.
|
29
|
-
# @return [void]
|
30
|
-
# @yieldparam c
|
31
|
-
# Item of the result sequence in order.
|
32
|
-
# @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
33
|
-
# @param [String, Array] alphabet
|
34
|
-
# Alphabet to be used.
|
35
|
-
# @param [Integer] n
|
36
|
-
# Length of substring that should be unique.
|
37
|
-
# @return [Enumerator]
|
38
|
-
# The result sequence.
|
39
|
-
def de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
40
|
-
return to_enum(__method__, alphabet: alphabet, n: n) { alphabet.size**n } unless block_given?
|
41
|
-
k = alphabet.size
|
42
|
-
a = [0] * (k * n)
|
18
|
+
module_function
|
43
19
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
20
|
+
# Generator for a sequence of unique substrings of length +n+.
|
21
|
+
# This is implemented using a De Bruijn Sequence over the given +alphabet+.
|
22
|
+
# Returns an Enumerator if no block given.
|
23
|
+
#
|
24
|
+
# @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
25
|
+
# @param [String, Array] alphabet
|
26
|
+
# Alphabet to be used.
|
27
|
+
# @param [Integer] n
|
28
|
+
# Length of substring that should be unique.
|
29
|
+
#
|
30
|
+
# @return [void]
|
31
|
+
#
|
32
|
+
# @yieldparam c
|
33
|
+
# Item of the result sequence in order.
|
34
|
+
#
|
35
|
+
# @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
36
|
+
# @param [String, Array] alphabet
|
37
|
+
# Alphabet to be used.
|
38
|
+
# @param [Integer] n
|
39
|
+
# Length of substring that should be unique.
|
40
|
+
#
|
41
|
+
# @return [Enumerator]
|
42
|
+
# The result sequence.
|
43
|
+
def de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
|
44
|
+
return to_enum(__method__, alphabet: alphabet, n: n) { alphabet.size**n } unless block_given?
|
45
|
+
k = alphabet.size
|
46
|
+
a = [0] * (k * n)
|
47
|
+
|
48
|
+
db = lambda do |t, p|
|
49
|
+
if t > n
|
50
|
+
(1..p).each { |j| yield alphabet[a[j]] } if (n % p).zero?
|
51
|
+
else
|
52
|
+
a[t] = a[t - p]
|
53
|
+
db.call(t + 1, p)
|
54
|
+
(a[t - p] + 1...k).each do |j|
|
55
|
+
a[t] = j
|
56
|
+
db.call(t + 1, t)
|
54
57
|
end
|
55
58
|
end
|
56
|
-
|
57
|
-
db[1, 1]
|
58
59
|
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
# @param [Integer, nil] length
|
63
|
-
# Desired length of the sequence,
|
64
|
-
# or +nil+ for the entire sequence.
|
65
|
-
# @param [String, Array] alphabet
|
66
|
-
# Alphabet to be used.
|
67
|
-
# @param [Integer] n
|
68
|
-
# Length of substring that should be unique.
|
69
|
-
# @return [String, Array]
|
70
|
-
# The result sequence of at most +length+ items,
|
71
|
-
# with same type as +alphabet+.
|
72
|
-
#
|
73
|
-
# @example
|
74
|
-
# cyclic(alphabet: 'ABC', n: 3) #=> 'AAABAACABBABCACBACCBBBCBCCC'
|
75
|
-
# cyclic(20) #=> 'aaaabaaacaaadaaaeaaa'
|
76
|
-
def cyclic(length = nil, alphabet: ASCII_LOWERCASE, n: 4)
|
77
|
-
enum = de_bruijn(alphabet: alphabet, n: n)
|
78
|
-
r = length.nil? ? enum.to_a : enum.take(length)
|
79
|
-
alphabet.is_a?(String) ? r.join : r
|
80
|
-
end
|
61
|
+
db[1, 1]
|
62
|
+
end
|
81
63
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
64
|
+
# Simple wrapper over {.de_bruijn}, returning at most +length+ items.
|
65
|
+
#
|
66
|
+
# @param [Integer, nil] length
|
67
|
+
# Desired length of the sequence, or +nil+ for the entire sequence.
|
68
|
+
# @param [String, Array] alphabet
|
69
|
+
# Alphabet to be used.
|
70
|
+
# @param [Integer] n
|
71
|
+
# Length of substring that should be unique.
|
72
|
+
#
|
73
|
+
# @return [String, Array]
|
74
|
+
# The result sequence of at most +length+ items, with same type as +alphabet+.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# cyclic(alphabet: 'ABC', n: 3) #=> 'AAABAACABBABCACBACCBBBCBCCC'
|
78
|
+
# cyclic(20) #=> 'aaaabaaacaaadaaaeaaa'
|
79
|
+
def cyclic(length = nil, alphabet: ASCII_LOWERCASE, n: 4)
|
80
|
+
enum = de_bruijn(alphabet: alphabet, n: n)
|
81
|
+
r = length.nil? ? enum.to_a : enum.take(length)
|
82
|
+
alphabet.is_a?(String) ? r.join : r
|
83
|
+
end
|
102
84
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
85
|
+
# Find the position of a substring in a De Bruijn sequence.
|
86
|
+
#
|
87
|
+
# @param [String, Array] subseq
|
88
|
+
# The substring to be found in the sequence.
|
89
|
+
# @param [String, Array] alphabet
|
90
|
+
# Alphabet to be used.
|
91
|
+
# @param [Integer] n
|
92
|
+
# Length of substring that should be unique.
|
93
|
+
# Default to +subseq.size+.
|
94
|
+
#
|
95
|
+
# @return [Integer, nil]
|
96
|
+
# The index +subseq+ first appear in the sequence, or +nil+ if not found.
|
97
|
+
#
|
98
|
+
# @todo Speed! See comment in Python pwntools.
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# cyclic_find(cyclic(300)[217, 4]) #=> 217
|
102
|
+
def cyclic_find(subseq, alphabet: ASCII_LOWERCASE, n: nil)
|
103
|
+
n ||= subseq.size
|
104
|
+
subseq = subseq.chars if subseq.is_a?(String)
|
105
|
+
return nil unless subseq.all? { |c| alphabet.include?(c) }
|
106
|
+
|
107
|
+
pos = 0
|
108
|
+
saved = []
|
109
|
+
de_bruijn(alphabet: alphabet, n: n).each do |c|
|
110
|
+
saved << c
|
111
|
+
if saved.size > subseq.size
|
112
|
+
saved.shift
|
113
|
+
pos += 1
|
112
114
|
end
|
113
|
-
|
115
|
+
return pos if saved == subseq
|
114
116
|
end
|
117
|
+
nil
|
115
118
|
end
|
116
|
-
|
117
|
-
extend ClassMethods
|
118
119
|
end
|
119
120
|
end
|
120
121
|
end
|