前言

网易云的Vip音乐下载下来,格式不是mp3/flac这种通用的音乐格式,而是经过加密的ncm文件。只有用网易云的音乐App才能够打开。于是想到可不可以把.ncm文件转换成mp3或者flac文件,上google查了一下,发现有不少人已经做了这件事,但没有发现C语言版本的,就想着写一个纯C语言版本的ncm转mp3/flac。

NCM文件结构

ncm文件的结构,网上有人解析出来了,分为下面几个部分

信息 大小 说明
Magic Header 10 bytes 文件头
Key Length 4 bytes AES128加密后的RC4密钥长度,字节是按小端排序。
Key Data Key Length 用AES128加密后的RC4密钥。
1. 先按字节对0x64进行异或。
2. AES解密,去除填充部分。
3. 去除最前面'neteasecloudmusic'17个字节,得到RC4密钥。
Music Info Length 4 bytes 音乐相关信息的长度,小端排序。
Music Info Data Music Info Length Json格式音乐信息数据。
1. 按字节对0x63进行异或。
2. 去除最前面22个字节。
3. Base64进行解码。
4. AES解密。
6. 去除前面6个字节得到Json数据。
CRC 4 bytes 跳过
Gap 5 bytes 跳过
Image Size 4 bytes 图片的大小
Image Image Size 图片数据
Music Data - 1. RC4-KSA生成S盒。
2. 用S盒解密(自定义的解密方法),不是RC4-PRGA解密。

两个AES对应密钥

unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };

unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 };

不得不佩服当初破解这个东西的人,不仅把文件结构摸得请清楚楚,还把密钥也搞到手,应该是个破解大神。有了上面的东西,剩下的就很简单了,按部就班来就行了。

一些算法准备

开始前我们需要把AES算法,BASE64算法,RC4算法和Json解析算法先写好。

除此之外还有一个编码问题,解析出来的ncm文件是用utf-8编码存储的,所以它在中文windows系统下汉字会出现乱码,因为中文windows系统采用的编码是GBK,两者不兼容,所以我们要写一个编码转换算法,将utf8格式字符串转位GBK的。Linux下不用转换,Linux本身就是用UTF-8的。

C语言没有这些库,都要自己来。

  • AES用GitHub上的

    tiny-AES-c
  • JSON用GitHub上的CJSON

    CJSON
  • Base64和RC4算法比较简单我们自己写
unsigned char* base64_decode(unsigned char* code,int len,int * actLen)
{
//根据base64表,以字符找到对应的十进制数据
int table[] = { 0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,52,53,54,55,56,57,58,
59,60,61,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,
22,23,24,25,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51
};
long str_len;
unsigned char* res;
int i, j; //计算解码后的字符串长度
//判断编码后的字符串后是否有=
if (strstr(code, "=="))
str_len = len / 4 * 3 - 2;
else if (strstr(code, "="))
str_len = len / 4 * 3 - 1;
else
str_len = len / 4 * 3; *actLen = str_len;
res = malloc(sizeof(unsigned char) * str_len + 1);
res[str_len] = '\0'; //以4个字符为一位进行解码
for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
{
res[j] = ((unsigned char)table[code[i]]) << 2 | (((unsigned char)table[code[i + 1]]) >> 4);
res[j + 1] = (((unsigned char)table[code[i + 1]]) << 4) | (((unsigned char)table[code[i + 2]]) >> 2);
res[j + 2] = (((unsigned char)table[code[i + 2]]) << 6) | ((unsigned char)table[code[i + 3]]);
}
return res; }
  • RC4生成S盒
//用key生成S盒
/*
* s: s盒
* key: 密钥
* len: 密钥长度
*/
void rc4Init(unsigned char* s, const unsigned char* key, int len)
{
int i = 0, j = 0;
unsigned char T[256] = { 0 }; for (i = 0; i < 256; i++)
{
s[i] = i;
T[i] = key[i % len];
} for (i = 0; i < 256; i++)
{
j = (j + s[i] + T[i]) % 256;
unsigned tmp = s[i];
s[i]=s[j];
s[j]=tmp;
}
}
//针对NCM文件的解密
//异或关系
/*
* s: s盒
* data: 要加密或者解密的数据
* len: data的长度
*/
void rc4PRGA(unsigned char* s, unsigned char* data, int len)
{
int i = 0;
int j = 0;
int k = 0;
int idx = 0;
for (idx = 0; idx < len; idx++)
{
i = (idx + 1) % 256;
j = (i + s[i]) % 256;
k= (s[i] + s[j]) % 256;
data[idx]^=s[k]; //异或
}
}
  • Windows下utf8转GBK
