|
MBR即Master Boot Record。它是硬盘的主引导记录,它独立于操作系统之外,不管你装Windows亦或是Linux,它都存在。MBR包括了引导程序、分区表以及结束标志(0xAA55)。他被BIOS将引导程序加载在0x7c00的起始位置,检查最后两个标志位是否正确。如果正确,取出活动分区的起始扇区(引导扇区),然后跳转到活动分区的引导扇区,通过引导扇区的引导程序找到NTLDR区域。如果NTLDR文件丢失或者损坏,就会出现一段熟悉的信息:“NTLDR is missing,press Ctrl+Alt+Del to restart.”。如果你不修复或者重装,就陷入这个死循环。NTLDR是一个16位程序,它运行在实模式下,其中的一个很重要的职责就是负责实模式切换到保护模式并开启分页功能(注:实模式不支持分页,采取段首址*0x10+偏移,其所有指令均是特权指令)。然后重定位osload程序,也就是核心的启动程序,它加载核心设备驱动,比如:ftdisk.sys,atapi.sys,disk.sys等,然后再加载内核文件(即ntkrnlpa.exe,不同的机器加载的文件可能不同),此时把控制权转交给ntkrnlpa.exe,之后再加载一些其他设备驱动,服务等,最后进入系统。
著名的Bootkit的鬼影2就是先替换fips.sys驱动,并且替换了BIOS的扩展INT13,以达到接管MBR的读写。因此MBR相当重要,在NT6的操作系统下根本就无法在Ring3下使用CreateFileA+WriteFileA写MBR(尼玛这句话是谁说的,我特么在Windows 7 x64下就能实现Ring3读写MBR!)
首先我们先看看一段操作MBR的代码(VB写的在Ring3下使用CreateFileA+WriteFileA实现读写MBR的代码,包含了读取MBR,破坏MBR,检测BK,备份MBR以及重置MBR的代码):
- Public Sub DetectBootkit(ByVal ListBoxItem As ListBox)
- If ListBoxItem.List(510) <> "55" Or ListBoxItem.List(511) <> "AA" Then
- MsgBox "Bootkit exists!", vbCritical, ""
- Else
- MsgBox "No Bootkit is detected", vbInformation, ""
- End If
- End Sub
- Public Sub DamageMBR()
- Dim hFile As Long
- Dim buffer(511) As Byte
- Dim dwReadWrite As Long
- If MsgBox("It's a highly dangerous operation for you to damage MBR!", vbCritical + vbYesNo, "") = vbNo Then Exit Sub
- hFile = CreateFile("\\.\PhysicalDrive0", GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
- If (hFile <> INVALID_HANDLE_VALUE) Then
- Call SetFilePointer(hFile, 0, 0, FILE_BEGIN)
- Call ReadFile(hFile, VarPtr(buffer(0)), 512, dwReadWrite, 0)
- Call SetFilePointer(hFile, 0, 0, FILE_BEGIN)
- Call RtlZeroMemory(VarPtr(buffer(0)), 512)
- Call WriteFile(hFile, VarPtr(buffer(0)), 512, dwReadWrite, 0)
- Call CloseHandle(hFile)
- Else
- MsgBox "Failed to call CreateFileA!", vbCritical, ""
- End If
- End Sub
- Public Sub BackupMBR(ByVal ListBoxItem As ListBox)
- Dim szTxt As String, szFileName As String: szFileName = Replace(App.Path & "\mbr.txt", "\", "")
- Dim i As Long
- For i = 0 To 511
- szTxt = szTxt & ListBoxItem.List(i) & vbCrLf
- Next
- szTxt = Left$(szTxt, Len(szTxt) - 2) '去掉最后的回车
- Open szFileName For Output As #1
- Print #1, szTxt
- Close #1
- MsgBox "The backup file has been saved as mbr.txt in current directory", vbInformation, ""
- End Sub
- Public Sub ResetMBR()
- 'Read Txt
- Dim i As Long
- Dim szMbr As String
- Dim bsMbr() As String
- Dim btMbr(511) As Byte
- szMbr = GetCodes(Replace(App.Path & "\mbr.txt", "\", ""))
- bsMbr = Split(szMbr, vbCrLf)
- For i = 0 To 511
- btMbr(i) = CByte(Val("&H" & bsMbr(i)))
- Next
- 'Write Mbr
- Dim hFile As Long
- Dim dwReadWrite As Long
- hFile = CreateFile("\\.\PhysicalDrive0", GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
- If (hFile <> INVALID_HANDLE_VALUE) Then
- Call SetFilePointer(hFile, 0, 0, FILE_BEGIN)
- Call WriteFile(hFile, VarPtr(btMbr(0)), 512, dwReadWrite, 0)
- Call CloseHandle(hFile)
- Else
- MsgBox "Failed to call CreateFileA!", vbCritical, ""
- Exit Sub
- End If
- End Sub
- Public Sub ReadMBR(ByVal ListBoxItem As ListBox)
- Dim i As Long
- Dim hFile As Long
- Dim buffer(511) As Byte
- Dim dwReadWrite As Long
- hFile = CreateFile("\\.\PhysicalDrive0", GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
- If (hFile <> INVALID_HANDLE_VALUE) Then
- Call SetFilePointer(hFile, 0, 0, FILE_BEGIN)
- Call ReadFile(hFile, VarPtr(buffer(0)), 512, dwReadWrite, 0)
- Call CloseHandle(hFile)
- Else
- MsgBox "CreateFile ERROR!", vbCritical, ""
- Exit Sub
- End If
- ListBoxItem.Clear
- For i = 0 To 511
- ListBoxItem.AddItem AddZero(Hex$(buffer(i)))
- Next
- End Sub
复制代码
作为良心作者,贴上相关声明:
- Option Explicit
- Public Declare Function CreateFile Lib "kernel32.dll" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
- Public Declare Function SetFilePointer Lib "kernel32.dll" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
- Public Declare Sub RtlZeroMemory Lib "kernel32.dll" (ByVal pDestination As Long, ByVal Length As Long)
- Public Declare Function ReadFile Lib "kernel32.dll" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Long) As Long
- Public Declare Function WriteFile Lib "kernel32.dll" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Long
- Public Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
- Public Const GENERIC_READ As Long = &H80000000
- Public Const GENERIC_WRITE As Long = &H40000000
- Public Const FILE_SHARE_READ As Long = &H1
- Public Const FILE_SHARE_WRITE As Long = &H2
- Public Const OPEN_EXISTING As Long = 3
- Public Const INVALID_HANDLE_VALUE As Long = (-1)
- Public Const FILE_BEGIN As Long = 0
- Public Function AddZero(ByVal sz As String) As String
- AddZero = String(2 - Len(sz), "0") & sz
- End Function
- Public Function GetCodes(ByVal szFileName As String) As String
- Dim szTextCodes As String
- Open szFileName For Binary As #1
- szTextCodes = StrConv(InputB(LOF(1), 1), vbUnicode)
- Close #1
- GetCodes = szTextCodes
- End Function
复制代码
看到操作MBR代码怎么写了吧,我们Hook相关函数就可以实现MBR的保护器。
初级保护MBR
由于CreateFileA的函数会走到SSDT的NtCreateFile函数下面,而Hook这个函数相当简单,由于它同时是导出函数,因此不需要用服务Index去找地址。挂钩这个函数的代码就显得很简单了。这里采用Inline Hook的方式。
- void HookNtCreateFile()
- {
- PMDL pMdl;
- ULONG PatchSize;
- BYTE g_HookCode[5]={0xE9,0,0,0,0};//相对跳转
- BYTE Jmp_Orig_Code[7]={0xEA,0,0,0,0,0x08,0x00 }; //绝对地址跳转
- UNICODE_STRING uniFuncName;
- RtlInitUnicodeString(&uniFuncName,L"NtCreateFile");
- NtCreateFile=MmGetSystemRoutineAddress(&uniFuncName);
- PatchSize=GetPatchSize(NtCreateFile,5);//获得要Patch的字节数
- //构造Proxy_Func
- Old_NtCreateFile=ExAllocatePool(NonPagedPool,20);
- memset(Old_NtCreateFile,0x90,20);
- RtlCopyMemory((PBYTE)Old_NtCreateFile,(PBYTE)NtCreateFile,PatchSize);//实现原函数头
- *((PULONG)(Jmp_Orig_Code+1))=(ULONG)((PBYTE)NtCreateFile+PatchSize );//原函数+N 地址
- RtlCopyMemory((PBYTE)Old_NtCreateFile+PatchSize,Jmp_Orig_Code,7);//绝对地址跳转
- *((ULONG*)(g_HookCode+1))=(ULONG)fake_NtCreateFile-(ULONG)NtCreateFile-5;//计算JMP 地址
- pMdl=IoAllocateMdl(NtCreateFile,5,FALSE,FALSE,NULL);
- if(pMdl)
- {
- __try
- {
- MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- IoFreeMdl(pMdl);
- }
- f_oldirql=KeRaiseIrqlToDpcLevel();
- KeInitializeSpinLock(&f_spinlock);
- KeAcquireSpinLockAtDpcLevel(&f_spinlock);
- _asm
- {
- cli
- mov eax, cr0
- and eax, not 10000h
- mov cr0, eax
- }
- RtlCopyMemory(NtCreateFile, g_HookCode, 5);
- _asm
- {
- mov eax, cr0
- or eax, 10000h
- mov cr0, eax
- sti
- }
- KeReleaseSpinLockFromDpcLevel(&f_spinlock);
- KeLowerIrql(f_oldirql);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- }
复制代码
挂钩了函数之后,别忘了退出程序需要Unhook,代码如下:
- void UnhookNtCreateFile()
- {
- PMDL pMdl;
- pMdl=IoAllocateMdl(NtCreateFile,5,FALSE,FALSE,NULL);
- if(pMdl)
- {
- __try
- {
- MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- IoFreeMdl(pMdl);
- }
- f_oldirql=KeRaiseIrqlToDpcLevel();
- KeInitializeSpinLock(&f_spinlock);
- KeAcquireSpinLockAtDpcLevel(&f_spinlock);
- _asm
- {
- cli
- mov eax, cr0
- and eax, not 10000h
- mov cr0, eax
- }
- RtlCopyMemory(NtCreateFile,Old_NtCreateFile,5);
- _asm
- {
- mov eax, cr0
- or eax, 10000h
- mov cr0, eax
- sti
- }
- KeReleaseSpinLockFromDpcLevel(&f_spinlock);
- KeLowerIrql(f_oldirql);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- ExFreePool(Old_NtCreateFile);
- }
复制代码
接下来便是处理参数的过程。根据操作MBR的代码我们可以看出,要过滤的关键字是"PhysicalDrive"。那么只要过滤掉这个关键字就可以了。首先先看看NtCreateFile的函数原型:
- typedef NTSTATUS (*NTCREATEFILE)
- (
- OUT PHANDLE FileHandle,
- IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes,
- OUT PIO_STATUS_BLOCK IoStatusBlock,
- IN PLARGE_INTEGER AllocationSize OPTIONAL,
- IN ULONG FileAttributes,
- IN ULONG ShareAccess,
- IN ULONG CreateDisposition,
- IN ULONG CreateOptions,
- IN PVOID EaBuffer OPTIONAL,
- IN ULONG EaLength
- );
复制代码
注意到第三个参数ObjectAttributes了没?对应的结构体OBJECT_ATTRIBUTES的结构如下:
- Public Type OBJECT_ATTRIBUTES
- Length As Long
- RootDirectory As Long
- ObjectName As Long 'PUNICODE_STRING 的指针
- Attributes As Long
- SecurityDescriptor As Long
- SecurityQualityOfService As Long
- End Type
复制代码
ObjectAttributes->ObjectName就是CreateFileA传递的文件名。知道了这一点,似乎就可以开始写过滤函数了。不过要知道一点:无论是ObjectAttributes还是ObjectAttributes->ObjectName以及该字符串对应Buffer,它们均是指针,而其中也有可能是无效的,所以要用MmIsAddressValid检查一遍地址的有效性。代码如下:
- NTSTATUS fake_NtCreateFile(OUT PHANDLE FileHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER AllocationSize OPTIONAL,IN ULONG FileAttributes,IN ULONG ShareAccess,IN ULONG CreateDisposition,IN ULONG CreateOptions,IN PVOID EaBuffer OPTIONAL,IN ULONG EaLength)
- {
- UNICODE_STRING uniFileName;
- NTSTATUS st;
- if(MmIsAddressValid(ObjectAttributes)==TRUE)
- {
- if(MmIsAddressValid(ObjectAttributes->ObjectName)==TRUE)
- {
- if(MmIsAddressValid(ObjectAttributes->ObjectName->Buffer)==TRUE)
- {
- RtlInitUnicodeString(&uniFileName,L"PhysicalDrive");
- if(SpyFindSubString(ObjectAttributes->ObjectName,&uniFileName)==TRUE)
- {
- st=STATUS_ACCESS_DENIED;
- }
- else
- {
- st=Old_NtCreateFile(FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,CreateDisposition,CreateOptions,EaBuffer,EaLength);
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- return st;
- }
复制代码
其中有个函数叫SpyFindSubString,它是用来检测字符串是否有重叠部分的,代码如下:
- BOOLEAN SpyFindSubString(PUNICODE_STRING String,PUNICODE_STRING SubString)
- {
- ULONG Index;
- if(RtlEqualUnicodeString(String,SubString,TRUE))
- {
- return TRUE;
- }
- for(Index=0;Index+(SubString->Length/sizeof(WCHAR))<=(String->Length/sizeof(WCHAR));Index++)
- {
- if(_wcsnicmp(&String->Buffer[Index],SubString->Buffer,(SubString->Length/sizeof(WCHAR)))==0)
- {
- return TRUE;
- }
- }
- return FALSE;
- }
复制代码
效果不错,已经起到了无法在Ring3下读写MBR的效果了。如图所示:
初级保护
然而加载了驱动的RK完全可以用导出的IoCreateFile来读写MBR,所以这种保护方法太弱。
中级保护MBR
我们知道,打开文件最最最底层的函数是IopCreateFile,由于这个函数是未导出函数,所以需要定位。首先看看XP下WinDbg对其反汇编的结果,如图所示:
反汇编结果
因此我们要搜索的特征码是0xE80875FF,注意不是0xFF7508E8。不明所以的人复习计算机基础去吧。。。
经检验,在windows server 2003,windows 7,windows 8的需要搜的特征码均是一样的,因此这方法可谓通用。
搜索特征码的代码如下:
- ULONG FindIopCreateFile()
- {
- ULONG SrcFuncAddr=0, i=0, DWordVal=0, OffsetAddr=0;
- UNICODE_STRING uniFuncName;
- RtlInitUnicodeString(&uniFuncName,L"IoCreateFile");
- SrcFuncAddr = (ULONG)MmGetSystemRoutineAddress(&uniFuncName);
- for(i=0;i<1024;i++)
- {
- if( MmIsAddressValid((PVOID)(SrcFuncAddr+i)) )
- {
- ReadKernelMemory((PVOID)(SrcFuncAddr+i),4,&DWordVal);
- if(DWordVal==0xE80875FF) //char code of xp/win7
- {
- ReadKernelMemory((PVOID)(SrcFuncAddr+i+4),4,&OffsetAddr);
- return (OffsetAddr + 5 + SrcFuncAddr+i+3);
- }
- }
- }
- return 0;
- }
复制代码
为了保证安全,不直接用RtlCopyMemory读写内核内存,而是自己定义了个ReadKernelMemory函数,代码如下:
- NTSTATUS ReadKernelMemory(PVOID Address, ULONG Size, PVOID OutBuffer)
- {
- NTSTATUS st = STATUS_UNSUCCESSFUL;
- PMDL pMdl = 0;
- PVOID pAddress = 0;
- if (!Address) return st;
- pMdl = IoAllocateMdl(Address, Size, FALSE, FALSE, 0);
- if (pMdl)
- {
- MmBuildMdlForNonPagedPool(pMdl);
- pAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
- if (pAddress)
- {
- __try
- {
- RtlCopyMemory(OutBuffer, pAddress, Size);
- st = STATUS_SUCCESS;
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- }
- }
- IoFreeMdl(pMdl);
- }
- return st;
- }
复制代码
找到函数的地址就好办了,直接Hook就可以了,代码如下:
- void HookIopCreateFile()
- {
- PMDL pMdl;
- ULONG PatchSize;
- BYTE g_HookCode[5]={0xE9,0,0,0,0};//相对跳转
- BYTE Jmp_Orig_Code[7]={0xEA,0,0,0,0,0x08,0x00 }; //绝对地址跳转
- IopCreateFile=(PVOID)FindIopCreateFile();
- PatchSize=GetPatchSize(IopCreateFile,5);//获得要Patch的字节数
- //构造Proxy_Func
- Old_IopCreateFile=ExAllocatePool(NonPagedPool,20);
- memset(Old_IopCreateFile,0x90,20);
- RtlCopyMemory((PBYTE)Old_IopCreateFile,(PBYTE)IopCreateFile,PatchSize);//实现原函数头
- *((PULONG)(Jmp_Orig_Code+1))=(ULONG)((PBYTE)IopCreateFile+PatchSize );//原函数+N 地址
- RtlCopyMemory((PBYTE)Old_IopCreateFile+PatchSize,Jmp_Orig_Code,7);//绝对地址跳转
- *((ULONG*)(g_HookCode+1))=(ULONG)fake_IopCreateFile-(ULONG)IopCreateFile-5;//计算JMP 地址
- pMdl=IoAllocateMdl(IopCreateFile,5,FALSE,FALSE,NULL);
- if(pMdl)
- {
- __try
- {
- MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- IoFreeMdl(pMdl);
- }
- f_oldirql=KeRaiseIrqlToDpcLevel();
- KeInitializeSpinLock(&f_spinlock);
- KeAcquireSpinLockAtDpcLevel(&f_spinlock);
- _asm
- {
- cli
- mov eax, cr0
- and eax, not 10000h
- mov cr0, eax
- }
- RtlCopyMemory(IopCreateFile, g_HookCode, 5);
- _asm
- {
- mov eax, cr0
- or eax, 10000h
- mov cr0, eax
- sti
- }
- KeReleaseSpinLockFromDpcLevel(&f_spinlock);
- KeLowerIrql(f_oldirql);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- }
- void UnhookIopCreateFile()
- {
- PMDL pMdl;
- pMdl=IoAllocateMdl(IopCreateFile,5,FALSE,FALSE,NULL);
- if(pMdl)
- {
- __try
- {
- MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- IoFreeMdl(pMdl);
- }
- f_oldirql=KeRaiseIrqlToDpcLevel();
- KeInitializeSpinLock(&f_spinlock);
- KeAcquireSpinLockAtDpcLevel(&f_spinlock);
- _asm
- {
- cli
- mov eax, cr0
- and eax, not 10000h
- mov cr0, eax
- }
- RtlCopyMemory(IopCreateFile,Old_IopCreateFile,5);
- _asm
- {
- mov eax, cr0
- or eax, 10000h
- mov cr0, eax
- sti
- }
- KeReleaseSpinLockFromDpcLevel(&f_spinlock);
- KeLowerIrql(f_oldirql);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- ExFreePool(Old_IopCreateFile);
- }
复制代码
注意IopCreateFile和NtCreateFile的函数原型不太一样。过滤函数的代码如下:
- NTSTATUS fake_IopCreateFile(OUT PHANDLE FileHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER AllocationSize OPTIONAL,IN ULONG FileAttributes,IN ULONG ShareAccess,IN ULONG Disposition,IN ULONG CreateOptions,IN PVOID EaBuffer OPTIONAL,IN ULONG EaLength,IN CREATE_FILE_TYPE CreateFileType,IN PVOID InternalParameters OPTIONAL,IN ULONG Options,IN ULONG InternalFlags,IN PVOID DeviceObject)
- {
- UNICODE_STRING uniFileName;
- NTSTATUS st;
- if(MmIsAddressValid(ObjectAttributes)==TRUE)
- {
- if(MmIsAddressValid(ObjectAttributes->ObjectName)==TRUE)
- {
- if(MmIsAddressValid(ObjectAttributes->ObjectName->Buffer)==TRUE)
- {
- RtlInitUnicodeString(&uniFileName,L"PhysicalDrive");
- if(SpyFindSubString(ObjectAttributes->ObjectName,&uniFileName)==TRUE)
- {
- st=STATUS_ACCESS_DENIED;
- }
- else
- {
- st=Old_IopCreateFile(FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,Disposition,CreateOptions,EaBuffer,EaLength,CreateFileType,InternalParameters,Options,InternalFlags,DeviceObject);
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- }
- else
- {
- st=STATUS_INVALID_PARAMETER;
- }
- return st;
- }
复制代码
这样一来,调用IoCreateFile来读写MBR的路就彻底封死了。效果如图所示:
中级保护
然而,我们可以通过发送IRP的方式读写MBR,不过鉴于难度较大,尤其是函数党根本就不会这玩意,因此这个办法搞定大多数MBR操作还是没有问题的。
高级保护MBR
这里,我就要解密T.A的《[原创]全平台支持的EAT/IAT/OBJECT/IRP/IDT HOOK范例(WIN32)》帖子中关于Disk Hook的代码(帖子的地址:http://www.m5home.com/bbs/thread-8128-1-1.html)。其实不能说是解密,因为我只是看到附件里面没有源码而已。为了防止读写MBR,我们要Hook IRP_MJ_READ和IRP_MJ_WRITE两个例程。这里就不使用Inline Hook的方式了。因为这两个例程的地址是一样的。
首先,这里修改地址的时候,我们使用InterlockedExchangePointer函数实现,主要是因为它以原子操作的方式修改4个字节的数据,修改的过程也不会被打断,而且,还会返回要修改的位置的值。其次是定位例程的地址,首先用ObReferenceObjectByName来取得Disk.sys的驱动对象,然后找MajorFunction就好了。挂钩这两个例程的代码如下:
- void StartHook()
- {
- NTSTATUS st;
- UNICODE_STRING uniDiskName;
- RtlInitUnicodeString(&uniDiskName,L"\\Driver\\Disk");
- st=ObReferenceObjectByName(&uniDiskName,OBJ_CASE_INSENSITIVE,0,0,*IoDriverObjectType,KernelMode,0,&DiskDrvObj);
- if(NT_SUCCESS(st))
- {
- IrpMjRead=(IRP_MJ_SERIES)InterlockedExchangePointer(&DiskDrvObj->MajorFunction[IRP_MJ_READ],fake_IrpMjRead);
- IrpMjWrite=(IRP_MJ_SERIES)InterlockedExchangePointer(&DiskDrvObj->MajorFunction[IRP_MJ_WRITE],fake_IrpMjWrite);
- IsHooked=TRUE;
- }
- }
- void StopHook()
- {
- InterlockedExchangePointer(&DiskDrvObj->MajorFunction[IRP_MJ_READ],IrpMjRead);
- InterlockedExchangePointer(&DiskDrvObj->MajorFunction[IRP_MJ_WRITE],IrpMjWrite);
- ObDereferenceObject(DiskDrvObj);
- IsHooked=FALSE;
- }
复制代码
有木有发现过程很简短啊。。。。。。用四个字来说就是返璞归真。只不过处理IRP的时候略麻烦。
任何处理IRP的Hook都要先用IoGetCurrentIrpStackLocation取得对应IRP堆栈的位置,否则得不到要处理的数据。
过滤的规则就很简单了,对读写的扇区偏移进行判定,如果是对象是磁盘设备而且是要从0扇区的位置开始的读写操作,那就拦截之。
代码如下:
- NTSTATUS fake_IrpMjWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
- {
- NTSTATUS status = STATUS_SUCCESS;
- IO_STACK_LOCATION *irpsp= IoGetCurrentIrpStackLocation(Irp);
- LARGE_INTEGER WriteOffsetInBytes= irpsp->Parameters.Write.ByteOffset;
- ULONG writeLength = irpsp->Parameters.Write.Length;
- LARGE_INTEGER Sectionpos={0};
- ULONG SectorRange;
- BOOLEAN IsDeniedAccess = FALSE;
- do
- {
- Sectionpos.HighPart = 0;
- Sectionpos.LowPart = 0x200;
- SectorRange= WriteOffsetInBytes.QuadPart/Sectionpos.QuadPart;
- if(SectorRange==0 && DeviceObject->DeviceType==FILE_DEVICE_DISK)
- {
- IsDeniedAccess =TRUE;
- break;
- }
- }while(0);
- if (IsDeniedAccess)
- {
- Irp->IoStatus.Information=writeLength;
- Irp->IoStatus.Status=STATUS_ACCESS_DENIED;
- IoCompleteRequest(Irp,IO_NO_INCREMENT);
- status=STATUS_ACCESS_DENIED;
- }
- else
- {
- status=IrpMjWrite(DeviceObject,Irp);
- }
- return status;
- }
- NTSTATUS fake_IrpMjRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
- {
- NTSTATUS status = STATUS_SUCCESS;
- IO_STACK_LOCATION *irpsp= IoGetCurrentIrpStackLocation(Irp);
- LARGE_INTEGER ReadOffsetInBytes= irpsp->Parameters.Read.ByteOffset;
- ULONG readLength = irpsp->Parameters.Write.Length;
- LARGE_INTEGER Sectionpos={0};
- ULONG SectorRange;
- BOOLEAN IsDeniedAccess = FALSE;
- do
- {
- Sectionpos.HighPart = 0;
- Sectionpos.LowPart = 0x200;
- SectorRange= ReadOffsetInBytes.QuadPart/Sectionpos.QuadPart;
- if(SectorRange==0 && DeviceObject->DeviceType==FILE_DEVICE_DISK)
- {
- IsDeniedAccess =TRUE;
- break;
- }
- }while(0);
- if (IsDeniedAccess)
- {
- Irp->IoStatus.Information=readLength;
- Irp->IoStatus.Status=STATUS_ACCESS_DENIED;
- IoCompleteRequest(Irp,IO_NO_INCREMENT);
- status=STATUS_ACCESS_DENIED;
- }
- else
- {
- status=IrpMjWrite(DeviceObject,Irp);
- }
- return status;
- }
复制代码
运行保护器后,虽然能用CreateFileA打开物理磁盘,但是不能进行读写操作了。无论是调用NtWriteFile还是构造FSD的IRP甚至是SCSI的IRP都无效。要绕过这种方式保护MBR的话,可以用解析Disk.sys的方式取得原始地址并恢复或者绕过钩子,或者构造更加底层的atapi.sys的IRP包发送到DriverStartIo。那样一来仍然可以绕过这种高级保护方式,本来安全就没有绝对的。
效果如图所示:
高级保护
文章似乎可以到此为止了,但还是有几句话要说。
一、初级以及中级的方式保护代码只需要把关键字"PhysicalDriver"改成自己的文件名就能实现文件保护器了。
二、高级的方式保护MBR可以通过禁止读取内核文件的方式防止被恢复或者绕过。
三、本文附带的附件,其附件描述摘自Hovi.Delphic的帖子,并不设置阅读权限以及水晶币。 |
评分
-
查看全部评分
|