1
0
mirror of synced 2025-02-07 15:01:21 +01:00

Fix a TODO, add a bit more RE.

This commit is contained in:
Jennifer Taylor 2021-03-30 04:50:51 +00:00
parent 02d2c9cd5a
commit 7beb518484

View File

@ -278,21 +278,21 @@ class SWF:
0x64: 'AFP_IMAGE', 0x64: 'AFP_IMAGE',
0x65: 'AFP_DEFINE_SOUND', 0x65: 'AFP_DEFINE_SOUND',
0x66: 'AFP_SOUND_STREAM_BLOCK', 0x66: 'AFP_SOUND_STREAM_BLOCK',
0x67: 'AFP_DEFINE_FONT', self.AFP_DEFINE_FONT: 'AFP_DEFINE_FONT',
0x68: 'AFP_DEFINE_SHAPE', self.AFP_DEFINE_SHAPE: 'AFP_DEFINE_SHAPE',
0x6e: 'AEP_PLACE_OBJECT', self.AEP_PLACE_OBJECT: 'AEP_PLACE_OBJECT',
0x78: 'AP2_DEFINE_FONT', self.AP2_DEFINE_FONT: 'AP2_DEFINE_FONT',
0x79: 'AP2_DEFINE_SPRITE', self.AP2_DEFINE_SPRITE: 'AP2_DEFINE_SPRITE',
0x7a: 'AP2_DO_ACTION', self.AP2_DO_ACTION: 'AP2_DO_ACTION',
0x7b: 'AP2_DEFINE_BUTTON', self.AP2_DEFINE_BUTTON: 'AP2_DEFINE_BUTTON',
0x7c: 'AP2_DEFINE_BUTTON_SOUND', self.AP2_DEFINE_BUTTON_SOUND: 'AP2_DEFINE_BUTTON_SOUND',
0x7d: 'AP2_DEFINE_TEXT', self.AP2_DEFINE_TEXT: 'AP2_DEFINE_TEXT',
0x7e: 'AP2_DEFINE_EDIT_TEXT', self.AP2_DEFINE_EDIT_TEXT: 'AP2_DEFINE_EDIT_TEXT',
0x7f: 'AP2_PLACE_OBJECT', self.AP2_PLACE_OBJECT: 'AP2_PLACE_OBJECT',
0x80: 'AP2_REMOVE_OBJECT', self.AP2_REMOVE_OBJECT: 'AP2_REMOVE_OBJECT',
0x81: 'AP2_START_SOUND', self.AP2_START_SOUND: 'AP2_START_SOUND',
0x82: 'AP2_DEFINE_MORPH_SHAPE', self.AP2_DEFINE_MORPH_SHAPE: 'AP2_DEFINE_MORPH_SHAPE',
0x83: 'AP2_IMAGE', self.AP2_IMAGE: 'AP2_IMAGE',
self.AP2_SHAPE: 'AP2_SHAPE', self.AP2_SHAPE: 'AP2_SHAPE',
self.AP2_SOUND: 'AP2_SOUND', self.AP2_SOUND: 'AP2_SOUND',
self.AP2_VIDEO: 'AP2_VIDEO', self.AP2_VIDEO: 'AP2_VIDEO',
@ -300,7 +300,7 @@ class SWF:
return resources.get(tagid, "UNKNOWN") return resources.get(tagid, "UNKNOWN")
def __parse_tag(self, tagid: int, size: int, dataoffset: int, verbose: bool = False) -> None: def __parse_tag(self, ap2data: bytes, tagid: int, size: int, dataoffset: int, verbose: bool = False) -> None:
# Suppress debug text unless asked # Suppress debug text unless asked
if verbose: if verbose:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
@ -318,18 +318,22 @@ class SWF:
if size != 4: if size != 4:
raise Exception(f"Invalid shape size {size}") raise Exception(f"Invalid shape size {size}")
# TODO, this should be little endian? But it only works out if I do big endian. _, shape_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
_, shape_id = struct.unpack(">HH", self.data[dataoffset:(dataoffset + 4)])
add_coverage(dataoffset, size) add_coverage(dataoffset, size)
shape_reference = f"{self.exported_name}_shape{shape_id}" shape_reference = f"{self.exported_name}_shape{shape_id}"
vprint(f" {shape_reference}") vprint(f" Tag ID: {shape_id}, Reference: {shape_reference}")
elif tagid == self.AP2_DEFINE_SPRITE:
wat, sprite_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
vprint(f" Tag ID: {sprite_id}")
elif tagid == self.AP2_DEFINE_FONT:
wat, font_id = struct.unpack("<HH", ap2data[dataoffset:(dataoffset + 4)])
vprint(f" Tag ID: {font_id}")
# TODO: Switch on tag types, parse out data. # TODO: Switch on tag types, parse out data.
elif False: elif False:
add_coverage(dataoffset, size) add_coverage(dataoffset, size)
def __parse_tags(self, ap2_version: int, afp_version: int, ap2data: bytearray, tags_base_offset: int, verbose: bool = False) -> None: def __parse_tags(self, ap2_version: int, afp_version: int, ap2data: bytes, tags_base_offset: int, verbose: bool = False) -> None:
# Suppress debug text unless asked # Suppress debug text unless asked
if verbose: if verbose:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
@ -348,6 +352,7 @@ class SWF:
add_coverage(tags_base_offset, 12) add_coverage(tags_base_offset, 12)
add_coverage(tags_base_offset + 20, 4) add_coverage(tags_base_offset + 20, 4)
# TODO: Seems that tags_unknown2 has something to do with end of movie stuff?
vprint(f"UNKNOWN: {hex(tags_unknown1)}, {hex(tags_unknown2)}") vprint(f"UNKNOWN: {hex(tags_unknown1)}, {hex(tags_unknown2)}")
vprint(f"Number of Tags: {tags_count}") vprint(f"Number of Tags: {tags_count}")
@ -359,37 +364,21 @@ class SWF:
size = ((tag & 0x3FFFFF) + 3) & 0xFFFFFFFC # Round to multiple of 4. size = ((tag & 0x3FFFFF) + 3) & 0xFFFFFFFC # Round to multiple of 4.
vprint(f" Tag: {hex(tagid)} ({self.tag_to_name(tagid)}), Size: {size}, Offset: {hex(tags_offset + 4)}") vprint(f" Tag: {hex(tagid)} ({self.tag_to_name(tagid)}), Size: {size}, Offset: {hex(tags_offset + 4)}")
self.__parse_tag(tagid, size, tags_offset + 4, verbose=verbose) self.__parse_tag(ap2data, tagid, size, tags_offset + 4, verbose=verbose)
tags_offset += size + 4 # Skip past tag header and data. tags_offset += size + 4 # Skip past tag header and data.
def parse(self, verbose: bool = False) -> None: def __descramble(self, scrambled_data: bytes, descramble_info: bytes) -> bytes:
# Suppress debug text unless asked
if verbose:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
print(*args, **kwargs, file=sys.stderr)
add_coverage = self.add_coverage
# Reinitialize coverage.
self.coverage = [False] * len(self.data)
else:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
pass
def add_coverage(*args: Any, **kwargs: Any) -> None: # type: ignore
pass
swap_len = { swap_len = {
1: 2, 1: 2,
2: 4, 2: 4,
3: 8, 3: 8,
} }
data = bytearray(self.data) data = bytearray(scrambled_data)
data_offset = 0 data_offset = 0
for i in range(0, len(self.descramble_info), 2): for i in range(0, len(descramble_info), 2):
swapword = struct.unpack("<H", self.descramble_info[i:(i + 2)])[0] swapword = struct.unpack("<H", descramble_info[i:(i + 2)])[0]
if swapword == 0: if swapword == 0:
break break
@ -411,6 +400,37 @@ class SWF:
data[data_offset:(data_offset + swap_len[swap_type])] = data[data_offset:(data_offset + swap_len[swap_type])][::-1] data[data_offset:(data_offset + swap_len[swap_type])] = data[data_offset:(data_offset + swap_len[swap_type])][::-1]
data_offset += swap_len[swap_type] data_offset += swap_len[swap_type]
return bytes(data)
def __descramble_stringtable(self, scrambled_data: bytes, stringtable_offset: int, stringtable_size: int) -> bytes:
data = bytearray(scrambled_data)
addition = 128
for i in range(stringtable_size):
data[stringtable_offset + i] = (data[stringtable_offset + i] - addition) & 0xFF
addition += 1
return bytes(data)
def parse(self, verbose: bool = False) -> None:
# Suppress debug text unless asked
if verbose:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
print(*args, **kwargs, file=sys.stderr)
add_coverage = self.add_coverage
# Reinitialize coverage.
self.coverage = [False] * len(self.data)
else:
def vprint(*args: Any, **kwargs: Any) -> None: # type: ignore
pass
def add_coverage(*args: Any, **kwargs: Any) -> None: # type: ignore
pass
data = self.__descramble(self.data, self.descramble_info)
def get_until_null(offset: int) -> bytes: def get_until_null(offset: int) -> bytes:
out = b"" out = b""
while data[offset] != 0: while data[offset] != 0:
@ -447,28 +467,25 @@ class SWF:
add_coverage(24, 4) add_coverage(24, 4)
if flags & 0x4: if flags & 0x4:
# I have no idea what this offset is for. # This seems related to imported tags.
unknown_offset = struct.unpack("<I", data[56:60])[0] imported_tag_something_offset = struct.unpack("<I", data[56:60])[0]
add_coverage(56, 4) add_coverage(56, 4)
else: else:
# Unknown offset is not present. # Unknown offset is not present.
unknown_offset = None imported_tag_something_offset = None
# String table # String table
stringtable_offset, stringtable_size = struct.unpack("<II", data[48:56]) stringtable_offset, stringtable_size = struct.unpack("<II", data[48:56])
add_coverage(48, 8) add_coverage(48, 8)
# Descramble string table. # Descramble string table.
addition = 128 data = self.__descramble_stringtable(data, stringtable_offset, stringtable_size)
for i in range(stringtable_size):
data[stringtable_offset + i] = (data[stringtable_offset + i] - addition) & 0xFF
addition += 1
# Get exported SWF name. # Get exported SWF name.
self.exported_name = get_until_null(nameoffset + stringtable_offset).decode('ascii') self.exported_name = get_until_null(nameoffset + stringtable_offset).decode('ascii')
add_coverage(nameoffset + stringtable_offset, len(self.exported_name) + 1, unique=False) add_coverage(nameoffset + stringtable_offset, len(self.exported_name) + 1, unique=False)
vprint(f"\nAFP name: {self.name}") vprint(f"\nAFP name: {self.name}")
vprint(f"Version: {version}") vprint(f"Version: {hex(version)}")
vprint(f"Exported Name: {self.exported_name}") vprint(f"Exported Name: {self.exported_name}")
vprint(f"SWF Flags: {hex(flags)}") vprint(f"SWF Flags: {hex(flags)}")
if flags & 0x1: if flags & 0x1:
@ -480,16 +497,12 @@ class SWF:
else: else:
vprint(" 0x2: FPS is a float") vprint(" 0x2: FPS is a float")
if flags & 0x4: if flags & 0x4:
vprint(f" 0x4: Unknown data section present at offset {hex(unknown_offset)}") vprint(f" 0x4: Unknown imported tag section present at offset {hex(imported_tag_something_offset)}")
else: else:
vprint(" 0x4: Unknown data section not present") vprint(" 0x4: Unknown imported tag section not present")
vprint(f"Dimensions: {width}x{height}") vprint(f"Dimensions: {width}x{height}")
vprint(f"Requested FPS: {fps}") vprint(f"Requested FPS: {fps}")
# Unknown offset
if unknown_offset is not None:
vprint(f"Unknown data offset: {hex(unknown_offset)}")
# Exported assets # Exported assets
num_exported_assets = struct.unpack("<H", data[32:34])[0] num_exported_assets = struct.unpack("<H", data[32:34])[0]
asset_offset = struct.unpack("<I", data[40:44])[0] asset_offset = struct.unpack("<I", data[40:44])[0]
@ -526,7 +539,7 @@ class SWF:
swf_name = get_until_null(swf_name_offset + stringtable_offset).decode('ascii') swf_name = get_until_null(swf_name_offset + stringtable_offset).decode('ascii')
add_coverage(swf_name_offset + stringtable_offset, len(swf_name) + 1, unique=False) add_coverage(swf_name_offset + stringtable_offset, len(swf_name) + 1, unique=False)
vprint(f" {swf_name}: {count}") vprint(f" Source SWF: {swf_name}")
# Now, grab the actual asset names being imported. # Now, grab the actual asset names being imported.
for j in range(count): for j in range(count):
@ -535,12 +548,27 @@ class SWF:
asset_name = get_until_null(asset_name_offset + stringtable_offset).decode('ascii') asset_name = get_until_null(asset_name_offset + stringtable_offset).decode('ascii')
add_coverage(asset_name_offset + stringtable_offset, len(asset_name) + 1, unique=False) add_coverage(asset_name_offset + stringtable_offset, len(asset_name) + 1, unique=False)
vprint(f" {asset_id_no}: {asset_name}") vprint(f" Tag ID: {asset_id_no}, Requested Asset: {asset_name}")
imported_tags_data_offset += 4 imported_tags_data_offset += 4
imported_tags_offset += 4 imported_tags_offset += 4
# Some imported tag data.
if imported_tag_something_offset is not None:
unk1, length = struct.unpack("<HH", data[imported_tag_something_offset:(imported_tag_something_offset + 4)])
add_coverage(imported_tag_something_offset, 4)
vprint(f"Imported tag unknown data offset: {hex(imported_tag_something_offset)}, length: {length}")
for i in range(length):
item_offset = imported_tag_something_offset + 4 + (i * 12)
tag_id, _, item_data_offset, flags_or_length = struct.unpack("<HHII", data[item_offset:(item_offset + 12)])
add_coverage(item_offset, 12)
vprint(f" Tag ID: {tag_id}, Data offset: {hex(item_data_offset + imported_tag_something_offset)}, Data present flag: {hex(flags_or_length)}")
# TODO: Remove this # TODO: Remove this
with open(f"/shares/Entertainment/{self.exported_name}.bin", "wb") as bfp: with open(f"/shares/Entertainment/{self.exported_name}.bin", "wb") as bfp:
bfp.write(data) bfp.write(data)