rubytube 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +3 -3
- data/.standard.yml +3 -3
- data/CHANGELOG.md +5 -5
- data/CODE_OF_CONDUCT.md +84 -84
- data/Gemfile +12 -12
- data/LICENSE.txt +21 -21
- data/README.md +28 -19
- data/Rakefile +10 -10
- data/lib/rubytube/cipher.rb +370 -371
- data/lib/rubytube/client.rb +173 -173
- data/lib/rubytube/error.rb +49 -0
- data/lib/rubytube/extractor.rb +171 -177
- data/lib/rubytube/innertube.rb +105 -105
- data/lib/rubytube/monostate.rb +5 -5
- data/lib/rubytube/parser.rb +159 -164
- data/lib/rubytube/request.rb +73 -75
- data/lib/rubytube/stream.rb +95 -97
- data/lib/rubytube/stream_format.rb +152 -152
- data/lib/rubytube/stream_query.rb +61 -36
- data/lib/rubytube/utils.rb +24 -24
- data/lib/rubytube/version.rb +5 -5
- data/lib/rubytube.rb +27 -68
- data/sig/rubytube.rbs +4 -4
- metadata +7 -6
data/lib/rubytube/cipher.rb
CHANGED
@@ -1,371 +1,370 @@
|
|
1
|
-
module RubyTube
|
2
|
-
class Cipher
|
3
|
-
attr_accessor :transform_plan, :transform_map, :js_func_patterns, :throttling_plan, :throttling_array, :calculation_plan, :calculation_n
|
4
|
-
|
5
|
-
def initialize(js)
|
6
|
-
self.transform_plan = get_transform_plan(js)
|
7
|
-
|
8
|
-
var_regex = %r
|
9
|
-
var_match = @transform_plan[0].match(var_regex)
|
10
|
-
|
11
|
-
if var_match.nil?
|
12
|
-
raise "RegexMatchError, caller: __init__, pattern: #{var_regex.source}"
|
13
|
-
end
|
14
|
-
|
15
|
-
var = var_match[0][0..-2]
|
16
|
-
|
17
|
-
self.transform_map = get_transform_map(js, var)
|
18
|
-
|
19
|
-
self.js_func_patterns = [
|
20
|
-
%r
|
21
|
-
%r
|
22
|
-
]
|
23
|
-
|
24
|
-
self.throttling_array = get_throttling_function_array(js)
|
25
|
-
self.throttling_plan = get_throttling_plan(js)
|
26
|
-
end
|
27
|
-
|
28
|
-
def calculate_n(initial_n)
|
29
|
-
throttling_array.map! do |item|
|
30
|
-
item ==
|
31
|
-
end
|
32
|
-
|
33
|
-
throttling_plan.each do |step|
|
34
|
-
curr_func = throttling_array[step[0].to_i]
|
35
|
-
|
36
|
-
unless curr_func.respond_to?(:call)
|
37
|
-
raise ExtractError.new(
|
38
|
-
end
|
39
|
-
|
40
|
-
first_arg = throttling_array[step[1].to_i]
|
41
|
-
|
42
|
-
case step.length
|
43
|
-
when 2
|
44
|
-
curr_func.call(first_arg)
|
45
|
-
when 3
|
46
|
-
second_arg = throttling_array[step[2].to_i]
|
47
|
-
curr_func.call(first_arg, second_arg)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
initial_n.join
|
52
|
-
end
|
53
|
-
|
54
|
-
def get_signature(ciphered_signature)
|
55
|
-
signature = ciphered_signature.
|
56
|
-
|
57
|
-
transform_plan.each do |js_func|
|
58
|
-
name, argument = parse_function(js_func)
|
59
|
-
signature = transform_map[name].call(signature, argument)
|
60
|
-
end
|
61
|
-
|
62
|
-
signature.join(
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def parse_function(js_func)
|
68
|
-
js_func_patterns.each do |pattern|
|
69
|
-
regex = Regexp.new(pattern)
|
70
|
-
parse_match = js_func.match(regex)
|
71
|
-
|
72
|
-
if parse_match
|
73
|
-
fn_name = parse_match[1]
|
74
|
-
fn_arg = parse_match[2]
|
75
|
-
|
76
|
-
return [fn_name, fn_arg]
|
77
|
-
end
|
78
|
-
|
79
|
-
raise RegexMatchError.new(
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def get_initial_function_name(js)
|
84
|
-
function_patterns = [
|
85
|
-
%r
|
86
|
-
%r
|
87
|
-
%r'(?:\b|[^a-zA-Z0-9$])(?<sig>[a-zA-Z0-9$]{2})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)', # noqa: E501
|
88
|
-
%r'(?<sig>[a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)', # noqa: E501
|
89
|
-
%r
|
90
|
-
%r
|
91
|
-
%r
|
92
|
-
%r
|
93
|
-
%r
|
94
|
-
%r
|
95
|
-
%r
|
96
|
-
%r
|
97
|
-
]
|
98
|
-
|
99
|
-
function_patterns.each do |pattern|
|
100
|
-
regex = Regexp.new(pattern)
|
101
|
-
function_match = js.match(regex)
|
102
|
-
if function_match
|
103
|
-
return function_match[:sig]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
raise RegexMatchError.new(
|
108
|
-
end
|
109
|
-
|
110
|
-
def get_transform_plan(js)
|
111
|
-
name = Regexp.escape(get_initial_function_name(js))
|
112
|
-
pattern = "#{name}=function\\(\\w\\)\\{[a-z
|
113
|
-
|
114
|
-
Utils.regex_search(pattern, js, 1).split(
|
115
|
-
end
|
116
|
-
|
117
|
-
def get_transform_object(js, var)
|
118
|
-
escaped_var = Regexp.escape(var)
|
119
|
-
pattern = "var #{escaped_var}={(.*?)};"
|
120
|
-
regex = Regexp.new(pattern, Regexp::MULTILINE)
|
121
|
-
transform_match = regex.match(js)
|
122
|
-
|
123
|
-
if transform_match.nil?
|
124
|
-
raise RegexMatchError.new(
|
125
|
-
end
|
126
|
-
|
127
|
-
transform_match[1].
|
128
|
-
end
|
129
|
-
|
130
|
-
def get_transform_map(js, var)
|
131
|
-
transform_obejct = get_transform_object(js, var)
|
132
|
-
mapper = {}
|
133
|
-
|
134
|
-
transform_obejct.each do |obj|
|
135
|
-
name, function = obj.split(
|
136
|
-
fn = map_functions(function)
|
137
|
-
mapper[name] = fn
|
138
|
-
end
|
139
|
-
|
140
|
-
mapper
|
141
|
-
end
|
142
|
-
|
143
|
-
def reverse(arr, _ = nil)
|
144
|
-
# Ruby equivalent of JavaScript's Array.reverse()
|
145
|
-
arr.reverse!
|
146
|
-
end
|
147
|
-
|
148
|
-
def splice(arr, index)
|
149
|
-
# Ruby equivalent of JavaScript's Array.splice(0, index)
|
150
|
-
arr.shift(index.to_i)
|
151
|
-
end
|
152
|
-
|
153
|
-
def swap(arr, index)
|
154
|
-
# Ruby equivalent of the JavaScript swapping function
|
155
|
-
temp = arr[0]
|
156
|
-
arr[0] = arr[index.to_i % arr.length]
|
157
|
-
arr[index.to_i % arr.length] = temp
|
158
|
-
arr
|
159
|
-
end
|
160
|
-
|
161
|
-
def push(arr, val)
|
162
|
-
arr.push(val)
|
163
|
-
end
|
164
|
-
|
165
|
-
def throttling_mod_func(d, e)
|
166
|
-
(e % d.length + d.length) % d.length
|
167
|
-
end
|
168
|
-
|
169
|
-
def throttling_unshift(d, e)
|
170
|
-
e = throttling_mod_func(d, e)
|
171
|
-
new_arr = d[-e..-1] + d[0...-e]
|
172
|
-
d.clear
|
173
|
-
new_arr.each { |el| d << el }
|
174
|
-
end
|
175
|
-
|
176
|
-
def throttling_cipher_function(d, e)
|
177
|
-
h =
|
178
|
-
f = 96
|
179
|
-
self_arr = e.
|
180
|
-
|
181
|
-
copied_array = d.clone
|
182
|
-
|
183
|
-
copied_array.each_with_index do |l, m|
|
184
|
-
bracket_val = (h.index(l) - h.index(self_arr[m]) + m - 32 + f) % h.length
|
185
|
-
self_arr << h[bracket_val]
|
186
|
-
d[m] = h[bracket_val]
|
187
|
-
f -= 1
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def throttling_nested_splice(d, e)
|
192
|
-
e = throttling_mod_func(d, e)
|
193
|
-
inner_splice = js_splice(d, e, 1, d[0])
|
194
|
-
js_splice(d, 0, 1, inner_splice[0])
|
195
|
-
end
|
196
|
-
|
197
|
-
def throttling_prepend(d, e)
|
198
|
-
start_len = d.length
|
199
|
-
|
200
|
-
e = throttling_mod_func(d, e)
|
201
|
-
|
202
|
-
new_arr = d[-e..-1] + d[0...-e]
|
203
|
-
|
204
|
-
d.clear.concat(new_arr)
|
205
|
-
|
206
|
-
end_len = d.length
|
207
|
-
raise
|
208
|
-
end
|
209
|
-
|
210
|
-
def js_splice(arr, start, delete_count=nil, *items)
|
211
|
-
if start.is_a? Integer
|
212
|
-
start = arr.length if start > arr.length
|
213
|
-
start += arr.length if start < 0
|
214
|
-
else
|
215
|
-
start = 0
|
216
|
-
end
|
217
|
-
|
218
|
-
delete_count = arr.length - start if delete_count.nil? || delete_count >= arr.length - start
|
219
|
-
deleted_elements = arr[start, delete_count]
|
220
|
-
|
221
|
-
new_arr = arr[0...start] + items + arr[(start + delete_count)..-1]
|
222
|
-
|
223
|
-
arr.clear.concat(new_arr)
|
224
|
-
|
225
|
-
deleted_elements
|
226
|
-
end
|
227
|
-
|
228
|
-
def map_functions(function)
|
229
|
-
mapper = [
|
230
|
-
# function(a){a.reverse()}
|
231
|
-
[%r"{\w\.reverse\(\)}", method(:reverse)],
|
232
|
-
# function(a,b){a.splice(0,b)}
|
233
|
-
[%r"{\w\.splice\(0,\w\)}", method(:splice)],
|
234
|
-
# function(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c}
|
235
|
-
[%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w
|
236
|
-
# function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}
|
237
|
-
[%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w
|
238
|
-
]
|
239
|
-
|
240
|
-
mapper.each do |pattern, fn|
|
241
|
-
return fn if Regexp.new(pattern).match?(function)
|
242
|
-
end
|
243
|
-
|
244
|
-
raise RegexMatchError.new(
|
245
|
-
end
|
246
|
-
|
247
|
-
def get_throttling_function_name(js)
|
248
|
-
function_patterns = [
|
249
|
-
%r
|
250
|
-
%r
|
251
|
-
]
|
252
|
-
|
253
|
-
function_patterns.each do |pattern|
|
254
|
-
regex = Regexp.new(pattern)
|
255
|
-
function_match = js.match(regex)
|
256
|
-
next unless function_match
|
257
|
-
|
258
|
-
if function_match.captures.length == 1
|
259
|
-
return function_match[1]
|
260
|
-
end
|
261
|
-
|
262
|
-
idx = function_match[2]
|
263
|
-
if idx
|
264
|
-
idx = idx.tr(
|
265
|
-
array_match = js.match(/var #{Regexp.escape(function_match[1])}\s*=\s*(\[.+?\])/)
|
266
|
-
if array_match
|
267
|
-
array = array_match[1].tr(
|
268
|
-
array = array.map(&:strip)
|
269
|
-
return array[idx.to_i]
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
raise RegexMatchError.new(
|
275
|
-
end
|
276
|
-
|
277
|
-
def get_throttling_function_code(js)
|
278
|
-
name = Regexp.escape(get_throttling_function_name(js))
|
279
|
-
|
280
|
-
pattern_start = %r
|
281
|
-
regex = Regexp.new(pattern_start)
|
282
|
-
match = js.match(regex)
|
283
|
-
|
284
|
-
code_lines_list = Parser.find_object_from_startpoint(js, match.end(0)).split("\n")
|
285
|
-
joined_lines = code_lines_list.join("")
|
286
|
-
|
287
|
-
"#{match[0]}#{joined_lines}"
|
288
|
-
end
|
289
|
-
|
290
|
-
def get_throttling_function_array(js)
|
291
|
-
raw_code = get_throttling_function_code(js)
|
292
|
-
|
293
|
-
array_regex = /,c=\[/
|
294
|
-
match = raw_code.match(array_regex)
|
295
|
-
array_raw = Parser.find_object_from_startpoint(raw_code, match.end(0) - 1)
|
296
|
-
str_array = Parser.throttling_array_split(array_raw)
|
297
|
-
|
298
|
-
converted_array = []
|
299
|
-
str_array.each do |el|
|
300
|
-
begin
|
301
|
-
converted_array << Integer(el)
|
302
|
-
next
|
303
|
-
rescue ArgumentError
|
304
|
-
# Not an integer value.
|
305
|
-
end
|
306
|
-
|
307
|
-
if el ==
|
308
|
-
converted_array << nil
|
309
|
-
next
|
310
|
-
end
|
311
|
-
|
312
|
-
if el.start_with?('"') && el.end_with?('"')
|
313
|
-
converted_array << el[1..-2]
|
314
|
-
next
|
315
|
-
end
|
316
|
-
|
317
|
-
if el.start_with?(
|
318
|
-
mapper = [
|
319
|
-
[%r"{for\(\w=\(\w%\w\.length\+\w\.length\)%\w\.length;\w--;\)\w\.unshift\(\w.pop\(\)\)}", method(:throttling_unshift)],
|
320
|
-
[%r"{\w\.reverse\(\)}", method(:reverse)],
|
321
|
-
[%r"{\w\.push\(\w\)}", method(:push)],
|
322
|
-
[%r";var\s\w=\w\[0\];\w\[0\]=\w\[\w\];\w\[\w\]=\w}", method(:swap)],
|
323
|
-
[%r
|
324
|
-
[%r
|
325
|
-
[%r";\w\.splice\(\w,1\)}", method(:js_splice)],
|
326
|
-
[%r"\w\.splice\(-\w\)\.reverse\(\)\.forEach\(function\(\w\){\w\.unshift\(\w\)}\)", method(:throttling_prepend)],
|
327
|
-
[%r"for\(var \w=\w\.length;\w;\)\w\.push\(\w\.splice\(--\w,1\)\[0\]\)}", method(:reverse)]
|
328
|
-
]
|
329
|
-
|
330
|
-
found = false
|
331
|
-
mapper.each do |pattern, fn|
|
332
|
-
if el.match?(pattern)
|
333
|
-
converted_array << fn
|
334
|
-
found = true
|
335
|
-
end
|
336
|
-
end
|
337
|
-
next if found
|
338
|
-
end
|
339
|
-
|
340
|
-
converted_array << el
|
341
|
-
end
|
342
|
-
|
343
|
-
converted_array.map! { |el| el.nil? ? converted_array : el }
|
344
|
-
converted_array
|
345
|
-
end
|
346
|
-
|
347
|
-
def get_throttling_plan(js)
|
348
|
-
raw_code = get_throttling_function_code(js)
|
349
|
-
|
350
|
-
transform_start = "try{"
|
351
|
-
plan_regex = Regexp.new(transform_start)
|
352
|
-
match = raw_code.match(plan_regex)
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
end
|
1
|
+
module RubyTube
|
2
|
+
class Cipher
|
3
|
+
attr_accessor :transform_plan, :transform_map, :js_func_patterns, :throttling_plan, :throttling_array, :calculation_plan, :calculation_n
|
4
|
+
|
5
|
+
def initialize(js)
|
6
|
+
self.transform_plan = get_transform_plan(js)
|
7
|
+
|
8
|
+
var_regex = %r{^\$*\w+\W}
|
9
|
+
var_match = @transform_plan[0].match(var_regex)
|
10
|
+
|
11
|
+
if var_match.nil?
|
12
|
+
raise "RegexMatchError, caller: __init__, pattern: #{var_regex.source}"
|
13
|
+
end
|
14
|
+
|
15
|
+
var = var_match[0][0..-2]
|
16
|
+
|
17
|
+
self.transform_map = get_transform_map(js, var)
|
18
|
+
|
19
|
+
self.js_func_patterns = [
|
20
|
+
%r{\w+\.(\w+)\(\w,(\d+)\)},
|
21
|
+
%r{\w+\[("\w+")\]\(\w,(\d+)\)}
|
22
|
+
]
|
23
|
+
|
24
|
+
self.throttling_array = get_throttling_function_array(js)
|
25
|
+
self.throttling_plan = get_throttling_plan(js)
|
26
|
+
end
|
27
|
+
|
28
|
+
def calculate_n(initial_n)
|
29
|
+
throttling_array.map! do |item|
|
30
|
+
(item == "b") ? initial_n : item
|
31
|
+
end
|
32
|
+
|
33
|
+
throttling_plan.each do |step|
|
34
|
+
curr_func = throttling_array[step[0].to_i]
|
35
|
+
|
36
|
+
unless curr_func.respond_to?(:call)
|
37
|
+
raise ExtractError.new("calculate_n", "curr_func")
|
38
|
+
end
|
39
|
+
|
40
|
+
first_arg = throttling_array[step[1].to_i]
|
41
|
+
|
42
|
+
case step.length
|
43
|
+
when 2
|
44
|
+
curr_func.call(first_arg)
|
45
|
+
when 3
|
46
|
+
second_arg = throttling_array[step[2].to_i]
|
47
|
+
curr_func.call(first_arg, second_arg)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
initial_n.join
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_signature(ciphered_signature)
|
55
|
+
signature = ciphered_signature.chars
|
56
|
+
|
57
|
+
transform_plan.each do |js_func|
|
58
|
+
name, argument = parse_function(js_func)
|
59
|
+
signature = transform_map[name].call(signature, argument)
|
60
|
+
end
|
61
|
+
|
62
|
+
signature.join("")
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def parse_function(js_func)
|
68
|
+
js_func_patterns.each do |pattern|
|
69
|
+
regex = Regexp.new(pattern)
|
70
|
+
parse_match = js_func.match(regex)
|
71
|
+
|
72
|
+
if parse_match
|
73
|
+
fn_name = parse_match[1]
|
74
|
+
fn_arg = parse_match[2]
|
75
|
+
|
76
|
+
return [fn_name, fn_arg]
|
77
|
+
end
|
78
|
+
|
79
|
+
raise RegexMatchError.new("parse_function", "js_func_patterns")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_initial_function_name(js)
|
84
|
+
function_patterns = [
|
85
|
+
%r{\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
86
|
+
%r{\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
87
|
+
%r'(?:\b|[^a-zA-Z0-9$])(?<sig>[a-zA-Z0-9$]{2})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)', # noqa: E501
|
88
|
+
%r'(?<sig>[a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)', # noqa: E501
|
89
|
+
%r{(?<quote>["\'])signature\k<quote>\s*,\s*(?<sig>[a-zA-Z0-9$]+)\(},
|
90
|
+
%r{\.sig\|\|(?<sig>[a-zA-Z0-9$]+)\(},
|
91
|
+
%r{yt\.akamaized\.net/\)\s*\|\|\s*.*?\s*[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?:encodeURIComponent\s*\()?\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
92
|
+
%r{\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
93
|
+
%r{\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
94
|
+
%r{\bc\s*&&\s*a\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
95
|
+
%r{\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?<sig>[a-zA-Z0-9$]+)\(}, # noqa: E501
|
96
|
+
%r{\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?<sig>[a-zA-Z0-9$]+)\(} # noqa: E501
|
97
|
+
]
|
98
|
+
|
99
|
+
function_patterns.each do |pattern|
|
100
|
+
regex = Regexp.new(pattern)
|
101
|
+
function_match = js.match(regex)
|
102
|
+
if function_match
|
103
|
+
return function_match[:sig]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
raise RegexMatchError.new("get_initial_function_name", "multiple")
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_transform_plan(js)
|
111
|
+
name = Regexp.escape(get_initial_function_name(js))
|
112
|
+
pattern = "#{name}=function\\(\\w\\)\\{[a-z=\\.(\"\\)]*;(.*);(?:.+)\\}"
|
113
|
+
|
114
|
+
Utils.regex_search(pattern, js, 1).split(";")
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_transform_object(js, var)
|
118
|
+
escaped_var = Regexp.escape(var)
|
119
|
+
pattern = "var #{escaped_var}={(.*?)};"
|
120
|
+
regex = Regexp.new(pattern, Regexp::MULTILINE)
|
121
|
+
transform_match = regex.match(js)
|
122
|
+
|
123
|
+
if transform_match.nil?
|
124
|
+
raise RegexMatchError.new("get_transform_object", pattern)
|
125
|
+
end
|
126
|
+
|
127
|
+
transform_match[1].tr("\n", " ").split(", ")
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_transform_map(js, var)
|
131
|
+
transform_obejct = get_transform_object(js, var)
|
132
|
+
mapper = {}
|
133
|
+
|
134
|
+
transform_obejct.each do |obj|
|
135
|
+
name, function = obj.split(":")
|
136
|
+
fn = map_functions(function)
|
137
|
+
mapper[name] = fn
|
138
|
+
end
|
139
|
+
|
140
|
+
mapper
|
141
|
+
end
|
142
|
+
|
143
|
+
def reverse(arr, _ = nil)
|
144
|
+
# Ruby equivalent of JavaScript's Array.reverse()
|
145
|
+
arr.reverse!
|
146
|
+
end
|
147
|
+
|
148
|
+
def splice(arr, index)
|
149
|
+
# Ruby equivalent of JavaScript's Array.splice(0, index)
|
150
|
+
arr.shift(index.to_i)
|
151
|
+
end
|
152
|
+
|
153
|
+
def swap(arr, index)
|
154
|
+
# Ruby equivalent of the JavaScript swapping function
|
155
|
+
temp = arr[0]
|
156
|
+
arr[0] = arr[index.to_i % arr.length]
|
157
|
+
arr[index.to_i % arr.length] = temp
|
158
|
+
arr
|
159
|
+
end
|
160
|
+
|
161
|
+
def push(arr, val)
|
162
|
+
arr.push(val)
|
163
|
+
end
|
164
|
+
|
165
|
+
def throttling_mod_func(d, e)
|
166
|
+
(e % d.length + d.length) % d.length
|
167
|
+
end
|
168
|
+
|
169
|
+
def throttling_unshift(d, e)
|
170
|
+
e = throttling_mod_func(d, e)
|
171
|
+
new_arr = d[-e..-1] + d[0...-e]
|
172
|
+
d.clear
|
173
|
+
new_arr.each { |el| d << el }
|
174
|
+
end
|
175
|
+
|
176
|
+
def throttling_cipher_function(d, e)
|
177
|
+
h = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".chars
|
178
|
+
f = 96
|
179
|
+
self_arr = e.chars
|
180
|
+
|
181
|
+
copied_array = d.clone
|
182
|
+
|
183
|
+
copied_array.each_with_index do |l, m|
|
184
|
+
bracket_val = (h.index(l) - h.index(self_arr[m]) + m - 32 + f) % h.length
|
185
|
+
self_arr << h[bracket_val]
|
186
|
+
d[m] = h[bracket_val]
|
187
|
+
f -= 1
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def throttling_nested_splice(d, e)
|
192
|
+
e = throttling_mod_func(d, e)
|
193
|
+
inner_splice = js_splice(d, e, 1, d[0])
|
194
|
+
js_splice(d, 0, 1, inner_splice[0])
|
195
|
+
end
|
196
|
+
|
197
|
+
def throttling_prepend(d, e)
|
198
|
+
start_len = d.length
|
199
|
+
|
200
|
+
e = throttling_mod_func(d, e)
|
201
|
+
|
202
|
+
new_arr = d[-e..-1] + d[0...-e]
|
203
|
+
|
204
|
+
d.clear.concat(new_arr)
|
205
|
+
|
206
|
+
end_len = d.length
|
207
|
+
raise "Length mismatch" unless start_len == end_len
|
208
|
+
end
|
209
|
+
|
210
|
+
def js_splice(arr, start, delete_count = nil, *items)
|
211
|
+
if start.is_a? Integer
|
212
|
+
start = arr.length if start > arr.length
|
213
|
+
start += arr.length if start < 0
|
214
|
+
else
|
215
|
+
start = 0
|
216
|
+
end
|
217
|
+
|
218
|
+
delete_count = arr.length - start if delete_count.nil? || delete_count >= arr.length - start
|
219
|
+
deleted_elements = arr[start, delete_count]
|
220
|
+
|
221
|
+
new_arr = arr[0...start] + items + arr[(start + delete_count)..-1]
|
222
|
+
|
223
|
+
arr.clear.concat(new_arr)
|
224
|
+
|
225
|
+
deleted_elements
|
226
|
+
end
|
227
|
+
|
228
|
+
def map_functions(function)
|
229
|
+
mapper = [
|
230
|
+
# function(a){a.reverse()}
|
231
|
+
[%r"{\w\.reverse\(\)}", method(:reverse)],
|
232
|
+
# function(a,b){a.splice(0,b)}
|
233
|
+
[%r"{\w\.splice\(0,\w\)}", method(:splice)],
|
234
|
+
# function(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c}
|
235
|
+
[%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w%\w.length\];\w\[\w\]=\w}", method(:swap)],
|
236
|
+
# function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}
|
237
|
+
[%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w%\w.length\];\w\[\w%\w.length\]=\w}", method(:swap)]
|
238
|
+
]
|
239
|
+
|
240
|
+
mapper.each do |pattern, fn|
|
241
|
+
return fn if Regexp.new(pattern).match?(function)
|
242
|
+
end
|
243
|
+
|
244
|
+
raise RegexMatchError.new("map_functions", "multiple")
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_throttling_function_name(js)
|
248
|
+
function_patterns = [
|
249
|
+
%r{a\.[a-zA-Z]\s*&&\s*\([a-z]\s*=\s*a\.get\("n"\)\)\s*&&.*?\|\|\s*([a-z]+)},
|
250
|
+
%r{\([a-z]\s*=\s*([a-zA-Z0-9$]+)(\[\d+\])\([a-z]\)}
|
251
|
+
]
|
252
|
+
|
253
|
+
function_patterns.each do |pattern|
|
254
|
+
regex = Regexp.new(pattern)
|
255
|
+
function_match = js.match(regex)
|
256
|
+
next unless function_match
|
257
|
+
|
258
|
+
if function_match.captures.length == 1
|
259
|
+
return function_match[1]
|
260
|
+
end
|
261
|
+
|
262
|
+
idx = function_match[2]
|
263
|
+
if idx
|
264
|
+
idx = idx.tr("[]", "")
|
265
|
+
array_match = js.match(/var #{Regexp.escape(function_match[1])}\s*=\s*(\[.+?\])/)
|
266
|
+
if array_match
|
267
|
+
array = array_match[1].tr("[]", "").split(",")
|
268
|
+
array = array.map(&:strip)
|
269
|
+
return array[idx.to_i]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
raise RegexMatchError.new("get_throttling_function_name", "multiple")
|
275
|
+
end
|
276
|
+
|
277
|
+
def get_throttling_function_code(js)
|
278
|
+
name = Regexp.escape(get_throttling_function_name(js))
|
279
|
+
|
280
|
+
pattern_start = %r{#{name}=function\(\w\)}
|
281
|
+
regex = Regexp.new(pattern_start)
|
282
|
+
match = js.match(regex)
|
283
|
+
|
284
|
+
code_lines_list = Parser.find_object_from_startpoint(js, match.end(0)).split("\n")
|
285
|
+
joined_lines = code_lines_list.join("")
|
286
|
+
|
287
|
+
"#{match[0]}#{joined_lines}"
|
288
|
+
end
|
289
|
+
|
290
|
+
def get_throttling_function_array(js)
|
291
|
+
raw_code = get_throttling_function_code(js)
|
292
|
+
|
293
|
+
array_regex = /,c=\[/
|
294
|
+
match = raw_code.match(array_regex)
|
295
|
+
array_raw = Parser.find_object_from_startpoint(raw_code, match.end(0) - 1)
|
296
|
+
str_array = Parser.throttling_array_split(array_raw)
|
297
|
+
|
298
|
+
converted_array = []
|
299
|
+
str_array.each do |el|
|
300
|
+
begin
|
301
|
+
converted_array << Integer(el)
|
302
|
+
next
|
303
|
+
rescue ArgumentError
|
304
|
+
# Not an integer value.
|
305
|
+
end
|
306
|
+
|
307
|
+
if el == "null"
|
308
|
+
converted_array << nil
|
309
|
+
next
|
310
|
+
end
|
311
|
+
|
312
|
+
if el.start_with?('"') && el.end_with?('"')
|
313
|
+
converted_array << el[1..-2]
|
314
|
+
next
|
315
|
+
end
|
316
|
+
|
317
|
+
if el.start_with?("function")
|
318
|
+
mapper = [
|
319
|
+
[%r"{for\(\w=\(\w%\w\.length\+\w\.length\)%\w\.length;\w--;\)\w\.unshift\(\w.pop\(\)\)}", method(:throttling_unshift)],
|
320
|
+
[%r"{\w\.reverse\(\)}", method(:reverse)],
|
321
|
+
[%r"{\w\.push\(\w\)}", method(:push)],
|
322
|
+
[%r";var\s\w=\w\[0\];\w\[0\]=\w\[\w\];\w\[\w\]=\w}", method(:swap)],
|
323
|
+
[%r{case\s\d+}, method(:throttling_cipher_function)],
|
324
|
+
[%r{\w\.splice\(0,1,\w\.splice\(\w,1,\w\[0\]\)\[0\]\)}, method(:throttling_nested_splice)],
|
325
|
+
[%r";\w\.splice\(\w,1\)}", method(:js_splice)],
|
326
|
+
[%r"\w\.splice\(-\w\)\.reverse\(\)\.forEach\(function\(\w\){\w\.unshift\(\w\)}\)", method(:throttling_prepend)],
|
327
|
+
[%r"for\(var \w=\w\.length;\w;\)\w\.push\(\w\.splice\(--\w,1\)\[0\]\)}", method(:reverse)]
|
328
|
+
]
|
329
|
+
|
330
|
+
found = false
|
331
|
+
mapper.each do |pattern, fn|
|
332
|
+
if el.match?(pattern)
|
333
|
+
converted_array << fn
|
334
|
+
found = true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
next if found
|
338
|
+
end
|
339
|
+
|
340
|
+
converted_array << el
|
341
|
+
end
|
342
|
+
|
343
|
+
converted_array.map! { |el| el.nil? ? converted_array : el }
|
344
|
+
converted_array
|
345
|
+
end
|
346
|
+
|
347
|
+
def get_throttling_plan(js)
|
348
|
+
raw_code = get_throttling_function_code(js)
|
349
|
+
|
350
|
+
transform_start = "try{"
|
351
|
+
plan_regex = Regexp.new(transform_start)
|
352
|
+
match = raw_code.match(plan_regex)
|
353
|
+
|
354
|
+
transform_plan_raw = Parser.find_object_from_startpoint(raw_code, match.end(0) - 1)
|
355
|
+
step_regex = %r{c\[(\d+)\]\(c\[(\d+)\](,c(\[(\d+)\]))?\)}
|
356
|
+
matches = transform_plan_raw.scan(step_regex)
|
357
|
+
transform_steps = []
|
358
|
+
|
359
|
+
matches.each do |match|
|
360
|
+
if match[3]
|
361
|
+
transform_steps.push([match[0], match[1], match[3]])
|
362
|
+
else
|
363
|
+
transform_steps.push([match[0], match[1]])
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
transform_steps
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|