闲来无事之--记用BAT(批处理脚本)实现文件下载功能

原创 2004年08月31日 15:02:00

作者:ZV(ZVROP)
邮件:zvrop@163.com
主页:(被整顿掉老~~~~~>_<~~~~~~)
网站:http://www.s8s8.net
日期:2004-8-18

转载请保全文档完整,谢谢

写的仓卒繁多,错漏难免,还请各位给予斧正.

有什么问题,可以给我发email.^_^...



目录:

一.写在前面的话

二.故事的起因

三.解决-腹稿

四.解决-实战

五.解决-打造

六.包装

七.小节

八.后记

九,参考文献





正文开始:


一.写在前面的话

这篇文档讲述的并不是什么新鲜的技术,我只是起到将他们混和起来实现了自己需要的功能的作用,如果你对批处理和PE格式相当了解,那我的这篇文档就权且当作浏览温习吧...^_^...
另外,由于我本人废话比较多,这也是不想给杂志写稿子的主要原因,给我干瘪瘪的3000字能讲出什么来,不如这样没有限制的爽快(当然也没什么报酬...一_一..),所以也为了防止你在看文章的途中睡着,请自备小锥子一把.....
最后,这篇文档说是用批处理下载文件,其实它包含了很多方面的知识,如果有时间,不妨一看哈,^_^,开始买瓜了..


二.故事的起因

