pwntools 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|