Browse Source

fit png format cover & empty meta data #14

pull/16/head
Nzix 6 years ago
parent
commit
cd9201d7f8
  1. 49
      ncmdump.py

49
ncmdump.py

@ -5,11 +5,10 @@ Created on Sun Jul 15 01:05:58 2018
@author: Nzix @author: Nzix
""" """
import binascii import binascii, struct
import struct import base64, json
import base64 import os, traceback
import json
import os
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor as XOR from Crypto.Util.strxor import strxor as XOR
from mutagen import mp3, flac, id3 from mutagen import mp3, flac, id3
@ -17,6 +16,8 @@ from mutagen import mp3, flac, id3
def dump(input_path, output_path = None, skip = True): def dump(input_path, output_path = None, skip = True):
output_path = (lambda path, meta: os.path.splitext(path)[0] + '.' + meta['format']) if not output_path else output_path output_path = (lambda path, meta: os.path.splitext(path)[0] + '.' + meta['format']) if not output_path else output_path
output_path = (lambda path, meta: path) if not callable(output_path) else output_path
core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857')
meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728')
unpad = lambda s : s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))] unpad = lambda s : s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))]
@ -52,6 +53,7 @@ def dump(input_path, output_path = None, skip = True):
meta_length = f.read(4) meta_length = f.read(4)
meta_length = struct.unpack('<I', bytes(meta_length))[0] meta_length = struct.unpack('<I', bytes(meta_length))[0]
if meta_length:
meta_data = bytearray(f.read(meta_length)) meta_data = bytearray(f.read(meta_length))
meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data])) meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data]))
meta_data = base64.b64decode(meta_data[22:]) meta_data = base64.b64decode(meta_data[22:])
@ -59,6 +61,8 @@ def dump(input_path, output_path = None, skip = True):
cryptor = AES.new(meta_key, AES.MODE_ECB) cryptor = AES.new(meta_key, AES.MODE_ECB)
meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:] meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:]
meta_data = json.loads(meta_data) meta_data = json.loads(meta_data)
else:
meta_data = {'format': 'flac' if os.fstat(f.fileno()).st_size > 1024 ** 2 * 16 else 'mp3'}
# crc32 # crc32
crc32 = f.read(4) crc32 = f.read(4)
@ -68,7 +72,7 @@ def dump(input_path, output_path = None, skip = True):
f.seek(5, 1) f.seek(5, 1)
image_size = f.read(4) image_size = f.read(4)
image_size = struct.unpack('<I', bytes(image_size))[0] image_size = struct.unpack('<I', bytes(image_size))[0]
image_data = f.read(image_size) image_data = f.read(image_size) if image_size else None
# media data # media data
output_path = output_path(input_path, meta_data) output_path = output_path(input_path, meta_data)
@ -78,15 +82,6 @@ def dump(input_path, output_path = None, skip = True):
f.close() f.close()
# stream cipher (modified RC4 Pseudo-random generation algorithm) # stream cipher (modified RC4 Pseudo-random generation algorithm)
# data = bytearray(data)
# i = 0
# j = 0
# for k, _ in enumerate(data):
# i = (i + 1) % 256
# j = (i + S[i]) % 256 # in RC4, is j = (j + S[i]) % 256
# # S[i], S[j] = S[j], S[i] # no swapping
# data[k] ^= S[(S[i] + S[j]) % 256]
stream = [S[(S[i] + S[(i + S[i]) & 0xFF]) & 0xFF] for i in range(256)] stream = [S[(S[i] + S[(i + S[i]) & 0xFF]) & 0xFF] for i in range(256)]
stream = bytes(bytearray(stream * (len(data) // 256 + 1))[1:1 + len(data)]) stream = bytes(bytearray(stream * (len(data) // 256 + 1))[1:1 + len(data)])
data = XOR(data, stream) data = XOR(data, stream)
@ -96,28 +91,28 @@ def dump(input_path, output_path = None, skip = True):
m.close() m.close()
# media tag # media tag
def embed(item, content):
item.encoding = 0
item.type = 3
item.mime = 'image/png' if content[0:4] == binascii.a2b_hex('89504E47') else 'image/jpeg'
item.data = content
if image_data:
if meta_data['format'] == 'flac': if meta_data['format'] == 'flac':
audio = flac.FLAC(output_path) audio = flac.FLAC(output_path)
# audio.delete()
image = flac.Picture() image = flac.Picture()
image.encoding = 0 embed(image, image_data)
image.type = 3
image.mime = 'image/jpeg'
image.data = image_data
audio.clear_pictures() audio.clear_pictures()
audio.add_picture(image) audio.add_picture(image)
elif meta_data['format'] == 'mp3': elif meta_data['format'] == 'mp3':
audio = mp3.MP3(output_path) audio = mp3.MP3(output_path)
# audio.delete()
image = id3.APIC() image = id3.APIC()
image.encoding = 0 embed(image, image_data)
image.type = 3
image.mime = 'image/jpeg'
image.data = image_data
audio.tags.add(image) audio.tags.add(image)
audio.save() audio.save()
audio = mp3.EasyMP3(output_path)
if meta_length:
audio = flac.FLAC(output_path) if meta_data['format'] == 'flac' else mp3.EasyMP3(output_path)
audio['title'] = meta_data['musicName'] audio['title'] = meta_data['musicName']
audio['album'] = meta_data['album'] audio['album'] = meta_data['album']
audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']]) audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']])
@ -143,5 +138,5 @@ if __name__ == '__main__':
dump(path) dump(path)
print(os.path.split(path)[-1]) print(os.path.split(path)[-1])
except Exception as e: except Exception as e:
print(e) print(traceback.format_exc())
pass pass
Loading…
Cancel
Save