rubytube 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,369 +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)
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
- end
159
-
160
- def push(arr, val)
161
- arr.push(val)
162
- end
163
-
164
- def throttling_mod_func(d, e)
165
- (e % d.length + d.length) % d.length
166
- end
167
-
168
- def throttling_unshift(d, e)
169
- e = throttling_mod_func(d, e)
170
- new_arr = d[-e..-1] + d[0...-e]
171
- d.clear
172
- new_arr.each { |el| d << el }
173
- end
174
-
175
- def throttling_cipher_function(d, e)
176
- h = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split('')
177
- f = 96
178
- self_arr = e.split('')
179
-
180
- copied_array = d.clone
181
-
182
- copied_array.each_with_index do |l, m|
183
- bracket_val = (h.index(l) - h.index(self_arr[m]) + m - 32 + f) % h.length
184
- self_arr << h[bracket_val]
185
- d[m] = h[bracket_val]
186
- f -= 1
187
- end
188
- end
189
-
190
- def throttling_nested_splice(d, e)
191
- e = throttling_mod_func(d, e)
192
- inner_splice = js_splice(d, e, 1, d[0])
193
- js_splice(d, 0, 1, inner_splice[0])
194
- end
195
-
196
- def throttling_prepend(d, e)
197
- start_len = d.length
198
-
199
- e = throttling_mod_func(d, e)
200
-
201
- new_arr = d[-e..-1] + d[0...-e]
202
-
203
- d.clear.concat(new_arr)
204
-
205
- end_len = d.length
206
- raise 'Length mismatch' unless start_len == end_len
207
- end
208
-
209
- def js_splice(arr, start, delete_count=nil, *items)
210
- if start.is_a? Integer
211
- start = arr.length if start > arr.length
212
- start += arr.length if start < 0
213
- else
214
- start = 0
215
- end
216
-
217
- delete_count = arr.length - start if delete_count.nil? || delete_count >= arr.length - start
218
- deleted_elements = arr[start, delete_count]
219
-
220
- new_arr = arr[0...start] + items + arr[(start + delete_count)..-1]
221
-
222
- arr.clear.concat(new_arr)
223
-
224
- deleted_elements
225
- end
226
-
227
- def map_functions(function)
228
- mapper = [
229
- # function(a){a.reverse()}
230
- [%r"{\w\.reverse\(\)}", method(:reverse)],
231
- # function(a,b){a.splice(0,b)}
232
- [%r"{\w\.splice\(0,\w\)}", method(:splice)],
233
- # function(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c}
234
- [%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w\%\w.length\];\w\[\w\]=\w}", method(:swap)],
235
- # function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}
236
- [%r"{var\s\w=\w\[0\];\w\[0\]=\w\[\w\%\w.length\];\w\[\w\%\w.length\]=\w}", method(:swap)]
237
- ]
238
-
239
- mapper.each do |pattern, fn|
240
- return fn if Regexp.new(pattern).match?(function)
241
- end
242
-
243
- raise RegexMatchError.new('map_functions', 'multiple')
244
- end
245
-
246
- def get_throttling_function_name(js)
247
- function_patterns = [
248
- %r'a\.[a-zA-Z]\s*&&\s*\([a-z]\s*=\s*a\.get\("n"\)\)\s*&&.*?\|\|\s*([a-z]+)',
249
- %r'\([a-z]\s*=\s*([a-zA-Z0-9$]+)(\[\d+\])\([a-z]\)',
250
- ]
251
-
252
- function_patterns.each do |pattern|
253
- regex = Regexp.new(pattern)
254
- function_match = js.match(regex)
255
- next unless function_match
256
-
257
- if function_match.captures.length == 1
258
- return function_match[1]
259
- end
260
-
261
- idx = function_match[2]
262
- if idx
263
- idx = idx.tr('[]', '')
264
- array_match = js.match(/var #{Regexp.escape(function_match[1])}\s*=\s*(\[.+?\])/)
265
- if array_match
266
- array = array_match[1].tr('[]', '').split(',')
267
- array = array.map(&:strip)
268
- return array[idx.to_i]
269
- end
270
- end
271
- end
272
-
273
- raise RegexMatchError.new('get_throttling_function_name', 'multiple')
274
- end
275
-
276
- def get_throttling_function_code(js)
277
- name = Regexp.escape(get_throttling_function_name(js))
278
-
279
- pattern_start = %r"#{name}=function\(\w\)"
280
- regex = Regexp.new(pattern_start)
281
- match = js.match(regex)
282
-
283
- code_lines_list = Parser.find_object_from_startpoint(js, match.end(0)).split("\n")
284
- joined_lines = code_lines_list.join("")
285
-
286
- "#{match[0]}#{joined_lines}"
287
- end
288
-
289
- def get_throttling_function_array(js)
290
- raw_code = get_throttling_function_code(js)
291
-
292
- array_regex = /,c=\[/
293
- match = raw_code.match(array_regex)
294
- array_raw = Parser.find_object_from_startpoint(raw_code, match.end(0) - 1)
295
- str_array = Parser.throttling_array_split(array_raw)
296
-
297
- converted_array = []
298
- str_array.each do |el|
299
- begin
300
- converted_array << Integer(el)
301
- next
302
- rescue ArgumentError
303
- # Not an integer value.
304
- end
305
-
306
- if el == 'null'
307
- converted_array << nil
308
- next
309
- end
310
-
311
- if el.start_with?('"') && el.end_with?('"')
312
- converted_array << el[1..-2]
313
- next
314
- end
315
-
316
- if el.start_with?('function')
317
- mapper = [
318
- [%r"{for\(\w=\(\w%\w\.length\+\w\.length\)%\w\.length;\w--;\)\w\.unshift\(\w.pop\(\)\)}", method(:throttling_unshift)],
319
- [%r"{\w\.reverse\(\)}", method(:reverse)],
320
- [%r"{\w\.push\(\w\)}", method(:push)],
321
- [%r";var\s\w=\w\[0\];\w\[0\]=\w\[\w\];\w\[\w\]=\w}", method(:swap)],
322
- [%r"case\s\d+", method(:throttling_cipher_function)],
323
- [%r"\w\.splice\(0,1,\w\.splice\(\w,1,\w\[0\]\)\[0\]\)", method(:throttling_nested_splice)],
324
- [%r";\w\.splice\(\w,1\)}", method(:js_splice)],
325
- [%r"\w\.splice\(-\w\)\.reverse\(\)\.forEach\(function\(\w\){\w\.unshift\(\w\)}\)", method(:throttling_prepend)],
326
- [%r"for\(var \w=\w\.length;\w;\)\w\.push\(\w\.splice\(--\w,1\)\[0\]\)}", method(:reverse)],
327
- ]
328
-
329
- found = false
330
- mapper.each do |pattern, fn|
331
- if el.match?(pattern)
332
- converted_array << fn
333
- found = true
334
- end
335
- end
336
- next if found
337
- end
338
-
339
- converted_array << el
340
- end
341
-
342
- converted_array.map! { |el| el.nil? ? converted_array : el }
343
- converted_array
344
- end
345
-
346
- def get_throttling_plan(js)
347
- raw_code = get_throttling_function_code(js)
348
-
349
- transform_start = "try{"
350
- plan_regex = Regexp.new(transform_start)
351
- match = raw_code.match(plan_regex)
352
-
353
- transform_plan_raw = Parser.find_object_from_startpoint(raw_code, match.end(0) - 1)
354
- step_regex = %r"c\[(\d+)\]\(c\[(\d+)\](,c(\[(\d+)\]))?\)"
355
- matches = transform_plan_raw.scan(step_regex)
356
- transform_steps = []
357
-
358
- matches.each do |match|
359
- if match[3]
360
- transform_steps.push([match[0], match[1], match[3]])
361
- else
362
- transform_steps.push([match[0], match[1]])
363
- end
364
- end
365
-
366
- transform_steps
367
- end
368
- end
369
- 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