我們知道,在NT/2K/XP中,操作系統(tǒng)利用虛擬內(nèi)存管理技術(shù)來維護(hù)地址空間映像,每個進(jìn)程分配一個4GB的虛擬地址空間。運(yùn)行在用戶態(tài)的應(yīng)用程序,不能直接訪問物理內(nèi)存地址;而運(yùn)行在核心態(tài)的驅(qū)動程序,能將虛擬地址空間映射為物理地址空間,從而訪問物理內(nèi)存地址。
如果要在應(yīng)用程序中以物理地址方式訪問內(nèi)存,自然而然的辦法,是編寫一個專用的驅(qū)動程序(如大家熟悉的WinIO),里面設(shè)置一定的IOCTL碼,應(yīng)用程序通過調(diào)用DeviceIoCtrol()來實(shí)現(xiàn)這樣的功能。
那么,有沒有一種方法,省去編寫專用驅(qū)動程序這一步,很方便地就能訪問物理內(nèi)存呢?答案是肯定的。實(shí)際上,微軟早就給我們準(zhǔn)備好了一套辦法,只是他們秘而不宣罷了。系統(tǒng)內(nèi)建一個叫做PhysicalMemory的內(nèi)核對象,可以通過系統(tǒng)核心文件NTDLL.DLL中的有關(guān)API進(jìn)行操縱,從而實(shí)現(xiàn)物理內(nèi)存的直接訪問。微軟聲稱這些API是用于驅(qū)動程序開發(fā)的,在VC/.NET中未提供原型說明和庫文件,然而事實(shí)證明在應(yīng)用程序中調(diào)用它們是沒有問題的。我們感興趣的API主要包括:
ZwOpenSection 或 NtOpenSection - 打開內(nèi)核對象
ZwMapViewOfSection 或 NtMapViewOfSection - 映射虛擬地址空間
ZwUnmapViewOfSection 或 NtUnmapViewOfSection - 取消地址空間映射
RtlInitUnicodeString - 用UNICODE串初始化UNICODE描述的結(jié)構(gòu)
以下的代碼描述了如何利用NTDLL.DLL中的上述幾個API,實(shí)現(xiàn)對物理內(nèi)存的讀取。需要指出的是,只有system擁有讀寫權(quán)限,administrator只有讀權(quán)限,而user連讀權(quán)限都沒有。這一點(diǎn),是不能與專用驅(qū)動程序方法向相比的。
在VC/.NET中,由于沒有相應(yīng)的原型說明和庫文件,我們用GetProcAddress()進(jìn)行DLL顯式調(diào)用。前面大段的代碼,用于說明必需的類型和結(jié)構(gòu)。讀取物理內(nèi)存的主要步驟為:打開內(nèi)核對象 → 映射虛擬地址空間 → 讀取(復(fù)制)內(nèi)存 → 取消地址空間映射。
typedef LONG NTSTATUS;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef enum _SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT, *PSECTION_INHERIT;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
// Interesting functions in NTDLL
typedef NTSTATUS (WINAPI *ZwOpenSectionProc)
(
PHANDLE SectionHandle,
DWORD DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
typedef NTSTATUS (WINAPI *ZwMapViewOfSectionProc)
(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG ZeroBits,
ULONG CommitSize,
PLARGE_INTEGER SectionOffset,
PULONG ViewSize,
SECTION_INHERIT InheritDisposition,
ULONG AllocationType,
ULONG Protect
);
typedef NTSTATUS (WINAPI *ZwUnmapViewOfSectionProc)
(
HANDLE ProcessHandle,
PVOID BaseAddress
);
typedef VOID (WINAPI *RtlInitUnicodeStringProc)
(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
// Global variables
static HMODULE hModule = NULL;
static HANDLE hPhysicalMemory = NULL;
static ZwOpenSectionProc ZwOpenSection;
static ZwMapViewOfSectionProc ZwMapViewOfSection;
static ZwUnmapViewOfSectionProc ZwUnmapViewOfSection;
static RtlInitUnicodeStringProc RtlInitUnicodeString;
// initialize
BOOL InitPhysicalMemory()
{
if (!(hModule = LoadLibrary("ntdll.dll")))
{
return FALSE;
}
// 以下從NTDLL獲取我們需要的幾個函數(shù)指針
if (!(ZwOpenSection = (ZwOpenSectionProc)GetProcAddress(hModule, "ZwOpenSection")))
{
return FALSE;
}
if (!(ZwMapViewOfSection = (ZwMapViewOfSectionProc)GetProcAddress(hModule, "ZwMapViewOfSection")))
{
return FALSE;
}
if (!(ZwUnmapViewOfSection = (ZwUnmapViewOfSectionProc)GetProcAddress(hModule, "ZwUnmapViewOfSection")))
{
return FALSE;
}
if (!(RtlInitUnicodeString = (RtlInitUnicodeStringProc)GetProcAddress(hModule, "RtlInitUnicodeString")))
{
return FALSE;
}
// 以下打開內(nèi)核對象
WCHAR PhysicalMemoryName[] = L"\\Device\\PhysicalMemory";
UNICODE_STRING PhysicalMemoryString;
OBJECT_ATTRIBUTES attributes;
RtlInitUnicodeString(&PhysicalMemoryString, PhysicalMemoryName);
InitializeObjectAttributes(&attributes, &PhysicalMemoryString, 0, NULL, NULL);
NTSTATUS status = ZwOpenSection(&hPhysicalMemory, SECTION_MAP_READ, &attributes );
return (status >= 0);
}
// terminate -- free handles
void ExitPhysicalMemory()
{
if (hPhysicalMemory != NULL)
{
CloseHandle(hPhysicalMemory);
}
if (hModule != NULL)
{
FreeLibrary(hModule);
}
}
BOOL ReadPhysicalMemory(PVOID buffer, DWORD address, DWORD length)
{
DWORD outlen; // 輸出長度,根據(jù)內(nèi)存分頁大小可能大于要求的長度
PVOID vaddress; // 映射的虛地址
NTSTATUS status; // NTDLL函數(shù)返回的狀態(tài)
LARGE_INTEGER base; // 物理內(nèi)存地址
vaddress = 0;
outlen = length;
base.QuadPart = (ULONGLONG)(address);
// 映射物理內(nèi)存地址到當(dāng)前進(jìn)程的虛地址空間
status = ZwMapViewOfSection(hPhysicalMemory,
(HANDLE) -1,
(PVOID *)&vaddress,
0,
length,
&base,
&outlen,
ViewShare,
0,
PAGE_READONLY);
if (status < 0)
{
return FALSE;
}
// 當(dāng)前進(jìn)程的虛地址空間中,復(fù)制數(shù)據(jù)到輸出緩沖區(qū)
memmove(buffer, vaddress, length);
// 完成訪問,取消地址映射
status = ZwUnmapViewOfSection((HANDLE)-1, (PVOID)vaddress);
return (status >= 0);
}
// 一個測試函數(shù),從物理地址0xfe000開始,讀取4096個字節(jié)
// 對于Award BIOS,可以從這段數(shù)據(jù)找到序列號等信息
BOOL test()
{
UCHAR buf[4096];
if (!InitPhysicalMemory())
{
return FALSE;
}
if (!ReadPhysicalMemory(buf, 0xfe000, 4096))
{
// ... 成功讀取了指定數(shù)據(jù)
ExitPhysicalMemory();
return FALSE;
}
ExitPhysicalMemory();
return TRUE;
}
補(bǔ)充說明一點(diǎn),由于Windows虛擬內(nèi)存頁面大小默認(rèn)是4KB,NtMapViewOfSection()返回的虛擬空間基址是按照4KB對齊的,返回的
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/10/28/1354465.aspx