📄 记用bat(批处理脚本)实现文件下载功能.txt
字号:
绕了这么大个圈子才回到正题....(一_一....其实我是想把问题写的详细,这样大家看了就没有态度的疑虑嘛..),还记得上面说过的数据类型吗,其中最重要的就是导入表,我们的URLDownloadToFile小朋友已经在板凳上坐了很久了.......这个导入表就是为他量身定做的.我们的目的就是让PE文件执行URLDownloadToFile的功能,自然得把URLDownloadToFile这个函数加入导入表.
说道导入表的定义呢?就不得不先说说WINDOWS加载可执行程序时候对IAT(IMPORT ADDRESS TABLE,导入地址表)的修改,我们知道,各个系统的每个函数在内存中的位置都是不同的(至少2K,XP,2003基本上都不一样),所以才有很多写人SHELLCODE的时候,位置计算个半天..这样来说的话,在我们编译EXE的时候就不可能确定某个函数的地址.要执行这个函数,必须找到他的入口地址,而这个地址就由系统在加载PE文件的时候帮你"填空",这动态的完成函数地址的填充也就是"动态连接"这个名词的由来.
现在我简单的模拟一下系统转载PE文件并给出函数地址的步骤,首先,我们给出一个PE文件中的导入表:
代码
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
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
可以看到,这个表被分为四个部分,其中中间两个等长为0x14的两段就是导入表中的IMAGE_IMPORT_DESCRIPTOR结构,该结构如下:
代码
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //指向一个"函数列表的指针结构".
};
DWORD TimeDateStamp; //暂时可以看作没用,0
DWORD ForwarderChain; //暂时可以看作没用,0
DWORD Name; //指向一个DLL,这个结构里面的函数必须都是这个DLL里面的
DWORD FirstThunk; //指向一个IAT表,最后操作系统修改的就是这个
} IMAGE_IMPORT_DESCRIPTOR;
注意,该结构也必须有N+1个,因为我们只需要一个函数"URLDownloadToFile",所以我们只有这一个结构,第二个结构是全0的.表示结束.
这个"函数列表指针结构"就是IMAGE_THUNK_DATA32结构:
代码
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
他只有一个双字类型的值,这个值如果是1XXXXXXXH的,那么说明该函数是一序号方式导入的,序号就是除了1外的剩下的7位,如果是0XXXXXXXH的,那么这个除了0外的7位就是作为一个虚拟地址指向这个函数的名字.
关于什么是序号导入什么是名字导入,我就不说了,这些涉及到导出表的概念.本文不需要.
假设我是WINDOWS操作系统的PE装载器,我从这个PE文件格式的某些参数中定位到了这个00000128H的地址是导入表地址,现在我的目的是要把"58 11 00 00"这个地址替换为正确的函数地址(注意,是00000120H处的,00000150H处的那个"58 11 00 00"是给系统提供"URLDownloadToFile"这个字符串位置的指针,这个地址不会变动,会变的是00000120H处的"58 11 00 00",其实00000120H处的"58 11 00 00"可以随便设置的.).
我开始定位到了:
代码
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
50 11 00 00 00 00 00 00 X.......P.......
00000130 00 00 00 00 6E 11 00 00 20 11 00 00
的地方,发现这个函数的位置是"50 11 00 00",相关的DLL是"6E 11 00 00",于是我找到PE文件的这个位置(是内存中的相对位置),发现"50 11 00 00"位置处的IMAGE_THUNK_DATA32结构的值是"58 11 00 00",这个值不是1开头的,于是我用这个值作为地址查找,发现这个值指向的位置的内容是"31 00 URLDownloadToFile",除去前面的两个序号,找到了这个函数的名称,接下来我根据在"6E 11 00 00"位置找到的字符串"URLMON.DLL",用LoadLibrary()和GetProcAddress()找到了函数"URLDownloadToFile"在内存中的位置,假设是"XX XX XX XX",然后把"XX XX XX XX",填入到"20 11 00 00"指向的位置中...完毕.
这样来说,大家就明白了,URLDownloadToFile这个函数的存放位置应该根据"50 11 00 00"(确切的说应该是"50 11 00 00"指向的位置的指针)和"6E 11 00 00"来确定(确定这个函数存在的DLL).
五.解决-打造
[THIS IS JMP S2]
现在我们再回头整理一下整个过程...结合这张表:
代码
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 4D 5A 5B 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 5D 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 5B 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 5D ...............]
这张PE文件数据图就是一个很大的"填空",除去重要的数据部分,我们可以随便写入数据的地方有2个(也就是两个大挂号挂起来的中间).
第一个是从地址00000002开始的,到地址0000003B结束的56字节.
第二个是从地址00000160开始的,到PE文件结尾的160字节.(也可以从000000158开始,这样就有168字节)
因为我们的程序很短,所以第二个168字节基本上可以满足要求全部,就不需要第一个56字节的数据了.把数据和在一起也方便呢,不是么?^_^.
那这些地方具体填写些什么东西呢?大致来说分为三个部分:
1.导入表,包括"URLDownloadToFile"这个函数的字符串和"URLMON.DLL"这个DLL的字符串.
2.文件的可执行机器码.
3.函数需要的数据.
首先是导入表,根据上一节说的那些,我们可以很容易的判断出这个"URLDownloadToFile"该填在"58 11 00 00"的位置.当然你可以改这个值,这个值只是我写的.总之你想吧这个导入表放在什么位置,这个"58 11 00 00"就要指向这个位置.于是我们在PE文件的00000158位置写入"31 00 URLDownloadToFile"字符串,前面两个16进制是序号是给转载器提供信息作为在DLL中导出地址的依据.
(对了,这里说明一个问题,这篇文档也注释了很多"注意点",为什么呢,仔细看看这些注意点,发现都是和位置有关的,那是因为:PE文件中的绝大多数的地址,都是采用文件加载后内存中的地址的,这样一方面加快了加载速度,另外一方面也省了不少加载器的工作,比如这个"58 11 00 00"的地址,因为我们加载的位置是1000H,所以根据这个位置,我们在文件中的位置就是158H,这里要申明的一点,并不是所有的地址都可以这么计算的,因为我们在PointerToRawData那里设置了100H,为的就是这样方便的计算相对地址,对于其他的PE文件,如果要根据这种内存地址计算出PE文件地址,还不是这么简单是事情,^_^..当然,网络上也有很多这种转换函数,RVA到OFFSET的)
然后把URLMON.DLL这个字符串填入"6E 11 00 00"指向的地址,当然这个值也是可以变的.
最后,我们要用笔记录一下最后这个函数被导出的地址的存放处,也就是"20 11 00 00".
[注意,以上的这些操作都和IMAGE_IMPORT_DESCRIPTOR结构和IMAGE_THUNK_DATA32结构相关,看不明白的多看看这两个结构]
接下来是可执行码.我们的目的很简单,只要这个PE文件能下载文件就行,所以我们只要调用URLDownloadToFile函数即可,写一小段汇编码(还记得前面说过的URLDownloadToFile的调用方法吗,花了点笔墨的那个):
代码
PUSH 0 ;6A 00
PUSH 0 ;6A 00
PUSH XXXXXXXX ;68 XXXXXXXX
PUSH XXXXXXXX ;68 XXXXXXXX
PUSH 0 ;6A 00
CALL XXXXXXXX ;E8 XXXXXXXX
由于函数的调用是符合PASCAL调用,也就是STDCALL,自右向左压栈,所以我们的参数也是最后一个先入栈.最后CALL出这个URLDownloadToFile函数.
前两个XXXXXXXX地址是两个字符串的地址,也就是URLDownloadToFile函数的两个重要参数,最后一个XXXXXXXX是这个函数在内存中的地址(操作系统已经帮我们填充了,还记得上面说的那个用笔记录的"20 11 00 00"么?)
主要的代码就是这么多,可是不幸的事情发生了,当我用WINHEX把这些代码填入PE框架并且保存的时候,居然被杀毒软件删除了!!!!他们把这个看作病毒????想来写病毒原来是这么容易的事情(.....一_一.)....
幸好有备份(如果没有,我可是要哭死了.....),我修改了这些代码,加入了一些垃圾(比如MOV EAX,1之列的)...最终的成品代码是:
代码
B8 01000000 ;mov eax,1
6A 00 ;push 0
6A 00 ;push 0
68 D0114000 ;push D0114000 ;指向你保存的本地路径字符串的位置,本文中是"c:\\gl123\\00204.jpg",注意是双杠.
68 A0114000 ;push A0114000 ;指向要下载的URL字符串保存的位置
6A 00 ;push 0
E8 02000000 ;call 02000000 ;也就是呼叫下两个字节的地址,这是机器中调用函数的通常做法
C9 ;leave
C3 ;ret
FF25 20114000 ;jmp 20114000 ;这个跳转地址就是"20 11 00 00",至于那个"40",
;就是程序的建议起始加载地址"00400000".另外,这里是仿机器格式.
00
00
00
00
将他们写入哪里呢?这个就随便你了,不过请翻翻上面说的,有个地址是(也就是注意1所在的位置)AddressOfEntryPoint:这个就是用来定位你代码的执行入口的,我们就放在导入表的后面,也就是"00000179H"的位置.
最后就是那两个字符串的地址了,我们在程序中已经给出
代码
68 D0114000
68 A0114000
那这两个字符串的位置就确定了,一个是"000001D0H",我们要下载的文件地址"/Article/UploadFiles/200408/20040818230017329.JPG"就是保存到这里..这里我每个分配了48字节存储区域,大家也可以根据具体需要设置.别忘了还有dos头部可以保存56字节的空白可以写数据,如果需要的话,修改指向就是.
对于上面的这一堆废话,我的目的是想让大家明白,而故意介绍的格式,即是说,如果让你换做其他的API函数也能轻易的调用,而不是局限于URLDownloadToFile.^_^...比如那些...那些...功能啊....(我可没说啊...嘿嘿)..
OK,这个PE文件最后的成形PE框架是这样的:
代码
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 31 00 55 52 4C 44 6F 77 X.......1.URLDow
00000160 6E 6C 6F 61 64 54 6F 46 69 6C 65 41 00 00 75 72 nloadToFileA..ur
00000170 6C 6D 6F 6E 2E 64 6C 6C 00 B8 01 00 00 00 6A 00 lmon.dll.?...j.
00000180 6A 00 68 D0 11 40 00 68 A0 11 40 00 6A 00 E8 02 j.h?@.h?@.j.?
00000190 00 00 00 C9 C3 FF 25 20 11 40 00 00 00 00 00 00 ...擅
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -