Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
kingsznhone committed Apr 17, 2024
2 parents 50ce24a + ed55dc3 commit abe3ac2
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 35 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Add WinUI 3 Publish.

Async optimize on RC4 Stream

Add linux Support


### v2.2.0 2024.03.24

Expand Down
1 change: 1 addition & 0 deletions src/NCMDumpCLI/NCMDumpCLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>2.3.0</Version>
<Platforms>x64</Platforms>
<PublishAot>False</PublishAot>
</PropertyGroup>


Expand Down
43 changes: 22 additions & 21 deletions src/NCMDumpCore/NCMDumper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace NCMDumpCore
public class NCMDumper
{
private readonly int vectorSize = Vector256<byte>.Count;
private readonly byte[] coreKey = { 0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57 };
private readonly byte[] metaKey = { 0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28 };
private readonly byte[] coreKey = [0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57];
private readonly byte[] metaKey = [0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28];

private bool VerifyHeader(ref MemoryStream ms)
{
Expand All @@ -35,7 +35,7 @@ private byte[] ReadRC4Key(ref MemoryStream ms)
ms.Read(buffer);

//SIMD XOR 0x64
Vector256<byte> xor = Vector256.Create(0x64646464).AsByte();
Vector256<byte> xor = Vector256.Create((byte)0x64);
int i;
for (i = 0; i <= buffer.Length - vectorSize; i += vectorSize)
{
Expand All @@ -49,11 +49,13 @@ private byte[] ReadRC4Key(ref MemoryStream ms)
}

// decrypt keybox data
using (var decrypter = new AesCng() { Key = coreKey, Mode = CipherMode.ECB }.CreateDecryptor())
using (Aes aes = Aes.Create())
{
buffer = decrypter.TransformFinalBlock(buffer.ToArray(), 0, buffer.Length).Skip(17).ToArray(); // 17 = len("neteasecloudmusic")
aes.Mode = CipherMode.ECB;
aes.Key = coreKey;
var cleanText = aes.DecryptEcb(buffer.ToArray(), PaddingMode.PKCS7).ToArray()[17..];
return cleanText;
}
return buffer.ToArray();
}

private MetaInfo ReadMeta(ref MemoryStream ms)
Expand All @@ -65,7 +67,7 @@ private MetaInfo ReadMeta(ref MemoryStream ms)
ms.Read(buffer);

//SIMD XOR 0x63
Vector256<byte> xor = Vector256.Create(0x63636363).AsByte();
Vector256<byte> xor = Vector256.Create((byte)0x63);
int i;
for (i = 0; i <= buffer.Length - vectorSize; i += vectorSize)
{
Expand All @@ -81,21 +83,23 @@ private MetaInfo ReadMeta(ref MemoryStream ms)
buffer = System.Convert.FromBase64String(Encoding.ASCII.GetString(buffer.ToArray()[22..]));

// decrypt meta data which is a json contains info of the song
using (var cryptor = new AesCng() { Key = metaKey, Mode = CipherMode.ECB }.CreateDecryptor())
using (Aes aes = Aes.Create())
{
buffer = cryptor.TransformFinalBlock(buffer.ToArray(), 0, buffer.Length);
var MetaJsonString = Encoding.UTF8.GetString(buffer).Replace("music:", "");
aes.Mode = CipherMode.ECB;
aes.Key = metaKey;
var cleanText = aes.DecryptEcb(buffer.ToArray(), PaddingMode.PKCS7);
var MetaJsonString = Encoding.UTF8.GetString(cleanText[6..]);
MetaInfo metainfo = JsonSerializer.Deserialize<MetaInfo>(MetaJsonString);
return metainfo;
}
}

private async Task<byte[]> ReadAudioData(MemoryStream ms, byte[] Key)
{
using (RC4_NCM_Stream rc4s = new RC4_NCM_Stream(ms, Key))
using (RC4_NCM_Stream rc4s = new(ms, Key))
{
byte[] data = new byte[ms.Length - ms.Position];
Memory<byte> m_data = new Memory<byte>(data);
Memory<byte> m_data = new(data);
await rc4s.ReadAsync(m_data);
return data;
}
Expand All @@ -122,7 +126,7 @@ private void AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo)
}
}

//Add more infomation
//Add more information
tagfile.Tag.Title = metainfo.musicName;
tagfile.Tag.Performers = metainfo.artist.Select(x => x[0]).ToArray();
tagfile.Tag.Album = metainfo.album;
Expand All @@ -132,7 +136,7 @@ private void AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo)

