一个简单CPP程序的防护

前几天做了个cpp程序但是考虑安全做了些防护,来看看怎么做的。

#define _WIN32_WINNT 0x0600
#include <windows.h>
#include <wininet.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <ctime>
#include <wincrypt.h>
#include "VMProtectSDK.h"

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "crypt32.lib")

using namespace std;

string GetSecretKey(){
//这里原来是和服务器通信的token这里我给hide了
    const char* part1 = "";
    const char* part2 = "";
    const char* part3 = "";
    const char* part4 = "";
    return string(part1) + part2 + part3 + part4;
}

void SecureClearString(string& str) {
    if (str.empty()) return;
    VirtualLock((void*)str.c_str(), str.length());
    RtlZeroMemory((void*)str.c_str(), str.length());
    VirtualUnlock((void*)str.c_str(), str.length());
    str.clear();
}

bool IsApiHooked(const char* module, const char* func) {
    HMODULE hMod = GetModuleHandleA(module);
    if (!hMod) return false;
    FARPROC addr = GetProcAddress(hMod, func);
    if (!addr) return false;

    unsigned char* p = (unsigned char*)addr;
    if (*p == 0xCC) return true;
    if (*p == 0xE9) return true;
    if (*p == 0x68 && p[5] == 0xC3) return true;
    return false;
}

bool IsHardwareBreakpointPresent() {
    CONTEXT ctx = { 0 };
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    if (GetThreadContext(GetCurrentThread(), &ctx)) {
        if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3) {
            return true;
        }
    }
    return false;
}

bool IsRunningInVM() {
    if (GetModuleHandleA("VBoxGuest")) return true;
    return false;
}

string md5_multipart(const string& part1, const string& part2, const string& part3) {
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    BYTE pbHash[16];
    DWORD dwHashLen = 16;
    string result = "";

    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        return "";
    }
    if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
        CryptReleaseContext(hProv, 0);
        return "";
    }

    CryptHashData(hHash, (PBYTE)(part2.c_str()), part2.length(), 0);
    CryptHashData(hHash, (PBYTE)(part1.c_str()), part1.length(), 0);
    CryptHashData(hHash, (PBYTE)(part3.c_str()), part3.length(), 0);

    CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, 0);

    stringstream ss;
    for (int i = 0; i < dwHashLen; i++) {
        ss << hex << setw(2) << setfill('0') << (int)pbHash[i];
    }
    result = ss.str();

    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);
    return result;
}

string fetchCipher() {
    VMProtectBeginUltra("fetchCipher");

    if (IsRunningInVM()) {
        VMProtectEnd();
        return "ERROR_VM_DETECTED";
    }
    if (IsHardwareBreakpointPresent()) {
        VMProtectEnd();
        return "ERROR_HARDWARE_BP";
    }
    if (IsApiHooked("crypt32.dll", "CryptHashData") ||
        IsApiHooked("wininet.dll", "HttpOpenRequestA") ||
        IsApiHooked("wininet.dll", "InternetConnectA")) {
        VMProtectEnd();
        return "ERROR_HOOK_DETECTED";
    }

    string token = VMProtectDecryptStringA("Sakurazuki291A");
    string secret = GetSecretKey();
    string timestamp = to_string(time(nullptr));

    string sign = md5_multipart(token, timestamp, secret);

    string base_path = VMProtectDecryptStringA("/proxy.php");
    string url = base_path + "?token=" + token + "&t=" + timestamp + "&sign=" + sign;

    HINTERNET hSession = InternetOpenA("User-Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    HINTERNET hConnect = InternetConnectA(hSession, "moongs.asia", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", url.c_str(), NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0);

    BOOL bSent = HttpSendRequestA(hRequest, NULL, 0, NULL, 0);

    string response;
    if (bSent) {
        char buffer[4096];
        DWORD bytesRead;
        while (InternetReadFile(hRequest, buffer, sizeof(buffer) - 1, &bytesRead) && bytesRead > 0) {
            buffer[bytesRead] = '\0';
            response += buffer;
        }
    }

    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hSession);

    string key = "\"cipher\":\"";
    size_t start = response.find(key);
    if (start != string::npos) {
        start += key.length();
        size_t end = response.find("\"", start);
        if (end != string::npos) {
            string result = response.substr(start, end - start);

            SecureClearString(token);
            SecureClearString(secret);
            SecureClearString(timestamp);
            SecureClearString(sign);
            SecureClearString(url);

            VMProtectEnd();
            return result;
        }
    }

    SecureClearString(token);
    SecureClearString(secret);
    SecureClearString(timestamp);
    SecureClearString(sign);
    SecureClearString(url);

    VMProtectEnd();
    return "ERROR";
}