#ifdef WIN32
#include<Windows.h>
//返回转换好的字符串指针
unsigned char* utf8ToGbk(unsigned char*src,int len)
{
wchar_t* tmp = (wchar_t*)malloc(sizeof(wchar_t) * len+2);
unsigned char* newSrc = (unsigned char*)malloc(sizeof(unsigned char) * len + 2); MultiByteToWideChar(CP_UTF8, 0, src, -1, tmp, len);
WideCharToMultiByte(CP_ACP, 0, tmp, -1, newSrc, len+2, NULL,NULL);
return newSrc;
}
#endif

NCM文件解析

按照NCM文件结构一步一步读取数据来进行解析

//fileName:要转换的文件
void readFileData(const char* fileName)
{
FILE* f;
f = fopen(fileName, "rb");
if (!f)
{
printf("No such file: %s\n", fileName);
return;
} unsigned char buf[16];
int len=0;
int i = 0; unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };
unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 }; fseek(f, 10, SEEK_CUR); //f从当前位置移动10个字节
fread(buf, 1, 4, f); //读取rc4 key 的长度 len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* rc4Key= (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(rc4Key, 1, len, f); //读取rc4数据 //解密rc4密钥
for (i = 0; i < len; i++)
{
rc4Key[i] ^= 0x64;
} struct AES_ctx ctx;
AES_init_ctx(&ctx, core_key); //使用core_key密钥
int packSize = len / 16; //采用的是AES-ECB加密方式,和Pkcs7padding填充
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &rc4Key[i * 16]);
}
int pad = rc4Key[len - 1]; //获取填充的长度
rc4Key[len - pad] = '\0'; //去除填充的部分,得到RC4密钥 fread(buf, 1, 4, f); //读取Music Info 长度数据
len = ((buf[3] << 8 | buf[2]) << 16) | (buf[1] << 8 | buf[0]);
unsigned char* meta = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(meta, 1, len, f); //读取Music Info数据
//解析Music info信息
for (i = 0; i < len; i++)
{
meta[i] ^= 0x63;
}
int act = 0;
unsigned char* data = base64_decode(&meta[22], len - 22, &act); //base64解码
AES_init_ctx(&ctx, meta_key); //AES解密
packSize = act / 16;
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &data[i * 16]);
}
pad = data[act - 1];
data[act - pad] = '\0'; //去除填充部分
unsigned char* newData = data;
#ifdef WIN32 newData = utf8ToGbk(data, strlen(data)); #endif cJSON* cjson = cJSON_Parse(&newData[6]); //json解析,获取格式和名字等
if (cjson == NULL)
{
printf("cjson parse failed\n");
return;
}
//printf("%s\n", cJSON_Print(cjson)); //输出json fseek(f, 9, SEEK_CUR); //从当前位置跳过9个字节
fread(buf, 1, 4, f); //读取图片大小
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* img = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(img, 1, len, f); //读取图片数据 int offset= 1024 * 1024 * 10; //10MB 音乐数据一般比较大一次读入10MB
int total = 0;
int reSize = offset;
unsigned char* musicData = (unsigned char*)malloc(offset); //10m while (!feof(f))
{
len = fread(musicData+total, 1, offset, f); //每次读取10M
total += len;
reSize += offset;
musicData=realloc(musicData,reSize); //扩容
} unsigned char sBox[256] = { 0 }; //s盒
rc4Init(sBox, &rc4Key[17], strlen(&rc4Key[17])); //用rC4密钥进行初始化s盒
rc4PRGA(sBox, musicData, total); //解密 //拼接文件名(artist + music name+format)
char* musicName = cJSON_GetObjectItem(cjson, "musicName")->valuestring;
cJSON* sub = cJSON_GetObjectItem(cjson, "artist");
char*artist=cJSON_GetArrayItem(cJSON_GetArrayItem(sub, 0),0)->valuestring;
char* format = cJSON_GetObjectItem(cjson, "format")->valuestring;
char* saveFileName =(char*)malloc(strlen(musicName) + strlen(artist) + strlen(format)+5);
sprintf(saveFileName, "%s - %s.%s", artist, musicName, format);
FILE* fo=fopen(saveFileName, "wb");
if (fo == NULL)
{
printf("The fileName - '%s' is invalid in this system\n", saveFileName);
}
else
{
fwrite(musicData, 1, total, fo);
fclose(fo);
} #ifdef WIN32
free(newData);
#endif
free(data);
free(meta);
free(img);
free(musicData);
fclose(f); }
  1. AES采用的是AES-ECB模式,pack7padding填充方式。即16个字节为一组,如果不够16个字节,那就缺几个字节就填充几个字节,每个字节的值都是缺少的字节数。所以获取最后一个字节的值就知道要填充了几个字节。
  2. RC4解密那里,不是按RC4的来的,虽说叫RC4,但只有生成S盒那里是一样的,其它的不是按RC4算法来的。
  3. 有些解析出来音乐的名字,系统是不支持的,比如带'/'的,在创建新文件写入时会失败。
  4. 以"結束バンド - ギターと孤独と蒼い惑星.ncm"为例看看它的json数据是怎么样的

