首个版本发布。完成批量解密及日志的功能。
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
src/main.rs.old
|
||||
src/lib.rs.bak
|
||||
839
Cargo.lock
generated
Normal file
839
Cargo.lock
generated
Normal file
@ -0,0 +1,839 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea31d69bda4949c1c1562c1e6f042a1caefac98cdc8a298260a2ff41c1e2d42b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ncmmiao"
|
||||
version = "1.1.3"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
"colored",
|
||||
"env_logger",
|
||||
"hex",
|
||||
"image",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
22
Cargo.toml
Normal file
22
Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "ncmmiao"
|
||||
version = "1.1.3"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8.3"
|
||||
base64 = "0.21.7"
|
||||
colored = "2.1.0"
|
||||
env_logger = "0.11.1"
|
||||
hex = "0.4.3"
|
||||
image = "0.24.8"
|
||||
log = "0.4.20"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde_derive = "1.0.195"
|
||||
serde_json = "1.0.111"
|
||||
walkdir = "2.4.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
77
README.md
Normal file
77
README.md
Normal file
@ -0,0 +1,77 @@
|
||||
# NcmMiao
|
||||
一个使用Rust语言编写的ncm文件解密工具。
|
||||
### 功能及特点
|
||||
- 支持单一文件,多文件夹递归批量解密。
|
||||
- 完善的日志功能
|
||||
- Colorful
|
||||
- 编译文件小,解密快
|
||||
|
||||
## 编译
|
||||
```
|
||||
cargo build -r
|
||||
```
|
||||
|
||||
## 使用
|
||||
支持单一文件,多文件夹递归批量解密。
|
||||
```
|
||||
cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ...
|
||||
```
|
||||
# TODO
|
||||
- [ ] 增加多线程支持
|
||||
- [ ] 增加解密进度条
|
||||
|
||||
|
||||
# CHANGELOG
|
||||
## [1.0.0] - 2024-1-27
|
||||
### Features
|
||||
- 初步完成解密函数。主程序成型
|
||||
|
||||
## [1.1.1] - 2024-2-1
|
||||
### Features
|
||||
- 完成批量解密
|
||||
### Fixed
|
||||
- 修正了提取音乐信息会有数据类型错误导致panic的问题
|
||||
|
||||
## [1.1.2] - 2024-2-5
|
||||
### Fixed
|
||||
- 修正了提取音乐信息时,部分歌曲信息提取失败的问题
|
||||
- 修正了音乐数据解密失败的问题
|
||||
## [1.1.3] - 2024-2-6
|
||||
### Fixed
|
||||
- 修正了部分音乐名称中含有不合法字符时创建文件失败的问题
|
||||
|
||||
|
||||
# 附 - ncm文件结构
|
||||
|信息|大小|作用|
|
||||
|:-:|:-:|:-:|
|
||||
|Magic Header|8 bytes|文件头|
|
||||
|Gap|2 bytes||
|
||||
|Key Length|4 bytes|RC4密钥长度,字节是按小端排序。|
|
||||
|Key Data|Key Length|RC4密钥|
|
||||
|Music Info Length|4 bytes|用AES128加密后的音乐相关信息的长度,小端排序。|
|
||||
|Music Info Data|Music Info Length|Json格式音乐信息数据。|
|
||||
|Gap|5 bytes||
|
||||
|CRC校验码|4 bytes|图片的CRC32校验码,小端排序。|
|
||||
|Image Size|4 bytes|图片的大小|
|
||||
|Image Data|Image Size|图片数据|
|
||||
|Music Data||音乐数据|
|
||||
---
|
||||
### Magic Header
|
||||
### Key Data
|
||||
用AES128加密后的RC4密钥。
|
||||
1. 先按字节对0x64进行异或。
|
||||
2. AES解密,去除填充部分。
|
||||
3. 去除最前面'neteasecloudmusic'17个字节,得到RC4密钥。
|
||||
### Music Info Data
|
||||
Json格式音乐信息数据。
|
||||
1. 按字节对0x63进行异或。
|
||||
2. 去除最前面22个字节。
|
||||
3. Base64进行解码。
|
||||
4. AES解密。
|
||||
6. 去除前面6个字节,后面数量为最后一个字节的字节数的垃圾数据,得到Json数据。
|
||||
|
||||
### Music Data
|
||||
1. RC4-KSA生成S盒。
|
||||
2. 用S盒解密(自定义的解密方法),不是RC4-PRGA解密。
|
||||
|
||||
|
||||
538
src/lib.rs
Normal file
538
src/lib.rs
Normal file
@ -0,0 +1,538 @@
|
||||
#[warn(dead_code)]
|
||||
use aes::cipher::generic_array::typenum::U16;
|
||||
use aes::cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit};
|
||||
use aes::Aes128;
|
||||
use base64;
|
||||
use colored::*;
|
||||
use core::panic;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::{self, Value};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufReader, Error, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||
// use std::iter::Enumerate;
|
||||
use std::path::Path;
|
||||
use std::str::from_utf8;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ncmfile {
|
||||
/// 文件对象
|
||||
pub file: File,
|
||||
/// 只是名称,不带后缀
|
||||
pub name: String,
|
||||
/// 带后缀名
|
||||
pub filename: String,
|
||||
/// 文件大小
|
||||
pub size: u64,
|
||||
/// 游标
|
||||
pub position: u64,
|
||||
}
|
||||
impl Ncmfile {
|
||||
pub fn new(filepath: &str) -> Result<Ncmfile, Error> {
|
||||
let file = File::open(filepath)?;
|
||||
let path = Path::new(filepath);
|
||||
let filename = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
let size = file.metadata().unwrap().len();
|
||||
let name = match Path::new(&filepath).file_stem() {
|
||||
Some(f) => f.to_str().unwrap().to_string(),
|
||||
None => panic!("获取文件名失败"),
|
||||
};
|
||||
Ok(Ncmfile {
|
||||
file,
|
||||
name,
|
||||
filename,
|
||||
size,
|
||||
position: 0,
|
||||
})
|
||||
}
|
||||
/// 根据传入的长度来读取文件
|
||||
///
|
||||
/// 该函数可以记录上次读取的位置,下次读取时从上次读取的位置开始
|
||||
/// - length 想要读取的长度
|
||||
pub fn seekread(&mut self, length: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
if self.position + length > self.size {
|
||||
return Err(Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
"无法读取!读取长度大于剩余文件大小!",
|
||||
));
|
||||
} else {
|
||||
let mut reader = BufReader::new(&self.file);
|
||||
reader.seek(SeekFrom::Start(self.position))?;
|
||||
let mut buf = vec![0; length as usize];
|
||||
reader.read_exact(&mut buf)?;
|
||||
self.position += length;
|
||||
Ok(buf[..].to_vec())
|
||||
}
|
||||
}
|
||||
/// 从指定位置开始读取。
|
||||
///
|
||||
/// !!!该函数仍然会更新游标
|
||||
///
|
||||
/// - offset 开始位置
|
||||
/// - length 想要读取的长度
|
||||
pub fn seekread_from(&mut self, offset: u64, length: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
if self.position + length > self.size {
|
||||
return Err(Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
"无法读取!读取长度大于剩余文件大小!",
|
||||
));
|
||||
} else {
|
||||
let mut reader = BufReader::new(&self.file);
|
||||
reader.seek(SeekFrom::Start(offset))?;
|
||||
let mut buf = vec![0; length as usize];
|
||||
reader.read_exact(&mut buf)?;
|
||||
self.position = offset + length;
|
||||
Ok(buf[..].to_vec())
|
||||
}
|
||||
}
|
||||
pub fn seekread_to_end(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
let mut reader = BufReader::new(&self.file);
|
||||
reader.seek(SeekFrom::Start(self.position))?;
|
||||
let mut buf = vec![0; self.size as usize - self.position as usize];
|
||||
reader.read_exact(&mut buf)?;
|
||||
self.position = self.size;
|
||||
Ok(buf[..].to_vec())
|
||||
}
|
||||
pub fn seekread_no_error(&mut self, length: u64) -> Vec<u8> {
|
||||
if self.position + length > self.size {
|
||||
if self.position >= self.size {
|
||||
return vec![];
|
||||
} else {
|
||||
let mut reader = BufReader::new(&self.file);
|
||||
let _ = reader.seek(SeekFrom::Start(self.position));
|
||||
|
||||
let mut buf: Vec<u8> = vec![0; (self.size - self.position) as usize];
|
||||
let _ = reader.read_exact(&mut buf);
|
||||
self.position += length;
|
||||
return buf[..].to_vec();
|
||||
}
|
||||
} else {
|
||||
let mut reader = BufReader::new(&self.file);
|
||||
let _ = reader.seek(SeekFrom::Start(self.position));
|
||||
let mut buf = vec![0; length as usize];
|
||||
let _ = reader.read_exact(&mut buf);
|
||||
self.position += length;
|
||||
buf[..].to_vec()
|
||||
}
|
||||
}
|
||||
/// 跳过某些数据
|
||||
pub fn skip(&mut self, length: u64) -> Result<(), std::io::Error> {
|
||||
if self.position + length > self.size {
|
||||
return Err(Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
"无法跳过!跳过长度大于剩余文件大小!",
|
||||
));
|
||||
} else {
|
||||
self.position += length;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
///按字节进行0x64异或。
|
||||
fn parse_key(key: &mut [u8]) -> &[u8] {
|
||||
for i in 0..key.len() {
|
||||
key[i] ^= 0x64;
|
||||
}
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
/// 存储元数据的结构体
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Metadata {
|
||||
//编号
|
||||
#[serde(rename = "musicId", skip)] //没用过,跳过
|
||||
music_id: String,
|
||||
// 音乐名称
|
||||
#[serde(rename = "musicName")]
|
||||
music_name: String,
|
||||
// 艺术家
|
||||
#[serde(rename = "artist")]
|
||||
music_artist: Vec<(String, String)>,
|
||||
// 专辑id
|
||||
#[serde(rename = "albumId")]
|
||||
album_id: String,
|
||||
// 专辑
|
||||
#[serde(rename = "album")]
|
||||
album: String,
|
||||
//
|
||||
#[serde(rename = "albumPicDocId", skip)]
|
||||
album_pic_doc_id: String,
|
||||
//
|
||||
#[serde(rename = "albumPic", skip)]
|
||||
album_pic: String,
|
||||
// 比特率
|
||||
#[serde(rename = "bitrate")]
|
||||
bitrate: u128,
|
||||
//
|
||||
#[serde(rename = "mp3DocId", skip)]
|
||||
mp3_doc_id: String,
|
||||
// 时间长短
|
||||
#[serde(rename = "duration")]
|
||||
duration: u128,
|
||||
//
|
||||
#[serde(rename = "mvId")]
|
||||
mv_id: String,
|
||||
// 别名
|
||||
#[serde(rename = "alias")]
|
||||
alias: Vec<String>,
|
||||
// 译名
|
||||
#[serde(rename = "transNames")]
|
||||
trans_names: Vec<String>,
|
||||
// 音乐格式
|
||||
#[serde(rename = "format")]
|
||||
format: String,
|
||||
}
|
||||
|
||||
// 存储各种密钥的结构体
|
||||
pub struct Key {
|
||||
pub core: Vec<u8>,
|
||||
pub meta: Vec<u8>,
|
||||
}
|
||||
|
||||
/// 解密函数
|
||||
pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), MyError> {
|
||||
info!("开始解密[{}]文件", ncmfile.filename.yellow());
|
||||
// 获取magic header 。应为CTENFDAM
|
||||
trace!("获取 magic header");
|
||||
let magic_header = match ncmfile.seekread(8) {
|
||||
Ok(header) => header,
|
||||
Err(_e) => {
|
||||
error!("读取magic header失败");
|
||||
return Err(MyError::MagicHeaderError);
|
||||
}
|
||||
};
|
||||
// 判断是否为ncm格式的文件
|
||||
match from_utf8(&magic_header) {
|
||||
Ok(header) => {
|
||||
if header != "CTENFDAM" {
|
||||
// 传播错误至dump
|
||||
return Err(MyError::MagicHeaderError);
|
||||
} else {
|
||||
trace!("[{}]为ncm格式文件", ncmfile.filename.yellow());
|
||||
}
|
||||
}
|
||||
// 传播错误至dump
|
||||
Err(_e) => return Err(MyError::MagicHeaderError),
|
||||
}
|
||||
|
||||
// 跳过2字节
|
||||
trace!("跳过2字节");
|
||||
match ncmfile.skip(2) {
|
||||
Ok(_) => (),
|
||||
Err(_e) => return Err(MyError::FileSkipError),
|
||||
};
|
||||
|
||||
trace!("获取RC4密钥长度");
|
||||
//小端模式读取RC4密钥长度 正常情况下应为128
|
||||
let key_length = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
|
||||
debug!("RC4密钥长度为:{}", key_length);
|
||||
|
||||
//读取密钥 开头应为 neteasecloudmusic
|
||||
trace!("读取RC4密钥");
|
||||
let mut key_data = ncmfile.seekread(key_length).unwrap();
|
||||
//aes128解密
|
||||
let key_data = &aes128_to_slice(&keys.core, Ncmfile::parse_key(&mut key_data[..])); //先把密钥按照字节进行0x64异或
|
||||
// RC4密钥
|
||||
let key_data = unpad(&key_data[..])[17..].to_vec();
|
||||
|
||||
//读取meta信息的数据大小
|
||||
trace!("获取meta信息数据大小");
|
||||
let meta_length = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
|
||||
|
||||
// 读取meta信息
|
||||
trace!("读取meta信息");
|
||||
let meta_data = {
|
||||
let mut meta_data = ncmfile.seekread(meta_length).unwrap(); //读取源数据
|
||||
//字节对0x63进行异或。
|
||||
for i in 0..meta_data.len() {
|
||||
meta_data[i] ^= 0x63;
|
||||
}
|
||||
// base64解密
|
||||
let decode_data = &base64::decode(&meta_data[22..]).unwrap()[..];
|
||||
// aes128解密
|
||||
let aes_data = aes128_to_slice(&keys.meta, decode_data);
|
||||
// unpadding
|
||||
let json_data = String::from_utf8(unpad(&aes_data)[6..].to_vec()).unwrap();
|
||||
debug!("json_data: {}", json_data);
|
||||
let data: Value = serde_json::from_str(&json_data[..]).unwrap(); //解析json数据
|
||||
data
|
||||
};
|
||||
|
||||
// 跳过4个字节的校验码
|
||||
trace!("跳过4个字节的校验码");
|
||||
let crc32 = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
|
||||
|
||||
// 跳过5个字节
|
||||
trace!("跳过5个字节");
|
||||
ncmfile.skip(5).unwrap();
|
||||
|
||||
// 获取图片数据的大小
|
||||
trace!("获取图片数据的大小");
|
||||
let image_data_length =
|
||||
u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
|
||||
|
||||
// 读取图片,并写入文件当中
|
||||
trace!("暂不需要保存图片,跳过{}字节", image_data_length);
|
||||
let image_data = ncmfile.seekread(image_data_length).unwrap(); //读取图片数据
|
||||
// let _ = ncmfile.skip(image_data_length); //暂不需要保存图片,直接跳过这些字节就好
|
||||
|
||||
//保存图片
|
||||
// trace!("保存图片");
|
||||
// let mut file = File::create(format!("TEST.jpg",)).unwrap();
|
||||
// file.write_all(&image_data).unwrap();
|
||||
|
||||
let key_box = {
|
||||
let key_length = key_data.len();
|
||||
let key_data = Vec::from(key_data);
|
||||
let mut key_box = (0..=255).collect::<Vec<u8>>();
|
||||
let mut temp = 0;
|
||||
let mut last_byte = 0;
|
||||
let mut key_offset = 0;
|
||||
|
||||
for i in 0..=255 {
|
||||
let swap = key_box[i as usize] as u64;
|
||||
temp = (swap + last_byte as u64 + key_data[key_offset as usize] as u64) & 0xFF;
|
||||
key_offset += 1;
|
||||
if key_offset >= key_length {
|
||||
key_offset = 0;
|
||||
}
|
||||
key_box[i as usize] = key_box[temp as usize];
|
||||
key_box[temp as usize] = swap as u8;
|
||||
last_byte = temp;
|
||||
}
|
||||
let key_box = key_box.clone();
|
||||
key_box
|
||||
};
|
||||
|
||||
/* let mut s_box = {
|
||||
let key_length = key_data.len();
|
||||
let key_box = Vec::from(key_data);
|
||||
let mut s = (0..=255).collect::<Vec<u8>>();
|
||||
let mut j = 0;
|
||||
|
||||
for i in 0..=255 {
|
||||
j = (j as usize + s[i] as usize + key_box[i % key_length] as usize) & 0xFF;
|
||||
|
||||
//记录 s[j]的值
|
||||
let temp = &s.get(j as usize).unwrap().to_owned();
|
||||
s[j as usize] = s[i];
|
||||
s[i] = temp.to_owned();
|
||||
}
|
||||
|
||||
s
|
||||
}; */
|
||||
|
||||
// let key_box = key_box[0..(key_box.len()-key_box[key_box.len() as usize-1] as usize)].to_vec();
|
||||
|
||||
//解密音乐数据
|
||||
trace!("解密音乐数据");
|
||||
let mut music_data: Vec<u8> = Vec::new();
|
||||
loop {
|
||||
let mut chunk = ncmfile.seekread_no_error(0x8000);
|
||||
|
||||
let chunk_length = chunk.len();
|
||||
if chunk_length != 0 {
|
||||
for i in 1..chunk_length + 1 {
|
||||
let j = i & 0xFF;
|
||||
|
||||
chunk[i - 1] ^= key_box[(key_box[j] as usize
|
||||
+ key_box[(key_box[j as usize] as usize + j) & 0xff] as usize)
|
||||
& 0xff]
|
||||
// chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF];
|
||||
}
|
||||
//向music_data中最追加chunk
|
||||
music_data.append(&mut chunk);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//组成流密钥
|
||||
/* let mut stream = Vec::new();
|
||||
for i in 0..256 {
|
||||
stream.push(
|
||||
s_box[(s_box[i] as usize + s_box[(i + s_box[i] as usize) & 0xFF] as usize) & 0xFF],
|
||||
)
|
||||
} */
|
||||
|
||||
// 解密音乐数据
|
||||
/* loop {
|
||||
let chunk = ncmfile.seekread_no_error(256); //每次读取256个字节
|
||||
if chunk.len() != 0 {
|
||||
for (count, &i) in chunk[..].iter().enumerate() {
|
||||
music_data.push(i ^ stream[count])
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} */
|
||||
// debug!("music_data:{:?}", music_data);
|
||||
// debug!("长度:{}", stream.len());
|
||||
|
||||
//退出循环,写入文件
|
||||
|
||||
//处理文件路径
|
||||
trace!("拼接文件路径");
|
||||
let path = {
|
||||
let mut artists = String::new();
|
||||
let mut music_artist = Vec::new();
|
||||
if let Some(i) = meta_data.get("artist") {
|
||||
for names in i.as_array().unwrap() {
|
||||
music_artist.push(names[0].as_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
for artist in music_artist {
|
||||
artists.push_str(&artist);
|
||||
artists.push(',');
|
||||
}
|
||||
// 移除最后一个字符
|
||||
artists.pop();
|
||||
debug!("艺术家名称:{}", artists.yellow());
|
||||
|
||||
let filename = format!(
|
||||
"{} - {}.{}",
|
||||
meta_data.get("musicName").unwrap().as_str().unwrap(),
|
||||
artists,
|
||||
meta_data.get("format").unwrap().as_str().unwrap()
|
||||
);
|
||||
// .replace("\"", """)
|
||||
// .replace("?", "?")
|
||||
// .replace(":", ":");
|
||||
|
||||
let filename = standardize_filename(filename);
|
||||
debug!("文件名:{}", filename.yellow());
|
||||
//链级创建输出目录
|
||||
fs::create_dir_all(outputdir).unwrap();
|
||||
outputdir.join(filename)
|
||||
};
|
||||
|
||||
debug!("文件路径: {:?}", path);
|
||||
// 创建文件
|
||||
trace!("创建文件");
|
||||
let mut music_file = File::create(&path).unwrap();
|
||||
trace!("保存文件");
|
||||
music_file.write_all(&music_data).unwrap();
|
||||
// 关闭文件
|
||||
music_file.sync_all().unwrap();
|
||||
music_file.flush().unwrap();
|
||||
|
||||
info!(
|
||||
"[{}]文件已保存到: {}",
|
||||
ncmfile.name.yellow(),
|
||||
path.to_str().unwrap().bright_cyan()
|
||||
);
|
||||
info!(
|
||||
"{}{}{}",
|
||||
"[".bright_green(),
|
||||
ncmfile.filename.yellow(),
|
||||
"]解密成功".bright_green()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn read_meta(file: &mut File, meta_length: u32) -> Result<Vec<u8>, Error> {}
|
||||
fn convert_to_generic_arrays(input: &[u8]) -> Vec<GenericArray<u8, U16>> {
|
||||
// 确保输入的长度是16的倍数
|
||||
assert!(
|
||||
input.len() % 16 == 0,
|
||||
"Input length must be a multiple of 16"
|
||||
);
|
||||
|
||||
input
|
||||
.chunks(16)
|
||||
.map(|chunk| {
|
||||
// 将每个块转换为GenericArray
|
||||
GenericArray::clone_from_slice(chunk)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
/// aes128解密
|
||||
/// !!!未对齐数据!!!
|
||||
/// TODO
|
||||
/// 解密NCM文件的rc4密钥前记得按字节对0x64进行异或
|
||||
fn aes128(key: &[u8], blocks: &[u8]) -> String {
|
||||
trace!("进行AES128解密");
|
||||
let key = GenericArray::from_slice(key);
|
||||
|
||||
let mut blocks = convert_to_generic_arrays(blocks);
|
||||
|
||||
// 初始化密钥
|
||||
let cipher = Aes128::new(&key);
|
||||
|
||||
// 开始解密
|
||||
cipher.decrypt_blocks(&mut blocks);
|
||||
|
||||
let mut x = String::new();
|
||||
for block in blocks.iter() {
|
||||
x.push_str(std::str::from_utf8(&block).unwrap())
|
||||
}
|
||||
// 去除所有空格及控制字符
|
||||
let x = x[..].trim();
|
||||
|
||||
x.to_string()
|
||||
}
|
||||
fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> {
|
||||
trace!("进行AES128解密");
|
||||
let key = GenericArray::from_slice(key);
|
||||
|
||||
let mut blocks = convert_to_generic_arrays(blocks);
|
||||
|
||||
// 初始化密钥
|
||||
let cipher = Aes128::new(&key);
|
||||
|
||||
// 开始解密
|
||||
cipher.decrypt_blocks(&mut blocks);
|
||||
let mut x: Vec<u8> = Vec::new();
|
||||
for block in blocks.iter() {
|
||||
for i in block {
|
||||
x.push(i.to_owned());
|
||||
}
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
/// ## 规范文件名称
|
||||
/// 防止创建文件失败
|
||||
/// 符号一一对应:
|
||||
/// - \ / * ? " : < > |
|
||||
/// - _ _ * ? " : ⟨ ⟩ _
|
||||
fn standardize_filename(old_filename: String) -> String {
|
||||
let mut new_filename = String::from(old_filename);
|
||||
debug!("规范文件名:{}", new_filename);
|
||||
let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"];
|
||||
let resolution = ["_", "_", "*", "?", """, ":", "⟨", "⟩", "_",];
|
||||
for i in 0..standard.len() {
|
||||
new_filename = new_filename.replace(&standard[i].to_string(), &resolution[i].to_string());
|
||||
}
|
||||
new_filename
|
||||
}
|
||||
|
||||
/// 使用PKCS5Padding标准,去掉填充信息
|
||||
fn unpad(data: &[u8]) -> Vec<u8> {
|
||||
data[..data.len() - data[data.len() - 1] as usize].to_vec()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MyError {
|
||||
MagicHeaderError,
|
||||
FileReadError,
|
||||
FileSkipError,
|
||||
FileWriteError,
|
||||
FilenameError,
|
||||
FileNotFoundError,
|
||||
}
|
||||
|
||||
impl std::error::Error for MyError {}
|
||||
|
||||
impl std::fmt::Display for MyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::MagicHeaderError => write!(f, "文件不为NCM格式"),
|
||||
Self::FileReadError => write!(f, "文件读取错误"),
|
||||
Self::FileWriteError => write!(f, "文件写入错误"),
|
||||
Self::FilenameError => write!(f, "文件名不符合规范"),
|
||||
_ => write!(f, "未知错误"),
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/main.rs
Normal file
92
src/main.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use env_logger::Builder;
|
||||
use hex::decode;
|
||||
use log::{error, warn};
|
||||
use ncmmiao::{dump, Key, Ncmfile};
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
// use std::time::SystemTime;
|
||||
use walkdir::WalkDir;
|
||||
#[warn(unreachable_code)]
|
||||
|
||||
fn main() {
|
||||
let mut builder = Builder::new();
|
||||
builder.filter(None, log::LevelFilter::Info);
|
||||
builder.init(); //初始化logger
|
||||
|
||||
let keys = Key {
|
||||
core: decode("687A4852416D736F356B496E62617857").unwrap(),
|
||||
meta: decode("2331346C6A6B5F215C5D2630553C2728").unwrap(),
|
||||
};
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let args = if args.len() == 1 {
|
||||
warn!("未指定文件夹,将使用默认文件夹。");
|
||||
let mut args_temp = Vec::new();
|
||||
if Path::new("CloudMusic").exists() {
|
||||
warn!("CloudMusic文件夹存在,将自动使用。");
|
||||
args_temp.push(String::from("CloudMusic"));
|
||||
};
|
||||
if Path::new("input").exists() {
|
||||
warn!("input文件夹存在,将自动使用。");
|
||||
args_temp.push(String::from("input"));
|
||||
};
|
||||
if args_temp.is_empty() {
|
||||
//TODO 增加软件介绍
|
||||
error!("没有参数\n没有CloudMusic或者input文件夹存在与于工作目录");
|
||||
panic!("没有参数\n没有CloudMusic或者input文件夹存在与于工作目录");
|
||||
}
|
||||
args_temp
|
||||
} else {
|
||||
args[1..].to_vec()
|
||||
};
|
||||
// let args = &args[1..];
|
||||
|
||||
let mut undumpfile = Vec::new(); // 该列表将存入文件的路径
|
||||
|
||||
for arg in &args {
|
||||
//解析传入的每一个路径:文件or文件夹
|
||||
let path = Path::new(arg);
|
||||
|
||||
if path.is_file() {
|
||||
// 当后缀符合为ncm时才加入列表
|
||||
match path.extension() {
|
||||
Some(extension) => {
|
||||
if extension == "ncm" {
|
||||
let _ = &mut undumpfile.push(arg.to_owned());
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
} else if path.is_dir() {
|
||||
for entry in WalkDir::new(path) {
|
||||
let new_entry = entry.unwrap().clone();
|
||||
let filepath = new_entry.into_path();
|
||||
// 当后缀符合为ncm时才加入列表
|
||||
match filepath.extension() {
|
||||
Some(extension) => {
|
||||
if extension == "ncm" {
|
||||
let _ = &mut undumpfile.push(String::from(filepath.to_str().unwrap()));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// let filepaths = undumpfile;
|
||||
let count = undumpfile.len();
|
||||
let mut time = 0usize;
|
||||
for filepath in undumpfile {
|
||||
time += 1;
|
||||
// println!("{}/{}", &time, &count);
|
||||
let mut ncmfile = Ncmfile::new(&filepath).unwrap();
|
||||
dump(&mut ncmfile, &keys, Path::new("output")).unwrap();
|
||||
|
||||
//TODO增加计时
|
||||
// let time = SystemTime::now();
|
||||
// let time = SystemTime::now().duration_since(time).unwrap().as_secs();
|
||||
// println!("{}",time);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user