X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=youtube_dl%2Fswfinterp.py;h=06c1d6cc1755ef022aa78967d4b651e21fd66618;hb=dfb1b1468cef4ddc7ecc43776abce03763f8e426;hp=4b47df29d20c2759c544365d6dae13880a05c044;hpb=6b592d93a29163664d3125761fbf6d7c9fe5f56c;p=youtube-dl diff --git a/youtube_dl/swfinterp.py b/youtube_dl/swfinterp.py index 4b47df29d..06c1d6cc1 100644 --- a/youtube_dl/swfinterp.py +++ b/youtube_dl/swfinterp.py @@ -4,8 +4,8 @@ import collections import io import zlib +from .compat import compat_str from .utils import ( - compat_str, ExtractorError, struct_unpack, ) @@ -62,15 +62,17 @@ class _ScopeDict(dict): class _AVMClass(object): - def __init__(self, name_idx, name): + def __init__(self, name_idx, name, static_properties=None): self.name_idx = name_idx self.name = name self.method_names = {} self.method_idxs = {} self.methods = {} self.method_pyfunctions = {} + self.static_properties = static_properties if static_properties else {} self.variables = _ScopeDict(self) + self.constants = {} def make_object(self): return _AVMClass_Object(self) @@ -150,25 +152,36 @@ def _read_byte(reader): StringClass = _AVMClass('(no name idx)', 'String') ByteArrayClass = _AVMClass('(no name idx)', 'ByteArray') +TimerClass = _AVMClass('(no name idx)', 'Timer') +TimerEventClass = _AVMClass('(no name idx)', 'TimerEvent', {'TIMER': 'timer'}) _builtin_classes = { StringClass.name: StringClass, ByteArrayClass.name: ByteArrayClass, + TimerClass.name: TimerClass, + TimerEventClass.name: TimerEventClass, } class _Undefined(object): - def __boolean__(self): + def __bool__(self): return False + __nonzero__ = __bool__ def __hash__(self): return 0 + def __str__(self): + return 'undefined' + __repr__ = __str__ + undefined = _Undefined() class SWFInterpreter(object): def __init__(self, file_contents): - self._patched_functions = {} + self._patched_functions = { + (TimerClass, 'addEventListener'): lambda params: undefined, + } code_tag = next(tag for tag_code, tag in _extract_tags(file_contents) if tag_code == 82) @@ -189,11 +202,13 @@ class SWFInterpreter(object): # Constant pool int_count = u30() + self.constant_ints = [0] for _c in range(1, int_count): - s32() + self.constant_ints.append(s32()) + self.constant_uints = [0] uint_count = u30() for _c in range(1, uint_count): - u32() + self.constant_uints.append(u32()) double_count = u30() read_bytes(max(0, (double_count - 1)) * 8) string_count = u30() @@ -281,13 +296,28 @@ class SWFInterpreter(object): kind = kind_full & 0x0f attrs = kind_full >> 4 methods = {} - if kind in [0x00, 0x06]: # Slot or Const + constants = None + if kind == 0x00: # Slot u30() # Slot id u30() # type_name_idx vindex = u30() if vindex != 0: read_byte() # vkind - elif kind in [0x01, 0x02, 0x03]: # Method / Getter / Setter + elif kind == 0x06: # Const + u30() # Slot id + u30() # type_name_idx + vindex = u30() + vkind = 'any' + if vindex != 0: + vkind = read_byte() + if vkind == 0x03: # Constant_Int + value = self.constant_ints[vindex] + elif vkind == 0x04: # Constant_UInt + value = self.constant_uints[vindex] + else: + return {}, None # Ignore silently for now + constants = {self.multinames[trait_name_idx]: value} + elif kind in (0x01, 0x02, 0x03): # Method / Getter / Setter u30() # disp_id method_idx = u30() methods[self.multinames[trait_name_idx]] = method_idx @@ -306,7 +336,7 @@ class SWFInterpreter(object): for _c3 in range(metadata_count): u30() # metadata index - return methods + return methods, constants # Classes class_count = u30() @@ -328,18 +358,22 @@ class SWFInterpreter(object): u30() # iinit trait_count = u30() for _c2 in range(trait_count): - trait_methods = parse_traits_info() + trait_methods, trait_constants = parse_traits_info() avm_class.register_methods(trait_methods) + if trait_constants: + avm_class.constants.update(trait_constants) assert len(classes) == class_count self._classes_by_name = dict((c.name, c) for c in classes) for avm_class in classes: - u30() # cinit + avm_class.cinit_idx = u30() trait_count = u30() for _c2 in range(trait_count): - trait_methods = parse_traits_info() + trait_methods, trait_constants = parse_traits_info() avm_class.register_methods(trait_methods) + if trait_constants: + avm_class.constants.update(trait_constants) # Scripts script_count = u30() @@ -352,6 +386,7 @@ class SWFInterpreter(object): # Method bodies method_body_count = u30() Method = collections.namedtuple('Method', ['code', 'local_count']) + self._all_methods = [] for _c in range(method_body_count): method_idx = u30() u30() # max_stack @@ -360,9 +395,10 @@ class SWFInterpreter(object): u30() # max_scope_depth code_length = u30() code = read_bytes(code_length) + m = Method(code, local_count) + self._all_methods.append(m) for avm_class in classes: if method_idx in avm_class.method_idxs: - m = Method(code, local_count) avm_class.methods[avm_class.method_idxs[method_idx]] = m exception_count = u30() for _c2 in range(exception_count): @@ -380,12 +416,20 @@ class SWFInterpreter(object): def patch_function(self, avm_class, func_name, f): self._patched_functions[(avm_class, func_name)] = f - def extract_class(self, class_name): + def extract_class(self, class_name, call_cinit=True): try: - return self._classes_by_name[class_name] + res = self._classes_by_name[class_name] except KeyError: raise ExtractorError('Class %r not found' % class_name) + if call_cinit and hasattr(res, 'cinit_idx'): + res.register_methods({'$cinit': res.cinit_idx}) + res.methods['$cinit'] = self._all_methods[res.cinit_idx] + cinit = self.extract_function(res, '$cinit') + cinit([]) + + return res + def extract_function(self, avm_class, func_name): p = self._patched_functions.get((avm_class, func_name)) if p: @@ -408,10 +452,12 @@ class SWFInterpreter(object): registers = [avm_class.variables] + list(args) + [None] * m.local_count stack = [] scopes = collections.deque([ - self._classes_by_name, avm_class.variables]) + self._classes_by_name, avm_class.constants, avm_class.variables]) while True: opcode = _read_byte(coder) - if opcode == 16: # jump + if opcode == 9: # label + pass # Spec says: "Do nothing." + elif opcode == 16: # jump offset = s24() coder.seek(coder.tell() + offset) elif opcode == 17: # iftrue @@ -436,6 +482,12 @@ class SWFInterpreter(object): value1 = stack.pop() if value2 != value1: coder.seek(coder.tell() + offset) + elif opcode == 21: # iflt + offset = s24() + value2 = stack.pop() + value1 = stack.pop() + if value1 < value2: + coder.seek(coder.tell() + offset) elif opcode == 32: # pushnull stack.append(None) elif opcode == 33: # pushundefined @@ -443,6 +495,9 @@ class SWFInterpreter(object): elif opcode == 36: # pushbyte v = _read_byte(coder) stack.append(v) + elif opcode == 37: # pushshort + v = u30() + stack.append(v) elif opcode == 38: # pushtrue stack.append(True) elif opcode == 39: # pushfalse @@ -516,6 +571,13 @@ class SWFInterpreter(object): res = obj.split(args[0]) stack.append(res) continue + elif mname == 'charCodeAt': + assert len(args) <= 1 + idx = 0 if len(args) == 0 else args[0] + assert isinstance(idx, int) + res = ord(obj[idx]) + stack.append(res) + continue elif isinstance(obj, list): if mname == 'slice': assert len(args) == 1 @@ -538,6 +600,12 @@ class SWFInterpreter(object): elif opcode == 72: # returnvalue res = stack.pop() return res + elif opcode == 73: # constructsuper + # Not yet implemented, just hope it works without it + arg_count = u30() + args = list(reversed( + [stack.pop() for _ in range(arg_count)])) + obj = stack.pop() elif opcode == 74: # constructproperty index = u30() arg_count = u30() @@ -615,9 +683,15 @@ class SWFInterpreter(object): break else: scope = avm_class.variables - # I cannot find where static variables are initialized - # so let's just return None - res = scope.get(mname) + + if mname in scope: + res = scope[mname] + elif mname in _builtin_classes: + res = _builtin_classes[mname] + else: + # Assume uninitialized + # TODO warn here + res = undefined stack.append(res) elif opcode == 97: # setproperty index = u30() @@ -643,7 +717,12 @@ class SWFInterpreter(object): stack.append(len(obj)) elif isinstance(pname, compat_str): # Member access obj = stack.pop() - assert isinstance(obj, (dict, _ScopeDict)), \ + if isinstance(obj, _AVMClass): + res = obj.static_properties[pname] + stack.append(res) + continue + + assert isinstance(obj, (dict, _ScopeDict)),\ 'Accessing member %r on %r' % (pname, obj) res = obj.get(pname, undefined) stack.append(res) @@ -653,6 +732,14 @@ class SWFInterpreter(object): obj = stack.pop() assert isinstance(obj, list) stack.append(obj[idx]) + elif opcode == 104: # initproperty + index = u30() + value = stack.pop() + idx = self.multinames[index] + if isinstance(idx, _Multiname): + idx = stack.pop() + obj = stack.pop() + obj[idx] = value elif opcode == 115: # convert_ value = stack.pop() intvalue = int(value) @@ -687,11 +774,23 @@ class SWFInterpreter(object): value1 = stack.pop() res = value1 - value2 stack.append(res) + elif opcode == 162: # multiply + value2 = stack.pop() + value1 = stack.pop() + res = value1 * value2 + stack.append(res) elif opcode == 164: # modulo value2 = stack.pop() value1 = stack.pop() res = value1 % value2 stack.append(res) + elif opcode == 168: # bitand + value2 = stack.pop() + value1 = stack.pop() + assert isinstance(value1, int) + assert isinstance(value2, int) + res = value1 & value2 + stack.append(res) elif opcode == 171: # equals value2 = stack.pop() value1 = stack.pop() @@ -702,6 +801,10 @@ class SWFInterpreter(object): value1 = stack.pop() result = value1 >= value2 stack.append(result) + elif opcode == 192: # increment_i + value = stack.pop() + assert isinstance(value, int) + stack.append(value + 1) elif opcode == 208: # getlocal_0 stack.append(registers[0]) elif opcode == 209: # getlocal_1 @@ -724,4 +827,3 @@ class SWFInterpreter(object): avm_class.method_pyfunctions[func_name] = resfunc return resfunc -