【有用的结构体】
~~~~~~~~~~~~~~
现在介绍我们已经讨论了很多的PSP。
%PSP(Program Segment Prefix)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSP结构如下:
___________________________________
| INT 20h(CD 20) |<------+0000h
|___________________________________| Size:1 WORD
| Pointer to the next segment |<------+0002h
|___________________________________| Size:1 WORD
| Reserved |<------+0004h
|___________________________________| Size:1 BYTE
| Far call to INT 21h |<------+0005h
|___________________________________| Size:5 BYTES
| Saved INT 22h vector |<------+000Ah
|___________________________________| Size:1 DWORD
| Saved INT 23h vector |<------+000Eh
|___________________________________| Size:1 DWORD
| Saved INT 24h vector |<------+0012h
|___________________________________| Size:1 DWORD
| Reserved |<------+0016h
|___________________________________| Size:22 BYTES
| Offset to Environment Segment |<------+002Ch
|___________________________________| Size:1 WORD
| Reserved |<------+002Eh
|___________________________________| Size:46 BYTES
| First default FCB |<------+005Ch
|___________________________________| Size:16 BYTES
| Second default FCB |<------+006Ch
|___________________________________| Size:16 BYTES
| Command Tail and default DTA |<------+0080h
|___________________________________| Size:180 BYTES
Total Size:256 BYTES
因为这个结构非常重要,让我一步一步地解释吧。
offset 0000h:
INT 20h是终止程序的过时的方法,现在我们使用INT 21h的4CH号函数。
offset 0002h:
这是pointer to the next segment,这个指针指向我们程序的下一段。我们利用它可以知道DOS 能给我们多少内存(它指向的偏移地址减去PSP的偏移地址0000)。它将以段返回给我们内存,所以我们必须把它乘以16来得到字节数。
offset 0005h:
这是调用INT 21h的一种奇特方式。而且,当然,我们可以利用它来达到我们的目的。这个函数用到CL而不是AH,而且我们只能在低于24h时调用这个函数。我将在TUNNELING这一章里介绍更多。
offset 000Ah:
这里存储的是原先的INT 22h中断向量。INT 22h当程序使用如下方法终止执行时接受控制:
- INT 20h
- INT 27h
- INT 21h(00h,21h,4Ch函数)
offset 000Eh:`
这里存储另一个中断的向量,INT 23h。这个中断处理CTRL+C按键组合。
offset 0012h:
存储在这里的另外一个中断,INT 24h。这个中断处理严重错误。这种类型错误的例子:当你的软驱里面没有软盘,或者软盘被写保护了。
offset 002Ch:
这里是环境块偏移地址的开始。
offset 005Ch:
这里存储第一个缺省FCB(File Control Block文件控制块)。这种访问文件的方式通常情况下不会被程序使用(在这里是为了和低版本的DOS兼容),但是病毒作者为了使病毒更隐蔽,经常使用它。你可以在FCB结构里看到更多的信息。
offset 006Ch:
同上,这是第二个缺省FCB。
Offset 0080h:
这一段有两个功能:
----存储命令尾(command tail)
---存储DTA的缺省文件缓冲区
这两个功能不能同时使用,所以我们开始一个程序要考虑的第一件事就是命令尾(command tail)。如果我们需要它,我建议你把它保存到一个安全的地方(我们代码中的一个变量中)。命令尾的第一个字节(80h)约束了它的长度,而且这里,它保存了真正的参数。DTA的结构将会在这一章中介绍。
%FCB(File Control Block 文件控制模块)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
现在有两种类型的FCB:普通和扩展类型。下面给出普通FCB:
________________________________
| Drive Letter(0=actual,1=A...) |<----+0000h
|________________________________| Size:1 BYTE
| Blank padded file name |<----+0001h
|________________________________| Size:8 BYTES
| Blank padded file extension |<----+0009h
|________________________________| Size:3 BYTES
| Current block number |<----+000Ch
|________________________________| Size:1 WORD
| Logical record size |<----+000Eh
|________________________________| Size:1 WORD
| File size |<----+0010h
|________________________________| Size:1 DWORD
| File date |<----+0014h
|________________________________| Size:1 WORD
| File time |<----+0016h
|________________________________| Size:1 WORD
| Reserved |<----+0018h
|________________________________| Size:8 BYTES
| Record within current block |<----+0020h
|________________________________| Size:1 BYTE
| Record access record number |<----+0021h
|________________________________| Size:1 DWORD
Total Size:37 BYTES
而在一个扩展FCB中,上面所有的偏移地址会向后移7个字节,而开始的7个字节如下:
_____________________________________
| FF(Signature for extended FCB) |<---- -0007h
|_____________________________________| Size:1 BYTE
| Reserved |<---- -0006h
|_____________________________________| Size:5 BYTES
| File attribute |<---- -0001h
|_____________________________________| Size:1 BYTE
Total Size:44 BYTES
检测FCB是普通的还是扩展的方法是看FCB 的第一个字节是否为FFh。如果是,那么就为扩展FCB,否则就为普通FCB。
有一种隐蔽病毒(STEALTH)方法就是改变FCB的某些值,我们将会在STEALTH一章里具体讨论。
%MCB(Memory Control Block 内存控制模块)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我们将在驻留内存病毒这一章介绍它(下一章)。下面给出MCB:
___________________________________
| ID(Z=last,M=there're more) |<----+0000h
|___________________________________| Size:1 BYTE
| Address of associated PSP |<----+0001h
|___________________________________| Size:1 WORD
| Number of paras in allocated mem |<----+0003h
|___________________________________| Size:1 BYTE
| Unused |<----+0005h
|___________________________________| Size:11 BYTES
| Block Name |<----+0008h
|___________________________________| Size:8 BYTES
| Zone of allocated memory |<----+0010h
|___________________________________| Size:?? PARAS
Total Size: VARIABLE
%DTA(Disk Transfer Area 磁盘交换区)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在病毒写作中这个结构非常重要。让我们看:
___________________________________
| Drive Letter(equal than above) |<----+0000h
|___________________________________| Size: 1 BYTE
| Search Template |<----+0001h
|___________________________________| Size: 11 BYTES
| Reserved |<----+000Ch
|___________________________________| Size: 9 BYTES
| File attribute |<----+0015h
|___________________________________| Size: 1 BYTE
| File time |<----+0016h
|___________________________________| Size: 1 WORD
| File date |<----+0018h
|___________________________________| Size: 1 WORD
| File size |<----+001Ah
|___________________________________| Size: 1 DWORD
| ASCIIZ Filename+extention |<----+001Eh
|___________________________________| Size: 13 BYTES
Total Size: 43 BYTES
原始DTA保存在PSP的偏移地址80h处。我们可以利用INT 21h的1Ah功能保存它。
%IVT(Interrupt Vector Table中断向量表)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这个并不是“真正的”结构体。恩...让我解释一下吧...IVT是保存所有中断向量的地方(哇,天才啊!)。所有的中断向量的位置为number_of_interrupt*4(中断号*4)。假如我们想把INT 21h中断向量赋给DS:DX...则:
xor ax,ax
mov ds,ax
lds dx,ds:[21h*4]
为什么我们清除DS?因为IVT是从地址0000:0000开始到高地址内存的。这种操作(不通过使用DOS)是获得/赋给一个中断的向量的直接方法。有关更多的介绍将在驻留内存病毒(RESIDENT VIRUSES)一章中介绍。嗨...我忘记了示意图了:)
__________________________________
| INT 00h vector |<-----+0000h
|__________________________________| Size:1 DWORD
| INT 01h vector |<-----+0004h
|__________________________________| Size:1 DWORD
|
//////////////////
|__________________________________
| INT FEh vector |<-----+03FCh
|__________________________________| Size:1 DWORD
| INT FFh vector |<-----+0400h
|__________________________________| Size:1 DWROD
Total Size:1024 BYTES
你能想象得到“断”行表示有256个中断,我不得不优化这篇教程(我可不想花5页来表示它!)
%SFT(System File Table 系统文件表)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这个结构真得很酷。它能帮助你使你的代码更健壮,更优化。它类似于FCB,但是,正如你看到的,它更强大。利用这个表,我们可以使病毒更隐蔽,改变文件指针的打开模式,属性...这里你看到的使DOS 4以上版本的结构(我相信在这个世界上已经没有人用DOS 3或更低了吧)。好了,如果你想为DOS 3编写代码,可以参考Ralph Brown的中断列表。但是DOS 3的SFT是和下面的非常类似的。重要的值在相同的地方:)
===================================== <----+0000h
‖ Pointer to next file table ‖ Size:1 DWORD
‖===================================‖<----+0004h
‖ Number of files in this table ‖-----------Size:1 WORD----------
‖===================================‖<----+0000h [3Bh bytes per file]
| Number of file handles of file | Size:1 WORD
|___________________________________|
| File open mode(AH=3Dh) |<-----+0002h
|___________________________________| Size:1 WORD
| File attribute |<-----+0004h
|___________________________________| Size:1 BYTE
| Device info block(AX=4400h) |<-----+0005h
|___________________________________| Size:1 WORD
| If char device points next dev h. |<-----+0007h
| else point to DOS DPB | Size:1 DWORD
|___________________________________|
| Starting cluster of file |<-----+000Bh
|___________________________________| Size:1 WORD
| File time |<-----+000Dh
|___________________________________| Size:1 WORD
| File date |<-----+000Fh
|___________________________________| Size:1 WORD
| File size |<-----+0011h
|___________________________________| Size:1 DWORD
| Current offset in file |<-----+0015h
|___________________________________| Size:1 DWORD
| Relative cluster within file of |<-----+0019h----------[If Local File]
| last cluster accessed | Size:1 WORD
|___________________________________|
| Number of sector with dir entry |<-----+001Bh
|___________________________________| Size:1 DWORD
| Number of dir entry within sector |<-----+001Fh
|___________________________________| Size:1 BYTE
| Pointer to REDIRIFS records |<-----+0019h----[Network redirector]
|___________________________________| Size:1 DWORD
| ??? |<-----+001Dh
|___________________________________|--------Size:3 BYTES--------
| Filename in FCB format |<-----+0020h
|___________________________________| Size:11 BYTES
| Pointer to prev SFT sharing file* |<-----+002Bh
|___________________________________| Size:1 DWORD
| Network machine num opened file* |<-----+002Fh
|___________________________________| Size:1 WORD
| PSP segment of file owner |<-----+0031h
|___________________________________| Size:1 WORD
| Offset to code segment of rec* |<-----+0033h
|___________________________________| Size:1 WORD
| Absolute clust num of last access |<-----+0035h
|___________________________________| Size:1 WORD
| Pointer to IFS driver for file |<-----+0037h
|___________________________________| Size:1 DWORD
Total Size:61 BYTES
Uhm...我忘记访问SFT的方法了...下面给出的程序把SFT赋给ES:DI,把文件句柄保存到BX中。
GetSFT:
mov ax,1220h
int 2Fh
jc BadSFT
xor bx,bx
mov ax,1216h
mov bl,byte ptr es:[di]
int 2Fh
BadSFT:
ret
我强烈地建议你把返回值保存到AX/BX中(BX非常重要:这里我们用来保存文件句柄)。
标志(*)的域将会被SHARE.EXE使用
%DIB(DOS Info Block DOS信息模块)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
利用DIB,我们能够访问那些重要的不能利用其它方法访问结构。这个结构并不是固定的在内存中的,我们必须利用INT 21h的52h功能。在DOS文档里没有这个函数的介绍。当我们调用这个函数的时候,我们将会在ES:BX里得到DIB的地址。你将会得到:
___________________________________
| Pointer to first MCB |<---- -0004h
|___________________________________| Size:1 DWORD
| Pointer to first DPB |<-----+0000h
|___________________________________| Size:1 DWORD
| Pointer to DOS last buffer |<-----+0004h
|___________________________________| Size:1 DWORD
| Pointer to $CLOCK |<-----+0008h
|___________________________________| Size:1 DWORD
| Pointer to CON |<-----+000Ch
|___________________________________| Size:1 DWORD
| Maximum sector length |<-----+0010h
|___________________________________| Size:1 WORD
| Pointer to DOS first buffer |<-----+0012h
|___________________________________| Size:1 DWORD
| Pointer to array of cur dir struc |<-----+0016h
|___________________________________| Size:1 DWORD
| Pointer to SFT |<-----+001Ah
|___________________________________| Size:1 DWORD
Total Size:34 BYTES
%DPB(Drive Parameter Block 驱动器参数模块)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这个结构为我们提供了达到我们的目的的非常有用的信息。通过使用DIB(看上文)里的第二个指针,我们能够知道它的位置。下面给出:
___________________________________
| Drive Letter(0=A,1=B...) |<----+0000h
|___________________________________| Size:1 BYTE
| Unit number within device driver |<----+0001h
|___________________________________| Size:1 BYTE
| Bytes per sector |<----+0002h
|___________________________________| Size:1 WORD
| Highest sect num within a cluster |<----+0004h
|___________________________________| Size:1 BYTE
| Shift count for clust to sectors |<----+0005h
|___________________________________| Size:1 BYTE
| Number of reserved clusters |<----+0006h
|___________________________________| Size:1 WORD
| Number of FATS |<----+0008h
|___________________________________| Size:1 BYTE
| Number of root directory entries |<----+0009h
|___________________________________| Size:1 WORD
| Number of first sector with data |<----+000Bh
|___________________________________| Size:1 WORD
| Number of last sector with data |<----+000Dh
|___________________________________| Size:1 WORD
| Number of sector per FAT |<----+000Fh
|___________________________________| Size:1 BYTE
| Sector number of first dir sector |<----+0010h
|___________________________________| Size:1 WORD
| Address of device driver header |<----+0012h
|___________________________________| Size:1 DWORD
| Media ID byte |<----+0016h
|___________________________________| Size:1 BYTE
| 00h if disk accessed,else FFh |<----+0017h
|___________________________________| Size:1 BYTE
| Pointer to next DPB |<----+0018h
|___________________________________| Size:1 DWORD
Total Size:28 BYTES
%分区表%
~~~~~~~~
所有编写启动病毒的人都知道这个结构,它是硬盘上的第一块。它总是在第一块的,无论在软盘还是在硬盘上。如果是硬盘,我们就叫它MBR(Master Boot Record 主启动记录),如果是软盘,就叫它启动扇区。
分区表是一个有着四个入口的数组,在偏移地址01BEh处。下面给出每个入口的格式:
___________________________________
| Boot indicator(Bootable=80h, |<----+0000h
| Non bootable 00h) | Size:1 BYTE
|___________________________________|
| Head where the partition begins |<----+0001h
|___________________________________| Size:1 BYTE
| Sector where the partition begins |<----+0002h
|___________________________________| Size:1 BYTE
| Cylinder where the part.begins |<----+0003h
|___________________________________| Size:1 BYTE
| System indicator*(What OS?) |<----+0004h
|___________________________________| Size:1 BYTE
| Head where partition ends |<----+0005h
|___________________________________| Size:1 BYTE
| Sector where the partition ends |<----+0006h
|___________________________________| Size:1 BYTE
| Cylinder where the partition ends |<----+0007h
|___________________________________| Size:1 BYTE
| Total blocks preceding partition |<----+0008h
|___________________________________| Size:1 DWORD
| Total blocks in the partition |<----+000Ch
|___________________________________| Size:1 DWORD
Total Size:16 BYTES
(*) 01=12-bit FAT
04=16-bit FAT
%BPB(Bios Parameter Block Bios 参数模块)%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在以DOS为基础的系统中,启动记录以一个跳转(jump)作为开始,后面跟这一个结构体——BPB。
_________________________________
| OEM name and version(ASCII) |<----+0000h
|_________________________________| Size:8 BYTES
| Bytes per sector |<----+0008h
|_________________________________| Size:1 WORD
| Sectors per cluster |<----+000Dh
|_________________________________| Size:1 BYTE
| Reserved sector(starting at 0) |<----+000Eh
|_________________________________| Size:1 WORD
| Number of FATs |<----+0010h
|_________________________________| Size:1 BYTE
| Total sectors in partition |<----+0011h
|_________________________________| Size:1 WORD
| Media descriptor |<----+0013h
|_________________________________| Size:1 WORD
| Sectors per FAT |<----+0015h
|_________________________________| Size:1 BYTE
| Sectors per track |<----+0017h
|_________________________________| Size:1 WORD
| Number of heads |<----+0019h
|_________________________________| Size:1 WORD
| Number of hidden sectors |<----+001Dh
|_________________________________| Size:1 WORD
Total Size:29 BYTES