rubytube 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,371 +1,371 @@
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.split('')
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].gsub("\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-_'.split('')
178
- f = 96
179
- self_arr = e.split('')
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
- transform_plan_raw = js
356
- step_regex = %r"c\[(\d+)\]\(c\[(\d+)\](,c(\[(\d+)\]))?\)"
357
- matches = transform_plan_raw.scan(step_regex)
358
- transform_steps = []
359
-
360
- matches.each do |match|
361
- if match[3]
362
- transform_steps.push([match[0], match[1], match[3]])
363
- else
364
- transform_steps.push([match[0], match[1]])
365
- end
366
- end
367
-
368
- transform_steps
369
- end
370
- end
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.split('')
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].gsub("\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-_'.split('')
178
+ f = 96
179
+ self_arr = e.split('')
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
+ transform_plan_raw = js
356
+ step_regex = %r"c\[(\d+)\]\(c\[(\d+)\](,c(\[(\d+)\]))?\)"
357
+ matches = transform_plan_raw.scan(step_regex)
358
+ transform_steps = []
359
+
360
+ matches.each do |match|
361
+ if match[3]
362
+ transform_steps.push([match[0], match[1], match[3]])
363
+ else
364
+ transform_steps.push([match[0], match[1]])
365
+ end
366
+ end
367
+
368
+ transform_steps
369
+ end
370
+ end
371
+ end