kgtemp文件转mp3工具

kgtemp文件是酷我音乐软件的缓存文件,本文从技术层面探讨如何解密该文件为mp3文件,并通过读取ID3信息来重命名。

kgtemp解密

kgtemp文件前1024个字节是固定的包头信息,解密方案详细可以参见(http://www.cnblogs.com/KMBlog/p/6877752.html):

class Program
    {
        static void Main(string[] args)
        {

            byte[] key={0xAC,0xEC,0xDF,0x57};
            using (var input = new FileStream(@"E:\KuGou\Temp\236909b6016c6e98365e5225f488dd7a.kgtemp", FileMode.Open, FileAccess.Read))
            {
                var output = File.OpenWrite(@"d:\test.mp3");//输出文件
                input.Seek(1024, SeekOrigin.Begin);//跳过1024字节的包头
                byte[] buffer = new byte[key.Length];
                int length;
                while((length=input.Read(buffer,0,buffer.Length))>0)
                {
                    for(int i=0;i<length;i++)
                    {
                        var k = key[i];
                        var kh = k >> 4;
                        var kl = k & 0xf;
                        var b = buffer[i];
                        var low = b & 0xf ^ kl;//解密后的低4位
                        var high = (b >> 4) ^ kh ^ low & 0xf;//解密后的高4位
                        buffer[i] = (byte)(high << 4 | low);
                    }
                    output.Write(buffer, 0, length);
                }
                output.Close();
            }
            Console.WriteLine("按任意键退出...");
            Console.ReadKey();
        }
    }

这样解密出来就是mp3文件了

读取ID3信息

解密出来的文件还需要手动命名,不是很方便,可以读取ID3V1信息重命名文件。 ID3V1比较简单,它是存放在MP3文件的末尾,用16进制的编辑器打开一个MP3文件,查看其末尾的128个顺序存放字节,数据结构定义如下: char Header3; /标签头必须是"TAG"否则认为没有标签/ char Title[30]; /标题/ char Artist[30]; /作者/ char Album[30]; /专集/ char Year4; /出品年代/ char Comment[30]; /备注/ char Genre; /类型,流派/

解析代码比较简单,注意中文歌曲用GBK编码就可以了:

  private static Mp3Info FormatMp3Info(byte[] Info, System.Text.Encoding Encoding)
        {
            Mp3Info myMp3Info = new Mp3Info();
            string str = null;
            int i;
            int position = 0主要代码jia,; //循环的起始值
            int currentIndex = 0; //Info的当前索引值

            //获取TAG标识
            for (i = currentIndex; i < currentIndex + 3; i++)
            {
                str = str + (char)Info[i];
                position++;
            }
            currentIndex = position;
            myMp3Info.identify = str;

            //获取歌名
            str = null;
            byte[] bytTitle = new byte[30]; //将歌名部分读到一个单独的数组中
            int j = 0;
            for (i = currentIndex; i < currentIndex + 30; i++)
            {
                bytTitle[j] = Info[i];
                position++;
                j++;
            }
            currentIndex = position;
            myMp3Info.Title = ByteToString(bytTitle, Encoding);

            //获取歌手名
            str = null;
            j = 0;
            byte[] bytArtist = new byte[30]; //将歌手名部分读到一个单独的数组中
            for (i = currentIndex; i < currentIndex + 30; i++)
            {
                bytArtist[j] = Info[i];
                position++;
                j++;
            }
            currentIndex = position;
            myMp3Info.Artist = ByteToString(bytArtist, Encoding);

            //获取唱片名
            str = null;
            j = 0;
            byte[] bytAlbum = new byte[30]; //将唱片名部分读到一个单独的数组中
            for (i = currentIndex; i < currentIndex + 30; i++)
            {
                bytAlbum[j] = Info[i];
                position++;
                j++;
            }
            currentIndex = position;
            myMp3Info.Album = ByteToString(bytAlbum, Encoding);

            //获取年
            str = null;
            j = 0;
            byte[] bytYear = new byte[4]; //将年部分读到一个单独的数组中
            for (i = currentIndex; i < currentIndex + 4; i++)
            {
                bytYear[j] = Info[i];
                position++;
                j++;
            }
            currentIndex = position;
            myMp3Info.Year = ByteToString(bytYear, Encoding);

            //获取注释
            str = null;
            j = 0;
            byte[] bytComment = new byte[28]; //将注释部分读到一个单独的数组中
            for (i = currentIndex; i < currentIndex + 25; i++)
            {
                bytComment[j] = Info[i];
                position++;
                j++;
            }
            currentIndex = position;
            myMp3Info.Comment = ByteToString(bytComment, Encoding);

            //以下获取保留位
            myMp3Info.reserved1 = (char)Info[++position];
            myMp3Info.reserved2 = (char)Info[++position];
            myMp3Info.reserved3 = (char)Info[++position];

            //
            return myMp3Info;
        }

转换小工具

写了一个小工具,来进行转换

下载地址:https://pan.baidu.com/s/1o7FIsPk

PS:上面只读取了IDV1,部分歌曲可能不存在 可以下载@缤纷 提供的程序,增加了ID3V2的支持: https://files.cnblogs.com/files/gxlxzys/kgtemp%E6%96%87%E4%BB%B6%E8%BD%ACmp3%E5%B7%A5%E5%85%B7.zip


作者:Jadepeng 出处:jqpeng的技术记事本--http://www.cnblogs.com/xiaoqi 您的支持是对博主最大的鼓励,感谢您的认真阅读。 本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Dato

关于ZK框架的onScroll事件的问题

由于我现在所在的公司用到的zk框架,遇到了一个需求frozen on top。 简单来说就是滚动超过范围后,希望有一块东西停留在滚动窗口的顶部。 一.zk框架 ...

2646
来自专栏james大数据架构

SwitchButton 开关按钮 的多种实现方式

刚开始接触开关样式的按钮是在IOS系统上面,它的切换以及滑动十分帅气,深入人心。 所谓的开关按钮,就是只有2个状态:on和off,下图就是系统IOS 7上开关按...

1967
来自专栏MasiMaro 的技术博文

windows 多任务与进程

多任务的本质就是并行计算,它能够利用至少2处理器相互协调,同时计算同一个任务的不同部分,从而提高求解速度,或者求解单机无法求解的大规模问题。以前的分布式计算正是...

814
来自专栏北京马哥教育

Tornado 源码阅读:初步认识

而`ioloop`的核心部分则是 `while True`这个循环内部的逻辑,贴上他的代码下

642
来自专栏大内老A

ASP.NET MVC下的异步Action的定义和执行原理

Visual Studio提供的Controller创建向导默认为我们创建一个继承自抽象类Controller的Controller类型,这样的Controll...

2075
来自专栏向治洪

深入理解React Native页面构建渲染原理

前言 React Native 是最近非常火的一个话题,因为它的语法简介,跨平台等特性,赢得了各大平台的青睐,虽然前期是有一些坑。 基本概念解释 React 是...

2769
来自专栏SDNLAB

OVS中Action源码分析&自定义Action

前言 在生产或是科研中,OpenFlow定义的Action有时候并不能完全满足需求,那么如何向OVS中添加一个自定义的action,本文对此做详细分析。 我们知...

3909
来自专栏JadePeng的技术博客

Angular快速学习笔记(4) -- Observable与RxJS

2612
来自专栏北京马哥教育

Tornado 源码阅读:初步认识

来源:国夫君 segmentfault.com/a/1190000002971992 ioloop `ioloop`是`tornado`的核心模块,也是个调度...

1964
来自专栏向治洪

深入理解React Native页面构建渲染原理

前言 React Native 是最近非常火的一个话题,因为它的语法简介,跨平台等特性,赢得了各大平台的青睐,虽然前期是有一些坑。 基本概念解释 React 是...

27110

扫码关注云+社区