{

"musicId": 1991012773,

"musicName": "ギターと孤独と蒼い惑星",

"artist": [["結束バンド", 54103171]],

"albumId": 153542094,

"album": "ギターと孤独と蒼い惑星",

"albumPicDocId": "109951167983448236",

"albumPic": "https://p4.music.126.net/rfstzrVK05hCPjU-4mzSFA==/109951167983448236.jpg",

"bitrate": 320000,

"mp3DocId": "f481d20151f01d5d681d2768d753ad64",

"duration": 229015,

"mvId": 0,

"alias": ["TV动画《孤独摇滚!》插曲"],

"transNames": [],

"format": "mp3",

"flag": 4

}

可以根据需要自由提取需要的信息

完整代码

点击查看代码
/*
* date:2022-12-12
* author: FL
* purpose: ncm file to mp3
*/ #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include "aes.h"
#include "cJSON.h" #ifdef WIN32
#include<Windows.h>
//返回转换好的字符串指针
unsigned char* utf8ToGbk(unsigned char*src,int len)
{
wchar_t* tmp = (wchar_t*)malloc(sizeof(wchar_t) * len+2);
unsigned char* newSrc = (unsigned char*)malloc(sizeof(unsigned char) * len + 2); MultiByteToWideChar(CP_UTF8, 0, src, -1, tmp, len); //转为unicode
WideCharToMultiByte(CP_ACP, 0, tmp, -1, newSrc, len+2, NULL,NULL); //转gbk return newSrc;
}
#endif void swap(unsigned char* a, unsigned char* b)
{
unsigned char t = *a;
*a = *b;
*b = t;
} //用key生成S盒
/*
* s: s盒
* key: 密钥
* len: 密钥长度
*/
void rc4Init(unsigned char* s, const unsigned char* key, int len)
{
int i = 0, j = 0;
unsigned char T[256] = { 0 }; for (i = 0; i < 256; i++)
{
s[i] = i;
T[i] = key[i % len];
} for (i = 0; i < 256; i++)
{
j = (j + s[i] + T[i]) % 256;
swap(s + i, s + j);
}
}
//针对NCM文件的解密
//异或关系
/*
* s: s盒
* data: 要加密或者解密的数据
* len: data的长度
*/
void rc4PRGA(unsigned char* s, unsigned char* data, int len)
{
int i = 0;
int j = 0;
int k = 0;
int idx = 0;
for (idx = 0; idx < len; idx++)
{
i = (idx + 1) % 256;
j = (i + s[i]) % 256;
k = (s[i] + s[j]) % 256;
data[idx] ^= s[k]; //异或
}
} //base64 解码
/*
* code: 要解码的数据
*/
unsigned char* base64_decode(unsigned char* code, int len, int* actLen)
{
//根据base64表,以字符找到对应的十进制数据
int table[] = { 0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,52,53,54,55,56,57,58,
59,60,61,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,
22,23,24,25,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51
};
long str_len;
unsigned char* res;
int i, j; //计算解码后的字符串长度
//判断编码后的字符串后是否有=
if (strstr(code, "=="))
str_len = len / 4 * 3 - 2;
else if (strstr(code, "="))
str_len = len / 4 * 3 - 1;
else
str_len = len / 4 * 3; *actLen = str_len;
res = malloc(sizeof(unsigned char) * str_len + 1);
res[str_len] = '\0'; //以4个字符为一位进行解码
for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
{
res[j] = ((unsigned char)table[code[i]]) << 2 | (((unsigned char)table[code[i + 1]]) >> 4);
res[j + 1] = (((unsigned char)table[code[i + 1]]) << 4) | (((unsigned char)table[code[i + 2]]) >> 2);
res[j + 2] = (((unsigned char)table[code[i + 2]]) << 6) | ((unsigned char)table[code[i + 3]]);
}
return res; }
void readFileData(const char* fileName)
{
FILE* f;
f = fopen(fileName, "rb");
if (!f)
{
printf("No such file: %s\n", fileName);
return;
} unsigned char buf[16];
int len=0;
int i = 0; unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };
unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 }; fseek(f, 10, SEEK_CUR); //f从当前位置移动10个字节
fread(buf, 1, 4, f); //读取rc4 key 的长度 len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* rc4Key= (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(rc4Key, 1, len, f); //读取rc4数据 //解密rc4密钥
for (i = 0; i < len; i++)
{
rc4Key[i] ^= 0x64;
} struct AES_ctx ctx;
AES_init_ctx(&ctx, core_key); //使用core_key密钥
int packSize = len / 16; //采用的是AES-ECB加密方式,和Pkcs7padding填充
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &rc4Key[i * 16]);
}
int pad = rc4Key[len - 1]; //获取填充的长度
rc4Key[len - pad] = '\0'; //去除填充的部分,得到RC4密钥 fread(buf, 1, 4, f); //读取Music Info 长度数据
len = ((buf[3] << 8 | buf[2]) << 16) | (buf[1] << 8 | buf[0]);
unsigned char* meta = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(meta, 1, len, f); //读取Music Info数据
//解析Music info信息
for (i = 0; i < len; i++)
{
meta[i] ^= 0x63;
}
int act = 0;
unsigned char* data = base64_decode(&meta[22], len - 22, &act); //base64解码
AES_init_ctx(&ctx, meta_key); //AES解密
packSize = act / 16;
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &data[i * 16]);
}
pad = data[act - 1];
data[act - pad] = '\0'; //去除填充部分
unsigned char* newData = data;
#ifdef WIN32 newData = utf8ToGbk(data, strlen(data)); #endif cJSON* cjson = cJSON_Parse(&newData[6]); //json解析,获取格式和名字等
if (cjson == NULL)
{
printf("cjson parse failed\n");
return;
}
//printf("%s\n", cJSON_Print(cjson)); //输出json fseek(f, 9, SEEK_CUR); //从当前位置跳过9个字节
fread(buf, 1, 4, f); //读取图片大小
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* img = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(img, 1, len, f); //读取图片数据 int offset= 1024 * 1024 * 10; //10MB 音乐数据一般比较大一次读入10MB
int total = 0;
int reSize = offset;
unsigned char* musicData = (unsigned char*)malloc(offset); //10m while (!feof(f))
{
len = fread(musicData+total, 1, offset, f); //每次读取10M
total += len;
reSize += offset;
musicData=realloc(musicData,reSize); //扩容
} unsigned char sBox[256] = { 0 }; //s盒
rc4Init(sBox, &rc4Key[17], strlen(&rc4Key[17])); //用rC4密钥进行初始化s盒
rc4PRGA(sBox, musicData, total); //解密 //拼接文件名(artist + music name+format)
char* musicName = cJSON_GetObjectItem(cjson, "musicName")->valuestring;
cJSON* sub = cJSON_GetObjectItem(cjson, "artist");
char*artist=cJSON_GetArrayItem(cJSON_GetArrayItem(sub, 0),0)->valuestring;
char* format = cJSON_GetObjectItem(cjson, "format")->valuestring;
char* saveFileName =(char*)malloc(strlen(musicName) + strlen(artist) + strlen(format)+5);
sprintf(saveFileName, "%s - %s.%s", artist, musicName, format);
FILE* fo=fopen(saveFileName, "wb");
if (fo == NULL)
{
printf("The fileName - '%s' is invalid in this system\n", saveFileName);
}
else
{
fwrite(musicData, 1, total, fo);
fclose(fo);
} #ifdef WIN32
free(newData);
#endif
free(data);
free(meta);
free(img);
free(musicData);
fclose(f); } int main(int argc,char**argv)
{
readFileData("結束バンド - ギターと孤独と蒼い惑星.ncm");
return 0;
}

