Dns被莫名篡改的逆向分析定位(笔记)

引言:最近发现用户的多台机器上出现了Dns被莫名修改的问题,从系统事件上看并未能正常确定到是那个具体软件所为,现在的需求就是确定和定位哪个软件具体所为。

解决思路:
  1. 首先到IPv4设置页面对Dns进行设置:
  2. 通过ProcExp确定了该窗口的宿主进程是Explorer.exe,通过ProcMon对Explorer进行监控,并未发现Explorer将静态Dns的地址写入注册表(后来发现其实Explorer是通过DllHost.exe来实现对注册表修改的,所以没监控到)。
  3. 通过对Explorer进行逆向分析发现Explorer实现比较复杂,后来通过网络发现修改Dns可以通过Netsh.exe这个程序来实现:
  4. 于是转到对Netsh.exe的逆向分析上来,经过仔细分析,发现Netsh.exe对dns的修改是通过netiohlp.dll的NhIpHandleSetDnsServer来实现的:

     
  5. 通过进一步定位发现是NhIpAddDeleteSetServer:
  6. 并发现会通过写入注册表来保存相关信息:

    并通过定位发现注册表地址是:
  7. 并且有重启Dnscahe服务等相关操作:
  8. 1)通过设置系统全局钩子来挂钩系统下所有进程然后挂钩SetRegvalue等api监控,该进程通过SetWindowHookEx来设置全局钩子(其实该挂钩方式不能挂钩没有消息循环的经常),通过inject-helper.exe进程来挂钩发现不能挂钩系统下的所有进程,而且新创建的进程也无法挂钩。
    2)通过设置KnowDlls注册表发现也无法正常挂钩所有进程。
    3)通过底层驱动挂钩,这个方法能监控到应用层的所有进程对注册表的操作,但为了回溯到目标进程,可能也需要加入对父子进程的回溯,这个相对麻烦一些。
  9. 笔者采用相对比较简单容易操作的方法。采用ProcMon来对注册表的监控:

    1)设置第一项筛选:operation is RegSetValue 操作

    2)设置第二项筛选:path is HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{33701f65-c437-47a4-9162-071bd72b3425}\NameServer (复制这个字符串修改UUID即可)

    最后的效果如下图:

    设置好后可以看到监控效果:

  10. 但是我们需要追踪最初始的设置Dns的进程,比如进程A调用了Netsh或DllHost等其他的三方进程来设置Dns,这个时候仅仅监控到Netsh或者DllHost等进程是没用的,需要对进程进行父进程的回溯,才知道源操作进程。
  11. 这个时候需要写一个ProcMon的插件,然后在ProcMon在监控到操作进程后能第一时间对父进程进行回溯。

    编写一个Dll插件,并通过窗口子类化方式来对ProcMon的ListView控件进行消息监控:
     
    static DWORD WINAPI DoWork(LPVOID param)
     {
         swprintf_s(g_szProfilePath, L"%s\\record_%d.log", GetCurrentExePath().c_str(), GetCurrentProcessId());
        
         do {
             HWND hwndTaskManager = FindWindowW(L"PROCMON_WINDOW_CLASS", L"Process Monitor - Sysinternals: www.sysinternals.com");
             if (!hwndTaskManager)
             {
                 MessageBox(NULL, L"查找进程窗口失败", L"错误", NULL);
                 break;
             }
             DWORD TaskManagerPID = 0;
             GetWindowThreadProcessId(hwndTaskManager, &TaskManagerPID);
             if (TaskManagerPID != GetCurrentProcessId())
             {
                 MessageBox(NULL, L"TaskManagerPID != GetCurrentProcessId()", L"错误", NULL);
                 break;
             }
             //EnumChildWindows(hwndTaskManager, _EnumChildProc, NULL);
             g_hListView = FindWindowExW(hwndTaskManager, NULL, L"SysListView32", L"");
             if (!g_hListView)
             {
                 MessageBox(NULL, L"获取listview句柄为空", L"错误", NULL);
                 break;
             }
    
             fnOriginNetworkList = (WNDPROC)SetWindowLongPtrW(
                 g_hListView,
                 GWLP_WNDPROC,
                 (LONG_PTR)NetworkListWndProc);
         } while (0);
         return 0;
     }
    // 这里是ListView控件的消息处理函数 
    LRESULT CALLBACK NetworkListWndProc(
         HWND hwnd,      // handle to window
         UINT_PTR uMsg,      // message identifier
         WPARAM wParam,  // first message parameter
         LPARAM lParam   // second message parameter
     )
     {
         if (uMsg == LVM_SETITEMTEXT || uMsg == LVM_SETITEMTEXTA || uMsg == 4211)
         { 
             wchar_t szSection[50] = { 0 };
             wsprintf(szSection, L"%d_%d", g_iPrevItem, pItem->iItem);
             wchar_t szSubItem[50] = { 0 };
             wsprintf(szSubItem, L"%d", pItem->iSubItem);
             WritePrivateProfileStringW(szSection, szSubItem, pItem->pszText, g_szProfilePath);
    
             if (pItem->iSubItem == 3) // process ID
             {
                 time_t tm = time(NULL);
                 struct tm now;
                 localtime_s(&now, &tm);
                 wchar_t str[100] = { 0 };
                 wcsftime(str, sizeof(str) / 2, L"%A %c", &now);
                 WritePrivateProfileStringW(szSection, L"time", str, g_szProfilePath);
    
                 DWORD pid = _wtoi(pItem->pszText);
                 stuProcNode node = { _wtoi(pItem->pszText), szSection };
                 PushProcNodeQueue(node);  // 这里不卡顿消息线程,把父进程回溯抛到另一个独立线程处理
             }
         }
    
         LRESULT rc = CallWindowProc(
             fnOriginNetworkList,
             hwnd,
             uMsg,
             wParam,
             lParam);
    
         return rc;
     }
    // 这里是父进程回溯操作
    static DWORD WINAPI  DoQueryProcess(LPVOID param)
     {
         while (1)
         {
             WaitForSingleObject(g_hQueryProcEvt, INFINITE); // 新的请求进入队列会触发事件
    
             std::vector<stuProcNode> ProcNodeList;
             GetProcNodeQueue(ProcNodeList);
    
             for (int i = 0; i < ProcNodeList.size(); i++)
             {
                 std::wstring strpPidNameList;
                 DWORD pid = ProcNodeList[i].dwPid;
                 while (1)
                 {  
                     // 回溯一下父进程
                     HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
                     if (hProcess == (HANDLE)-1 || hProcess == 0)
                     {
                         break;
                     }
    
                     WCHAR szPath[MAX_PATH] = { 0 };
                     GetProcessImageFileName(hProcess, szPath, MAX_PATH);
    
                     strpPidNameList += L"  [";
                     wchar_t buffer[20] = { 0 };
                     _itow_s(pid, buffer, 20, 10);
                     strpPidNameList += buffer;
                     strpPidNameList += L"  ";
                     strpPidNameList += wcsrchr(szPath, L'\\') ? wcsrchr(szPath, L'\\') + 1 : L"NULL";
                     strpPidNameList += L"]  ";
    
                     PROCESS_BASIC_INFORMATION pbi = { 0 };
                     if (0 == NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL))
                     {
                         pid = pbi.InheritedFromUniqueProcessId;
                     }
                     else
                     {
                         break;
                     }
                     
                     CloseHandle(hProcess);
                 }
                 WritePrivateProfileStringW(ProcNodeList[i].strSection.c_str(), L"PidNameINFO", strpPidNameList.c_str(), g_szProfilePath);
             }
         }
         return 0;
     }
  12. 代码写好了,这个时候通过CFF软件修改ProcMon的导入表,使其依赖我们的插件,这个时候当ProcMon启动的时候就会自动加载我们的插件了,相当于变相注入到了ProcMon进程。
    看看效果:

  13. 这样的监控程序就算写好了,可以交付运维去部署监控了,一旦监控到就会输出到日志文件,并把父子进程进行了回溯。
  14. 附录(全局钩子注入代码):
    BOOL create_inject_process(LPCWSTR inject_helper, LPCWSTR hook_dll, DWORD id, bool bRemoteThread)
    {
    	BOOL success = FALSE;
    	WCHAR sz_command_line[4096] = { 0 };
    	PROCESS_INFORMATION pi = { 0 };
    	STARTUPINFO si = { 0 };
    	si.cb = sizeof(si);
    
    	swprintf_s(sz_command_line, L"\"%s\" \"%s\" %s %lu",
    		inject_helper,
    		hook_dll,
    		bRemoteThread ? L"0" : L"1",
    		id);
    
    	success = CreateProcessW(inject_helper, sz_command_line, NULL, NULL,
    		false, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    	if (success) {
    		CloseHandle(pi.hThread);
    		WaitForSingleObject(pi.hProcess, 300);
    		CloseHandle(pi.hProcess);
    	}
    	else {
    		GetLastError();
    	}
    
    	return success;
    }
    
    BOOL remote_thread_inject(LPCWSTR inject_helper, LPCWSTR hook_dll, DWORD process_pid)
    {
    	return create_inject_process(inject_helper, hook_dll, process_pid, true);
    }
    
    BOOL UI_Message_inject(LPCWSTR inject_helper, LPCWSTR hook_dll, DWORD process_tid)
    {
    	return create_inject_process(inject_helper, hook_dll, process_tid, false);
    }
    
    
    调用:
    #define SHELLPLUGINNAME64   L"PatchPlg64.dll"
    #define SHELLPLUGINNAME32   L"PatchPlg32.dll"
    #define INJECTHELPER64      L"inject-helper64.exe"
    #define INJECTHELPER32      L"inject-helper32.exe"
    void CHookMonitorDlg::OnBnClickedOk()
    {
    	HWND hwnd = (HWND)0x6060241A; //FindWindowExW(NULL, NULL, L"TaskManagerWindow", L"");
    	DWORD dwPid = 0;
    	DWORD dwTid = GetWindowThreadProcessId(hwnd, &dwPid);
    	BOOL b64BitProcess = IsProcessBit(dwPid);
    
    	std::wstring strCurpath = GetCurrentExePath();
    	std::wstring strInjectHelper = strCurpath + L"\\" + (b64BitProcess ? INJECTHELPER64 : INJECTHELPER32);
    	std::wstring strInjectPlugin = strCurpath + L"\\" + (b64BitProcess ? SHELLPLUGINNAME64 : SHELLPLUGINNAME32);
    	//remote_thread_inject(strInjectHelper.c_str(), strInjectPlugin.c_str(), dwPid);
    	UI_Message_inject(strInjectHelper.c_str(), strInjectPlugin.c_str(), /*dwTid*/0);
    }
    

    插件代码:

    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
    
            /* this prevents the library from being automatically unloaded
             * by the next FreeLibrary call */
            GetModuleFileNameW(hModule, name, MAX_PATH);
            LoadLibraryW(name);
    
            dll_inst =(HINSTANCE)hModule;
            init_dummy_window_thread();
            Hook();
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
    
    #define DEF_FLAGS (WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)
    
    HWND dummy_window;
    static DWORD WINAPI dummy_window_thread(LPVOID* unused)
    {
        static const wchar_t dummy_window_class[] = L"temp_d3d_window_4033485";
        WNDCLASSW wc;
        MSG msg;
    
        OutputDebugStringA("lzlong dummy_window_thread");
    
        memset(&wc, 0, sizeof(wc));
        wc.style = CS_OWNDC;
        wc.hInstance = dll_inst;
        wc.lpfnWndProc = (WNDPROC)DefWindowProc;
        wc.lpszClassName = dummy_window_class;
    
        if (!RegisterClass(&wc)) {
            //hlog("Failed to create temp D3D window class: %lu",
            //    GetLastError());
            return 0;
        }
    
        dummy_window = CreateWindowExW(0, dummy_window_class, L"Temp Window",
            DEF_FLAGS, 0, 0, 1, 1, NULL, NULL,
            dll_inst, NULL);
        if (!dummy_window) {
            //hlog("Failed to create temp D3D window: %lu", GetLastError());
            return 0;
        }
    
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        (void)unused;
        return 0;
    }
    
    static inline void init_dummy_window_thread(void)
    {
        HANDLE thread =
            CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dummy_window_thread, NULL, 0, NULL);
        if (!thread) {
            //hlog("Failed to create temp D3D window thread: %lu",
            //    GetLastError());
            return;
        }
    
        CloseHandle(thread);
    }
    
    extern "C" {
        __declspec(dllexport) LRESULT CALLBACK
            dummy_debug_proc(int code, WPARAM wparam, LPARAM lparam)
        {
            static bool hooking = true;
            MSG* msg = (MSG*)lparam;
    
            if (hooking && msg->message == (WM_USER + 432)) {
                HMODULE user32 = GetModuleHandleW(L"USER32");
                BOOL(WINAPI * unhook_windows_hook_ex)(HHOOK) = NULL;
    
                unhook_windows_hook_ex = (BOOL(WINAPI*)(HHOOK))GetProcAddress(GetModuleHandleW(L"user32"), "UnhookWindowsHookEx");
    
                if (unhook_windows_hook_ex)
                    unhook_windows_hook_ex((HHOOK)msg->lParam);
                hooking = false;
            }
    
            return CallNextHookEx(0, code, wparam, lparam);
        }
    }

    部分inject-helper的代码:

    
    typedef HHOOK (WINAPI *set_windows_hook_ex_t)(int, HOOKPROC, HINSTANCE, DWORD);
    
    #define RETRY_INTERVAL_MS      500
    #define TOTAL_RETRY_TIME_MS    4000
    #define RETRY_COUNT            (TOTAL_RETRY_TIME_MS / RETRY_INTERVAL_MS)
    
    int inject_library_safe_obf(DWORD thread_id, const wchar_t *dll,
    		const char *set_windows_hook_ex_obf)
    {
    	HMODULE user32 = GetModuleHandleW(L"USER32");
    	set_windows_hook_ex_t set_windows_hook_ex;
    	HMODULE lib = LoadLibraryW(dll);
    	LPVOID proc;
    	HHOOK hook;
    	size_t i;
    
    	if (!lib || !user32) {
    		return INJECT_ERROR_UNLIKELY_FAIL;
    	}
    
    #ifdef _WIN64
    	proc = GetProcAddress(lib, "dummy_debug_proc");
    #else
    	proc = GetProcAddress(lib, "_dummy_debug_proc@12");
    #endif
    
    	if (!proc) {
    		return INJECT_ERROR_UNLIKELY_FAIL;
    	}
    
    	set_windows_hook_ex = (void*)GetProcAddress(user32, set_windows_hook_ex_obf);
    
    	hook = set_windows_hook_ex(WH_GETMESSAGE, proc, lib, thread_id);
    	if (!hook) {
    		return GetLastError();
    	}
    
    	/* SetWindowsHookEx does not inject the library in to the target
    	 * process unless the event associated with it has occurred, so
    	 * repeatedly send the hook message to start the hook at small
    	 * intervals to signal to SetWindowsHookEx to process the message and
    	 * therefore inject the library in to the target process.  Repeating
    	 * this is mostly just a precaution. */
    
    	for (i = 0; i < RETRY_COUNT; i++) {
    		Sleep(RETRY_INTERVAL_MS);
    		PostThreadMessage(thread_id, WM_USER + 432, 0, (LPARAM)hook);
    	}
    	return 0;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/773400.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Axure高保真原型】中继器表格——移入显示详情卡片案例

今天和大家分享中继器表格——移入显示详情卡片的原型模板&#xff0c;鼠标移入员工号或姓名会弹出员工卡片&#xff0c;可以查看更详细的信息。这个表格是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;只需要维护中继器表格里的信息&#xff0c;即可自动生成交互效…

实操Nginx+Tomcat多实例部署,实现负载均衡和动静分离

192.168.10.10 192.168.10.20 192.168.10.30 location ~ \.jsp$ {proxy_pass http://192.168.10.50:8080;} location ~ \.(jsp|html)$ {root /usr/share/nginx/html;}192.168.10.40和192.168.10.50用脚本完成搭建此处安装附上脚本&#xff1a; #!/bin/bash# 定义变量 JDK_PACKA…

生态系统NPP及碳源、碳汇模拟技术教程

原文链接&#xff1a;生态系统NPP及碳源、碳汇模拟技术教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608293&idx3&sn2604c5c4e061b4f15bb8aa81cf6dadd1&chksmfa826602cdf5ef145c4d170bed2e803cd71266626d6a6818c167e8af0da93557c1288da21a71&a…

nginx 搭理禅道

1.安装nginx。 2.安装禅道。 3.nginx 配置文件 location /zentao/ { proxy_pass http://192.168.100.66/zentao/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-F…

边界无限陈佩文:红蓝对抗安全演练常态化的各方分析

虽然常态化演练尚未正式开始&#xff0c;但我们仍然希望对各方的表现进行一些分析和预测&#xff0c;以辅助我们对市场的判断和决策。同时&#xff0c;也希望通过这些初步的见解&#xff0c;抛砖引玉&#xff0c;引发更多有价值的讨论和观点。 “船停在码头是最安全的&#xf…

【数据库】E-R图、E-R模型到关系模式的转换、关系代数表达式、范式

一、E-R图 1、基本概念 2、实体集之间的联系 3、E-R图要点 &#xff08;1&#xff09;实体&#xff08;型&#xff09;的表示 &#xff08;2&#xff09;E-R图属性的表示 &#xff08;3&#xff09;联系的表示 4、E-R模型的例题 二、E-R模型到关系模式的转换 1、实体型的转换…

pytorch-时间序列

目录 1. 时间序列2. word embedding2.1 one hot2.2 word2vec2.3 GloVe 1. 时间序列 具有时间相关性的序列叫做时间序列&#xff0c;比如&#xff1a;语音、文本句子 2. word embedding 2.1 one hot 针对句子来说&#xff0c;可以用[seq_len, vector_len] 有多少个单词vecto…

因为文件共享不安全,所以你不能连接到文件共享。此共享需要过时的SMB1协议,而此协议是不安全的 解决方法

目录 1. 问题所示2. 解决方法3. 解决方法1. 问题所示 输入共享文件地址的时候,出现如下信息: 因为文件共享不安全,所以你不能连接到文件共享。此共享需要过时的SMB1协议,而此协议是不安全的,可能会是你的系统遭受攻击。你的系统需要SMB2或更高版本截图如下所示: 2. 解决…

竞赛 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

【Linux】Linux常用指令合集精讲,一篇让你彻底掌握(万字真言)

文章目录 一、文件与目录操作1.1 ls - 列出目录内容1.2 cd - 切换目录1.3 pwd - 显示当前目录1.4 mkdir - 创建目录1.5 rmdir - 删除空目录1.6 rm - 删除文件或目录1.7 cp - 复制文件或目录1.8 mv - 移动或重命名文件或目录1.9 touch - 创建空文件或更新文件时间戳 二、文件内容…

朗新天霁eHR GetFunc_code.asmx SQL注入致RCE漏洞复现

0x01 产品简介 朗新天霁人力资源管理系统(LongShine eHR)是一款由北京朗新天霁软件技术有限公司研发的人力资源管理系统,该产品融合了国外先进的人力资源管理理念和国内大量人力资源管理实践经验,是国内功能较为全面、性价比较高的人力资源管理系统之一,系统凭借其集成化…

@amap/amap-jsapi-loader实现高德地图嵌入React项目中,并且做到点击地图任意一处,获得它的经纬度

1.第一步要加入项目package.json中或者直接yarn install它都可以 想必大家应该都会 "amap/amap-jsapi-loader": "0.0.7"2.加入项目中 关于接口获取key的接口 大家改成自己对应的项目请求方法 import React, { PureComponent } from react; import { Input…

厂家技术 最新钨蚀刻液的制作方法

网盘 https://pan.baidu.com/s/15ZAiUicstYEiFPvNKK72VA?pwd6u4x VCSEL芯片金薄膜蚀刻液及其蚀刻方法.pdf 废铝蚀刻液回收磷酸的方法.pdf 抑制二氧化硅蚀刻的无C蚀刻液.pdf 氮化硅和钨的选择性蚀刻液.pdf 用于在穿孔中选择性沉积钨的系统和方法.pdf 蚀刻液和使用了所述蚀刻液…

香橙派AIpro开发板评测:部署yolov5模型实现图像和视频中物体的识别

OrangePi AIpro 作为业界首款基于昇腾深度研发的AI开发板&#xff0c;自发布以来就引起了我的极大关注。其配备的8/20TOPS澎湃算力&#xff0c;堪称目前开发板市场中的顶尖性能&#xff0c;实在令人垂涎三尺。如此强大的板子&#xff0c;当然要亲自体验一番。今天非常荣幸地拿到…

【C++】BMP图片结构深度解析及其在C++中的操作与应用

引言 BMP&#xff08;Bitmap Image File&#xff09;是一种与设备无关的图像文件格式&#xff0c;它采用了一种非常直接的方式来存储图像数据&#xff0c;即按照图像的行和列顺序&#xff0c;逐像素地存储颜色值。由于其简单性和可移植性&#xff0c;BMP文件在图像处理、图像分…

看看这组B端规范,你就会感叹:钱真是万能的。

B端设计规范的作用和价值主要体现在以下几个方面&#xff1a; 统一视觉风格和用户体验&#xff1a;B端设计规范可以规定统一的视觉风格和用户界面&#xff0c;使得不同的产品和服务在外观和交互上保持一致&#xff0c;提升用户的使用体验和满意度。 提高产品开发效率&#xf…

Android Studio下载Gradle特别慢,甚至超时,失败。。。解决方法

使用Android studio下载或更新gradle时超级慢怎么办&#xff1f; 切换服务器&#xff0c;立马解决。打开gradle配置文件 修改服务器路径 distributionUrlhttps\://mirrors.cloud.tencent.com/gradle/gradle-7.3.3-bin.zip 最后&#xff0c;同步&#xff0c;下载&#xff0c;速…

【RAG检索增强生成】MaxKB:构建企业级知识库问答系统(Ollama+Qwen2)

目录 引言1、MaxKB概述1.1 定义与目标1.2 特点与优势 2、MaxKB原理3、MaxKB架构4、基于MaxKBOllamaQwen2搭建本地知识库4.1 环境准备4.2 部署MaxKB4.3 部署Ollama4.4 部署运行qwen24.5 知识库配置4.5.1登录 MaxKB 系统4.5.2上传文档4.5.3设置分段规则 4.6 模型配置4.7 创建应用…

一入“网贷”深似海:来自多名负债人的真实自述!

在温州&#xff0c;有个名叫小琴的25岁女孩&#xff0c;她的故事&#xff0c;是许多年轻人深陷网贷泥潭的一个缩影。小琴&#xff0c;一个普通的大学毕业生&#xff0c;两年的职场生涯并未能让她摆脱大学时期留下的网贷阴影。那时&#xff0c;她每月靠着1000元的生活费勉强维持…

注意!Vue.js 或 Nuxt.js 中请停止使用.value

大家好&#xff0c;我是CodeQi&#xff01; 一位热衷于技术分享的码仔。 当您在代码中使用.value时&#xff0c;必须每次都检查变量是否存在并且是引用。 这可能很麻烦&#xff0c;因为在运行时使用.value可能会导致错误。然而&#xff0c;有一个简单的解决方法&#xff0c;即…