论磁盘内存映射
这个题目有点眼熟吧,文件内存映射用的比较多,用于大文件读写效率较高。其原理是将映射的内存页的来源标记为磁盘驱动器,这样对地址的读写产生中断时,会直接操作磁盘,这样对打文件的读写速度提高很多。然而这种技术微软只用在文件上,对于目录、磁盘和设备均不能使用。而普通缓冲区buffer方式读写,总是要有内存拷贝,所以相对慢一些,在我3年前做数据恢复的时候,为了研究大文件恢复接触到文件内存映射,第一次就很纳闷到底有没有磁盘映射,这样我就不需要总是读来读去。下面给出这2种方式的区别:Buffered方式:
CreateFile
ReadFile/WriteFile
CloseHandle
mapping方式:
CreateFile
CreateFileMapping
MapViewOfFile
指针操作
UnmapViewOfFile
CloseHandle
CloseHandle
在这里我对相关内核API做了逆向和破解,写了一个驱动支持对于磁盘实现内存映射。主要是对IRP_MJ_QUERY_INFORMATION和FastIoQueryStandardInfo的hook,代码如下:
驱动层:
#include <ntddk.h>
#include "DeviceMappingCommon.h"
typedef struct _DEVICE_MAPPING
{
HANDLE hDevice;
PFILE_OBJECT pFileObject;
LARGE_INTEGER dwMaximumSize;
}DEVICE_MAPPING, *PDEVICE_MAPPING;
PDEVICE_MAPPING gMappingData = NULL;
PDRIVER_DISPATCH OriginQuery = NULL;
PFAST_IO_QUERY_STANDARD_INFO OriginFastDispatch = NULL;
FAST_MUTEX gLock;
extern "C"
{
BOOLEAN NTAPI ObFindHandleForObject(PEPROCESS, PVOID, PVOID, PVOID, PHANDLE);
NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS);
}
VOID DeviceMappingDriverUnload(PDRIVER_OBJECT DriverObject)
{
ExAcquireFastMutex(&gLock);
if(gMappingData)
{
ExFreePool((PVOID)gMappingData);
gMappingData = NULL;
}
ExReleaseFastMutex(&gLock);
if(DriverObject->DeviceObject)
{
UNICODE_STRING DriverName;
RtlInitUnicodeString(&DriverName, L"\\Device\\DeviceMapping");
IoDeleteSymbolicLink(&DriverName);
IoDeleteDevice(DriverObject->DeviceObject);
}
return;
}
NTSTATUS DeviceMappingQueryInformation(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
if(stack->Parameters.QueryFile.FileInformationClass == FileStandardInformation && stack->FileObject)
{
HANDLE hDevice = NULL;
BOOLEAN IsRef = FALSE;
ExAcquireFastMutex(&gLock);
if(gMappingData && stack->FileObject == gMappingData->pFileObject)
{
PFILE_STANDARD_INFORMATION FileInformation = (PFILE_STANDARD_INFORMATION)pIrp->AssociatedIrp.SystemBuffer;
FileInformation->EndOfFile.QuadPart = gMappingData->dwMaximumSize.QuadPart;
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
IsRef = TRUE;
}
ExReleaseFastMutex(&gLock);
if(IsRef)
return STATUS_SUCCESS;
}
OriginQuery(pDevObj, pIrp);
}
BOOLEAN DeviceMappingFastIo(PFILE_OBJECT FileObject, BOOLEAN Wait, PFILE_STANDARD_INFORMATION Buffer,PIO_STATUS_BLOCK IoStatus,struct _DEVICE_OBJECT *DeviceObject)
{
BOOLEAN ret = OriginFastDispatch(FileObject, Wait, Buffer, IoStatus, DeviceObject);
ExAcquireFastMutex(&gLock);
if(FileObject == gMappingData->pFileObject)
{
Buffer->Directory = FALSE;
}
ExReleaseFastMutex(&gLock);
return ret;
}
NTSTATUS DeviceMappingIOControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch(code)
{
case IOCTL_ENABLE_DEVICEMAPPING:
{
DEVICE_MAPPING tmp;
PFILE_OBJECT fo = NULL;
PDRIVER_DISPATCH* ppdd = NULL;
PFAST_IO_QUERY_STANDARD_INFO* ppfiqsi = NULL;
DEVICEMAPPING_IOCTL_DATA* InputBuffer = (DEVICEMAPPING_IOCTL_DATA*)pIrp->AssociatedIrp.SystemBuffer;
RtlZeroMemory(&tmp, sizeof(tmp));
tmp.hDevice = InputBuffer->hDevice;
status = ObReferenceObjectByHandle(InputBuffer->hDevice, NULL, *IoFileObjectType, KernelMode, (PVOID*)&fo, NULL);
if(NT_SUCCESS(status))
{
tmp.pFileObject = fo;
PDEVICE_OBJECTrelatedevice = IoGetRelatedDeviceObject(fo);
PDRIVER_OBJECT relatedriver = NULL;
PFAST_IO_DISPATCH relatedfastdispatch = NULL;
tmp.dwMaximumSize.QuadPart = InputBuffer->size;
if(relatedriver = relatedevice->DriverObject)
{
OriginQuery = relatedriver->MajorFunction;
ppdd = &relatedriver->MajorFunction;
relatedfastdispatch = relatedriver->FastIoDispatch;
if(relatedfastdispatch && relatedfastdispatch->FastIoQueryStandardInfo)
{
OriginFastDispatch = relatedfastdispatch->FastIoQueryStandardInfo;
ppfiqsi = &relatedfastdispatch->FastIoQueryStandardInfo;
}
}
ObDereferenceObject(fo);
}
ExAcquireFastMutex(&gLock);
if(gMappingData)
{
ExFreePool((PVOID)gMappingData);
gMappingData = NULL;
}
gMappingData = (DEVICE_MAPPING*)ExAllocatePool(NonPagedPool,sizeof(DEVICE_MAPPING));
if(gMappingData && ppdd)
{
RtlCopyMemory(gMappingData, &tmp, sizeof(DEVICE_MAPPING));
*ppdd = DeviceMappingQueryInformation;
if(ppfiqsi)
{
*ppfiqsi = DeviceMappingFastIo;
}
}
ExReleaseFastMutex(&gLock);
break;
}
case IOCTL_DISABLE_DEVICEMAPPING:
{
ExAcquireFastMutex(&gLock);
if(gMappingData)
{
PFILE_OBJECT fo = NULL;
PDEVICE_OBJECT relatedevice = NULL;
PDRIVER_OBJECT relatedriver = NULL;
status = ObReferenceObjectByHandle(gMappingData->hDevice, NULL, *IoFileObjectType, KernelMode, (PVOID*)&fo, NULL);
if(NT_SUCCESS(status))
{
relatedevice = IoGetRelatedDeviceObject(fo);
if(relatedevice)
relatedriver = relatedevice->DriverObject;
if(relatedriver && relatedriver->MajorFunction == DeviceMappingQueryInformation)
{
relatedriver->MajorFunction = OriginQuery;
}
if(relatedriver && relatedriver->FastIoDispatch && relatedriver->FastIoDispatch->FastIoQueryStandardInfo == DeviceMappingFastIo)
{
relatedriver->FastIoDispatch->FastIoQueryStandardInfo = OriginFastDispatch;
}
ObDereferenceObject(fo);
}
ExFreePool((PVOID)gMappingData);
gMappingData = NULL;
}
ExReleaseFastMutex(&gLock);
break;
}
default:
status = STATUS_INVALID_VARIANT;
}
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = info;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
NTSTATUS DeviceMappingDriverDefaultDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#pragma code_seg("INIT")
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
UNICODE_STRING DevName;
PDEVICE_OBJECT pDevObj = NULL;
NTSTATUS status = STATUS_SUCCESS;
pDriverObj->DriverUnload = DeviceMappingDriverUnload;
pDriverObj->MajorFunction = DeviceMappingDriverDefaultDispatch;
pDriverObj->MajorFunction = DeviceMappingDriverDefaultDispatch;
pDriverObj->MajorFunction = DeviceMappingDriverDefaultDispatch;
pDriverObj->MajorFunction = DeviceMappingDriverDefaultDispatch;
pDriverObj->MajorFunction = DeviceMappingIOControl;
RtlInitUnicodeString(&DevName, L"\\Device\\DeviceMapping");
ExInitializeFastMutex(&gLock);
status = IoCreateDevice(pDriverObj, 0, &DevName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);
if(NT_SUCCESS(status))
{
pDevObj->Flags |= DO_BUFFERED_IO;
UNICODE_STRING SymLinkName;
RtlInitUnicodeString(&SymLinkName, L"\\??\\DeviceMapping");
IoCreateSymbolicLink(&SymLinkName, &DevName);
}
return status;
}
#define MAXNAME 256
#define IOCTL_ENABLE_DEVICEMAPPING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_DISABLE_DEVICEMAPPING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _DEVICEMAPPING_IOCTL_DATA//和驱动通信所用data
{
HANDLE hDevice;
ULONG size;
}DEVICEMAPPING_IOCTL_DATA, *PDEVICEMAPPING_IOCTL_DATA;
应用层:
#include <windows.h>
#include "DeviceMappingCommon.h"
#include <time.h>
#include <iostream>
using namespace std;
#define BUFSIZE 0x1000
#define WRITECOUNT 0x10000
void MappingRead(HANDLE hc)
{
HANDLE hObj = CreateFileA("c:\\1.dat", GENERIC_ALL, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if(hObj == INVALID_HANDLE_VALUE)
{
cout<<"Open 1.dat failed"<<endl;
return;
}
HANDLE hMap = CreateFileMapping(hc, NULL, PAGE_READONLY, 0, BUFSIZE * WRITECOUNT, NULL);
if(hMap)
{
PBYTE pbDev = (PBYTE)MapViewOfFile(hMap, FILE_MAP_COPY, 0, 0, 0);
DWORD cbwrite;
if(pbDev)
{
for(int i=0;i < WRITECOUNT; i++)
{
WriteFile(hObj, pbDev + i * BUFSIZE, BUFSIZE, &cbwrite, NULL);
}
UnmapViewOfFile(pbDev);
}
CloseHandle(hMap);
}
else
cout<<"error:"<<GetLastError();
CloseHandle(hObj);
}
void BufferedRead(HANDLE hc)
{
BYTE buf;
int count = 0;
DWORD cbwrite;
HANDLE hObj = CreateFileA("c:\\2.dat", GENERIC_ALL, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if(hObj == INVALID_HANDLE_VALUE)
{
cout<<"Open 2.dat failed"<<endl;
return;
}
while(ReadFile(hc, buf, BUFSIZE, &cbwrite, NULL) && cbwrite && count++ < WRITECOUNT)
{
WriteFile(hObj, buf, BUFSIZE, &cbwrite, NULL);
}
CloseHandle(hObj);
}
void main()
{
HANDLE hFile;
hFile = CreateFileA("\\\\.\\c:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
cout<<"Open c: failed"<<endl;
return;
}
time_t begin,end;
_asm{int 3};
HANDLE hDeviceMapping = CreateFileA("\\\\.\\DeviceMapping",0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
int i=GetLastError();
if(hDeviceMapping != INVALID_HANDLE_VALUE)
{
DEVICEMAPPING_IOCTL_DATA did = {hFile, BUFSIZE * WRITECOUNT};
DWORD retlen;
DeviceIoControl(hDeviceMapping, IOCTL_ENABLE_DEVICEMAPPING, &did, sizeof(did), &did, 0, &retlen, NULL);
begin = time(NULL);
MappingRead(hFile);
end = time(NULL);
DeviceIoControl(hDeviceMapping, IOCTL_DISABLE_DEVICEMAPPING, &did, sizeof(did), &did, 0, &retlen, NULL);
CloseHandle(hDeviceMapping);
cout << "Mapping time:" <<end - begin << endl;
}
else
cout<< "Error info:"<<GetLastError()<<endl;
begin = time(NULL);
BufferedRead(hFile);
end=time(NULL);
cout << "Buffer time:" << end - begin << endl;
CloseHandle(hFile);
}
分析过程:
//#include <ntddk.h>
#include <ntifs.h>
typedef struct _HANDLE_TABLE
{
ULONG_PTR TableCode;
PEPROCESS QuotaProcess;
HANDLE UniqueProcessId;
#define HANDLE_TABLE_LOCKS 4
EX_PUSH_LOCK HandleTableLock;
LIST_ENTRY HandleTableList;
EX_PUSH_LOCK HandleContentionEvent;
PVOID DebugInfo;
LONG ExtraInfoPages;
ULONG FirstFree;
ULONG LastFree;
ULONG NextHandleNeedingPool;
LONG HandleCount;
union
{
ULONG Flags;
BOOLEAN StrictFIFO : 1;
};
} HANDLE_TABLE, *PHANDLE_TABLE;
typedef struct _HANDLE_TABLE_ENTRY
{
union
{
PVOID Object;
ULONG ObAttributes;
PACCESS_MASK AuditMask;
ULONG_PTR Value;
};
union
{
union
{
ACCESS_MASK GrantedAccess;
struct
{
USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
};
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
extern "C"
{
typedef BOOLEAN (*EX_ENUMERATE_HANDLE_ROUTINE)(PHANDLE_TABLE_ENTRY,HANDLE,PVOID );
BOOLEAN __stdcall ObFindHandleForObject(PEPROCESS,PVOID,PVOID,PVOID,PHANDLE);
PHANDLE_TABLE __stdcall ObReferenceProcessHandleTable (PEPROCESS);
BOOLEAN __stdcall ExEnumHandleTable (PHANDLE_TABLE, EX_ENUMERATE_HANDLE_ROUTINE ,PVOID,PHANDLE);
}
VOID Unload(PDRIVER_OBJECT pDriverObject)
{
}
PDRIVER_DISPATCH defaultdispatch=NULL;
NTSTATUS QuerySize (PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
PAGED_CODE();
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(pIrp);
KdPrint(("MajorFunction=%d MinorFunction=%d\n",IrpSp->MajorFunction,IrpSp->MinorFunction));
if(IrpSp->Parameters.QueryFile.FileInformationClass == FileStandardInformation)
{
IO_STATUS_BLOCK isb;
FILE_FS_FULL_SIZE_INFORMATION ffsi;
HANDLE handle = NULL;
RtlZeroMemory(&ffsi,sizeof(ffsi));
if(!IrpSp->FileObject){}
else
{
if(ObFindHandleForObject(IoGetCurrentProcess(),IrpSp->FileObject,NULL,NULL,&handle))
{
status = NtQueryVolumeInformationFile(handle,&isb,&ffsi,sizeof(ffsi),FileFsFullSizeInformation);
if(NT_SUCCESS(status))
{
// KdPrint(("ActualAvailableAllocationUnits=%L\n",&ffsi.ActualAvailableAllocationUnits));
// KdPrint(("BytesPerSector=%d\n",ffsi.BytesPerSector));
// KdPrint(("TotalAllocationUnits=%L\n",&ffsi.TotalAllocationUnits));
// KdPrint(("CallerAvailableAllocationUnits=%L\n",&ffsi.CallerAvailableAllocationUnits));
// KdPrint(("SectorsPerAllocationUnit=%d\n",ffsi.SectorsPerAllocationUnit));
}
}
PFILE_STANDARD_INFORMATION FileInformation = (PFILE_STANDARD_INFORMATION)pIrp->AssociatedIrp.SystemBuffer;
FileInformation->EndOfFile.QuadPart = ffsi.TotalAllocationUnits.QuadPart;
FileInformation->EndOfFile.QuadPart *= ffsi.SectorsPerAllocationUnit * ffsi.BytesPerSector;
return STATUS_SUCCESS;
}
}
return defaultdispatch(pDeviceObject,pIrp);
}
typedef struct _DEVICE_EXTENSION
{
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE hfile;
pDriverObj->DriverUnload=Unload;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING drivename;
IO_STATUS_BLOCK isb;
PFILE_OBJECT fo;
LARGE_INTEGER li={0};
RtlInitUnicodeString(&drivename,L"\\??\\c:");
InitializeObjectAttributes(&oa,&drivename,OBJ_CASE_INSENSITIVE,NULL,NULL);
status = ZwOpenFile(&hfile,FILE_READ_DATA|FILE_READ_ATTRIBUTES|SYNCHRONIZE,&oa,&isb,
FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(status))
{
KdPrint(("\n\nDriverEntry Entered\nHandle=%08x\n",hfile));
status = ObReferenceObjectByHandle(hfile,0,*IoFileObjectType,KernelMode,(PVOID*)&fo,NULL);
KdPrint(("fo=%p\n",fo));
FILE_FS_FULL_SIZE_INFORMATION ffsi;
status = NtQueryVolumeInformationFile(hfile,&isb,&ffsi,sizeof(ffsi),FileFsFullSizeInformation);
status = FsRtlGetFileSize(fo,&li);
PDRIVER_OBJECT passocdrive = IoGetRelatedDeviceObject(fo)->DriverObject;
defaultdispatch = passocdrive->MajorFunction;
passocdrive->MajorFunction=QuerySize;
// PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(fo);
status = FsRtlGetFileSize(fo,&li);
HANDLE sechandle;
LARGE_INTEGER li={0};
li.LowPart=0x1000;
RtlInitUnicodeString(&drivename,L"\\??\\map1");
status = ZwCreateSection(&sechandle,SECTION_MAP_READ,&oa,&li,PAGE_READONLY,SEC_COMMIT,hfile);
PVOID addr=NULL;
SIZE_T size=0;
if(NT_SUCCESS(status))
{
status = ZwMapViewOfSection(sechandle,ZwCurrentProcess(),&addr,0,0x1000,NULL,&size,ViewShare,
MEM_TOP_DOWN,PAGE_READONLY);
ULONG data=*(ULONG*)addr;
data=0;
}
status = 0;
}
return STATUS_SUCCESS;
}
测试结果:
分别用缓冲区方式和内存映射方式,分别读取c盘前256MB,分别写入文件中:
Mapping time:23
Buffer time:26
可见还是有一些效果的~~~。
文件内容:
Offset 01234567 89ABCDEF
00000000 EB 52 90 4E 54 46 53 2020 20 20 00 02 08 00 00 隦 NTFS
00000010 00 00 00 00 00 F8 00 003F 00 FF 00 3F 00 00 00 ? ? ?
00000020 00 00 00 00 80 00 80 00B1 8C 7F 02 00 00 00 00 € € 睂
00000030 00 00 0C 00 00 00 00 00CB F8 27 00 00 00 00 00 锁'
00000040 F6 00 00 00 01 00 00 00CC 44 E9 BC 90 E9 BC 3C ? 藾榧 榧<
00000050 00 00 00 00 FA 33 C0 8ED0 BC 00 7C FB B8 C0 07 ?缼屑 |?
00000060 8E D8 E8 16 00 B8 00 0D8E C0 33 DB C6 06 0E 00 庁? ? 幚3燮
00000070 10 E8 53 00 68 00 0D 686A 02 CB 8A 16 24 00 B4 鑃 hhj 藠 $ ?
00000080 08 CD 13 73 05 B9 FF FF8A F1 66 0F B6 C6 40 66 ?s ?婑f 镀@f
00000090 0F B6 D1 80 E2 3F F7 E286 CD C0 ED 06 41 66 0F 堆€?麾喭理 Af
000000A0 B7 C9 66 F7 E1 66 A3 2000 C3 B4 41 BB AA 55 8A 飞f麽f? 么A华U?
000000B0 16 24 00 CD 13 72 0F 81FB 55 AA 75 09 F6 C1 01 $ ?r鸘猽 隽
000000C0 74 04 FE 06 14 00 C3 6660 1E 06 66 A1 10 00 66 t ?胒`f? f
000000D0 03 06 1C 00 66 3B 06 2000 0F 82 3A 00 1E 66 6A f; ?fj
000000E0 00 66 50 06 53 66 68 1000 01 00 80 3E 14 00 00 fP Sfh €>
000000F0 0F 85 0C 00 E8 B3 FF 803E 14 00 00 0F 84 61 00 ? 璩€> 刟
00000100 B4 42 8A 16 24 00 16 1F8B F4 CD 13 66 58 5B 07 碆?$ 嬼?fX[
00000110 66 58 66 58 1F EB 2D 6633 D2 66 0F B7 0E 18 00 fXfX ?f3襢 ?
00000120 66 F7 F1 FE C2 8A CA 668B D0 66 C1 EA 10 F7 36 f黢娛f嬓f陵 ?
00000130 1A 00 86 D6 8A 16 24 008A E8 C0 E4 06 0A CC B8 喼?$ 婅冷谈
00000140 01 02 CD 13 0F 82 19 008C C0 05 20 00 8E C0 66 ? ? 尷 幚f
00000150 FF 06 10 00 FF 0E 0E 000F 85 6F FF 07 1F 66 61 卭fa
00000160 C3 A0 F8 01 E8 09 00 A0FB 01 E8 03 00 FB EB FE 脿?? 狖 ? ?
00000170 B4 01 8B F0 AC 3C 00 7409 B4 0E BB 07 00 CD 10 ?嬸? t ?? ?
00000180 EB F2 C3 0D 0A 41 20 6469 73 6B 20 72 65 61 64 腧? A disk read
00000190 20 65 72 72 6F 72 20 6F63 63 75 72 72 65 64 00 error occurred
000001A0 0D 0A 4E 54 4C 44 52 2069 73 20 6D 69 73 73 69 NTLDR is missi
000001B0 6E 67 00 0D 0A 4E 54 4C44 52 20 69 73 20 63 6F ng NTLDR is co
000001C0 6D 70 72 65 73 73 65 6400 0D 0A 50 72 65 73 73 mpressed Press
000001D0 20 43 74 72 6C 2B 41 6C74 2B 44 65 6C 20 74 6F Ctrl+Alt+Del to
000001E0 20 72 65 73 74 61 72 740D 0A 00 00 00 00 00 00 restart
000001F0 00 00 00 00 00 00 00 0083 A0 B3 C9 00 00 55 AA 儬成U?
。。。。。。。。。。。。。。。。。。。。。 前排学习~ 学习了 :lol 666666666666 我天,厉害orz
页:
[1]