标题: [系统相关] 【已解】如何在命令行中使指定文件处于独占状态 [打印本页]
作者: qzwqzw 时间: 2014-4-9 12:03 标题: 【已解】如何在命令行中使指定文件处于独占状态
我的需求是
在命令行中用任意命令打开指定文件
在该命令执行期间
该文件无法被其它程序复制、改名、删除以及覆盖
类似C:\WINDOWS\system32\config\sam文件的状态
如果上述需求是实现有困难
可以考虑用命令行程序独占该文件
不允许任意程序复制、改名、删除以及覆盖
除非接触独占状态
————————————————————————————
结语
关于这个需求
CrLf巡查付出了不懈的努力
我从中收获良多
虽然最终没有采用这些方案
但是这可求索的过程
让我不虚此问
你若看到此贴
如果也与我有一样的想法
那么请点赞 ^_^
作者: CrLf 时间: 2014-4-9 13:54
本帖最后由 CrLf 于 2014-4-9 14:32 编辑
参考旧作最后一条:
http://bbs.bathome.net/redirect. ... 7&fromuid=30406
延伸:复制代码
- @echo off 3>&1l 4>>"a.txt"
复制代码
- start /b ping /t 127.0.0.1 >nul 2>&1 9>>"a.txt"
复制代码
- taskkill /f /im explorer* & explorer >>"a.txt"
复制代码
- @set @n=0;while(1){WScript.Sleep(999999)} /*
-
- @echo off
- wscript -e:javascript %0 >>"a.txt"
- echo 啊哈
- pause */
复制代码
作者: qzwqzw 时间: 2014-4-9 22:47
Batcher版主可以预知未来
把未来的奖励都预支给我了
CrLf的代码都很有意思
每一段都值得仔细品读- (for /r %%a in (*.*) do del /f /s "%%~nxa" 3>>"%%a") 2>nul 4>>%0
复制代码
这段代码中的4>>%0是什么意思还没有揣摩出来- @echo off 3>&1l 4>>"a.txt"
复制代码
这段里面3>&1后面的字母l是何意啊?
这些代码都很好
这些部分的解决我的问题
只是还有两点遗憾啊
一是没有办法防复制
看来对SAM文件的处理
还是与普通的文件独占与不同之处
怀疑是相关驱动程序有特别的过滤逻辑
二是需要解除占用时都不是很轻便
另外测试时发现了一个不太明白的问题
type test.txt > test.txt
会将test.txt原本的内容清空
type test.txt >> test.txt
却会将test.txt原本的内容加倍
如果说>>test.txt会先将文件独占
那么type test.txt就应该无法读取文件内容
那应该保持test.txt内容原样才对
作者: CrLf 时间: 2014-4-10 00:53
1、呃,没细看顶楼描述,我好像又跑题了...
for /f 会受影响,但 type 和 find 等命令都能使用,不晓得为什么:- (for /f %%a in (a.txt) do xxx)>>a.txt
复制代码
2、4>>%0 是为了保证脚本在该目录下运行时不会被删。
3、那个 l 确系笔误。
4、百度到一个 function OpenFile(const lpFileName: LPCSTR; var lpReOpenBuff: TOFStruct; uStyle: UINT): HFILE; stdcall;
UStyle 有这两个常量,不知道是不是楼主说的那种:- Of_Share_Deny_Read
- 禁止其它程序读该文件
-
- Of_Share_Exclusive
- 独占方式打开文件,其它程序不得再打开该文件
复制代码
---------------------------------------------------------------------------------------------
妈蛋,加班熬夜干活
休息的时候拼拼凑凑了一下,vb6 折腾了个,可实现楼主要求
把源文件与 exe 打包了一份,受网络限制,rar 上传不了,gethex 了一份,qzw 要是解压不了一定是假冒的:
原来把 rar 加密就可以了,这是压缩包,密码 bathome:
[attach]7157[/attach]- lock.exe File [wStyle]
- wStyle = exclusive|read|write
复制代码
不干掉进程就一直 lock 着,源文件没啥注释但不复杂,楼主有兴趣用 c 写个完整的呗,坐等开源
作者: qzwqzw 时间: 2014-4-10 09:06
CrLf的敬业让我佩服啊!
for /f改成下面这样就内容加倍了- for /f %%a in (a.txt) do echo %%a>>a.txt
复制代码
4>>%0 是为了保证脚本在该目录下运行时不会被删。
从上下文看这个脚本肯定是在for /r的根上
如果这个目录树下与脚本同名的文件组
那脚本必然是第一个枚举并被占用保护的啊
不过为了脚本通用性这样设计也可以理解
那个vb程序应该不算是纯CLI程序
不过管它GUI,CLI
达到目的就行
要想改成CLI有难度
关键是程序退出前怎么处理打开的文件句柄
我再想想吧
也许有人可以帮忙
不过下次上传别绕这么多弯好不好
不让rar上传那直接改后缀名不行吗?
作者: CrLf 时间: 2014-4-10 10:30
本帖最后由 CrLf 于 2014-4-10 10:34 编辑
回复 5# qzwqzw - for /f %%a in (a.txt) do echo %%a>>a.txt
复制代码
逻辑上 for /f 是先把 a.txt 读入内存再 >>a.txt 的,证据:- for /f %%a in (a.txt) do echo %%a>a.txt
复制代码
这样也能读到 a.txt,for /f 读入文件和句柄占用的先后次序就很清楚了
----------------------------------------------------------------------------------
确实是个没窗口的gui,话说我试着用 Me.Caption = Argv(1) 来将窗口标题修改为正在锁定的文件路径,以便 taskkill /f /im /fi "windowtitle eq 路径" 来准确结束占用,但实测中 tasklist 不知道为什么读不到 form 的标题,why?!
----------------------------------------------------------------------------------
vb6 写 cli 挺蛋疼,用过两种方案:- 1、完全编译成 cli,要把 link.exe 替换掉,需要的话我发给你。此方案进程和界面貌似得同生共死。
- 2、中途可以创建、退出命令行界面,进程可以在退出界面后继续运行,参考:http://hi.baidu.com/console_app。不过这个方案比较繁琐,要附加在父进程窗口下还要多加几行代码,而且设置退出码需要 terminalprocess,所以好像无法通过退出码传出 pid 后仍保持运行。
复制代码
----------------------------------------------------------------------------------
试过改后缀名仍被墙,感觉单位防火墙是过滤文件头的,奇葩得很。本来想把所有字节 +1,但后来还是觉得用 gethex.exe 更省事(俺自写的命令行工具~)...反正楼主肯定能还原
作者: Spring 时间: 2014-4-10 19:37
本帖最后由 Spring 于 2014-4-10 19:50 编辑
我之前也试过,好像不能达到无法读取的目的,
要想不被修改,比如删除、重命名、追加内容之类的,好多方式都可以,原理可能都是一样的,只要占住这个文件就行了。
试了好些,发现下面这样可以让文件不被修改,而且执行之后不会对原文件内容造成影响,并且要结束只需要按回车就行了复制代码
作者: qzwqzw 时间: 2014-4-10 21:40
回复 6# CrLf
根据以往的分析结果
重定向符号的分析必然优先于前面的语句命令
这也是type test.txt>test.txt会清空内容的本质原因
而不含()的for /f也与这个原则并不悖逆
因为可以认为那个>位于for/f的“子句”中
它仍然优先于子句的命令被解析了
但是type test.txt>>test.txt的结果
与上面这个原则似乎存在矛盾
这其中一定有什么我还不清楚的逻辑
看起来>>没有想象中的那样简单
lock.exe的CLI化我已经放弃了
希望有别人有这个兴趣吧
关于准确结束占用的话题
你想的可能有些复杂了
可以把lock改成一个指定的名字
比如lock_testfile.exe
然后再用它锁住文件
lock_testfile.exe testfile.txt
结束时只需要按进程名kill就可以了
我之前就是这样设想的
不过还是觉得杀进程太暴力了
而且为了一个并非主体的需求
消耗一个第三方程序代价有些高了
我回头由重新梳理了一下我的需求出发点
我只是需要在我的代码执行的某个周期内
某个指定的文件不会被其他程序有意无意的读写而已
于是我重新想到了一个不需使用文件占用的方法
这个方法也是收到7楼的Spring上尉的启发
就是修改文件的NTFS权限禁止当前用户读写
有了思路代码其实很简单了- ::锁定的
- cacls testfile.txt /e /d %userdomain%\%username%
- ::解锁的
- cacls testfile.txt /e /p %userdomain%\%username%:f
复制代码
虽然代码并不十分完美和通用
但是在我所应用的情境下
这段代码完全可以胜任了
在此非常感谢两位兄弟
我的问题至此已经了结了
虽然留下一些未竟的遗憾
但是——
有时候就是这样
理论需要为实践做出让步
理想需要为现实做出妥协
不过——
如果大家有兴趣
还可以就文中的一些话题延伸讨论
我有时间也会尽可能关注的
作者: Demon 时间: 2014-4-11 00:45
我就看看,不说话。
作者: CrLf 时间: 2014-4-11 01:12
回复 9# Demon
又一枚大神粗线!真是良辰吉日
作者: PowerShell 时间: 2014-4-11 17:13
我也来谈谈:
1 首先我想到了ntfs权限法,用bat实现也简单。取消权限,pause,还原权限。
2 霸占文件法,诸位已经发了很多了,哪个灵我也没细看,最好总结出来。霸占文件还有一法,就是内存映射文件。这不是swap。 。net有此类,和方法。我的理解是内存映射文件期间,无法用file相关的io函数来读写,来复制。
3其他用户法。恐怕要结合上面2法用。即把ntfs权限搞成其他用户,pause,在搞回来。让其他用户霸占文件的打开句柄。这样我就或许不能读写。
4等级用户法。意为我用system账户霸占一个文件的打开句柄,administrator 就不能读写,复制。
5底层注入过滤法。
不论你怎么搞,大方向也就这些。
1不是啥东西都要拿出代码来,
2当然你也可以拿出代码,但是若不灵,或者不太灵,然后你又换,那就难免 破铜烂铁,一堆堆,。。。当然你本意是好地,赞你的本意。
3奇技淫巧+代码 绕来绕去把大家弄蒙了,推上了歧路,饶了弯子,还真不如不看代码,看【大方向语言描述】。
作者: Demon 时间: 2014-4-11 23:53
还是简单说几句吧。
重定向符号的分析必然优先于前面的语句命令,这是无需争议的。
重定向输出>和>>都会用CreateFile函数打开目标文件,dwDesiredAccess参数为GENERIC_WRITE,dwShareMode参数为FILE_SHARE_READ,所以打开之后文件是只读的,无法写入。(详见批处理技术内幕:重定向与句柄)
for /f也会用CreateFile函数打开目标文件,dwDesiredAccess参数为GENERIC_READ,dwShareMode参数为FILE_SHARE_READ | FILE_SHARE_DELETE,如果文件已经在重定向的时候被打开,则CreateFile函数调用会失败。- (for /f %%a in (a.txt) do xxx)>>a.txt
复制代码
代码运行后的错误信息是“系统找不到文件 a.txt。”,与a.txt不存在的错误信息是一样的,但是正确的原因应该是“另一个程序正在使用此文件,进程无法访问。 ”
CMD的错误处理真是渣渣,CreateFile函数失败以后也不调用GetLastError看看是什么原因,直接说系统找不到文件。
至于type命令,用CreateFile函数打开目标文件时,dwDesiredAccess参数为GENERIC_READ,dwShareMode参数为FILE_SHARE_READ | FILE_SHARE_WRITE,所以即使文件已经在重定向的时候被打开,CreateFile函数调用也不会失败。
可以用C程序验证如下:- #include <stdio.h>
- #include <windows.h>
-
- // by Demon
- // http://demon.tw
-
- int main()
- {
- HANDLE hFile;
-
- hFile = CreateFile(
- "a.txt",
- GENERIC_WRITE,
- // 重定向的dwShareMode
- FILE_SHARE_READ,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (hFile == INVALID_HANDLE_VALUE) {
- printf("1: %d\n", GetLastError());
- }
-
-
- hFile = CreateFile(
- "a.txt",
- GENERIC_READ,
- // FOR /F命令的dwShareMode
- FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (hFile == INVALID_HANDLE_VALUE) {
- printf("2: %d\n", GetLastError());
- }
-
- hFile = CreateFile(
- "a.txt",
- GENERIC_READ,
- // TYPE命令的dwShareMode
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (hFile == INVALID_HANDLE_VALUE) {
- printf("3: %d\n", GetLastError());
- }
-
- return 0;
- }
复制代码
作者: Demon 时间: 2014-4-11 23:57
至于用VB6写CLI程序,也不见得有多复杂,有现成的插件可以用,只不过调试起来确实蛋疼。
作者: qzwqzw 时间: 2014-4-12 11:34
Demon辛苦了
经过的你的分析
受到一点启发
终于比较明白>和>>的不同了
它们最本质的不同在于锁创建文件句柄是Desired Access参数不同
>创建的是Genric Write|Read Attributes模式的文件句柄
它会Overwrite原文件
即对文件长度置0
因此等到type再次打开该文件时
文件内容已被“清零”
>>创建的是Genric Read/Write模式的文件句柄
它会保持文件长度
并移动文件指针到原文件尾部
因此等到type再次打开该文件时
文件内容可以再次读取并写入
可以确认的是
在>和>>打开文件后
一直等待type再次打开并关闭同一文件后
才关闭文件
也就是>和>>并未以所谓“独占”的方式打开文件
另外一点意外的发现
在>正式打开文件之前
会预先打开一次文件查询文件的属性
主要包括长度、分配块大小、链接数等等
然后在关闭文件后再次正式打开
而>>是在正式打开文件的过程中查询该属性的
作者: Demon 时间: 2014-4-12 12:28
>和>>最本质的不同在于调用CreateFile函数时的参数不同。
>的参数:- 002FEF50 00446C10 |FileName = "a.txt"
- 002FEF54 40000000 |Access = GENERIC_WRITE
- 002FEF58 00000001 |ShareMode = FILE_SHARE_READ
- 002FEF5C 002FEF78 |pSecurity = 002FEF78
- 002FEF60 00000002 |Mode = CREATE_ALWAYS
- 002FEF64 00000080 |Attributes = NORMAL
- 002FEF68 00000000 \hTemplateFile = NULL
复制代码
>>的参数:- 0023F148 00326C10 |FileName = "b.txt"
- 0023F14C C0000000 |Access = GENERIC_READ|GENERIC_WRITE
- 0023F150 00000001 |ShareMode = FILE_SHARE_READ
- 0023F154 0023F170 |pSecurity = 0023F170
- 0023F158 00000003 |Mode = OPEN_EXISTING
- 0023F15C 00000080 |Attributes = NORMAL
- 0023F160 00000000 \hTemplateFile = NULL
复制代码
当然,>>打开文件后还会移动文件指针到文件尾部。
至于你最后说的意外的发现,我调试的时候没有发现,不知道你是如何得出的结论。
作者: Demon 时间: 2014-4-12 13:10
另外,要实现楼主所谓的“独占状态”,只需用CreateFile函数打开文件,dwShareMode传入0即可。
作者: qzwqzw 时间: 2014-4-13 17:23
我的分析都来自于Procmon的监测数据
对应的命令操作如下- Microsoft Windows XP [版本 5.1.2600]
- (C) 版权所有 1985-2001 Microsoft Corp.
-
- C:\Documents and Settings\Administrator>d:
-
- D:\>cd test
-
- D:\test>dir
- 驱动器 D 中的卷没有标签。
- 卷的序列号是 5491-6D80
-
- D:\test 的目录
-
- 2014-04-13 17:15 <DIR> .
- 2014-04-13 17:15 <DIR> ..
- 2014-04-13 17:14 3 testfile.dat
- 1 个文件 3 字节
- 2 个目录 15,396,376,576 可用字节
-
- D:\test>type testfile.dat >> testfile.dat
-
- D:\test>type testfile.dat > testfile.dat
-
- D:\test>
复制代码
进一步的测试发现
如果不存在的对应的文件
>预先的CreateFile会失败
自然也没有随后的查询与关闭
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |