Board logo

标题: [原创]批处理搜索提取二进制文件小记 [打印本页]

作者: 无奈何     时间: 2006-11-25 05:54    标题: [原创]批处理搜索提取二进制文件小记

批处理搜索提取二进制文件小记

        最近完成了一个批处理,功能是打开快捷方式(*.lnk)指向的文件所在的目录。其中使用到一些有意思的小技巧,这篇短文记录下一步步实现这个功能的过程,和大家一起分享一下。

        我们首先要做的是先分析一下快捷方式的内容,为记事本创建两个快捷方式作为例子。第一个 notepad.lnk ,目标处填入 c:\WINDOWS\system32\notepad.exe ;第二个 notepad2.lnk ,目标处填入 %windir%\system32\notepad.exe 。我们知道快捷方式是以二进制文件的形式存在的,用任意一款十六进制编辑软件打开 notepad.lnk 与 notepad2.lnk 观察一下。提示一下部分编辑软件会打开指向的文件而不是快捷方式,但一般都有设置选项可以更改,具体软件的使用情况不做过多的说明。打开后会发现无论哪种方式建立的快捷方式都会找到一串字符 C:\WINDOWS\system32\notepad.exe ,现在要做的就是把这串字符提取出来。想想我们可以用到的命令好像只有 find 和 findstr 两个,两个命令都能够进行二进制文件的搜索,他们各有优缺点,find 会将搜到的二进制信息分行显示,findstr 会原样显示二进制信息,后者的强项是支持简单的正则表达式。我们利用两个命令的组合将所要的信息提取出来。
        我们先看一下合法的文件与文件夹的全路径都会有哪些组合:
c:\
c:\windows
c:\windows\
c:\windows\explorer.exe
D:\TeSt\.\
D:\TeSt\test1\test2
D:\TeSt\tEsT.txt.lnk
D:\TeSt\tEsT (2).txt.lnk
D:\TeSt\tEsT (3).txt.lnk 不存在
还有其他类似的组合,等等,哪怕最后一个看起来有些怪异仍是可能存在的。用 PERL 样式的正则表达式多行模式下可能需要这样描述“^([A-Za-z]:\\)(.+?\\)*.*$”,但这个表达式不够严格,更好一点的应该这样写“^([A-Za-z]:\\)(([^\\/:?*"<>|]+\\)*)([^\\/:?*"<>|])*$”,如果不幸在一行上的话可能需要这样匹配“([A-Za-z]:\\)(([^/:*?"<>|](?![A-Za-z]:))*)”。我们在这里只是讨论一下完整路径名形式的特征,不多谈 PERL 正则表达式。回到我们的问题上来,不管那样的组合都会出现 “盘符:\” 这样字符组合,据此可以提取出我们想要的字串。先用 find 过滤一下,find ":" *.lnk ,观察一下结果会发现我们想要的字串在一行上,再用 findstr 二次过滤一下就 OK 了,find ":" *.lnk |findstr /r "^[A-Za-z]:[\\]" ,findstr 的 /r 参数表明搜索的字符串为一般表达式,“^”匹配行的开始,“[A-Za-z]”字符集表示匹配大小写的字母。重点谈一下 findstr 查找 “\” 字符,在其他的正则表达式中一般在字符前添加 “\”会脱掉元字符的特殊性,简单说就是两个 “\\” 表示一个 “\” 。而在 findstr 却要用四个代替一个,比如查找字符“D:\”需要这样来写 findstr "D:\\\\" ,如果查找多个连续的“\” 如:“D:\\\” 会把人搞晕的,另一个方法是利用字符集两个成表示一个,这个例子可以这样来写,findstr /r "D:[\\][\\][\\]" 。继续回到我们的问题,勤于思考的朋友会问,如果某快捷方式的路径为 “盘符:” 我们的查找会不会失败,这种情况是不会出现的,不信可建立一个快捷方式,目标位置填入 “C:” 会发现 widows 自动在后面添加上一个 “\”。

        事实上还有一个命令会格式化二进制文件输出,就是 more ,如命令:more /s notepad.lnk |findstr /r "^[A-Za-z]:[\\]" ,会输出同样的效果,但是 more 非交互方式下不能应用于多文件,如命令:more /s *.lnk |findstr /r "^[A-Za-z]:[\\]" ,会一直等待用户的按键。非用 more 的不可情况是 find 无法完成第一遍过滤,比如搜索的是一个表达式没有简单的固定特征字符的情况。
        最后给出我们的成果。可以放到 sendto 文件夹下,或命令行调用。

  Quote:

  1. @echo off
  2. if "%~1" == "" goto :EOF
  3. ::检查扩展名是否是快捷方式文件
  4. if /i "%~x1" NEQ ".lnk" goto error
  5. for /f "delims=" %%i in ('find ":" "%~1" ^|findstr /r "^[A-z]:[\\]"') do (
  6. start %%~dpi
  7. )
  8. goto :EOF
  9. :error
  10. ::抛出错误提示窗口
  11. start cmd /c "title 提示!&mode con  cols=30 lines=5 &for /l %%i in (5,-1,1) do cls &echo. 所选文件不是快捷方式!( %%i )&ping/n 2 127.1>nul"
        无奈何发表于    2006-10-25  17:57

后记:我一直希望有更多的人了解批处理并对此感兴趣,尝试性的写一些小短文,发觉并不顺手,可能有些批处理知识的朋友看的会更累。效仿了一下 3742668 兄爱用的 mode 命令,让批处理错误提示更有趣了。
如果希望在快捷方式上直接右键打开目录的话,可以参考下面的注册表项修改一下。
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\lnkfile\shell]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\lnkfile\shell\ShortcutOpen.cmd]
@="打开指向目录"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\lnkfile\shell\ShortcutOpen.cmd\command]
@="\"D:\\BAT\\ShortcutOpen.cmd\" \"%1\""

作者: 无奈何     时间: 2006-11-25 05:57
非常抱歉,由于操作失误,不小心将帖子删除了,我重新发一遍,不幸的是相关讨论丢失了。
再次致歉,希望大家原谅。
作者: ccwan     时间: 2006-11-25 06:03
版主勿忧,如果需要的话,我将讨论贴上来怎样?不过只有12楼,以后的灌水贴我没保存。(请看我签名)
作者: tao0610     时间: 2006-11-25 06:31
楼上是传说中的备份数据库?
作者: 无奈何     时间: 2006-11-25 06:34
RE ccwan
非常感谢,兄整理一下贴上来吧,减少一点损失。
作者: ccwan     时间: 2006-11-25 06:43
NO!
我只是每个帖子复制到文本文件中。不好意思,不太工整。


electronixtar         『第 2 楼』:
金牌会员

沙发,强贴留名

2006-10-26 06:37     


lxmxn                  『第 3 楼』:
银牌会员


呵呵,学习了。。


2006-10-26 06:42            



electronixtar          『第 4 楼』:
金牌会员

  
请教无奈何兄:more 可以重定向输出一个二进制文件不?


2006-10-26 06:44            


无奈何                 『第 5 楼』:
版主   

   
RE electronixtar
可以啊!只是一些不可显示字符也会被输出。



2006-10-26 06:58                  


electronixtar          『第 6 楼』:
金牌会员  



斗胆提点建议呢,

第6句 改成 start "" explorer /select,/n,%%~fi

最后一句改成 start "title 提示!" cmd /c "mode con  cols=30 lines=5 &for /l %%i in (5,-1,1) do cls &echo. 所选文件不是快捷方式!( %%i )&ping/n 2 127.1>nul"

P.S. 我试了下 more %ComSpec% >a.exe,不行啊,而且我用了一个 596字节的标准exe做实验,生成了一个 930 字节的exe,而且PE结构被破坏了,没法运行了。
[ Last edited by electronixtar on 2006-10-26 at 07:26 AM ]

2006-10-26 07:16                    


无奈何                  『第 7 楼』:
版主

   

Re  electronixtar
不好意思,刚才出去一下。谢谢兄的第一条建议甚好,马上改。只是第二条没发现效果上有什么区别,还请指教。
more 不能从二进制文件中脱出嵌入的文件,因为数据会被重新解释并格式化。好像咱俩说的是两个方向,我提及的是从二进制文件中提取字符信息。



2006-10-26 09:21               


3742668                  『第 8 楼』:
版主


6F的
start "title 提示!" cmd /c "mode con  cols=30 lines=5 &for /l %%i in (5,-1,1) do cls &echo. 所选文件不是快捷方式!( %%i )&ping/n 2 127.1>nul"
貌似应该理解为 start "提示"  cmd /c "mode con  cols=30 lines=5 &for /l %%i in (5,-1,1) do cls &echo. 所选文件不是快捷方式!( %%i )&ping/n 2 127.1>nul"
用start来指定标题而不在命令中指定标题.
p.s:个人认为,所有磁盘上的文件都是以二进制存储的,所有都应该叫做二进制文件,大家讨论的应该是以二进制方式读取和保存文件.


2006-10-26 11:10              


electronixtar             『第 9 楼』:
金牌会员


  Quote:
貌似应该理解为 start "提示"  



恩恩,我就是那个意思呢,Ctrl+C 太快了,呵呵


  Quote:
所有都应该叫做二进制文件



通常所说的二进制文件就是指用记事本打开是乱码的文件,例如exe等,呵呵,叫习惯喽

[ Last edited by electronixtar on 2006-10-26 at 11:30 AM ]

2006-10-26 11:28               



yfd11                   『第 10 楼』:
初级用户

   以前看了一个叫世界第一名的程序写的。

REM 将GIF,JPG等小于64Kb的文件转成十六进制格式。EXE的话要改名。
@echo off
REM 获得常用信息
color 2f
set ASCII=0123456789T
%ASCII:~-1,1%itle=弱智制作QQ:%ASCII:~4,1%%ASCII:~-7,1%%ASCII:~1,1%%ASCII:~-6,1%%ASCII:~-7,1%%ASCII:~0,1%%ASCII:~-9,1%%ASCII:~3,1%%ASCII:~0,1%
set/p file=请输入文件名:
::dir %file%
::下一行获得文件大小
::for /F "usebackq  tokens=3" %%i IN (`"dir /-c %file%|find "%file%""`) DO set z=%%i
::下一行获得文件大小另一方法
::for /F %%I IN ("%file%") do set z=%%~zI
::echo 文件大小为%z%字节

REM 下面实现获得寄存器CX与BX值
echo r cx>temp1.txt
echo.>>temp1.txt
echo r bx>>temp1.txt
echo,>>temp1.txt
echo q >>temp1.txt
echo;>>temp1.txt
::debug %file%<temp1.txt>temp2.txt

for /F "usebackq  tokens=2" %%i IN (`"debug %file%<temp1.txt|find "CX""`) DO set CX=%%i
set CX0=%CX%
for /F "usebackq  tokens=2" %%i IN (`"debug %file%<temp1.txt|find "BX""`) DO set BX=%%i
::for /F "usebackq  tokens=2" %%i IN (`"type temp2.txt|find "CX""`) DO set CX=%%i
::for /F "usebackq  tokens=2" %%i IN (`"type temp2.txt|find "BX""`) DO set BX=%%i
::chcp 936
::rem 活动代码页更改为 中文语言
::echo CX的值是%CX% bx的值是%BX%
::pause
::for /F "usebackq  tokens=2" %%i IN (`"echo H %CX% 100|debug|find /V "H %CX% 100""`) DO set BX=%%i

REM 下面获得CX+100H的值
echo H %CX% 100>temp1.txt
echo q>>temp1.txt
for /F "skip=1 usebackq  tokens=1" %%i IN (`"debug<temp1.txt|find "%CX:~-2%""`) DO set CX=%%i
::chcp 936
::echo CX的值是%CX% bx的值是%BX%
::pause &rem 如果CX进位则BX+1
if %CX0:~0,2% LSS %CX:~0,2% goto :add1
echo H %BX% 1>temp1.txt
echo q>>temp1.txt
for /F "skip=1 usebackq  tokens=1" %%i IN (`"debug<temp1.txt|find /v "H""`) DO set BX=%%i
:add1
::算了,BX不用了,反正输入的64k的有点不对,大文件如何读入Debug谁知道告诉我我的QQ是(441540230)

REM 输出文件为了用中文文件名下面所以输出之前加chcp 936
echo D 100 %CX% 1>temp1.txt
echo q>>temp1.txt
chcp 936>nul 2>nul
debug %file%<temp1.txt>输出.txt
chcp 936>nul 2>nul

REM 修改一下以后Debug可变回的文件
set j=1&&set/a CX1=0X%CX:~0,-1%-15
echo/>修改输出.txt &&rem 换行可以是.,;/[\]+等字符
echo 正在修改中......请等待。
::echo cx1=%CX1% j=%j%&pause
:Start
for /F "delims= skip=%j% tokens=*" %%i in (输出.txt) do set t1=%%i && goto :Label
:Label
echo e %t1:~5,29% %t1:~35,-19% >>修改输出.txt
if %j% == %CX1% goto :end
set /a j+=1
goto :Start
:end
echo+>>修改输出.txt
echo rcx>>修改输出.txt
echo %CX0%>>修改输出.txt
echo n 1%file%>>修改输出.txt
echo w>>修改输出.txt
echo q>>修改输出.txt

REM 返回文件
echo set ASCII="<" >得到以前文件.bat
echo debug%%ASCII:~1,1%%修改输出.txt >>得到以前文件.bat
echo chcp 936>>得到以前文件.bat
echo start 1%file%>>得到以前文件.bat
echo 将修改输出.txt与得到以前文件.bat两个文件保存,要用只要双击bat文件即可
echo 完成操作,谢谢您使用.按任意键退出 &pause>nul 2>nul




2006-10-29 10:32              


electronixtar             『第 11 楼』:
金牌会员
  

原来是debug做的


2006-10-29 10:57                       


lxmxn                     『第 12 楼』:
银牌会员


   


10楼的帖子还不错,开头的标题设置还简单的加密了一下,呵呵。。。

p.s:你的签名是不是侵犯了 无奈何 兄的专利权哦??呵呵。不怕他把你开除"坛籍"啊?呵呵~~
作者: redtek     时间: 2006-11-25 08:31
ccwan兄真是心细,幸好还有备份:)
作者: 无奈何     时间: 2006-11-27 12:03
谢谢 ccwan 兄将丢失的部分贴出来。
为了感谢,特制作了一个更方便的存贴脚本。

===========
由于考虑到一些原因,我重新开了一个帖子,限定了浏览积分。
请到下面链接查看:http://www.cn-dos.net/forum/viewthread.php?tid=25130

我自己不能给自己评分,麻烦那位可以评分的版主或会员扣帮忙扣除这 9 点加分。

[ Last edited by 无奈何 on 2006-11-27 at 02:55 PM ]
作者: electronixtar     时间: 2006-11-27 23:43
再次强贴留名
作者: ccwan     时间: 2006-11-28 04:31
回过头来再看,感觉还是超强!再顶一个。
作者: newaifi     时间: 2007-2-24 06:43
To:redtek
非常抱歉,因一时心情激动,鼠标不听使唤,扣除了redtek兄一点积分.纯熟失误,还望见谅.
作者: slore     时间: 2007-2-24 08:05
vbs直接可以对快捷方式操作.
作者: anqing     时间: 2007-2-24 08:23
为什么,我把qq的ink,拖进去,提示找不到文件呢?报错?
作者: qingfushuan     时间: 2007-2-25 00:22    标题: 为了看这个链接



  Quote:
Originally posted by 无奈何 at 2006-11-26 11:03 PM:
谢谢 ccwan 兄将丢失的部分贴出来。
为了感谢,特制作了一个更方便的存贴脚本。

===========
由于考虑到一些原因,我重新开了一个 ...

看到这个时我才170分,专门到水区灌水2小时,挣到301分,就回来了
作者: skyearth     时间: 2007-3-8 04:07
不错,有价值
作者: Eblis     时间: 2007-3-8 04:39
刚刚试了下..好像不支持带空格的文件夹名称```
c:\program files\qq\qq.exe就提示出错找不到文件c:\program
作者: zh159     时间: 2007-3-8 07:45


  Quote:
Originally posted by Eblis at 2007-3-7 15:39:
刚刚试了下..好像不支持带空格的文件夹名称```
c:\program files\qq\qq.exe就提示出错找不到文件c:\program

带空格的文件夹名称要用""引用
作者: vkill     时间: 2007-3-10 08:43
早看这篇文章我就不试了,呵呵,我是

type *.lnk|more|findstr "^[a-zA-Z]:\\\\"

来弄的,复杂多了
作者: Billunique     时间: 2007-3-21 08:03
真是强!学习了
作者: rootport     时间: 2007-4-20 01:14
看看呢.
作者: pooloo     时间: 2007-10-10 11:42
好文章,非常喜欢这类心得式的帖子~~
作者: bdpq     时间: 2007-11-1 11:58
总是弹出  用什么程序打开的提示窗口
作者: loquat     时间: 2009-9-17 11:27
学习了。以前用的那个批处理不知道哪里去了。
作者: matlan     时间: 2009-9-18 10:07
第六句 可改成 start "" "%%~dpi"  避免空格问题
作者: ywkj     时间: 2009-10-10 16:57
学习了!!
作者: AriosLuch     时间: 2009-10-14 18:15
哇!学到了~~多谢LZ的指点额!收益终身~~~
作者: mwm5     时间: 2009-10-22 14:17
看来我得去那先学习下了。。。。 -_-!