HWND resultLabel = NULL;
HWND statusLabel = NULL;

DWORD WINAPI fetchCipherThread(LPVOID lpParam) {
    SetWindowTextA(statusLabel, VMProtectDecryptStringA("Requesting..."));
    string result = fetchCipher();

    if (result == "ERROR" || result == "ERROR_HOOK_DETECTED" ||
        result == "ERROR_HARDWARE_BP" || result == "ERROR_VM_DETECTED") {
        SetWindowTextA(statusLabel, VMProtectDecryptStringA("Failed"));
        SetWindowTextA(resultLabel, "");
    } else {
        SetWindowTextA(statusLabel, VMProtectDecryptStringA("Success"));
        SetWindowTextA(resultLabel, ("Cipher: " + result).c_str());
    }
    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_CREATE: {
            CreateWindowA("STATIC", VMProtectDecryptStringA("Cipher Tool"), WS_VISIBLE | WS_CHILD | SS_CENTER,
                          0, 10, 400, 30, hwnd, NULL, NULL, NULL);
            CreateWindowA("BUTTON", VMProtectDecryptStringA("Get Cipher"), WS_VISIBLE | WS_CHILD,
                          140, 50, 120, 40, hwnd, (HMENU)1, NULL, NULL);
            statusLabel = CreateWindowA("STATIC", VMProtectDecryptStringA("Click to get cipher"), WS_VISIBLE | WS_CHILD | SS_CENTER,
                                        0, 110, 400, 25, hwnd, NULL, NULL, NULL);
            resultLabel = CreateWindowA("STATIC", "", WS_VISIBLE | WS_CHILD | SS_CENTER,
                                        0, 150, 400, 35, hwnd, NULL, NULL, NULL);
            CreateWindowA("STATIC", VMProtectDecryptStringA("(C) Moon STUDIO / Sakurazuki"), WS_VISIBLE | WS_CHILD | SS_CENTER,
                          0, 195, 400, 20, hwnd, NULL, NULL, NULL);
            break;
        }
        case WM_COMMAND: {
            if (LOWORD(wParam) == 1) {
                SetWindowTextA(statusLabel, VMProtectDecryptStringA("Requesting..."));
                SetWindowTextA(resultLabel, "");
                CreateThread(NULL, 0, fetchCipherThread, NULL, 0, NULL);
            }
            break;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    VMProtectBeginUltra("WinMain");

    WNDCLASSA wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "CipherWindowClass";
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClassA(&wc);

    HWND hwnd = CreateWindowExA(
        0,
        "CipherWindowClass",
        VMProtectDecryptStringA("Cipher Getter"),
        WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, 410, 270,
        NULL, NULL, hInstance, NULL
    );

    ShowWindow(hwnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    VMProtectEnd();
    return 0;
}

程序本身其实并没有什么 所以主要就是来看这上面做的一些安全措施

string timestamp = to_string(time(nullptr));
string sign = md5(timestamp + token + secret);

string url = "/proxy.php?token=" + token + "&t=" + timestamp + "&sign=" + sign;

这里用当前时间戳 + Token + Secret 拼成字符串计算MD5,然后发起请求,从服务器返回的json中提取出所需的片段再在UI里表示出来

好的那么再来看网页端:

if ($token !== '-----') {
    //403
}

这里是对客户端发送的GET进行校验,如果token参数不等于这个值那么就返回403,

if (abs($now - intval($timestamp)) > 0.7) {
    // 403
}

这里是校验时间窗口,为了防止重放只留了0.7s(所以会导致某些网特别卡的使用不了)

$expected_sign = md5($timestamp . $token . $secret);
if ($sign !== $expected_sign) {
    // 403
}

确保请求没有被篡改,只有知道 Secret 的客户端才能生成正确的签名

$nonce_file = '/tmp/proxy_nonces/' . md5($token . '_' . $timestamp);
if (file_exists($nonce_file)) {
    // 返回 403
}
file_put_contents($nonce_file, 'used');
touch($nonce_file, $timestamp + 10);

nonce防止重放,一个token只能用10s,过了10s自动过期

下一步就是给另外一层服务器转发并且将结果返回了,这里就不放了。

那么cpp端还使用VMP超级模式进行了虚拟化和变异这点在代码中也是可以看出来的。

Mfuns_Dark_Mode的技术解析

接上一个文章,来对这个js做一些简单的解析。

原理其实很简单,因为我懒得去一个一个css的去适配所以采用了一个很简单的方法,直接把所有的白色元素替换成黑的就可以了,然后对文字颜色进行反转,但是这样就会出现一个问题那就是子元素没办法即使替换,本来想过10ms替换一下但这样会导致站点原有的动画不停循环所以我写的是鼠标点击一次就进行一次替换以及在页面第一次加载也进行一次替换。

Script==<br>// @name         喵御宅深色模式-Mfuns Dark Mode<br>// @namespace    https://moongs.asia/<br>// @version      0.01<br>// @description  安装后自动将Mfuns变为深色模式<br>// @author       Sakurazuki<br>// @match        https://www.mfuns.net/*<br>// @match        https://mfuns.net/*<br>// @grant        none<br>// @run-at       document-start<br>// ==/UserScript==<br><br>(function() {<br>    'use strict';<br>    const CONFIG = {<br>        bgDark: '#0d0d0d',<br>        bgCard: '#1a1a1a',<br>        bgHover: '#242424',<br>        bgInput: '#2a2a2a',<br>        textPrimary: '#e8e8e8',<br>        textSecondary: '#888888',<br>        textMuted: '#666666',<br>        borderColor: '#2a2a2a',<br>        accentColor: '#4a9eff',<br>        dangerColor: '#e88080',<br>    };<br>    function getColorBrightness(color) {<br>        if (!color || color === 'transparent' || color === 'rgba(0, 0, 0, 0)') return -1;<br>        const match = color.match(/\d+/g);<br>        if (!match) return -1;<br>        const r = parseInt(match[0]);<br>        const g = parseInt(match[1]);<br>        const b = parseInt(match[2]);<br>        return (r * 299 + g * 587 + b * 114) / 1000;<br>    }<br>    function isLightColor(color) {<br>        const brightness = getColorBrightness(color);<br>        return brightness > 160;<br>    }<br>    function getPageElements() {<br>        const allElements = document.querySelectorAll('*');<br>        const elements = [];<br>        const seen = new Set();<br>        allElements.forEach(el => {<br>            if (el.offsetWidth === 0 && el.offsetHeight === 0) return;<br>            const tag = el.tagName.toLowerCase();<br>            if (['video', 'img', 'svg', 'canvas', 'iframe', 'script', 'style'].includes(tag)) return;<br>            const classes = el.className ? String(el.className).split(/\s+/) : [];<br>            const id = el.id || '';<br>            let selector = '';<br>            if (id) {<br>                selector = `#${CSS.escape(id)}`;<br>            } else if (classes.length) {<br>                const validClasses = classes.filter(c => c && !c.startsWith('__') && !c.match(/^[0-9]/));<br>                if (validClasses.length) {<br>                    selector = '.' + validClasses.join('.');<br>                }<br>            }<br>            if (!selector || seen.has(selector)) return;<br>            seen.add(selector);<br>            const computed = window.getComputedStyle(el);<br>            const bg = computed.backgroundColor;<br>            const color = computed.color;<br>            const border = computed.borderColor;<br>            const isLightBg = bg && bg !== 'transparent' && bg !== 'rgba(0, 0, 0, 0)' && isLightColor(bg);<br>            const hasBorder = border && border !== 'rgba(0, 0, 0, 0)' && border !== 'transparent';<br>            if (isLightBg || hasBorder) {<br>                elements.push({<br>                    selector: selector,<br>                    tag: tag,<br>                    bg: bg,<br>                    color: color,<br>                    border: border,<br>                    isLightBg: isLightBg,<br>                    hasBorder: hasBorder,<br>                    bgBrightness: getColorBrightness(bg),<br>                });<br>            }<br>        });<br>        return elements;<br>    }<br>    function generateStyles(elements) {<br>        const { bgDark, bgCard, bgHover, bgInput, textPrimary, textSecondary, textMuted, borderColor, accentColor, dangerColor } = CONFIG;<br>        let css = `<br>            html, body, [id="m-app"], .m-layout, .m-layout-page,<br>            .container, .container--full, .m-home-page,<br>            .m-account-center__container {<br>                background-color: ${bgDark} !important;<br>                color: ${textPrimary} !important;<br>            }<br>        `;<br>        const processed = new Set();<br>        elements.forEach(el => {<br>            const selector = el.selector;<br>            if (processed.has(selector)) return;<br>            if (selector.includes('video') || selector.includes('img') || selector.includes('svg')) return;<br>            processed.add(selector);<br>            let styles = [];<br>            if (el.isLightBg) {<br>                const isCard = /card|item|list|post|feed|menu|panel|box|container|section|article/.test(selector);<br>                const isHeader = /header|nav|footer/.test(selector);<br>                const isButton = /button|btn/.test(selector);<br>                const isInput = /input|search|text/.test(selector);<br>                if (isCard) {<br>                    styles.push(`background-color: ${bgCard} !important`);<br>                } else if (isHeader) {<br>                    styles.push(`background-color: ${bgDark} !important`);<br>                } else if (isButton) {<br>                    styles.push(`background-color: ${bgHover} !important`);<br>                } else if (isInput) {<br>                    styles.push(`background-color: ${bgInput} !important`);<br>                } else {<br>                    styles.push(`background-color: transparent !important`);<br>                }<br>            }<br>            if (el.hasBorder) {<br>                styles.push(`border-color: ${borderColor} !important`);<br>            }<br>            if (el.color && isLightColor(el.color)) {<br>                const isSecondary = /secondary|sub|meta|time|count|info|desc/.test(selector);<br>                const isMuted = /muted|placeholder|hint/.test(selector);<br>                if (isSecondary) {<br>                    styles.push(`color: ${textSecondary} !important`);<br>                } else if (isMuted) {<br>                    styles.push(`color: ${textMuted} !important`);<br>                } else {<br>                    styles.push(`color: ${textPrimary} !important`);<br>                }<br>            }<br>            if (styles.length) {<br>                css += `\n    ${selector} {\n      ${styles.join(';\n      ')};\n    }`;<br>            }<br>        });<br>        css += `<br>            .m-header, .m-header__container, .m-header__container--mobile,<br>            .m-nav, .m-nav__container {<br>                background-color: ${bgCard} !important;<br>                border-bottom: 1px solid ${borderColor} !important;<br>            }<br>            .m-header__logo, .m-header__logo--mini {<br>                filter: brightness(0) invert(1) !important;<br>            }<br>            .m-input-search__placeholder {<br>                background-color: ${bgInput} !important;<br>                color: ${textSecondary} !important;<br>                border-color: ${borderColor} !important;<br>            }<br>            .m-nav__link { color: ${textSecondary} !important; }<br>            .m-nav__link--active { color: ${accentColor} !important; }<br>            .m-nav__link:hover { color: ${textPrimary} !important; }<br>            .m-post-card, .m-recommend__list__item {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 12px !important;<br>            }<br>            .m-post-card:hover {<br>                background-color: ${bgHover} !important;<br>            }<br>            .m-post-card__title-text { color: ${textPrimary} !important; }<br>            .m-post-card__title-text:hover { color: ${accentColor} !important; }<br>            .m-post-card__info-text .text { color: ${textSecondary} !important; }<br>            .m-post-card__info-text .icon { color: ${textMuted} !important; }<br>            .m-post-card__duration {<br>                background: rgba(0,0,0,0.7) !important;<br>                color: #fff !important;<br>            }<br>            .m-home-page__posts h2, .m-home-page__hots h2 {<br>                color: ${textPrimary} !important;<br>                border-bottom: 2px solid ${borderColor} !important;<br>            }<br>            .m-home-page__hots {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 12px !important;<br>            }<br>            .m-home-page__hots .m-post-card {<br>                background: transparent !important;<br>                border: none !important;<br>            }<br>            .m-home-page__hots .m-post-card:hover {<br>                background: ${bgHover} !important;<br>            }<br>            .m-home-swiper .title {<br>                background: rgba(0,0,0,0.6) !important;<br>                color: #fff !important;<br>            }<br>            .m-side-menu {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 12px !important;<br>            }<br>            .m-menu__list {<br>                background-color: transparent !important;<br>                padding: 8px 0 !important;<br>                margin: 0 !important;<br>                list-style: none !important;<br>            }<br>            .m-menu__list li {<br>                background-color: transparent !important;<br>                margin: 2px 6px !important;<br>                border-radius: 8px !important;<br>                list-style: none !important;<br>            }<br>            .m-menu__list .item {<br>                display: flex !important;<br>                align-items: center !important;<br>                gap: 10px !important;<br>                padding: 10px 14px !important;<br>                color: ${textSecondary} !important;<br>                border-radius: 8px !important;<br>                text-decoration: none !important;<br>                transition: all 0.2s ease !important;<br>                font-size: 14px !important;<br>            }<br>            .m-menu__list .item .icon {<br>                color: ${textMuted} !important;<br>                font-size: 18px !important;<br>                flex-shrink: 0 !important;<br>                transition: color 0.2s !important;<br>            }<br>            .m-menu__list .item:hover {<br>                background-color: ${bgHover} !important;<br>                color: ${textPrimary} !important;<br>            }<br>            .m-menu__list .item:hover .icon {<br>                color: ${textPrimary} !important;<br>            }<br>            .m-menu__list .item.active {<br>                background-color: ${bgHover} !important;<br>                color: ${accentColor} !important;<br>            }<br>            .m-menu__list .item.active .icon {<br>                color: ${accentColor} !important;<br>            }<br>            .m-menu__list .item.danger {<br>                color: ${dangerColor} !important;<br>            }<br>            .m-menu__list .item.danger .icon {<br>                color: ${dangerColor} !important;<br>            }<br>            .m-menu__list .item.danger:hover {<br>                background-color: rgba(232, 128, 128, 0.12) !important;<br>                color: #ff6b6b !important;<br>            }<br>            .m-menu__list .item.danger:hover .icon {<br>                color: #ff6b6b !important;<br>            }<br>            .m-menu__list .item.disabled {<br>                color: #555 !important;<br>                cursor: not-allowed !important;<br>                opacity: 0.5 !important;<br>            }<br>            .m-menu__list .item.disabled .icon {<br>                color: #555 !important;<br>            }<br>            .m-menu__list .item.disabled:hover {<br>                background-color: transparent !important;<br>                color: #555 !important;<br>            }<br>            .m-menu__list .item.disabled:hover .icon {<br>                color: #555 !important;<br>            }<br>            .m-menu__list .n-badge {<br>                background-color: transparent !important;<br>            }<br>            .m-menu__list .n-badge .n-badge-sup {<br>                background-color: ${accentColor} !important;<br>                color: #fff !important;<br>            }<br>            .m-side-menu .n-scrollbar-rail__scrollbar {<br>                background-color: #444 !important;<br>            }<br>            .m-side-menu .n-scrollbar-rail__scrollbar:hover {<br>                background-color: #666 !important;<br>            }<br>            .m-account-center__header {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 16px !important;<br>                overflow: hidden !important;<br>            }<br>            .m-account-center__header-banner {<br>                background-color: #161616 !important;<br>            }<br>            .m-account-center__header-banner_mask {<br>                background: linear-gradient(to bottom, transparent 50%, rgba(0,0,0,0.7) 100%) !important;<br>            }<br>            .m-account-center__header-banner_user-info .username {<br>                color: ${textPrimary} !important;<br>            }<br>            .m-account-center__header-banner_user-info-detail > div:last-child {<br>                color: ${textSecondary} !important;<br>            }<br>            .m-account-center__header-user {<br>                background-color: ${bgCard} !important;<br>                padding: 16px 20px !important;<br>            }<br>            .m-account-center__header-user-bio { color: #ccc !important; }<br>            .m-account-center__header-user hr {<br>                border-color: ${borderColor} !important;<br>                opacity: 1 !important;<br>            }<br>            .m-account-center__header-user-count { background-color: transparent !important; }<br>            .m-account-center__header-user-count > div { background-color: transparent !important; }<br>            .m-account-center__header-user-count .number { color: ${textPrimary} !important; }<br>            .m-account-center__header-user-count .label { color: ${textSecondary} !important; }<br>            .m-follow-button {<br>                background-color: ${accentColor} !important;<br>                color: #fff !important;<br>                border: none !important;<br>                border-radius: 20px !important;<br>            }<br>            .m-follow-button:hover { background-color: #6bb3ff !important; }<br>            .m-container.m-feed-list {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 16px !important;<br>            }<br>            .m-load span { color: ${textSecondary} !important; }<br>            .m-account-center__side { background-color: transparent !important; }<br>            .m-account-center__side-section {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 12px !important;<br>                padding: 16px !important;<br>            }<br>            .m-account-center__side-section h4 {<br>                color: ${textPrimary} !important;<br>                border-bottom: 1px solid ${borderColor} !important;<br>                padding-bottom: 8px !important;<br>            }<br>            .m-account-center__side-section h4 .icon { color: ${accentColor} !important; }<br>            .m-account-center__side-section .content a { color: ${textSecondary} !important; }<br>            .m-account-center__side-section .content a:hover { color: ${accentColor} !important; }<br>            .m-user-card {<br>                background-color: ${bgCard} !important;<br>                border: 1px solid ${borderColor} !important;<br>                border-radius: 16px !important;<br>                color: ${textPrimary} !important;<br>            }<br>            .m-user-card hr { border-color: ${borderColor} !important; opacity: 1 !important; }<br>            .m-user-card__premium .m-text:last-child {<br>                color: ${accentColor} !important;<br>                font-weight: 500 !important;<br>            }<br>            .m-user-card__fans .m-text.secondary { color: ${textSecondary} !important; }<br>            .m-user-card__action:hover { background-color: ${bgHover} !important; }<br>            .m-user-card .n-progress-graph-line-rail { background-color: ${bgInput} !important; }<br>            .m-user-card .n-progress-graph-line-fill { background-color: #a78bfa !important; }<br>            .m-footer, .m-footer__container {<br>                background-color: ${bgDark} !important;<br>                border-top: 1px solid ${borderColor} !important;<br>            }<br>            .m-footer__section .content a { color: ${textSecondary} !important; }<br>            .m-footer__section .content a:hover { color: ${accentColor} !important; }<br>            .m-footer__copyright { color: ${textMuted} !important; }<br>            .m-footer__copyright a { color: ${textSecondary} !important; }<br>            p, h1, h2, h3, h4, span, div, a, li, label {<br>                color: ${textPrimary} !important;<br>            }<br>            a:hover { color: ${accentColor} !important; }<br>            input, textarea, select {<br>                background-color: ${bgInput} !important;<br>                color: ${textPrimary} !important;<br>                border-color: ${borderColor} !important;<br>            }<br>            ::-webkit-scrollbar { width: 8px; height: 8px; }<br>            ::-webkit-scrollbar-track { background: ${bgCard}; }<br>            ::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }<br>            ::-webkit-scrollbar-thumb:hover { background: #555; }<br>            video, .video-player, [class*="player"] {<br>                background-color: #000 !important;<br>            }<br>            img, svg, canvas, .m-img {<br>                background-color: transparent !important;<br>            }<br>            .n-skeleton {<br>                --n-color-start: ${bgHover} !important;<br>                --n-color-end: ${bgInput} !important;<br>            }<br>            .m-badge .m-badge__item {<br>                background-color: transparent !important;<br>            }<br>            .m-account-center__header-banner_user .m-avatar {<br>                background-color: ${bgInput} !important;<br>                border: 3px solid #333 !important;<br>            }<br>            .m-side-qualification .m-text.secondary { color: ${textSecondary} !important; }<br>            .m-side-qualification .m-text.tertiary { color: ${textMuted} !important; }<br>            .m-side-qualification hr { border-color: ${borderColor} !important; opacity: 1 !important; }<br>            .m-side-qualification a { color: ${accentColor} !important; }<br>            .m-side-qualification .lic-img { filter: brightness(0.8) !important; }<br>        `;<br>        return css;<br>    }<br>    function applyDarkMode() {<br>        const oldStyle = document.getElementById('mfuns-dark-mode-auto');<br>        if (oldStyle) {<br>            oldStyle.remove();<br>        }<br>        const elements = getPageElements();<br>        console.log(`find ${elements.length} elements`);<br>        const css = generateStyles(elements);<br>        const style = document.createElement('style');<br>        style.id = 'mfuns-dark-mode-auto';<br>        style.textContent = css;<br>        document.head.appendChild(style);<br>        document.documentElement.classList.add('mfuns-dark-active');<br>        console.log('darkmode=1');<br>    }<br>    function debounce(func, wait) {<br>        let timeout;<br>        return function(...args) {<br>            clearTimeout(timeout);<br>            timeout = setTimeout(() => func.apply(this, args), wait);<br>        };<br>    }<br>    function init() {<br>        if (document.readyState === 'complete') {<br>            applyDarkMode();<br>        } else {<br>            document.addEventListener('DOMContentLoaded', applyDarkMode);<br>            window.addEventListener('load', applyDarkMode);<br>        }<br>        const debouncedApply = debounce(applyDarkMode, 300);<br>        document.addEventListener('click', function(e) {<br>            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {<br>                return;<br>            }<br>            if (e.target.tagName === 'VIDEO') {<br>                return;<br>            }<br>            debouncedApply();<br>        }, true);<br>        const observer = new MutationObserver(debounce(applyDarkMode, 500));<br>        observer.observe(document.body, {<br>            childList: true,<br>            subtree: true,<br>            attributes: true,<br>            attributeFilter: ['class', 'style'],<br>        });<br>        console.log('done');<br>    }<br>    if (document.readyState === 'loading') {<br>        document.addEventListener('DOMContentLoaded', init);<br>    } else {<br>        init();<br>    }<br>})();

当然肯定是不会那么简单的,因为站点原来是紫色色调,直接反转会有点不协调,所以我参照一个bilibili的美化插件改成了蓝色并且给很多需要突出的元素比如侧边栏,视频这些都做了一个圆角边框的处理,这样显着也挺好看的我感觉。

但是这还是有许多的bug毕竟有的css原来就是实时刷新的比如说投稿的时候那个自动保存,这就会带动其他元素一起刷新导致黑色失效,目前我也不怎么喜欢投稿所以这个问题我现在也懒得修,只能说等有时间了去针对这些特别的页面去专门做适配。

喵御宅Mfuns深色模式油猴脚本

Mfuns深色模式js脚本

0*001 前言

因为我每次打开网页端都会直接给我来一发闪光弹,找半天都没有发现深色模式开关

所以早上一起来就糊了一个js脚本来实现深色模式

注意:这个脚本由于我没有针对每个css进行处理导致在某些地方会出现一些问题。

很多地方都要等一秒

已知:用户主页点击是侧边栏会抽搐(很小范围 不影响使用) 创作中心在内容自动保存时左侧边栏会抽搐 分区,MKD和富文本按钮会闪烁

这篇文章纯当所有人都是文盲不然我实在没功夫写那么多

0*002 效果图

略

0*004使用教程

0*004.1 下载油猴Tampermonkey

以Edge为例,其他浏览器也大差不差。

点击上方的插件按钮

点击”获取Microsoft Edge扩展”按钮

搜索”篡改猴”并点击安装按钮

随后再次点击第一步的插件按钮,并点击篡改猴图标。

0*004.2 安装脚本

访问以下任意链接进行下载

123:Click(提取码0721)

自建:Click

点击上一步图片中的”添加新脚本”按钮

将下载好的脚本拖入并点击安装按钮即可

https://moongs.asia/

code by @Sakurazuki

admin@moongs.asia

EOF!