GitHub项目

NcmToMp3

星期五女孩

网易云VIP音乐NCM文件转MP3,C语言版本。的更多相关文章

  1. 下载网易云VIP音乐

    有偿帮助.联系方式在个人信息里.

  2. 网易云音乐ncm格式分析以及ncm与mp3格式转换

    目录 NCM格式分析 音频知识简介 两种可能 GitHub项目 格式分析 总体结构 密钥问题 代码分析 main函数 导入模块 dump函数 参考资料 代码完整版 转换工具 ncmdump ncmdu ...

  3. 网易云音乐 歌词制作软件 BesLyric (最新版本下载)

    导读 BesLyric , 一款专门制作 网易云音乐 LRC 滚动歌词的软件! 搜索.下载.制作 歌词更方便! 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦!Beslyric 为你排忧解难! 本文 ...

  4. 网易云音乐PC客户端加密API逆向解析

    1.前言 网上已经有大量的web端接口解析的方法了,但是对客户端的接口解析基本上找不到什么资料,本文主要分析网易云音乐PC客户端的API接口交互方式. 通过内部的代理设置,使用fiddler作为代理工 ...

  5. Python 爬取网易云歌手的50首热门作品

    使用 requests 爬取网易云音乐 Python 代码: import json import os import time from bs4 import BeautifulSoup impor ...

  6. 网易云音乐ncm加密格式批量转换为flac,mp3

    从网易云下载的某些付费歌曲下载下来会是ncm格式.ncm是个啥?就是你下完一首歌被网易云加密成它自己独有的ncm格式,这个ncm不能在其他播放器播放,如果网易云你会员到期了同样也会提示你无法播放(不是 ...

  7. Python解密网易云音乐缓存文件获取MP3

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:GeneralMonkey Python解密网易云音乐缓存文件获取MP3 ...

  8. 教你PC端网易云音乐自定义代理,VIP免费听歌!

    今天分享一份福利吧,使用网易云音乐自定义代理实现免费听和下载VIP.极高音质.付费的歌曲,这里主要针对PC端电脑版的,需要自己写脚本运行. 01 安装node.js Node.js是一个让 JavaS ...

  9. 网易云音乐mp3外链、真实地址下载方法

    一个网易音乐外链地址长期有效,很简单的方法: 第一步打开网易云音乐,随便找到一首歌,播放,复制网址的ID, 例如:杨钰莹的心雨,网址是: http://music.163.com/#/song?id= ...

  10. 网易云音乐MP3外链地址

      网易云音乐MP3外链地址下载方法很简单的方法: 下载公式:http://music.163.com/song/media/outer/url?id=ID数字.mp3 把上面红色部分(ID数字)换成 ...

随机推荐

  1. MySQL手动恢复数据库测试操作

    事件背景 MySQL数据库每日零点自动全备 某天上午9点,二狗子不小心drop了一个数据库 我们需要通过全备的数据文件,以及增量的binlog文件进行数据恢复 主要思想与原理 利用全备的sql文件中记 ...

  2. 常量的定义(const和#define)

    定义常量的方法 //均要在调用前(区别全局变量!!) 1.使用#define预处理器 2.使用const关键字 1.#define #define 常量名 常量值 //定义形式,常量名不可以是数字开头 ...

  3. JVM运行模式和逃逸分析

    JVM三种运行模式: 解释模式(Interpreted Mode):只使用解释器(-Xint强制JVM使用解释模式),执行一行JVM字节码就编译一行为机器码.(可以马上看到效果,但是运行过程比较慢) ...

  4. 齐博x1一段不错的小js提高一点点阅读体验 计算本文阅读所需的时长

    如图所示很多比较大的站点都有这样的一个小玩意 就是本文有多少字 阅读需要多少时间. 一段小小的js就可以实现,当然了php也可以功能太小了不值得做钩子或者插件自己需要的话再模板加一下吧. <sc ...

  5. 记录一次sshd服务启动失败

    记录一次sshd服务启动失败 问题描述: 服务器开机之后发现无法通过远程连接服务器终端,但是服务器并未宕机,于是考虑到sshd服务出现异常 解决思路: 查看服务器sshd服务运行情况 [root@ha ...

  6. 驱动开发:内核监控FileObject文件回调

    本篇文章与上一篇文章<驱动开发:内核注册并监控对象回调>所使用的方式是一样的都是使用ObRegisterCallbacks注册回调事件,只不过上一篇博文中LyShark将回调结构体OB_O ...

  7. HTML躬行记(3)——WebRTC视频通话

    WebRTC 在创建点对点(P2P)的连接之前,会先通过信令服务器交换两端的 SDP 和 ICE Candidate,取两者的交集,决定最终的音视频参数.传输协议.NAT 打洞方式等信息. 在完成媒体 ...

  8. 几个实用 shell 脚本

    1. Dos攻击防范(自动屏蔽攻击 IP) #!/bin/bash DATE=$(date +%d/%b/%Y:%H:%M) LOG_FILE=/usr/local/nginx/logs/demo2. ...

  9. ES6 学习笔记(十三)promise的简单使用

    1.什么是promise 在JavaScript中,我们经常会用到回调函数,而回调函数的使用让我们没法使用return,throw等关键字.JS引用promise正好解决了这个问题. promise单 ...

  10. VideoPipe可视化视频结构化框架新增功能详解(2022-11-4)

    VideoPipe从国庆节上线源代码到现在经历过了一个月时间,期间吸引了若干小伙伴的参与,现将本阶段新增内容总结如下,有兴趣的朋友可以加微信拉群交流. 项目地址:https://github.com/ ...