private byte[]? FetchUrl(Uri uri)
{
HttpClient client = new HttpClient();
HttpClient client = new();
try
{
var response = client.GetAsync(uri).Result;
Expand Down Expand Up @@ -182,7 +186,7 @@ public async Task<bool> ConvertAsync(string path)
}

//Read all bytes to ram.
MemoryStream ms = new MemoryStream(await System.IO.File.ReadAllBytesAsync(path));
MemoryStream ms = new(await System.IO.File.ReadAllBytesAsync(path));

//Verify Header
if (!VerifyHeader(ref ms))
Expand All @@ -201,9 +205,7 @@ public async Task<bool> ConvertAsync(string path)
MetaInfo metainfo = ReadMeta(ref ms);

//CRC32 Check
var crc32bytes = new byte[4];
ms.Read(crc32bytes, 0, crc32bytes.Length);
var crc32len = MemoryMarshal.Read<int>(crc32bytes);
uint crc32 = ReadUint32(ref ms);

// skip 5 character,
ms.Seek(5, SeekOrigin.Current);
Expand All @@ -226,16 +228,15 @@ public async Task<bool> ConvertAsync(string path)
byte[] AudioData = await ReadAudioData(ms, RC4Key);

//Flush Audio Data to disk drive
string OutputPath = path.Substring(0, path.LastIndexOf("."));
string OutputPath = path[..path.LastIndexOf('.')];

string format = metainfo.format;
if (format is null or "") format = "mp3";
await System.IO.File.WriteAllBytesAsync($"{OutputPath}.{format}", AudioData);

//Add tag and cover
await Task.Run(() => AddTag($"{OutputPath}.{format}", ImageData, metainfo));
ms.Flush();
ms.Close();
await ms.DisposeAsync();
return true;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/NCMDumpCore/RC4_NCM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
public class RC4_NCM
{
private byte[] Keybox;
private readonly byte[] Keybox;
private int i = 0, j = 0;

public RC4_NCM(byte[] key)
Expand All @@ -19,7 +19,7 @@ public RC4_NCM(byte[] key)

public byte[] Encrypt(byte[] data)
{
Span<byte> span = new Span<byte>(data);
Span<byte> span = new(data);
Encrypt(ref span);
return span.ToArray();
}
Expand Down
16 changes: 8 additions & 8 deletions src/NCMDumpCore/RC4_NCM_Stream.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace NCMDumpCore
{
internal class RC4_NCM_Stream : Stream
public class RC4_NCM_Stream : Stream
{
private Stream innerStream;
private RC4_NCM rc4;
private readonly Stream innerStream;
private readonly RC4_NCM rc4;

public RC4_NCM_Stream(Stream innerStream, byte[] key)
{
Expand Down Expand Up @@ -73,16 +73,16 @@ public override void SetLength(long value)

public override void Write(byte[] buffer, int offset, int count)
{
Span<byte> span = buffer[offset..(offset + count)];
int bytesWrite = rc4.Encrypt(ref span);
Span<byte> span = buffer.AsSpan(offset, count);
rc4.Encrypt(ref span);
innerStream.Write(span);
}

public override void Write(ReadOnlySpan<byte> buffer)
{
Span<byte> span = new byte[buffer.Length];
buffer.CopyTo(span);
int bytesWrite = rc4.Encrypt(ref span);
rc4.Encrypt(ref span);

innerStream.Write(buffer);
}
Expand All @@ -91,15 +91,15 @@ public override async Task WriteAsync(byte[] data, int offset, int count, Cancel
{
byte[] buffer = data[offset..(offset + count)];
byte[] encrypted = await Task.Run(() => rc4.Encrypt(buffer));
await innerStream.WriteAsync(encrypted);
await innerStream.WriteAsync(encrypted, cancellationToken);
return;
}

public override async ValueTask WriteAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
{
byte[] buffer = data.ToArray();
await Task.Run(() => rc4.Encrypt(buffer));
await innerStream.WriteAsync(buffer);
await innerStream.WriteAsync(buffer, cancellationToken);
return;
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/NCMDumpGUI/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public MainWindowViewModel(NCMDumper _core)
Core = _core;
WillDeleteNCM = true;
ApplicationTitle = "NCMDump.NET";
NCMCollection = new();
NCMCollection = [];
AddFolderCommand = new AsyncRelayCommand(FolderDialog);
AddFileCommand = new AsyncRelayCommand(FileDialog);
ClearCommand = new AsyncRelayCommand(ClearList, () => NCMCollection.Count > 0);
Expand Down Expand Up @@ -191,9 +191,11 @@ private async Task FolderDialog()

private async Task FileDialog()
{
Microsoft.Win32.OpenFileDialog ofp = new();
ofp.Multiselect = true;
ofp.Filter = "NCM File(*.ncm)|*.ncm";
Microsoft.Win32.OpenFileDialog ofp = new()
{
Multiselect = true,
Filter = "NCM File(*.ncm)|*.ncm"
};
if (ofp.ShowDialog() == true)
{
foreach (string file in ofp.FileNames)
Expand Down

0 comments on commit abe3ac2

Please sign in to comment.