mirror of
				https://git.unlock-music.dev/um/web.git
				synced 2025-11-04 19:23:28 +08:00 
			
		
		
		
	添加WASM部分源代码
This commit is contained in:
		
							parent
							
								
									914020563e
								
							
						
					
					
						commit
						f6af50077a
					
				
							
								
								
									
										65
									
								
								src/KgmWasm/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/KgmWasm/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
# CMakeList.txt : CMake project for KgmWasm, include source and define
 | 
			
		||||
# project specific logic here.
 | 
			
		||||
#
 | 
			
		||||
cmake_minimum_required (VERSION 3.8)
 | 
			
		||||
 | 
			
		||||
project ("KgmWasm")
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_STANDARD 14)
 | 
			
		||||
 | 
			
		||||
include_directories(
 | 
			
		||||
	$<TARGET_PROPERTY:INTERFACE_INCLUDE_DIRECTORIES>
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Add source to this project's executable.
 | 
			
		||||
set(RUNTIME_METHODS_LIST
 | 
			
		||||
	getValue
 | 
			
		||||
	writeArrayToMemory
 | 
			
		||||
	UTF8ToString
 | 
			
		||||
)
 | 
			
		||||
list(JOIN RUNTIME_METHODS_LIST "," RUNTIME_METHODS)
 | 
			
		||||
 | 
			
		||||
set(EMSCRIPTEN_FLAGS
 | 
			
		||||
	"--bind"
 | 
			
		||||
	"-s NO_DYNAMIC_EXECUTION=1"
 | 
			
		||||
	"-s MODULARIZE=1"
 | 
			
		||||
	"-s EXPORT_NAME=KgmCryptoModule"
 | 
			
		||||
	"-s EXPORTED_RUNTIME_METHODS=${RUNTIME_METHODS}"
 | 
			
		||||
)
 | 
			
		||||
set(EMSCRIPTEN_LEGACY_FLAGS
 | 
			
		||||
	${EMSCRIPTEN_FLAGS}
 | 
			
		||||
	"-s WASM=0"
 | 
			
		||||
	"--memory-init-file 0"
 | 
			
		||||
)
 | 
			
		||||
set(EMSCRIPTEN_WASM_BUNDLE_FLAGS
 | 
			
		||||
	${EMSCRIPTEN_FLAGS}
 | 
			
		||||
	"-s SINGLE_FILE=1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(JOIN EMSCRIPTEN_FLAGS             " " EMSCRIPTEN_FLAGS_STR)
 | 
			
		||||
list(JOIN EMSCRIPTEN_LEGACY_FLAGS      " " EMSCRIPTEN_LEGACY_FLAGS_STR)
 | 
			
		||||
list(JOIN EMSCRIPTEN_WASM_BUNDLE_FLAGS " " EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR)
 | 
			
		||||
 | 
			
		||||
# Define projects config
 | 
			
		||||
set(WASM_SOURCES
 | 
			
		||||
	"KgmWasm.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(KgmWasm ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	KgmWasm
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(KgmWasmBundle ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	KgmWasmBundle
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(KgmLegacy ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	KgmLegacy
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_LEGACY_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								src/KgmWasm/KgmWasm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/KgmWasm/KgmWasm.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
// KgmWasm.cpp : Defines the entry point for the application.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "KgmWasm.h"
 | 
			
		||||
 | 
			
		||||
#include "kgm.hpp"
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
size_t preDec(uintptr_t blob, size_t blobSize, std::string ext)
 | 
			
		||||
{
 | 
			
		||||
    return PreDec((uint8_t*)blob, blobSize, ext == "vpr");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void decBlob(uintptr_t blob, size_t blobSize, size_t offset)
 | 
			
		||||
{
 | 
			
		||||
    Decrypt((uint8_t*)blob, blobSize, offset);
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								src/KgmWasm/KgmWasm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/KgmWasm/KgmWasm.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
// KgmWasm.h : Include file for standard system include files,
 | 
			
		||||
// or project specific include files.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <emscripten/bind.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace em = emscripten;
 | 
			
		||||
 | 
			
		||||
size_t preDec(uintptr_t blob, size_t blobSize, std::string ext);
 | 
			
		||||
void decBlob(uintptr_t blob, size_t blobSize, size_t offset);
 | 
			
		||||
 | 
			
		||||
EMSCRIPTEN_BINDINGS(QmcCrypto)
 | 
			
		||||
{
 | 
			
		||||
  em::function("preDec", &preDec, em::allow_raw_pointers());
 | 
			
		||||
  em::function("decBlob", &decBlob, em::allow_raw_pointers());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/KgmWasm/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/KgmWasm/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
# KgmWasm
 | 
			
		||||
 | 
			
		||||
## 构建
 | 
			
		||||
 | 
			
		||||
在 Linux 环境下执行 `bash build-wasm` 即可构建。
 | 
			
		||||
 | 
			
		||||
## Build
 | 
			
		||||
 | 
			
		||||
Linux environment required. Build wasm binary by execute `bash build-wasm`.
 | 
			
		||||
							
								
								
									
										41
									
								
								src/KgmWasm/build-wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/KgmWasm/build-wasm
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
pushd "$(realpath "$(dirname "$0")")"
 | 
			
		||||
 | 
			
		||||
CURR_DIR="${PWD}"
 | 
			
		||||
 | 
			
		||||
BUILD_TYPE="$1"
 | 
			
		||||
if [ -z "$BUILD_TYPE" ]; then
 | 
			
		||||
	BUILD_TYPE=Release
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
mkdir -p build/wasm
 | 
			
		||||
if [ ! -d build/emsdk ]; then
 | 
			
		||||
	git clone https://github.com/emscripten-core/emsdk.git build/emsdk
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
pushd build/emsdk
 | 
			
		||||
#git pull
 | 
			
		||||
./emsdk install 3.0.0
 | 
			
		||||
./emsdk activate 3.0.0
 | 
			
		||||
source ./emsdk_env.sh
 | 
			
		||||
popd # build/emsdk
 | 
			
		||||
 | 
			
		||||
pushd build/wasm
 | 
			
		||||
emcmake cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" ../..
 | 
			
		||||
make -j
 | 
			
		||||
TARGET_FILES="
 | 
			
		||||
	KgmLegacy.js
 | 
			
		||||
	KgmWasm.js
 | 
			
		||||
	KgmWasm.wasm
 | 
			
		||||
	KgmWasmBundle.js
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
#mkdir -p "${CURR_DIR}/npm"
 | 
			
		||||
#cp $TARGET_FILES "${CURR_DIR}/npm/"
 | 
			
		||||
cp $TARGET_FILES "${CURR_DIR}/"
 | 
			
		||||
popd # build/wasm
 | 
			
		||||
 | 
			
		||||
popd
 | 
			
		||||
							
								
								
									
										112
									
								
								src/KgmWasm/kgm.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/KgmWasm/kgm.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,112 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> VprHeader = {
 | 
			
		||||
    0x05, 0x28, 0xBC, 0x96, 0xE9, 0xE4, 0x5A, 0x43,
 | 
			
		||||
    0x91, 0xAA, 0xBD, 0xD0, 0x7A, 0xF5, 0x36, 0x31 };
 | 
			
		||||
std::vector<uint8_t> KgmHeader = {
 | 
			
		||||
    0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
 | 
			
		||||
    0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14 };
 | 
			
		||||
std::vector<uint8_t> VprMaskDiff = {
 | 
			
		||||
    0x25, 0xDF, 0xE8, 0xA6, 0x75, 0x1E, 0x75, 0x0E,
 | 
			
		||||
    0x2F, 0x80, 0xF3, 0x2D, 0xB8, 0xB6, 0xE3, 0x11, 0x00 };
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> MaskV2;
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> table1 = {
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01, 0xe1, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01,
 | 
			
		||||
    0xd2, 0x23, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02, 0xc2, 0xc2, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02,
 | 
			
		||||
    0xd3, 0xd3, 0x02, 0x03, 0x63, 0x43, 0x63, 0x03, 0xe3, 0xc3, 0xe3, 0x03, 0x63, 0x43, 0x63, 0x03,
 | 
			
		||||
    0x94, 0xb4, 0x94, 0x65, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0x84, 0x04, 0x04, 0x04, 0x04,
 | 
			
		||||
    0x95, 0x95, 0x95, 0x95, 0x04, 0x05, 0x25, 0x05, 0xe5, 0x85, 0xa5, 0x85, 0xe5, 0x05, 0x25, 0x05,
 | 
			
		||||
    0xd6, 0xb6, 0x96, 0xb6, 0xd6, 0x27, 0x06, 0x06, 0xc6, 0xc6, 0x86, 0x86, 0xc6, 0xc6, 0x06, 0x06,
 | 
			
		||||
    0xd7, 0xd7, 0x97, 0x97, 0xd7, 0xd7, 0x06, 0x07, 0xe7, 0xc7, 0xe7, 0x87, 0xe7, 0xc7, 0xe7, 0x07,
 | 
			
		||||
    0x18, 0x38, 0x18, 0x78, 0x18, 0x38, 0x18, 0xe9, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
 | 
			
		||||
    0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x08, 0x09, 0x29, 0x09, 0x69, 0x09, 0x29, 0x09,
 | 
			
		||||
    0xda, 0x3a, 0x1a, 0x3a, 0x5a, 0x3a, 0x1a, 0x3a, 0xda, 0x2b, 0x0a, 0x0a, 0x4a, 0x4a, 0x0a, 0x0a,
 | 
			
		||||
    0xdb, 0xdb, 0x1b, 0x1b, 0x5b, 0x5b, 0x1b, 0x1b, 0xdb, 0xdb, 0x0a, 0x0b, 0x6b, 0x4b, 0x6b, 0x0b,
 | 
			
		||||
    0x9c, 0xbc, 0x9c, 0x7c, 0x1c, 0x3c, 0x1c, 0x7c, 0x9c, 0xbc, 0x9c, 0x6d, 0x0c, 0x0c, 0x0c, 0x0c,
 | 
			
		||||
    0x9d, 0x9d, 0x9d, 0x9d, 0x1d, 0x1d, 0x1d, 0x1d, 0x9d, 0x9d, 0x9d, 0x9d, 0x0c, 0x0d, 0x2d, 0x0d,
 | 
			
		||||
    0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x3e, 0x1e, 0x3e, 0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x2f, 0x0e, 0x0e,
 | 
			
		||||
    0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x1f, 0x1f, 0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x0e, 0x0f,
 | 
			
		||||
    0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xe0, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xf1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> table2 = {
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01, 0xef, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01,
 | 
			
		||||
    0xdf, 0x21, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02, 0xce, 0xce, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02,
 | 
			
		||||
    0xde, 0xde, 0x02, 0x03, 0x65, 0x47, 0x65, 0x03, 0xed, 0xcf, 0xed, 0x03, 0x65, 0x47, 0x65, 0x03,
 | 
			
		||||
    0x9d, 0xbf, 0x9d, 0x63, 0x04, 0x04, 0x04, 0x04, 0x8c, 0x8c, 0x8c, 0x8c, 0x04, 0x04, 0x04, 0x04,
 | 
			
		||||
    0x9c, 0x9c, 0x9c, 0x9c, 0x04, 0x05, 0x27, 0x05, 0xeb, 0x8d, 0xaf, 0x8d, 0xeb, 0x05, 0x27, 0x05,
 | 
			
		||||
    0xdb, 0xbd, 0x9f, 0xbd, 0xdb, 0x25, 0x06, 0x06, 0xca, 0xca, 0x8e, 0x8e, 0xca, 0xca, 0x06, 0x06,
 | 
			
		||||
    0xda, 0xda, 0x9e, 0x9e, 0xda, 0xda, 0x06, 0x07, 0xe9, 0xcb, 0xe9, 0x8f, 0xe9, 0xcb, 0xe9, 0x07,
 | 
			
		||||
    0x19, 0x3b, 0x19, 0x7f, 0x19, 0x3b, 0x19, 0xe7, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
 | 
			
		||||
    0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x09, 0x2b, 0x09, 0x6f, 0x09, 0x2b, 0x09,
 | 
			
		||||
    0xd7, 0x39, 0x1b, 0x39, 0x5f, 0x39, 0x1b, 0x39, 0xd7, 0x29, 0x0a, 0x0a, 0x4e, 0x4e, 0x0a, 0x0a,
 | 
			
		||||
    0xd6, 0xd6, 0x1a, 0x1a, 0x5e, 0x5e, 0x1a, 0x1a, 0xd6, 0xd6, 0x0a, 0x0b, 0x6d, 0x4f, 0x6d, 0x0b,
 | 
			
		||||
    0x95, 0xb7, 0x95, 0x7b, 0x1d, 0x3f, 0x1d, 0x7b, 0x95, 0xb7, 0x95, 0x6b, 0x0c, 0x0c, 0x0c, 0x0c,
 | 
			
		||||
    0x94, 0x94, 0x94, 0x94, 0x1c, 0x1c, 0x1c, 0x1c, 0x94, 0x94, 0x94, 0x94, 0x0c, 0x0d, 0x2f, 0x0d,
 | 
			
		||||
    0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x3d, 0x1f, 0x3d, 0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x2d, 0x0e, 0x0e,
 | 
			
		||||
    0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x1e, 0x1e, 0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x0e, 0x0f,
 | 
			
		||||
    0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xee, 0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xfe
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> MaskV2PreDef = {
 | 
			
		||||
    0xB8, 0xD5, 0x3D, 0xB2, 0xE9, 0xAF, 0x78, 0x8C, 0x83, 0x33, 0x71, 0x51, 0x76, 0xA0, 0xCD, 0x37,
 | 
			
		||||
    0x2F, 0x3E, 0x35, 0x8D, 0xA9, 0xBE, 0x98, 0xB7, 0xE7, 0x8C, 0x22, 0xCE, 0x5A, 0x61, 0xDF, 0x68,
 | 
			
		||||
    0x69, 0x89, 0xFE, 0xA5, 0xB6, 0xDE, 0xA9, 0x77, 0xFC, 0xC8, 0xBD, 0xBD, 0xE5, 0x6D, 0x3E, 0x5A,
 | 
			
		||||
    0x36, 0xEF, 0x69, 0x4E, 0xBE, 0xE1, 0xE9, 0x66, 0x1C, 0xF3, 0xD9, 0x02, 0xB6, 0xF2, 0x12, 0x9B,
 | 
			
		||||
    0x44, 0xD0, 0x6F, 0xB9, 0x35, 0x89, 0xB6, 0x46, 0x6D, 0x73, 0x82, 0x06, 0x69, 0xC1, 0xED, 0xD7,
 | 
			
		||||
    0x85, 0xC2, 0x30, 0xDF, 0xA2, 0x62, 0xBE, 0x79, 0x2D, 0x62, 0x62, 0x3D, 0x0D, 0x7E, 0xBE, 0x48,
 | 
			
		||||
    0x89, 0x23, 0x02, 0xA0, 0xE4, 0xD5, 0x75, 0x51, 0x32, 0x02, 0x53, 0xFD, 0x16, 0x3A, 0x21, 0x3B,
 | 
			
		||||
    0x16, 0x0F, 0xC3, 0xB2, 0xBB, 0xB3, 0xE2, 0xBA, 0x3A, 0x3D, 0x13, 0xEC, 0xF6, 0x01, 0x45, 0x84,
 | 
			
		||||
    0xA5, 0x70, 0x0F, 0x93, 0x49, 0x0C, 0x64, 0xCD, 0x31, 0xD5, 0xCC, 0x4C, 0x07, 0x01, 0x9E, 0x00,
 | 
			
		||||
    0x1A, 0x23, 0x90, 0xBF, 0x88, 0x1E, 0x3B, 0xAB, 0xA6, 0x3E, 0xC4, 0x73, 0x47, 0x10, 0x7E, 0x3B,
 | 
			
		||||
    0x5E, 0xBC, 0xE3, 0x00, 0x84, 0xFF, 0x09, 0xD4, 0xE0, 0x89, 0x0F, 0x5B, 0x58, 0x70, 0x4F, 0xFB,
 | 
			
		||||
    0x65, 0xD8, 0x5C, 0x53, 0x1B, 0xD3, 0xC8, 0xC6, 0xBF, 0xEF, 0x98, 0xB0, 0x50, 0x4F, 0x0F, 0xEA,
 | 
			
		||||
    0xE5, 0x83, 0x58, 0x8C, 0x28, 0x2C, 0x84, 0x67, 0xCD, 0xD0, 0x9E, 0x47, 0xDB, 0x27, 0x50, 0xCA,
 | 
			
		||||
    0xF4, 0x63, 0x63, 0xE8, 0x97, 0x7F, 0x1B, 0x4B, 0x0C, 0xC2, 0xC1, 0x21, 0x4C, 0xCC, 0x58, 0xF5,
 | 
			
		||||
    0x94, 0x52, 0xA3, 0xF3, 0xD3, 0xE0, 0x68, 0xF4, 0x00, 0x23, 0xF3, 0x5E, 0x0A, 0x7B, 0x93, 0xDD,
 | 
			
		||||
    0xAB, 0x12, 0xB2, 0x13, 0xE8, 0x84, 0xD7, 0xA7, 0x9F, 0x0F, 0x32, 0x4C, 0x55, 0x1D, 0x04, 0x36,
 | 
			
		||||
    0x52, 0xDC, 0x03, 0xF3, 0xF9, 0x4E, 0x42, 0xE9, 0x3D, 0x61, 0xEF, 0x7C, 0xB6, 0xB3, 0x93, 0x50,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint8_t getMask(size_t pos) {
 | 
			
		||||
    size_t offset = pos >> 4;
 | 
			
		||||
    uint8_t value = 0;
 | 
			
		||||
    while (offset >= 0x11) {
 | 
			
		||||
        value ^= table1[offset % 272];
 | 
			
		||||
        offset >>= 4;
 | 
			
		||||
        value ^= table2[offset % 272];
 | 
			
		||||
        offset >>= 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return MaskV2PreDef[pos % 272] ^ value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> key(17);
 | 
			
		||||
bool isVpr = false;
 | 
			
		||||
 | 
			
		||||
size_t PreDec(uint8_t* fileData, size_t size, bool iV) {
 | 
			
		||||
    uint32_t headerLen = *(uint32_t*)(fileData + 0x10);
 | 
			
		||||
    memcpy(key.data(), (fileData + 0x1C), 0x10);
 | 
			
		||||
    key[16] = 0;
 | 
			
		||||
    isVpr = iV;
 | 
			
		||||
    return headerLen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Decrypt(uint8_t* fileData, size_t size, size_t offset) {
 | 
			
		||||
    for (size_t i = 0; i < size; ++i) {
 | 
			
		||||
        uint8_t med8 = key[(i + offset) % 17] ^ fileData[i];
 | 
			
		||||
        med8 ^= (med8 & 0xf) << 4;
 | 
			
		||||
 | 
			
		||||
        uint8_t msk8 = getMask(i + offset);
 | 
			
		||||
        msk8 ^= (msk8 & 0xf) << 4;
 | 
			
		||||
        fileData[i] = med8 ^ msk8;
 | 
			
		||||
 | 
			
		||||
        if (isVpr) {
 | 
			
		||||
            fileData[i] ^= VprMaskDiff[(i + offset) % 17];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								src/QmcWasm/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/QmcWasm/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
# CMakeList.txt : CMake project for QmcWasm, include source and define
 | 
			
		||||
# project specific logic here.
 | 
			
		||||
#
 | 
			
		||||
cmake_minimum_required (VERSION 3.8)
 | 
			
		||||
 | 
			
		||||
project ("QmcWasm")
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_STANDARD 14)
 | 
			
		||||
 | 
			
		||||
include_directories(
 | 
			
		||||
	$<TARGET_PROPERTY:INTERFACE_INCLUDE_DIRECTORIES>
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Add source to this project's executable.
 | 
			
		||||
set(RUNTIME_METHODS_LIST
 | 
			
		||||
	getValue
 | 
			
		||||
	writeArrayToMemory
 | 
			
		||||
	UTF8ToString
 | 
			
		||||
)
 | 
			
		||||
list(JOIN RUNTIME_METHODS_LIST "," RUNTIME_METHODS)
 | 
			
		||||
 | 
			
		||||
set(EMSCRIPTEN_FLAGS
 | 
			
		||||
	"--bind"
 | 
			
		||||
	"-s NO_DYNAMIC_EXECUTION=1"
 | 
			
		||||
	"-s MODULARIZE=1"
 | 
			
		||||
	"-s EXPORT_NAME=QmcCryptoModule"
 | 
			
		||||
	"-s EXPORTED_RUNTIME_METHODS=${RUNTIME_METHODS}"
 | 
			
		||||
)
 | 
			
		||||
set(EMSCRIPTEN_LEGACY_FLAGS
 | 
			
		||||
	${EMSCRIPTEN_FLAGS}
 | 
			
		||||
	"-s WASM=0"
 | 
			
		||||
	"--memory-init-file 0"
 | 
			
		||||
)
 | 
			
		||||
set(EMSCRIPTEN_WASM_BUNDLE_FLAGS
 | 
			
		||||
	${EMSCRIPTEN_FLAGS}
 | 
			
		||||
	"-s SINGLE_FILE=1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(JOIN EMSCRIPTEN_FLAGS             " " EMSCRIPTEN_FLAGS_STR)
 | 
			
		||||
list(JOIN EMSCRIPTEN_LEGACY_FLAGS      " " EMSCRIPTEN_LEGACY_FLAGS_STR)
 | 
			
		||||
list(JOIN EMSCRIPTEN_WASM_BUNDLE_FLAGS " " EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR)
 | 
			
		||||
 | 
			
		||||
# Define projects config
 | 
			
		||||
set(WASM_SOURCES
 | 
			
		||||
	"QmcWasm.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(QmcWasm ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	QmcWasm
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(QmcWasmBundle ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	QmcWasmBundle
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(QmcLegacy ${WASM_SOURCES})
 | 
			
		||||
set_target_properties(
 | 
			
		||||
	QmcLegacy
 | 
			
		||||
	PROPERTIES LINK_FLAGS ${EMSCRIPTEN_LEGACY_FLAGS_STR}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								src/QmcWasm/QmcWasm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/QmcWasm/QmcWasm.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
// QmcWasm.cpp : Defines the entry point for the application.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "QmcWasm.h"
 | 
			
		||||
 | 
			
		||||
#include "qmc.hpp"
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
std::string err = "";
 | 
			
		||||
std::string sid = "";
 | 
			
		||||
QmcDecode e;
 | 
			
		||||
 | 
			
		||||
int preDec(uintptr_t blob, size_t blobSize, std::string ext)
 | 
			
		||||
{
 | 
			
		||||
    if (!e.SetBlob((uint8_t*)blob, blobSize))
 | 
			
		||||
    {
 | 
			
		||||
        err = "cannot allocate memory";
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    int tailSize = e.PreDecode(ext);
 | 
			
		||||
    if (e.error != "")
 | 
			
		||||
    {
 | 
			
		||||
        err = e.error;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    sid = e.songId;
 | 
			
		||||
    return tailSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t decBlob(uintptr_t blob, size_t blobSize, size_t offset)
 | 
			
		||||
{
 | 
			
		||||
    if (!e.SetBlob((uint8_t*)blob, blobSize))
 | 
			
		||||
    {
 | 
			
		||||
        err = "cannot allocate memory";
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<uint8_t> decData = e.Decode(offset);
 | 
			
		||||
    if (e.error != "")
 | 
			
		||||
    {
 | 
			
		||||
        err = e.error;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy((uint8_t*)blob, decData.data(), decData.size());
 | 
			
		||||
    return decData.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string getErr()
 | 
			
		||||
{
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string getSongId()
 | 
			
		||||
{
 | 
			
		||||
    return sid;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								src/QmcWasm/QmcWasm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/QmcWasm/QmcWasm.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
// QmcWasm.h : Include file for standard system include files,
 | 
			
		||||
// or project specific include files.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <emscripten/bind.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace em = emscripten;
 | 
			
		||||
 | 
			
		||||
int preDec(uintptr_t blob, size_t blobSize, std::string ext);
 | 
			
		||||
size_t decBlob(uintptr_t blob, size_t blobSize, size_t offset);
 | 
			
		||||
std::string getErr();
 | 
			
		||||
std::string getSongId();
 | 
			
		||||
 | 
			
		||||
EMSCRIPTEN_BINDINGS(QmcCrypto)
 | 
			
		||||
{
 | 
			
		||||
  em::function("getErr", &getErr);
 | 
			
		||||
  em::function("getSongId", &getSongId);
 | 
			
		||||
 | 
			
		||||
  em::function("preDec", &preDec, em::allow_raw_pointers());
 | 
			
		||||
  em::function("decBlob", &decBlob, em::allow_raw_pointers());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/QmcWasm/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/QmcWasm/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
# QmcWasm
 | 
			
		||||
 | 
			
		||||
## 构建
 | 
			
		||||
 | 
			
		||||
在 Linux 环境下执行 `bash build-wasm` 即可构建。
 | 
			
		||||
 | 
			
		||||
## Build
 | 
			
		||||
 | 
			
		||||
Linux environment required. Build wasm binary by execute `bash build-wasm`.
 | 
			
		||||
							
								
								
									
										289
									
								
								src/QmcWasm/TencentTea.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/QmcWasm/TencentTea.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,289 @@
 | 
			
		||||
#ifndef QQMUSIC_CPP_TENCENTTEA_HPP
 | 
			
		||||
#define QQMUSIC_CPP_TENCENTTEA_HPP
 | 
			
		||||
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
const uint32_t DELTA = 0x9e3779b9;
 | 
			
		||||
 | 
			
		||||
#define ROUNDS 32
 | 
			
		||||
#define SALT_LEN 2
 | 
			
		||||
#define ZERO_LEN 7
 | 
			
		||||
 | 
			
		||||
void TeaDecryptECB(uint8_t* src, uint8_t* dst, std::vector<uint8_t> key, size_t rounds = ROUNDS) {
 | 
			
		||||
    if (key.size() != 16 || (rounds & 1) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t y, z, sum;
 | 
			
		||||
    uint32_t k[4];
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    //now encrypted buf is TCP/IP-endian;
 | 
			
		||||
    //TCP/IP network byte order (which is big-endian).
 | 
			
		||||
    y = ntohl(*((uint32_t*)src));
 | 
			
		||||
    z = ntohl(*((uint32_t*)(src + 4)));
 | 
			
		||||
    //std::cout << ntohl(0x0a3aea41);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 4; i++) {
 | 
			
		||||
        //key is TCP/IP-endian;
 | 
			
		||||
        k[i] = ntohl(*((uint32_t*)(key.data() + i * 4)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sum = (DELTA * rounds);
 | 
			
		||||
    for (i = 0; i < rounds; i++) {
 | 
			
		||||
        z -= ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]);
 | 
			
		||||
        y -= ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]);
 | 
			
		||||
        sum -= DELTA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *((uint32_t*)dst) = ntohl(y);
 | 
			
		||||
    *((uint32_t*)(dst + 4)) = ntohl(z);
 | 
			
		||||
 | 
			
		||||
    //now plain-text is TCP/IP-endian;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeaEncryptECB(uint8_t* src, uint8_t* dst, std::vector<uint8_t> key, size_t rounds = ROUNDS) {
 | 
			
		||||
    if (key.size() != 16 || (rounds & 1) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t y, z, sum;
 | 
			
		||||
    uint32_t k[4];
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    //now encrypted buf is TCP/IP-endian;
 | 
			
		||||
    //TCP/IP network byte order (which is big-endian).
 | 
			
		||||
    y = ntohl(*((uint32_t*)src));
 | 
			
		||||
    z = ntohl(*((uint32_t*)(src + 4)));
 | 
			
		||||
    //std::cout << ntohl(0x0a3aea41);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 4; i++) {
 | 
			
		||||
        //key is TCP/IP-endian;
 | 
			
		||||
        k[i] = ntohl(*((uint32_t*)(key.data() + i * 4)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sum = 0;
 | 
			
		||||
    for (i = 0; i < rounds; i++) {
 | 
			
		||||
        sum += DELTA;
 | 
			
		||||
        y += ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]);
 | 
			
		||||
        z += ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *((uint32_t*)dst) = ntohl(y);
 | 
			
		||||
    *((uint32_t*)(dst + 4)) = ntohl(z);
 | 
			
		||||
 | 
			
		||||
    //now plain-text is TCP/IP-endian;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*pKey为16byte*/
 | 
			
		||||
/*
 | 
			
		||||
    输入:nInBufLen为需加密的明文部分(Body)长度;
 | 
			
		||||
    输出:返回为加密后的长度(是8byte的倍数);
 | 
			
		||||
*/
 | 
			
		||||
/*TEA加密算法,CBC模式*/
 | 
			
		||||
/*密文格式:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
 | 
			
		||||
int encryptTencentTeaLen(int nInBufLen)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    int nPadSaltBodyZeroLen/*PadLen(1byte)+Salt+Body+Zero的长度*/;
 | 
			
		||||
    int nPadlen;
 | 
			
		||||
 | 
			
		||||
    /*根据Body长度计算PadLen,最小必需长度必需为8byte的整数倍*/
 | 
			
		||||
    nPadSaltBodyZeroLen = nInBufLen/*Body长度*/ + 1 + SALT_LEN + ZERO_LEN/*PadLen(1byte)+Salt(2byte)+Zero(7byte)*/;
 | 
			
		||||
    if ((nPadlen = nPadSaltBodyZeroLen % 8)) /*len=nSaltBodyZeroLen%8*/
 | 
			
		||||
    {
 | 
			
		||||
        /*模8余0需补0,余1补7,余2补6,...,余7补1*/
 | 
			
		||||
        nPadlen = 8 - nPadlen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nPadlen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*pKey为16byte*/
 | 
			
		||||
/*
 | 
			
		||||
    输入:pInBuf为需加密的明文部分(Body),nInBufLen为pInBuf长度;
 | 
			
		||||
    输出:pOutBuf为密文格式,pOutBufLen为pOutBuf的长度是8byte的倍数;
 | 
			
		||||
*/
 | 
			
		||||
/*TEA加密算法,CBC模式*/
 | 
			
		||||
/*密文格式:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
 | 
			
		||||
bool encryptTencentTea(std::vector<uint8_t> inBuf, std::vector<uint8_t> key, std::vector<uint8_t> &outBuf)
 | 
			
		||||
{
 | 
			
		||||
    srand(time(0));
 | 
			
		||||
    int nPadlen = encryptTencentTeaLen(inBuf.size());
 | 
			
		||||
    size_t ivCrypt;
 | 
			
		||||
    std::vector<uint8_t> srcBuf;
 | 
			
		||||
    srcBuf.resize(8);
 | 
			
		||||
    std::vector<uint8_t> ivPlain;
 | 
			
		||||
    ivPlain.resize(8);
 | 
			
		||||
    int tmpIdx, i, j;
 | 
			
		||||
 | 
			
		||||
    /*加密第一块数据(8byte),取前面10byte*/
 | 
			
		||||
    srcBuf[0] = (((char)rand()) & 0x0f8)/*最低三位存PadLen,清零*/ | (char)nPadlen;
 | 
			
		||||
    tmpIdx = 1; /*tmpIdx指向srcBuf下一个位置*/
 | 
			
		||||
 | 
			
		||||
    while (nPadlen--) srcBuf[tmpIdx++] = (char)rand(); /*Padding*/
 | 
			
		||||
 | 
			
		||||
    /*come here, tmpIdx must <= 8*/
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 8; i++) ivPlain[i] = 0;
 | 
			
		||||
    ivCrypt = 0;//ivPlain /*make zero iv*/
 | 
			
		||||
 | 
			
		||||
    auto outBufPos = 0; /*init outBufPos*/
 | 
			
		||||
 | 
			
		||||
#define cryptBlock {\
 | 
			
		||||
        /*tmpIdx==8*/\
 | 
			
		||||
        outBuf.resize(outBuf.size() + 8);\
 | 
			
		||||
        for (j = 0; j < 8; j++) /*加密前异或前8个byte的密文(iv_crypt指向的)*/\
 | 
			
		||||
            srcBuf[j] ^= outBuf[j + ivCrypt];\
 | 
			
		||||
        /*pOutBuffer、pInBuffer均为8byte, pKey为16byte*/\
 | 
			
		||||
        /*加密*/\
 | 
			
		||||
        TeaEncryptECB(srcBuf.data(), outBuf.data()+outBufPos, key, 16);\
 | 
			
		||||
        for (j = 0; j < 8; j++) /*加密后异或前8个byte的明文(iv_plain指向的)*/\
 | 
			
		||||
            outBuf[j + outBufPos] ^= ivPlain[j];\
 | 
			
		||||
        /*保存当前的iv_plain*/\
 | 
			
		||||
        for (j = 0; j < 8; j++) ivPlain[j] = srcBuf[j];\
 | 
			
		||||
        /*更新iv_crypt*/\
 | 
			
		||||
        tmpIdx = 0;\
 | 
			
		||||
        ivCrypt = outBufPos;\
 | 
			
		||||
        outBufPos += 8;\
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    for (i = 1; i <= SALT_LEN;) /*Salt(2byte)*/
 | 
			
		||||
    {
 | 
			
		||||
        if (tmpIdx < 8)
 | 
			
		||||
        {
 | 
			
		||||
            srcBuf[tmpIdx++] = (char)rand();
 | 
			
		||||
            i++; /*i inc in here*/
 | 
			
		||||
        }
 | 
			
		||||
        if (tmpIdx == 8)
 | 
			
		||||
        {
 | 
			
		||||
            cryptBlock
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*tmpIdx指向srcBuf下一个位置*/
 | 
			
		||||
 | 
			
		||||
    auto inBufPos = 0;
 | 
			
		||||
    while (inBufPos < inBuf.size())
 | 
			
		||||
    {
 | 
			
		||||
        if (tmpIdx < 8)
 | 
			
		||||
        {
 | 
			
		||||
            srcBuf[tmpIdx++] = inBuf[inBufPos];
 | 
			
		||||
            inBufPos++;
 | 
			
		||||
        }
 | 
			
		||||
        if (tmpIdx == 8)
 | 
			
		||||
        {
 | 
			
		||||
            cryptBlock
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*tmpIdx指向srcBuf下一个位置*/
 | 
			
		||||
 | 
			
		||||
    for (i = 1; i <= ZERO_LEN;)
 | 
			
		||||
    {
 | 
			
		||||
        if (tmpIdx < 8)
 | 
			
		||||
        {
 | 
			
		||||
            srcBuf[tmpIdx++] = 0;
 | 
			
		||||
            i++; //i inc in here
 | 
			
		||||
        }
 | 
			
		||||
        if (tmpIdx == 8)
 | 
			
		||||
        {
 | 
			
		||||
            cryptBlock
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
#undef cryptBlock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool decryptTencentTea(std::vector<uint8_t> inBuf, std::vector<uint8_t> key, std::vector<uint8_t> &out) {
 | 
			
		||||
    if (inBuf.size() % 8 != 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
        //inBuf size not a multiple of the block size
 | 
			
		||||
    }
 | 
			
		||||
    if (inBuf.size() < 16) {
 | 
			
		||||
        return false;
 | 
			
		||||
        //inBuf size too small
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    std::vector<uint8_t> tmpBuf;
 | 
			
		||||
    tmpBuf.resize(8);
 | 
			
		||||
    
 | 
			
		||||
    TeaDecryptECB(inBuf.data(), tmpBuf.data(), key, 16);
 | 
			
		||||
    
 | 
			
		||||
    auto nPadLen = tmpBuf[0] & 0x7; //只要最低三位
 | 
			
		||||
    /*密文格式:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
 | 
			
		||||
    auto outLen = inBuf.size() - 1 /*PadLen*/ - nPadLen - SALT_LEN - ZERO_LEN;
 | 
			
		||||
    std::vector<uint8_t> outBuf;
 | 
			
		||||
    outBuf.resize(outLen);
 | 
			
		||||
    
 | 
			
		||||
    std::vector<uint8_t> ivPrev;
 | 
			
		||||
    ivPrev.resize(8);
 | 
			
		||||
    std::vector<uint8_t> ivCur;
 | 
			
		||||
    ivCur.resize(8);
 | 
			
		||||
    for (size_t i = 0; i < 8; i++)
 | 
			
		||||
    {
 | 
			
		||||
        ivCur[i] = inBuf[i]; // init iv
 | 
			
		||||
    }
 | 
			
		||||
    auto inBufPos = 8;
 | 
			
		||||
    
 | 
			
		||||
    // 跳过 Padding Len 和 Padding
 | 
			
		||||
    auto tmpIdx = 1 + nPadLen;
 | 
			
		||||
    
 | 
			
		||||
    // CBC IV 处理
 | 
			
		||||
#define cryptBlock {\
 | 
			
		||||
    ivPrev = ivCur;\
 | 
			
		||||
    for (size_t k = inBufPos; k < inBufPos + 8; k++)\
 | 
			
		||||
    {\
 | 
			
		||||
        ivCur[k - inBufPos] = inBuf[k];\
 | 
			
		||||
    }\
 | 
			
		||||
    for (size_t j = 0; j < 8; j++) {\
 | 
			
		||||
        tmpBuf[j] ^= ivCur[j];\
 | 
			
		||||
    }\
 | 
			
		||||
    TeaDecryptECB(tmpBuf.data(), tmpBuf.data(), key, 16);\
 | 
			
		||||
    inBufPos += 8;\
 | 
			
		||||
    tmpIdx = 0;\
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 跳过 Salt
 | 
			
		||||
    for (size_t i = 1; i <= SALT_LEN; ) {
 | 
			
		||||
        if (tmpIdx < 8) {
 | 
			
		||||
            tmpIdx++;
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            cryptBlock
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 还原明文
 | 
			
		||||
    auto outBufPos = 0;
 | 
			
		||||
    while (outBufPos < outLen) {
 | 
			
		||||
        if (tmpIdx < 8) {
 | 
			
		||||
            outBuf[outBufPos] = tmpBuf[tmpIdx] ^ ivPrev[tmpIdx];
 | 
			
		||||
            outBufPos++;
 | 
			
		||||
            tmpIdx++;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            cryptBlock
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 校验Zero
 | 
			
		||||
    for (size_t i = 1; i <= ZERO_LEN; i++) {
 | 
			
		||||
        if (tmpBuf[i] != ivPrev[i]) {
 | 
			
		||||
            return false;
 | 
			
		||||
            //zero check failed
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    out = outBuf;
 | 
			
		||||
    return true;
 | 
			
		||||
#undef cryptBlock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif //QQMUSIC_CPP_TENCENTTEA_HPP
 | 
			
		||||
							
								
								
									
										207
									
								
								src/QmcWasm/base64.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/QmcWasm/base64.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,207 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
 | 
			
		||||
//
 | 
			
		||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
 | 
			
		||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
			
		||||
//
 | 
			
		||||
// Official repository: https://github.com/boostorg/beast
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
 | 
			
		||||
   Copyright notice:
 | 
			
		||||
 | 
			
		||||
   base64.cpp and base64.h
 | 
			
		||||
 | 
			
		||||
   Copyright (C) 2004-2008 Rene Nyffenegger
 | 
			
		||||
 | 
			
		||||
   This source code is provided 'as-is', without any express or implied
 | 
			
		||||
   warranty. In no event will the author be held liable for any damages
 | 
			
		||||
   arising from the use of this software.
 | 
			
		||||
 | 
			
		||||
   Permission is granted to anyone to use this software for any purpose,
 | 
			
		||||
   including commercial applications, and to alter it and redistribute it
 | 
			
		||||
   freely, subject to the following restrictions:
 | 
			
		||||
 | 
			
		||||
   1. The origin of this source code must not be misrepresented; you must not
 | 
			
		||||
      claim that you wrote the original source code. If you use this source code
 | 
			
		||||
      in a product, an acknowledgment in the product documentation would be
 | 
			
		||||
      appreciated but is not required.
 | 
			
		||||
 | 
			
		||||
   2. Altered source versions must be plainly marked as such, and must not be
 | 
			
		||||
      misrepresented as being the original source code.
 | 
			
		||||
 | 
			
		||||
   3. This notice may not be removed or altered from any source distribution.
 | 
			
		||||
 | 
			
		||||
   Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef BASE64_HPP
 | 
			
		||||
#define BASE64_HPP
 | 
			
		||||
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace base64 {
 | 
			
		||||
 | 
			
		||||
    /// Returns max chars needed to encode a base64 string
 | 
			
		||||
    std::size_t constexpr
 | 
			
		||||
        encoded_size(std::size_t n)
 | 
			
		||||
    {
 | 
			
		||||
        return 4 * ((n + 2) / 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns max bytes needed to decode a base64 string
 | 
			
		||||
    inline
 | 
			
		||||
        std::size_t constexpr
 | 
			
		||||
        decoded_size(std::size_t n)
 | 
			
		||||
    {
 | 
			
		||||
        return n / 4 * 3; // requires n&3==0, smaller
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char const*
 | 
			
		||||
        get_alphabet()
 | 
			
		||||
    {
 | 
			
		||||
        static char constexpr tab[] = {
 | 
			
		||||
            "ABCDEFGHIJKLMNOP"
 | 
			
		||||
            "QRSTUVWXYZabcdef"
 | 
			
		||||
            "ghijklmnopqrstuv"
 | 
			
		||||
            "wxyz0123456789+/"
 | 
			
		||||
        };
 | 
			
		||||
        return &tab[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    signed char const*
 | 
			
		||||
        get_inverse()
 | 
			
		||||
    {
 | 
			
		||||
        static signed char constexpr tab[] = {
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //   0-15
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //  16-31
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, //  32-47
 | 
			
		||||
             52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, //  48-63
 | 
			
		||||
             -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, //  64-79
 | 
			
		||||
             15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, //  80-95
 | 
			
		||||
             -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //  96-111
 | 
			
		||||
             41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
 | 
			
		||||
             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  // 240-255
 | 
			
		||||
        };
 | 
			
		||||
        return &tab[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Encode a series of octets as a padded, base64 string.
 | 
			
		||||
 | 
			
		||||
        The resulting string will not be null terminated.
 | 
			
		||||
 | 
			
		||||
        @par Requires
 | 
			
		||||
 | 
			
		||||
        The memory pointed to by `out` points to valid memory
 | 
			
		||||
        of at least `encoded_size(len)` bytes.
 | 
			
		||||
 | 
			
		||||
        @return The number of characters written to `out`. This
 | 
			
		||||
        will exclude any null termination.
 | 
			
		||||
    */
 | 
			
		||||
    std::size_t
 | 
			
		||||
        encode(void* dest, void const* src, std::size_t len)
 | 
			
		||||
    {
 | 
			
		||||
        char* out = static_cast<char*>(dest);
 | 
			
		||||
        char const* in = static_cast<char const*>(src);
 | 
			
		||||
        auto const tab = base64::get_alphabet();
 | 
			
		||||
 | 
			
		||||
        for (auto n = len / 3; n--;)
 | 
			
		||||
        {
 | 
			
		||||
            *out++ = tab[(in[0] & 0xfc) >> 2];
 | 
			
		||||
            *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
 | 
			
		||||
            *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
 | 
			
		||||
            *out++ = tab[in[2] & 0x3f];
 | 
			
		||||
            in += 3;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (len % 3)
 | 
			
		||||
        {
 | 
			
		||||
        case 2:
 | 
			
		||||
            *out++ = tab[(in[0] & 0xfc) >> 2];
 | 
			
		||||
            *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
 | 
			
		||||
            *out++ = tab[(in[1] & 0x0f) << 2];
 | 
			
		||||
            *out++ = '=';
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 1:
 | 
			
		||||
            *out++ = tab[(in[0] & 0xfc) >> 2];
 | 
			
		||||
            *out++ = tab[((in[0] & 0x03) << 4)];
 | 
			
		||||
            *out++ = '=';
 | 
			
		||||
            *out++ = '=';
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 0:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out - static_cast<char*>(dest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Decode a padded base64 string into a series of octets.
 | 
			
		||||
 | 
			
		||||
        @par Requires
 | 
			
		||||
 | 
			
		||||
        The memory pointed to by `out` points to valid memory
 | 
			
		||||
        of at least `decoded_size(len)` bytes.
 | 
			
		||||
 | 
			
		||||
        @return The number of octets written to `out`, and
 | 
			
		||||
        the number of characters read from the input string,
 | 
			
		||||
        expressed as a pair.
 | 
			
		||||
    */
 | 
			
		||||
    std::pair<std::size_t, std::size_t>
 | 
			
		||||
        decode(void* dest, char const* src, std::size_t len)
 | 
			
		||||
    {
 | 
			
		||||
        char* out = static_cast<char*>(dest);
 | 
			
		||||
        auto in = reinterpret_cast<unsigned char const*>(src);
 | 
			
		||||
        unsigned char c3[3], c4[4];
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        int j = 0;
 | 
			
		||||
 | 
			
		||||
        auto const inverse = base64::get_inverse();
 | 
			
		||||
 | 
			
		||||
        while (len-- && *in != '=')
 | 
			
		||||
        {
 | 
			
		||||
            auto const v = inverse[*in];
 | 
			
		||||
            if (v == -1)
 | 
			
		||||
                break;
 | 
			
		||||
            ++in;
 | 
			
		||||
            c4[i] = v;
 | 
			
		||||
            if (++i == 4)
 | 
			
		||||
            {
 | 
			
		||||
                c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
 | 
			
		||||
                c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
 | 
			
		||||
                c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
 | 
			
		||||
 | 
			
		||||
                for (i = 0; i < 3; i++)
 | 
			
		||||
                    *out++ = c3[i];
 | 
			
		||||
                i = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (i)
 | 
			
		||||
        {
 | 
			
		||||
            c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
 | 
			
		||||
            c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
 | 
			
		||||
            c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
 | 
			
		||||
 | 
			
		||||
            for (j = 0; j < i - 1; j++)
 | 
			
		||||
                *out++ = c3[j];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return { out - static_cast<char*>(dest),
 | 
			
		||||
            in - reinterpret_cast<unsigned char const*>(src) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // base64
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										41
									
								
								src/QmcWasm/build-wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/QmcWasm/build-wasm
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
pushd "$(realpath "$(dirname "$0")")"
 | 
			
		||||
 | 
			
		||||
CURR_DIR="${PWD}"
 | 
			
		||||
 | 
			
		||||
BUILD_TYPE="$1"
 | 
			
		||||
if [ -z "$BUILD_TYPE" ]; then
 | 
			
		||||
	BUILD_TYPE=Release
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
mkdir -p build/wasm
 | 
			
		||||
if [ ! -d build/emsdk ]; then
 | 
			
		||||
	git clone https://github.com/emscripten-core/emsdk.git build/emsdk
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
pushd build/emsdk
 | 
			
		||||
#git pull
 | 
			
		||||
./emsdk install 3.0.0
 | 
			
		||||
./emsdk activate 3.0.0
 | 
			
		||||
source ./emsdk_env.sh
 | 
			
		||||
popd # build/emsdk
 | 
			
		||||
 | 
			
		||||
pushd build/wasm
 | 
			
		||||
emcmake cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" ../..
 | 
			
		||||
make -j
 | 
			
		||||
TARGET_FILES="
 | 
			
		||||
	QmcLegacy.js
 | 
			
		||||
	QmcWasm.js
 | 
			
		||||
	QmcWasm.wasm
 | 
			
		||||
	QmcWasmBundle.js
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
#mkdir -p "${CURR_DIR}/npm"
 | 
			
		||||
#cp $TARGET_FILES "${CURR_DIR}/npm/"
 | 
			
		||||
cp $TARGET_FILES "${CURR_DIR}/"
 | 
			
		||||
popd # build/wasm
 | 
			
		||||
 | 
			
		||||
popd
 | 
			
		||||
							
								
								
									
										233
									
								
								src/QmcWasm/qmc.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/QmcWasm/qmc.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,233 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include "qmc_key.hpp"
 | 
			
		||||
#include "qmc_cipher.hpp"
 | 
			
		||||
 | 
			
		||||
class QmcDecode {
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<uint8_t> blobData;
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> rawKeyBuf;
 | 
			
		||||
    std::string cipherType = "";
 | 
			
		||||
 | 
			
		||||
    size_t dataOffset = 0;
 | 
			
		||||
    size_t keySize = 0;
 | 
			
		||||
    int mediaVer = 0;
 | 
			
		||||
 | 
			
		||||
    std::string checkType(std::string fn) {
 | 
			
		||||
        if (fn.find(".qmc") < fn.size() || fn.find(".m") < fn.size())
 | 
			
		||||
        {
 | 
			
		||||
            std::string buf_tag = "";
 | 
			
		||||
            for (int i = 4; i > 0; --i)
 | 
			
		||||
            {
 | 
			
		||||
                buf_tag += *((char*)blobData.data() + blobData.size() - i);
 | 
			
		||||
            }
 | 
			
		||||
            if (buf_tag == "QTag")
 | 
			
		||||
            {
 | 
			
		||||
                keySize = ntohl(*(uint32_t*)(blobData.data() + blobData.size() - 8));
 | 
			
		||||
                return "QTag";
 | 
			
		||||
            }
 | 
			
		||||
            else if (buf_tag == "STag")
 | 
			
		||||
            {
 | 
			
		||||
               return "STag";
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                keySize = (*(uint32_t*)(blobData.data() + blobData.size() - 4));
 | 
			
		||||
                if (keySize < 0x400)
 | 
			
		||||
                {
 | 
			
		||||
                    return "Map/RC4";
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    keySize = 0;
 | 
			
		||||
                    return "Static";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (fn.find(".cache") < fn.size())
 | 
			
		||||
        {
 | 
			
		||||
            return "cache";
 | 
			
		||||
        }
 | 
			
		||||
        else if (fn.find(".tm") < fn.size())
 | 
			
		||||
        {
 | 
			
		||||
            return "ios";
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return "invalid";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool parseRawKeyQTag() {
 | 
			
		||||
        std::string ketStr = "";
 | 
			
		||||
        std::string::size_type index = 0;
 | 
			
		||||
        ketStr.append((char*)rawKeyBuf.data(), rawKeyBuf.size());
 | 
			
		||||
        index = ketStr.find(",", 0);
 | 
			
		||||
        if (index != std::string::npos)
 | 
			
		||||
        {
 | 
			
		||||
            rawKeyBuf.resize(index);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        ketStr = ketStr.substr(index + 1);
 | 
			
		||||
        index = ketStr.find(",", 0);
 | 
			
		||||
        if (index != std::string::npos)
 | 
			
		||||
        {
 | 
			
		||||
            this->songId = ketStr.substr(0, index);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        ketStr = ketStr.substr(index + 1);
 | 
			
		||||
        index = ketStr.find(",", 0);
 | 
			
		||||
        if (index == std::string::npos)
 | 
			
		||||
        {
 | 
			
		||||
            this->mediaVer = std::stoi(ketStr);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool readRawKey(size_t tailSize) {
 | 
			
		||||
        // get raw key data length
 | 
			
		||||
        rawKeyBuf.resize(keySize);
 | 
			
		||||
        if (rawKeyBuf.size() != keySize) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        for (size_t i = 0; i < keySize; i++)
 | 
			
		||||
        {
 | 
			
		||||
            rawKeyBuf[i] = blobData[i + blobData.size() - (tailSize + keySize)];
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void DecodeStatic();
 | 
			
		||||
 | 
			
		||||
    void DecodeMapRC4();
 | 
			
		||||
 | 
			
		||||
    void DecodeCache();
 | 
			
		||||
 | 
			
		||||
    void DecodeTm();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    bool SetBlob(uint8_t* blob, size_t blobSize) {
 | 
			
		||||
        blobData.resize(blobSize);
 | 
			
		||||
        if (blobData.size() != blobSize) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(blobData.data(), blob, blobSize);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    int PreDecode(std::string ext) {
 | 
			
		||||
        cipherType = checkType(ext);
 | 
			
		||||
        size_t tailSize = 0;
 | 
			
		||||
        if (cipherType == "QTag") {
 | 
			
		||||
            tailSize = 8;
 | 
			
		||||
        }
 | 
			
		||||
        else if (cipherType == "Map/RC4") {
 | 
			
		||||
            tailSize = 4;
 | 
			
		||||
        }
 | 
			
		||||
        if (keySize > 0) {
 | 
			
		||||
            if (!readRawKey(tailSize)) {
 | 
			
		||||
                error = "cannot read embedded key from file";
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            if (tailSize == 8) {
 | 
			
		||||
                cipherType = "Map/RC4";
 | 
			
		||||
                if (!parseRawKeyQTag()) {
 | 
			
		||||
                    error = "cannot parse embedded key";
 | 
			
		||||
                    return -1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            std::vector<uint8_t> tmp;
 | 
			
		||||
            if (!QmcDecryptKey(rawKeyBuf, tmp)) {
 | 
			
		||||
                error = "cannot decrypt embedded key";
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            rawKeyBuf = tmp;
 | 
			
		||||
        }
 | 
			
		||||
        if (cipherType == "invalid") {
 | 
			
		||||
            error = "file is invalid or not supported(Please downgrade your app.)";
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        return keySize + tailSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> Decode(size_t offset);
 | 
			
		||||
 | 
			
		||||
    std::string songId = "";
 | 
			
		||||
    std::string error = "";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void QmcDecode::DecodeStatic()
 | 
			
		||||
{
 | 
			
		||||
    QmcStaticCipher sc;
 | 
			
		||||
    sc.proc(blobData, dataOffset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QmcDecode::DecodeMapRC4() {
 | 
			
		||||
    if (rawKeyBuf.size() > 300)
 | 
			
		||||
    {
 | 
			
		||||
        QmcRC4Cipher c(rawKeyBuf, 2);
 | 
			
		||||
        c.proc(blobData, dataOffset);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        QmcMapCipher c(rawKeyBuf, 2);
 | 
			
		||||
        c.proc(blobData, dataOffset);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QmcDecode::DecodeCache()
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < blobData.size(); i++) {
 | 
			
		||||
        blobData[i] ^= 0xf4;
 | 
			
		||||
        if (blobData[i] <= 0x3f) blobData[i] = blobData[i] * 4;
 | 
			
		||||
        else if (blobData[i] <= 0x7f) blobData[i] = (blobData[i] - 0x40) * 4 + 1;
 | 
			
		||||
        else if (blobData[i] <= 0xbf) blobData[i] = (blobData[i] - 0x80) * 4 + 2;
 | 
			
		||||
        else blobData[i] = (blobData[i] - 0xc0) * 4 + 3;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QmcDecode::DecodeTm()
 | 
			
		||||
{
 | 
			
		||||
    uint8_t const TM_HEADER[] = { 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70 };
 | 
			
		||||
    for (size_t cur = dataOffset, i = 0; cur < 8 && i < blobData.size(); ++cur, ++i) {
 | 
			
		||||
        blobData[i] = TM_HEADER[dataOffset];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> QmcDecode::Decode(size_t offset)
 | 
			
		||||
{
 | 
			
		||||
    dataOffset = offset;
 | 
			
		||||
    if (cipherType == "Map/RC4")
 | 
			
		||||
    {
 | 
			
		||||
        DecodeMapRC4();
 | 
			
		||||
    }
 | 
			
		||||
    else if (cipherType == "Static")
 | 
			
		||||
    {
 | 
			
		||||
        DecodeStatic();
 | 
			
		||||
    }
 | 
			
		||||
    else if (cipherType == "cache")
 | 
			
		||||
    {
 | 
			
		||||
        DecodeCache();
 | 
			
		||||
    }
 | 
			
		||||
    else if (cipherType == "ios")
 | 
			
		||||
    {
 | 
			
		||||
        DecodeTm();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        error = "File is invalid or encryption type is not supported.";
 | 
			
		||||
    }
 | 
			
		||||
    return blobData;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										290
									
								
								src/QmcWasm/qmc_cipher.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								src/QmcWasm/qmc_cipher.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,290 @@
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
class QmcStaticCipher {
 | 
			
		||||
private:
 | 
			
		||||
    uint8_t staticCipherBox[256] = {
 | 
			
		||||
    0x77, 0x48, 0x32, 0x73, 0xDE, 0xF2, 0xC0, 0xC8, //0x00
 | 
			
		||||
    0x95, 0xEC, 0x30, 0xB2, 0x51, 0xC3, 0xE1, 0xA0, //0x08
 | 
			
		||||
    0x9E, 0xE6, 0x9D, 0xCF, 0xFA, 0x7F, 0x14, 0xD1, //0x10
 | 
			
		||||
    0xCE, 0xB8, 0xDC, 0xC3, 0x4A, 0x67, 0x93, 0xD6, //0x18
 | 
			
		||||
    0x28, 0xC2, 0x91, 0x70, 0xCA, 0x8D, 0xA2, 0xA4, //0x20
 | 
			
		||||
    0xF0, 0x08, 0x61, 0x90, 0x7E, 0x6F, 0xA2, 0xE0, //0x28
 | 
			
		||||
    0xEB, 0xAE, 0x3E, 0xB6, 0x67, 0xC7, 0x92, 0xF4, //0x30
 | 
			
		||||
    0x91, 0xB5, 0xF6, 0x6C, 0x5E, 0x84, 0x40, 0xF7, //0x38
 | 
			
		||||
    0xF3, 0x1B, 0x02, 0x7F, 0xD5, 0xAB, 0x41, 0x89, //0x40
 | 
			
		||||
    0x28, 0xF4, 0x25, 0xCC, 0x52, 0x11, 0xAD, 0x43, //0x48
 | 
			
		||||
    0x68, 0xA6, 0x41, 0x8B, 0x84, 0xB5, 0xFF, 0x2C, //0x50
 | 
			
		||||
    0x92, 0x4A, 0x26, 0xD8, 0x47, 0x6A, 0x7C, 0x95, //0x58
 | 
			
		||||
    0x61, 0xCC, 0xE6, 0xCB, 0xBB, 0x3F, 0x47, 0x58, //0x60
 | 
			
		||||
    0x89, 0x75, 0xC3, 0x75, 0xA1, 0xD9, 0xAF, 0xCC, //0x68
 | 
			
		||||
    0x08, 0x73, 0x17, 0xDC, 0xAA, 0x9A, 0xA2, 0x16, //0x70
 | 
			
		||||
    0x41, 0xD8, 0xA2, 0x06, 0xC6, 0x8B, 0xFC, 0x66, //0x78
 | 
			
		||||
    0x34, 0x9F, 0xCF, 0x18, 0x23, 0xA0, 0x0A, 0x74, //0x80
 | 
			
		||||
    0xE7, 0x2B, 0x27, 0x70, 0x92, 0xE9, 0xAF, 0x37, //0x88
 | 
			
		||||
    0xE6, 0x8C, 0xA7, 0xBC, 0x62, 0x65, 0x9C, 0xC2, //0x90
 | 
			
		||||
    0x08, 0xC9, 0x88, 0xB3, 0xF3, 0x43, 0xAC, 0x74, //0x98
 | 
			
		||||
    0x2C, 0x0F, 0xD4, 0xAF, 0xA1, 0xC3, 0x01, 0x64, //0xA0
 | 
			
		||||
    0x95, 0x4E, 0x48, 0x9F, 0xF4, 0x35, 0x78, 0x95, //0xA8
 | 
			
		||||
    0x7A, 0x39, 0xD6, 0x6A, 0xA0, 0x6D, 0x40, 0xE8, //0xB0
 | 
			
		||||
    0x4F, 0xA8, 0xEF, 0x11, 0x1D, 0xF3, 0x1B, 0x3F, //0xB8
 | 
			
		||||
    0x3F, 0x07, 0xDD, 0x6F, 0x5B, 0x19, 0x30, 0x19, //0xC0
 | 
			
		||||
    0xFB, 0xEF, 0x0E, 0x37, 0xF0, 0x0E, 0xCD, 0x16, //0xC8
 | 
			
		||||
    0x49, 0xFE, 0x53, 0x47, 0x13, 0x1A, 0xBD, 0xA4, //0xD0
 | 
			
		||||
    0xF1, 0x40, 0x19, 0x60, 0x0E, 0xED, 0x68, 0x09, //0xD8
 | 
			
		||||
    0x06, 0x5F, 0x4D, 0xCF, 0x3D, 0x1A, 0xFE, 0x20, //0xE0
 | 
			
		||||
    0x77, 0xE4, 0xD9, 0xDA, 0xF9, 0xA4, 0x2B, 0x76, //0xE8
 | 
			
		||||
    0x1C, 0x71, 0xDB, 0x00, 0xBC, 0xFD, 0x0C, 0x6C, //0xF0
 | 
			
		||||
    0xA5, 0x47, 0xF7, 0xF6, 0x00, 0x79, 0x4A, 0x11  //0xF8
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    uint8_t getMask(size_t offset) {
 | 
			
		||||
        if (offset > 0x7fff) offset %= 0x7fff;
 | 
			
		||||
        return staticCipherBox[(offset * offset + 27) & 0xff];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    void proc(std::vector<uint8_t>& buf, size_t offset) {
 | 
			
		||||
        for (size_t i = 0; i < buf.size(); i++) {
 | 
			
		||||
            buf[i] ^= getMask(offset + i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class QmcMapCipher {
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<uint8_t> key;
 | 
			
		||||
 | 
			
		||||
    uint8_t rotate(uint8_t value, size_t bits) {
 | 
			
		||||
        auto rotate = (bits + 4) % 8;
 | 
			
		||||
        auto left = value << rotate;
 | 
			
		||||
        auto right = value >> rotate;
 | 
			
		||||
        return (left | right) & 0xff;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t getMask(size_t offset) {
 | 
			
		||||
        if (offset > 0x7fff) offset %= 0x7fff;
 | 
			
		||||
 | 
			
		||||
        const auto idx = (offset * offset + 71214) % key.size();
 | 
			
		||||
        return rotate(key[idx], idx & 0x7);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    QmcMapCipher(std::vector<uint8_t> &argKey, short operation) {
 | 
			
		||||
        if (operation == 2)
 | 
			
		||||
        {
 | 
			
		||||
            if (argKey.size() == 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (operation == 1)
 | 
			
		||||
        {
 | 
			
		||||
            const char WordList[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
			
		||||
            srand(time(0));
 | 
			
		||||
            uint32_t number = 0;
 | 
			
		||||
            while (number > 300 || number == 0)
 | 
			
		||||
            {
 | 
			
		||||
                number = rand();
 | 
			
		||||
            }
 | 
			
		||||
            argKey.resize(number);
 | 
			
		||||
            for (int i = 0; i < argKey.size(); i++) {
 | 
			
		||||
                number = rand();
 | 
			
		||||
                argKey[i] = WordList[number % 62];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        key = argKey;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void proc(std::vector<uint8_t>& buf, size_t offset) {
 | 
			
		||||
        for (size_t i = 0; i < buf.size(); i++) {
 | 
			
		||||
            buf[i] ^= getMask(offset + i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class QmcRC4Cipher {
 | 
			
		||||
public:
 | 
			
		||||
    void proc(std::vector<uint8_t>& buf, size_t offset) {
 | 
			
		||||
        // Macro: common code after each process
 | 
			
		||||
#define postProcess(len)            \
 | 
			
		||||
        {                           \
 | 
			
		||||
            toProcess -= len;       \
 | 
			
		||||
            processed += len;       \
 | 
			
		||||
            offset += len;          \
 | 
			
		||||
            /* no more data */      \
 | 
			
		||||
            if (toProcess == 0) {   \
 | 
			
		||||
                return;             \
 | 
			
		||||
            }                       \
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t toProcess = buf.size();
 | 
			
		||||
        size_t processed = 0;
 | 
			
		||||
        std::vector<uint8_t> tmpbuf;
 | 
			
		||||
 | 
			
		||||
        // 前 128 字节使用不同的解密方案
 | 
			
		||||
        if (offset < FIRST_SEGMENT_SIZE) {
 | 
			
		||||
            size_t len_segment = std::min(FIRST_SEGMENT_SIZE - offset, buf.size());
 | 
			
		||||
            tmpbuf.resize(len_segment);
 | 
			
		||||
            for (size_t i = 0; i < len_segment; i++)
 | 
			
		||||
            {
 | 
			
		||||
                tmpbuf[i] = buf[processed + i];
 | 
			
		||||
            }
 | 
			
		||||
            procFirstSegment(tmpbuf, offset);
 | 
			
		||||
            for (size_t i = 0; i < len_segment; i++)
 | 
			
		||||
            {
 | 
			
		||||
                buf[processed + i] = tmpbuf[i];
 | 
			
		||||
            }
 | 
			
		||||
            postProcess(len_segment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 区块对齐
 | 
			
		||||
        if (offset % SEGMENT_SIZE != 0) {
 | 
			
		||||
            size_t len_segment = std::min(SEGMENT_SIZE - (offset % SEGMENT_SIZE), toProcess);
 | 
			
		||||
            tmpbuf.resize(len_segment);
 | 
			
		||||
            for (size_t i = 0; i < len_segment; i++)
 | 
			
		||||
            {
 | 
			
		||||
                tmpbuf[i] = buf[processed + i];
 | 
			
		||||
            }
 | 
			
		||||
            procASegment(tmpbuf, offset);
 | 
			
		||||
            for (size_t i = 0; i < len_segment; i++)
 | 
			
		||||
            {
 | 
			
		||||
                buf[processed + i] = tmpbuf[i];
 | 
			
		||||
            }
 | 
			
		||||
            postProcess(len_segment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 对每个区块逐一进行解密
 | 
			
		||||
        while (toProcess > SEGMENT_SIZE) {
 | 
			
		||||
            tmpbuf.resize(SEGMENT_SIZE);
 | 
			
		||||
            for (size_t i = 0; i < SEGMENT_SIZE; i++)
 | 
			
		||||
            {
 | 
			
		||||
                tmpbuf[i] = buf[processed + i];
 | 
			
		||||
            }
 | 
			
		||||
            procASegment(tmpbuf, offset);
 | 
			
		||||
            for (size_t i = 0; i < SEGMENT_SIZE; i++)
 | 
			
		||||
            {
 | 
			
		||||
                buf[processed + i] = tmpbuf[i];
 | 
			
		||||
            }
 | 
			
		||||
            postProcess(SEGMENT_SIZE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (toProcess > 0) {
 | 
			
		||||
            tmpbuf.resize(toProcess);
 | 
			
		||||
            for (size_t i = 0; i < toProcess; i++)
 | 
			
		||||
            {
 | 
			
		||||
                tmpbuf[i] = buf[processed + i];
 | 
			
		||||
            }
 | 
			
		||||
            procASegment(tmpbuf, offset);
 | 
			
		||||
            for (size_t i = 0; i < toProcess; i++)
 | 
			
		||||
            {
 | 
			
		||||
                buf[processed + i] = tmpbuf[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#undef postProcess
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QmcRC4Cipher(std::vector<uint8_t>& argKey, short operation) {
 | 
			
		||||
        if (operation == 2)
 | 
			
		||||
        {
 | 
			
		||||
            if (argKey.size() == 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (operation == 1)
 | 
			
		||||
        {
 | 
			
		||||
            const char WordList[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
			
		||||
            srand(time(0));
 | 
			
		||||
            uint32_t number = 0;
 | 
			
		||||
            while (number <= 300 || number >= 512)
 | 
			
		||||
            {
 | 
			
		||||
                number = rand();
 | 
			
		||||
            }
 | 
			
		||||
            argKey.resize(number);
 | 
			
		||||
            for (int i = 0; i < argKey.size(); i++) {
 | 
			
		||||
                number = rand();
 | 
			
		||||
                argKey[i] = WordList[number % 62];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        key = argKey;
 | 
			
		||||
 | 
			
		||||
        // init seed box
 | 
			
		||||
        S.resize(key.size());
 | 
			
		||||
        for (size_t i = 0; i < key.size(); ++i) {
 | 
			
		||||
            S[i] = i & 0xff;
 | 
			
		||||
        }
 | 
			
		||||
        size_t j = 0;
 | 
			
		||||
        for (size_t i = 0; i < key.size(); ++i) {
 | 
			
		||||
            j = (S[i] + j + key[i % key.size()]) % key.size();
 | 
			
		||||
            std::swap(S[i], S[j]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // init hash base
 | 
			
		||||
        hash = 1;
 | 
			
		||||
        for (size_t i = 0; i < key.size(); i++) {
 | 
			
		||||
            uint8_t value = key[i];
 | 
			
		||||
 | 
			
		||||
            // ignore if key char is '\x00'
 | 
			
		||||
            if (!value) continue;
 | 
			
		||||
 | 
			
		||||
            auto next_hash = hash * value;
 | 
			
		||||
            if (next_hash == 0 || next_hash <= hash) break;
 | 
			
		||||
 | 
			
		||||
            hash = next_hash;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    const size_t FIRST_SEGMENT_SIZE = 0x80;
 | 
			
		||||
    const size_t SEGMENT_SIZE = 5120;
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> S;
 | 
			
		||||
    std::vector<uint8_t> key;
 | 
			
		||||
    uint32_t hash = 1;
 | 
			
		||||
 | 
			
		||||
    void procFirstSegment(std::vector<uint8_t>& buf, size_t offset) {
 | 
			
		||||
        for (size_t i = 0; i < buf.size(); i++) {
 | 
			
		||||
            buf[i] ^= key[getSegmentKey(offset + i)];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void procASegment(std::vector<uint8_t>& buf, size_t offset) {
 | 
			
		||||
        // Initialise a new seed box
 | 
			
		||||
        std::vector<uint8_t> nS;
 | 
			
		||||
        nS = S;
 | 
			
		||||
 | 
			
		||||
        // Calculate the number of bytes to skip.
 | 
			
		||||
        // The initial "key" derived from segment id, plus the current offset.
 | 
			
		||||
        int64_t skipLen = (offset % SEGMENT_SIZE) + getSegmentKey(int(offset / SEGMENT_SIZE));
 | 
			
		||||
 | 
			
		||||
        // decrypt the block
 | 
			
		||||
        size_t j = 0;
 | 
			
		||||
        size_t k = 0;
 | 
			
		||||
        int i = -skipLen;
 | 
			
		||||
        for (; i < (int)buf.size(); i++) {
 | 
			
		||||
            j = (j + 1) % key.size();
 | 
			
		||||
            k = (nS[j] + k) % key.size();
 | 
			
		||||
            std::swap(nS[k], nS[j]);
 | 
			
		||||
 | 
			
		||||
            if (i >= 0) {
 | 
			
		||||
                buf[i] ^= nS[(nS[j] + nS[k]) % key.size()];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t getSegmentKey(int id) {
 | 
			
		||||
        auto seed = key[id % key.size()];
 | 
			
		||||
        uint64_t idx = ((double)hash / ((id + 1) * seed)) * 100.0;
 | 
			
		||||
        return idx % key.size();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										217
									
								
								src/QmcWasm/qmc_key.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/QmcWasm/qmc_key.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
			
		||||
#include"TencentTea.hpp"
 | 
			
		||||
#include "base64.hpp"
 | 
			
		||||
 | 
			
		||||
void simpleMakeKey(uint8_t salt, int length, std::vector<uint8_t> &key_buf) {
 | 
			
		||||
    for (size_t i = 0; i < length; ++i) {
 | 
			
		||||
        double tmp = tan((float)salt + (double)i * 0.1);
 | 
			
		||||
        key_buf[i] = 0xFF & (uint8_t)(fabs(tmp) * 100.0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> v2KeyPrefix = { 0x51, 0x51, 0x4D, 0x75, 0x73, 0x69, 0x63, 0x20, 0x45, 0x6E, 0x63, 0x56, 0x32, 0x2C, 0x4B, 0x65, 0x79, 0x3A };
 | 
			
		||||
 | 
			
		||||
bool decryptV2Key(std::vector<uint8_t> key, std::vector<uint8_t>& outVec)
 | 
			
		||||
{
 | 
			
		||||
    if (v2KeyPrefix.size() > key.size())
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = 0; i < v2KeyPrefix.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (key[i] != v2KeyPrefix[i])
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> mixKey1 = { 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28 };
 | 
			
		||||
    std::vector<uint8_t> mixKey2 = { 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54 };
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> out;
 | 
			
		||||
    std::vector<uint8_t> tmpKey;
 | 
			
		||||
    tmpKey.resize(key.size() - 18);
 | 
			
		||||
    for (size_t i = 0; i < tmpKey.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        tmpKey[i] = key[18 + i];
 | 
			
		||||
    }
 | 
			
		||||
    if (!decryptTencentTea(tmpKey, mixKey1, out))
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key decode failed.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tmpKey.resize(out.size());
 | 
			
		||||
    for (size_t i = 0; i < tmpKey.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        tmpKey[i] = out[i];
 | 
			
		||||
    }
 | 
			
		||||
    out.resize(0);
 | 
			
		||||
    if (!decryptTencentTea(tmpKey, mixKey2, out))
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key decode failed.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    outVec.resize(base64::decoded_size(out.size()));
 | 
			
		||||
    auto n = base64::decode(outVec.data(), (const char*)(out.data()), out.size()).first;
 | 
			
		||||
 | 
			
		||||
    if (n < 16)
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key size is too small.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    outVec.resize(n);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool encryptV2Key(std::vector<uint8_t> key, std::vector<uint8_t>& outVec)
 | 
			
		||||
{
 | 
			
		||||
    if (key.size() < 16)
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key size is too small.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> in;
 | 
			
		||||
    in.resize(base64::encoded_size(key.size()));
 | 
			
		||||
    auto n = base64::encode(in.data(), (const char*)(key.data()), key.size());
 | 
			
		||||
    in.resize(n);
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> mixKey1 = { 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28 };
 | 
			
		||||
    std::vector<uint8_t> mixKey2 = { 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54 };
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> tmpKey;
 | 
			
		||||
    if (!encryptTencentTea(in, mixKey2, tmpKey))
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key decode failed.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    in.resize(tmpKey.size());
 | 
			
		||||
    for (size_t i = 0; i < tmpKey.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        in[i] = tmpKey[i];
 | 
			
		||||
    }
 | 
			
		||||
    tmpKey.resize(0);
 | 
			
		||||
 | 
			
		||||
    if (!encryptTencentTea(in, mixKey1, tmpKey))
 | 
			
		||||
    {
 | 
			
		||||
        outVec.resize(0);
 | 
			
		||||
        //EncV2 key decode failed.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    outVec.resize(tmpKey.size() + 18);
 | 
			
		||||
    for (size_t i = 0; i < tmpKey.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        outVec[18 + i] = tmpKey[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < v2KeyPrefix.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        outVec[i] = v2KeyPrefix[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QmcDecryptKey(std::vector<uint8_t> raw, std::vector<uint8_t> &outVec) {
 | 
			
		||||
    std::vector<uint8_t> rawDec;
 | 
			
		||||
    rawDec.resize(base64::decoded_size(raw.size()));
 | 
			
		||||
    auto n = base64::decode(rawDec.data(), (const char*)(raw.data()), raw.size()).first;
 | 
			
		||||
    if (n < 16) {
 | 
			
		||||
        return false;
 | 
			
		||||
        //key length is too short
 | 
			
		||||
    }
 | 
			
		||||
    rawDec.resize(n);
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> tmpIn = rawDec;
 | 
			
		||||
    if (!decryptV2Key(tmpIn, rawDec))
 | 
			
		||||
    {
 | 
			
		||||
        //decrypt EncV2 failed.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> simpleKey;
 | 
			
		||||
    simpleKey.resize(8);
 | 
			
		||||
    simpleMakeKey(106, 8, simpleKey);
 | 
			
		||||
    std::vector<uint8_t> teaKey;
 | 
			
		||||
    teaKey.resize(16);
 | 
			
		||||
    for (size_t i = 0; i < 8; i++) {
 | 
			
		||||
        teaKey[i << 1] = simpleKey[i];
 | 
			
		||||
        teaKey[(i << 1) + 1] = rawDec[i];
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<uint8_t> out;
 | 
			
		||||
    std::vector<uint8_t> tmpRaw;
 | 
			
		||||
    tmpRaw.resize(rawDec.size() - 8);
 | 
			
		||||
    for (size_t i = 0; i < tmpRaw.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        tmpRaw[i] = rawDec[8 + i];
 | 
			
		||||
    }
 | 
			
		||||
    if (decryptTencentTea(tmpRaw, teaKey, out))
 | 
			
		||||
    {
 | 
			
		||||
        rawDec.resize(8 + out.size());
 | 
			
		||||
        for (size_t i = 0; i < out.size(); i++)
 | 
			
		||||
        {
 | 
			
		||||
            rawDec[8 + i] = out[i];
 | 
			
		||||
        }
 | 
			
		||||
        outVec = rawDec;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QmcEncryptKey(std::vector<uint8_t> raw, std::vector<uint8_t>& outVec, bool useEncV2 = true) {
 | 
			
		||||
    std::vector<uint8_t> simpleKey;
 | 
			
		||||
    simpleKey.resize(8);
 | 
			
		||||
    simpleMakeKey(106, 8, simpleKey);
 | 
			
		||||
    std::vector<uint8_t> teaKey;
 | 
			
		||||
    teaKey.resize(16);
 | 
			
		||||
    for (size_t i = 0; i < 8; i++) {
 | 
			
		||||
        teaKey[i << 1] = simpleKey[i];
 | 
			
		||||
        teaKey[(i << 1) + 1] = raw[i];
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<uint8_t> out;
 | 
			
		||||
    out.resize(raw.size() - 8);
 | 
			
		||||
    for (size_t i = 0; i < out.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        out[i] = raw[8 + i];
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<uint8_t> tmpRaw;
 | 
			
		||||
    if (encryptTencentTea(out, teaKey, tmpRaw))
 | 
			
		||||
    {
 | 
			
		||||
        raw.resize(tmpRaw.size() + 8);
 | 
			
		||||
        for (size_t i = 0; i < tmpRaw.size(); i++)
 | 
			
		||||
        {
 | 
			
		||||
            raw[i + 8] = tmpRaw[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (useEncV2)
 | 
			
		||||
        {
 | 
			
		||||
            std::vector<uint8_t> tmpIn = raw;
 | 
			
		||||
            if (!encryptV2Key(tmpIn, raw))
 | 
			
		||||
            {
 | 
			
		||||
                //encrypt EncV2 failed.
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::vector<uint8_t> rawEnc;
 | 
			
		||||
        rawEnc.resize(base64::encoded_size(raw.size()));
 | 
			
		||||
        auto n = base64::encode(rawEnc.data(), (const char*)(raw.data()), raw.size());
 | 
			
		||||
        rawEnc.resize(n);
 | 
			
		||||
        outVec = rawEnc;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user