diff --git a/binding.gyp b/binding.gyp index 5420293..509e041 100644 --- a/binding.gyp +++ b/binding.gyp @@ -13,6 +13,7 @@ 'target_name': 'main', 'sources': [ 'src/main.cpp', + 'src/base64.c', 'src/aes/aes.c' ], 'includes': [ @@ -23,6 +24,7 @@ 'target_name': 'renderer', 'sources': [ 'src/main.cpp', + 'src/base64.c', 'src/aes/aes.c' ], 'includes': [ diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..7820cd2 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,184 @@ +#include "string.h" +#include "base64.h" + +static const char table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// supports regular and URL-safe base64 +static const int8_t unbase64_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +size_t base64_encode(const uint8_t* src, size_t len, char* dst) { + size_t slen, dlen; + unsigned i, k, n, a, b, c; + if (src == NULL) { + return 0; + } + + if (len == -1) { + slen = strlen((const char*)src); + } else { + slen = len; + } + + dlen = ((slen + 2 - ((slen + 2) % 3)) / 3 * 4); + + if (dst == NULL) { + return dlen; + } + + i = 0; + k = 0; + n = slen / 3 * 3; + + while (i < n) { + a = src[i + 0] & 0xff; + b = src[i + 1] & 0xff; + c = src[i + 2] & 0xff; + + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; + dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; + dst[k + 3] = table[c & 0x3f]; + + i += 3; + k += 4; + } + + if (n != slen) { + switch (slen - n) { + case 1: + a = src[i + 0] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[(a & 3) << 4]; + dst[k + 2] = '='; + dst[k + 3] = '='; + break; + + case 2: + a = src[i + 0] & 0xff; + b = src[i + 1] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; + dst[k + 2] = table[(b & 0x0f) << 2]; + dst[k + 3] = '='; + break; + } + } + + return dlen; +} + +static int base64_decode_group_slow(char* const dst, const size_t dstlen, + const char* const src, const size_t srclen, + size_t* const i, size_t* const k) { + uint8_t hi; + uint8_t lo; + uint8_t c; +#define V(expr) \ + for (;;) { \ + c = src[*i]; \ + lo = unbase64_table[c]; \ + *i += 1; \ + if (lo < 64) \ + break; /* Legal character. */ \ + if (c == '=' || *i >= srclen) \ + return 0; /* Stop decoding. */ \ + } \ + expr; \ + if (*i >= srclen) \ + return 0; \ + if (*k >= dstlen) \ + return 0; \ + hi = lo; + V((void)0); + V(dst[(*k)++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4)); + V(dst[(*k)++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2)); + V(dst[(*k)++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0)); +#undef V + return 1; // Continue decoding. +} + +size_t base64_decode(const char* src, size_t len, uint8_t* dst) { + size_t slen, dlen, remainder, size; + size_t available, max_k, max_i, i, k, v; + + if (src == NULL) { + return 0; + } + + if (len == -1) { + slen = strlen(src); + } else { + slen = len; + } + + if (slen == 0) { + dlen = 0; + } else { + if (src[slen - 1] == '=') slen--; + if (slen > 0 && src[slen - 1] == '=') slen--; + + size = slen; + remainder = size % 4; + + size = (size / 4) * 3; + if (remainder) { + if (size == 0 && remainder == 1) { + size = 0; + } else { + size += 1 + (remainder == 3); + } + } + + dlen = size; + } + + if (dst == NULL) { + return dlen; + } + + available = dlen; + max_k = available / 3 * 3; + max_i = slen / 4 * 4; + i = 0; + k = 0; + while (i < max_i && k < max_k) { + v = unbase64_table[src[i + 0]] << 24 | + unbase64_table[src[i + 1]] << 16 | + unbase64_table[src[i + 2]] << 8 | + unbase64_table[src[i + 3]]; + // If MSB is set, input contains whitespace or is not valid base64. + if (v & 0x80808080) { + if (!base64_decode_group_slow((char*)dst, dlen, src, slen, &i, &k)) + return k; + max_i = i + (slen - i) / 4 * 4; // Align max_i again. + } else { + dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03); + dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F); + dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F); + i += 4; + k += 3; + } + } + if (i < slen && k < dlen) { + base64_decode_group_slow((char*)dst, dlen, src, slen, &i, &k); + } + return k; +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..1b47c9f --- /dev/null +++ b/src/base64.h @@ -0,0 +1,18 @@ +#ifndef SRC_BASE64_H_ +#define SRC_BASE64_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t base64_encode(const uint8_t* src, size_t len, char* dst); +size_t base64_decode(const char* src, size_t len, uint8_t* dst); + +#ifdef __cplusplus +} +#endif + +#endif // SRC_BASE64_H_ diff --git a/src/main.cpp b/src/main.cpp index 7f038e4..faf7b56 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,6 +61,7 @@ try { #include #include "napi.h" #include "script.h" +#include "base64.h" #include "aes/aes.hpp" @@ -77,12 +78,12 @@ struct AddonData { const char errmsg[] = "This program has been changed by others."; -void ConsoleLog(const Napi::Env& env, Napi::Value value) { +/* void ConsoleLog(const Napi::Env& env, Napi::Value value) { Napi::Object console = env.Global().As() .Get("console").As(); Napi::Function log = console.Get("log").As(); log.Call(console, { value }); -} +} */ void ConsoleError(const Napi::Env& env, Napi::Value value) { Napi::Object console = env.Global().As() @@ -91,18 +92,18 @@ void ConsoleError(const Napi::Env& env, Napi::Value value) { error.Call(console, { value }); } -std::vector GetKeyVector() { - const uint8_t key[KEY_LENGTH] = { +const uint8_t* GetKey() { + static const uint8_t key[KEY_LENGTH] = { #include "key.txt" }; - return std::vector(key, key + KEY_LENGTH); + return key; } -Napi::Array GetKey(const Napi::Env& env) { - std::vector key = GetKeyVector(); - Napi::Array arrkey = Napi::Array::New(env, key.size()); - for (uint32_t i = 0; i < key.size(); i++) { +Napi::Array GetKeyArray(const Napi::Env& env) { + const uint8_t* key = GetKey(); + Napi::Array arrkey = Napi::Array::New(env, KEY_LENGTH); + for (uint32_t i = 0; i < KEY_LENGTH; i++) { arrkey.Set(i, key[i]); } return arrkey; @@ -123,14 +124,14 @@ int Pkcs7cut(uint8_t *p, int plen) { } std::string Aesdec(const std::vector& data, - const std::vector& key, - const std::vector& iv) { + const uint8_t* key, + const uint8_t* iv) { size_t l = data.size(); uint8_t* encrypt = new uint8_t[l]; memcpy(encrypt, data.data(), l); struct AES_ctx ctx; - AES_init_ctx_iv(&ctx, key.data(), iv.data()); + AES_init_ctx_iv(&ctx, key, iv); AES_CBC_decrypt_buffer(&ctx, encrypt, l); uint8_t* out = new uint8_t[l + 1]; @@ -146,46 +147,30 @@ std::string Aesdec(const std::vector& data, return res; } -std::vector BufferToVector(const Napi::Buffer& buf) { - uint8_t* data = buf.Data(); - return std::vector(data, data + buf.ByteLength()); -} +std::string Decrypt(const std::string& base64) { + size_t buflen = base64_decode(base64.c_str(), base64.length(), nullptr); + if (buflen == 0) return ""; + std::vector buf(buflen); + base64_decode(base64.c_str(), base64.length(), &buf[0]); -Napi::String Base64toCode(const Napi::Env& env, - const Napi::String& base64) { - Napi::Object buffer_constructor = env.Global().Get("Buffer") - .As(); - Napi::Buffer body = buffer_constructor.Get("from") - .As() - .Call(buffer_constructor, { base64, Napi::String::New(env, "base64") }) - .As>(); - - Napi::Buffer iv = body.Get("slice").As() - .Call(body, { Napi::Number::New(env, 0), Napi::Number::New(env, 16) }) - .As>(); - Napi::Buffer data = body.Get("slice").As() - .Call(body, { Napi::Number::New(env, 16) }) - .As>(); - - std::string plain_content = Aesdec(BufferToVector(data), - GetKeyVector(), BufferToVector(iv)); - - return Napi::String::New(env, plain_content); + std::vector iv(buf.begin(), buf.begin() + 16); + std::vector data(buf.begin() + 16, buf.end()); + + return Aesdec(data, GetKey(), iv.data()); } Napi::Value ModulePrototypeCompile(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); AddonData* addon_data = static_cast(info.Data()); - Napi::Object content = info[0].As(); - Napi::Object filename = info[1].As(); + Napi::String content = info[0].As(); + Napi::String filename = info[1].As(); + std::string filename_str = filename.Utf8Value(); Napi::Function old_compile = addon_data->functions[FN_MODULE_PROTOTYPE__COMPILE].Value(); - if (-1 != filename.Get("indexOf").As() - .Call(filename, { Napi::String::New(env, "app.asar") }) - .As().Int32Value()) { + if (filename_str.find("app.asar") != std::string::npos) { return old_compile.Call(info.This(), - { Base64toCode(env, content.As()), filename }); + { Napi::String::New(env, Decrypt(content.Utf8Value())), filename }); } return old_compile.Call(info.This(), { content, filename }); } @@ -337,7 +322,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) { try { require({ Napi::String::New(env, "./main.js") }) - .As().Call({ GetKey(env) }); + .As().Call({ GetKeyArray(env) }); } catch (const Napi::Error& e) { ShowErrorAndQuit(env, electron, e.Get("stack").As()); }