最初萌发这个想法的是不久前,在论坛(广告一下:http://www.s8s8.net)上的UNIX SHELL板块有个会员发了一篇帖子,内容是用BASH SHELL写的一个成批下载图片的脚本(其实是H图片...一_一..),接下来跟贴那个多啊...,有繁衍出PHP的,VBS的,C的,C#的,JAVA的,甚至交流到多线程,断点续传....引用花大哥的一句话"无语,为了MM照片,大家的动力都很足啊!"...汗~~..

在发了一份PHP和C的代码后(感觉我动力特足~大色狼...一_一..),觉得很简单(因为用C或者PHP等脚本来实现文件的下载本来就是很基础的东西)我就开始想用微软最原始的脚本--Batch(批处理)来尝试实现(本文标题中的"闲来无事"就是纪念此处,一_一.),这似乎有点不可思议,因为批处理几乎没有实现的支持网络的功能(当然,如果你说你能用TELNET下载到文件的我是很佩服的..一_一..),但也不是完全没有办法,毕竟WINDOWS里面能用的东西这么多,没有完不成的事情....在这种挑战的勾引下,我完成了用批处理下载文件的功能....现在让我一步一步回放我的思路,揭开用批处理下载文件的奥秘...


三.解决-腹稿

如果用批处理来下载文件的话,肯定会马上想到Cscript脚本(或者是JAVA脚本),那是当然,太多的批处理脚本实现一些本身并不可能实现的功能的时候都是采用ECHO出一个其他脚本的方法来解决.可是我们的目的就在于用批处理实现下载的功能,如果要用VBS来帮忙的话不如直接写VBS了.这个想法顺即告吹....

再来,记得以前有流行过一阵用RUNDLL32来加载DLL中的API,似乎和我们需要的目的沾边,因为下载文件能用的API太多了,如果RUNDLL能调用,那最好不过了.于是我打开MSDN,找了一个API: URLDownloadToFile

URLDownloadToFile函数原型:

代码

HRESULT URLDownloadToFile(          
   LPUNKNOWN pCaller,
   LPCTSTR szURL,
   LPCTSTR szFileName,
   DWORD dwReserved,
   LPBINDSTATUSCALLBACK lpfnCB
);


URLDownloadToFile函数的一些信息:

引用

Header Urlmon.h
Import library Urlmon.lib
Minimum availability Internet Explorer 3.0
Minimum operating systems Windows NT 4.0, Windows 95


根据这些,我们可以知道,这个API是在URLMON.DLL文件中的一个导出函数,简单的实现了把一个文件从WEB服务器下载本机的功能,其实用这个函数还不错的,至少它帮我们处理了断点续传,缓存等等的功能,比起直接使用SOCKET函数来实现或者用WININET里的函数来实现简单多得多了.

URLDownloadToFile有五个参数:

第一个参数是仅当调用者是一个ActiveX对象才使用,一般为NULL.

第二个参数就是要下载文件的目标URL,完整路径.

第三个是本地保存路径,也是完整路径

第四个是保留,必须为0

第五个是指向一个IBindStatusCallback接口的指针,这就类似一种回调机制,你可以参考这些来活动当前下载进度,选择是否继续下载等等.

这里面我们只关心第二和第三个参数.其他的通通设置成0.(当然你写C的时候最好设置为NULL)

嗯,敲了点键盘介绍了这个函数,是因为整篇的文档都和这个函数息息相关,有了这个函数,就可以呼叫RUNDLL32来调用它,但是很可惜,这个美好的计划马上也破裂了...

我去微软看了他们的164787号文档(http://support.microsoft.com/default.aspx?...kb;en-us;164787),该文档阐述了RUNDLL32的调用方式和能被他调用的函数的格式:

它们是这么说的:

引用

Rundll and Rundll32 programs do not allow you to call any exported function from any DLL. For example, you can not use these utility programs to call the Win32 API (Application Programming Interface) calls exported from the system DLLs. The programs only allow you to call functions from a DLL that are explicitly written to be called by them.


这个是规定的格式:

代码

void CALLBACK
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);


很不幸,我们的URLDownloadToFile小兄弟并没有符合这些条件,被RUNDLL32无情的抛弃了(汗一滴..)...但是我们并没有因此而嫌弃它(汗一滴AGAIN..),毕竟,在后来实现的过程里,它是为我们的工作省下了不少功夫.

到此,用RUNDLL32运行计划流产....(寒...)

想了一根烟功夫,现在URLDownloadToFile有了,怎么才能调用这个函数呢?总不能模仿汇编PUSH 5个参数进栈,然后CALL吧,那这个函数的地址还要用LoadLibrary()和GetProcAddress()计算得来,那这两个函数的地址.....还是放弃...等等,如果用一个EXE来实现的话就简单很多了(至少EXE是不需要任何解释器的),对,写一个EXE来下载文件.可我们的目的是用BAT来下载呢,BAT文件能包裹EXE的数据吗?答案是肯定的...往下看..

记得以前看过一篇文档<<Do All in Cmd Shell>>里面介绍过一种方法.先卖个关子.大家都知道,如果用ECHO加上重定向符来写文件的话,只能写入ASCII的一部分,也就可以显示出来的那些ASCII(也就是ASCII值小于128的那些),对于那些无法显示的字符就没有办法了.但是这让我们想起一个工具,一个微软历史上同样古老的,批处理的兄弟--DEBUG!

现在思路清晰了:可以让批处理把ECHO不能显示的字符转化为16进制数据(比如EXE中的那些数据)保存在批处理中,然后用DEBUG写道文件里,最后用BAT调用生成的EXE,下载文件!(想完了这里,我感觉还是太麻烦,不知道哪位牛人对这个实现还有什么更加简单的办法吗??)


四.解决-实战

倘若就此编写一个可下载文件的EXE,然后直接用BAT包裹,定然会被同行耻笑,不单是因为那几千个字节的数据拖着大大臃肿的BAT文件,更加让为这种简单的想法立刻现形,为了不达到这些负面效果,也为了让这篇文档不至于干瘪瘪的让人感觉没什么看头(事实上是因为早些时候看过watercloud的一篇大作感悟颇深),我决定手工写一串16进制代码来代替机器编译的EXE.既美观了界面,又增强了技术性.....(一_一...简直是在卖作...)

现在当务之急是要一个可以下载文件的EXE程序,实现这个目标只要一个URLDownloadToFile即可,放在最后实现,先来写一个PE框架:大家都知道PE文件的格式吧,不懂的就去看看那个著名的电信黑客罗某某的书.(Who!?...~)

先给出我们的PE框架,基于XP的FileAlignment对齐大小最小就支持到0x200(也就是10进制的512字节,以下有在前面加上0x的都表示16进制数值),我们的框架就打出512字节(注意,我下面留有空白表示各个PE部分,结合下面的文档,大家方便理解),这个框架里没有任何的代码或者数据:

(ZV友情提示:下面是最枯燥的部分,各位手握锥子,要有一不怕苦,二不怕痛的精神看完它....)
(如果定力不高的朋友,或者堆PE文件再熟悉不过的朋友,可以字节转到"JMP S1"处往下看.)
(如果只想知道到底怎么回事,或者对这篇作文报浏览态度的朋友,可以直接转到"JMP S2"处继续浏览)
(睡着了的继续睡觉....)

代码

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   4D 5A 00 00 00 00 00 00  00 00 00 00 00 00 00 00   MZ..............
00000010   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000020   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000030   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 00   ............@...

==============================================================================

00000040   50 45 00 00 4C 01 02 00  00 00 00 00 00 00 00 00   PE..L...........
00000050   00 00 00 00 70 00 0F 01  

                                    0B 01 00 00 00 02 00 00   ....p...........
00000060   00 00 00 00 00 00 00 00  79 01 00 00 00 00 00 00   ........y.......
00000070   00 00 00 00 00 00 40 00  00 10 00 00 00 02 00 00   ......@.........
00000080   00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00   ................
00000090   00 30 00 00 00 02 00 00  00 00 00 00 02 00 00 00   .0..............
000000A0   00 01 00 00 00 00 00 00  00 01 00 00 00 10 00 00   ................
000000B0   00 00 00 00 02 00 00 00  

                                    00 00 00 00 00 00 00 00   ................
000000C0   28 11 00 00 28 00 00 00  

==============================================================================

                                    00 00 00 00 00 00 00 00   (...(...........
000000D0   00 02 00 00 00 10 00 00  00 02 00 00 00 01 00 00   ................
000000E0   00 00 00 00 00 00 00 00  00 00 00 00 60 00 00 60   ............`..`
000000F0   00 00 00 00 00 00 00 00  02 00 00 00 00 20 00 00   ............. ..
00000100   00 02 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000110   00 00 00 00 60 00 00 60  00 00 00 00 00 00 00 00   ....`..`........


00000120   58 11 00 00 00 00 00 00  50 11 00 00 00 00 00 00   X.......P.......
00000130   00 00 00 00 6E 11 00 00  20 11 00 00 00 00 00 00   ....n... .......
00000140   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000150   58 11 00 00 00 00 00 00

                                    00 00 00 00 00 00 00 00   ................
00000160   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000170   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................


这里简单介绍一下PE文件格式的组成:

大致来分呢,PE格式文件可以分为这三个部分(就是上述框架中用"=="分割的三个部分):

引用

++++++++++++++++++++++++
+DOS信息部分          +
++++++++++++++++++++++++

++++++++++++++++++++++++
+PE信息部分            +
++++++++++++++++++++++++

++++++++++++++++++++++++
+数据部分              +
++++++++++++++++++++++++



下面来简单介绍每一部分的结构,首先的"DOS信息部分":

引用

+++++++++++++++++++++++++++++++++++++++++++++
+  +++++++++++++++++++++++++++++++++++++++  +
+  +[DOS文件头][0x40]                    +  +
+  +++++++++++++++++++++++++++++++++++++++  +
+                                          + <==DOS信息部分
+  +++++++++++++++++++++++++++++++++++++++  +
+  +[DOS块][0x70,可变]                  +  +
+  +++++++++++++++++++++++++++++++++++++++  +
+++++++++++++++++++++++++++++++++++++++++++++


这部分我觉得是最冗余的地方,首先DOS文件头的结构:

代码

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
 WORD   e_magic;                       // Magic number
 WORD   e_cblp;                        // Bytes on last page of file
 WORD   e_cp;                          // Pages in file
 WORD   e_crlc;                        // Relocations
 WORD   e_cparhdr;                     // Size of header in paragraphs
 WORD   e_minalloc;                    // Minimum extra paragraphs needed
 WORD   e_maxalloc;                    // Maximum extra paragraphs needed
 WORD   e_ss;                          // Initial (relative) SS value
 WORD   e_sp;                          // Initial SP value
 WORD   e_csum;                        // Checksum
 WORD   e_ip;                          // Initial IP value
 WORD   e_cs;                          // Initial (relative) CS value
 WORD   e_lfarlc;                      // File address of relocation table
 WORD   e_ovno;                        // Overlay number
 WORD   e_res[4];                      // Reserved words
 WORD   e_oemid;                       // OEM identifier (for e_oeminfo)
 WORD   e_oeminfo;                     // OEM information; e_oemid specific
 WORD   e_res2[10];                    // Reserved words
 LONG   e_lfanew;                      // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;


其中最重要的就是e_lfanew,它指向了下面的"PE信息部分"的起始地址(也就是俗称的PE头部).其他的是一些DOS下运行这个PE文件必须的结构,比如看注解就明白,什么代码初始化堆栈段,初始化堆栈指针,入口IP,CS等等,都是在WIN32上没有用的东西,我就不翻译拉,这些都是说DOS下的,如果这个PE文件一开始就打定在WINDOWS下运行,这些乱写都无所谓,你甚至可以把你的名字都写进去(.....一_一..).当然,你这么作后这个文件就不能在DOS下运行了..不然当机是几乎可以肯定的....(寒....).

需要记的除了e_lfanew是指向PE头的指针外还要记得这个DOS文件头结构长0x40,也就是64个字节.还有第一个参数e_magic,这个地方永远是"0x40 0x5a",也就是字符的"MZ".

DOS块部分保存的就是一段DOS下可以执行的代码,比如现在大多编译器就简单的输出一个"This program cannot be run in DOS mode"的字符串,和"DOS信息部分"一样,如果你不打算在DOS执行这个EXE文件,那么这里完全可以删除,为什么?因为WIN32的PE装载器只关心"DOS信息部分"的e_lfanew指向的而已.

Windows 下使用ftp批处理脚本

一般状况下, 我们使用FTP 到某台机器上进行文件上传、下载的操作, 在命令方式下,基本上是: 1.ftp 主机名 2.输入用户名 3. 输入密码 4. 切换模式(asc 或者是 bin)...
  • oscar999
  • oscar999
  • 2015年04月18日 19:40
  • 18328

批处理bat脚本编写(附详细例子)

批处理bat脚本编写(附详细例子)      由于在项目开发的过程中经常需要编写bat脚本,而看大牛们编写的bat脚本简直一头雾水,木有办法,作为bat菜鸟的我只有通过最最简单的例子来熟悉bat脚本中...
  • u010093630
  • u010093630
  • 2014年06月01日 21:12
  • 4312

ping的批处理文件编写

原文地址:ping的批处理文件编写作者:迷你猫做为一名网络工程师,经常要测试某地网络的好坏,而最直接的命令就是ping,但是一次ping好多天,而且每隔一段时间就要ping和tracert,使工作繁琐...
  • huwei0518
  • huwei0518
  • 2015年02月06日 13:52
  • 1424

ftp命令批处理下载文件

在工作中,有时候需要从服务器上下载一些固定的文件,虽然通常我们可以使用winscp等软件来登录服务器,下载相对来讲已经是比较方便了,但是试想一下,假如我们在windows上写个脚本,双击一下就可以自动...
  • zgf19930504
  • zgf19930504
  • 2016年06月06日 14:12
  • 2357

一天一篇批处理之——Part3:复制/移动文件命令move,copy,xcopy

一天一篇批处理之——Part3:复制/移动文件命令move,copy,xcopy 一,move Move是一个移动文件/文件夹的命令 例1.1 move a.txt \file\ move file ...
  • jiangxian2008
  • jiangxian2008
  • 2016年10月01日 17:38
  • 3008

Windows下递归搜索文件及其所在目录 .bat脚本实现

【说明】         当我们要查找文件时,在 Linux 下可以使用 find 命令很方便的递归搜索到文件名含有某一关键字的文件及其件所在位置,如 图1 所示。 图1         但在 W...
  • Qidi_Huang
  • Qidi_Huang
  • 2016年08月17日 16:06
  • 4064

windows下监控进程的脚本 批处理实

[转载]windows下监控进程的脚本 (2012-06-09 23:27:43) 转载▼ 标签: 转载   原文地址:windows下监控进程的脚...
  • samxx8
  • samxx8
  • 2014年02月12日 10:31
  • 4757

BAT CMD 批处理文件脚本总结

1.               综述 1、”.bat”: 这是微软的第一个批处理文件的后缀名,在几乎所有的Windows 操作系统内都能运行。 2、 “.cmd”: 是为Windows NT ...
  • ligaoyang
  • ligaoyang
  • 2016年08月22日 14:30
  • 2687

BAT批处理脚本教程

转自:点击打开链接 第一章 批处理基础 第一节 常用批处理内部命令简介 批处理定义:顾名思义,批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD。这些...
  • tianzhaixing
  • tianzhaixing
  • 2016年07月13日 16:00
  • 873

开启所有activex等Internet选项的批处理bat

方法(1) REM 添加信任站点ip reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zon...
  • serialization_
  • serialization_
  • 2013年07月15日 15:01
  • 1394
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:闲来无事之--记用BAT(批处理脚本)实现文件下载功能
举报原因:
原因补充:

(最多只允许输入30个字)