Использование native kernel32 функций для получения проекции физической памяти

=== Использование native kernel32 функций для получения проекции физической памяти ===
В процессе написания программного обеспечения в ряде случаев возникает необходимость получения данных из физических ячеек памяти. Документации Delphi по данному вопросу, я найти не смог, поэтому хочу восполнить данный пробел.
Платформа WinNT(XP) не допускает возможность непосредственного доступа к памяти средствами Win32API. В этом случае программист должен или написать свой драйвер доступа к физической памяти или использовать native kernel32 функции ядра.
Рассмотрим второй вариант использующий объект "проекция файла" (file-mapping object), представляющем собой блок памяти(раздел) доступный двум и более процессам для совместного использования.
Совместное использование данных с помощью объекта "раздел" происходит следующим образом: Задав атрибуты с помощью функции, которая фактически заполняет структуру NtObjectAttributes
procedure InitializeObjectAttributes(InitializedAttributes: PNtObjectAttributes;
 pObjectName: PNtUnicodeString;
 const uAttributes: ULONG;
 const hRootDirectory: THandle;
 pSecurityDescriptor: PSECURITY_DESCRIPTOR);
begin
 with InitializedAttributes^ do
 begin
 Length := SizeOf(TNtObjectAttributes);
 ObjectName := pObjectName;
 Attributes := uAttributes;
 RootDirectory := hRootDirectory;
 SecurityDescriptor := pSecurityDescriptor;
 SecurityQualityOfService := nil;
 end;
end;
Используем объект '\Device\PhysicalMemory' и преобразовав его в тип TNtUnicodeString;
RtlInitAnsiString(@AnsiPhysicalMemory, '\Device\PhysicalMemory');
RtlAnsiStringToUnicodeString(@UniPhysicalMemory, @AnsiPhysicalMemory, true);
InitializeObjectAttributes(@NtObjectAttributes, @UniPhysicalMemory, OBJ_KERNEL_HANDLE, 0, nil);
Получаем дескриптор секции вызывая функцию ядра
NtOpenSection(SectionHandle, SECTION_MAP_READ, @NtObjectAttributes);
Этим самым мы открываем объект '\Device\PhysicalMemory' для чтения отображенного участка физической памяти в процессе пользователя.
Отображение осуществляем с помощью функции NtMapViewOfSection возвращающей указатель на участок памяти процесса пользователя в который осуществляется отображение. Более подробную информацию можно найти в MicrosoftDDK.
Привожу несложный пример.
unit PhysMemWorks;
interface
uses Windows;
type
 NTSTATUS = LongInt;
 PLARGE_INTEGER = ^LARGE_INTEGER;
 TSectionInherit = (ViewNone, ViewShare, ViewUnmap);
 SECTION_INHERIT = TSectionInherit;
 PHYSICAL_ADDRESS = record
 LowPart: DWORD;
 HighPart: DWORD;
 end;
 TNtAnsiString = packed record
 Length: Word;
 MaximumLength: Word;
 Buffer: PChar;
 end;
 PNtAnsiString = ^TNtAnsiString;
 ANSI_STRING = TNtAnsiString;
 TNtUnicodeString = packed record
 Length: Word;
 MaximumLength: Word;
 Buffer: PWideChar;
 end;
 UNICODE_STRING = TNtUnicodeString;
 PNtUnicodeString = ^TNtUnicodeString;
 TNtObjectAttributes = packed record
 Length: ULONG;
 RootDirectory: THandle;
 ObjectName: PNtUnicodeString;
 Attributes: ULONG;
 SecurityDescriptor: Pointer;
 SecurityQualityOfService: Pointer;
 end;
 OBJECT_ATTRIBUTES = TNtObjectAttributes;
 PNtObjectAttributes = ^TNtObjectAttributes;
function OpenPhysicalMemory: DWORD;
function MapPhysicalMemory(hPhysMem: tHANDLE; pdwAddress: DWORD;
 pdwLength: DWORD; pdwBaseAddress: pDWORD): DWORD;
///////////
const
 DLL = 'ntdll.dll';
function RtlAnsiStringToUnicodeString(DestinationString: PNtUnicodeString;
 SourceString: PNtAnsiString; AllocateDestinationString: boolean): NTSTATUS;
 stdcall; external DLL Name 'RtlAnsiStringToUnicodeString';
procedure RtlInitAnsiString(DestinationString: PNtAnsiString; SourceString: PChar);
 stdcall; external DLL Name 'RtlInitAnsiString';
function NtMapViewOfSection(SectionHandle: THandle; ProcessHandle: THandle;
 var BaseAddress: PDWORD; ZeroBits: ULONG; CommitSize: ULONG;
 SectionOffset: PLARGE_INTEGER;
 ViewSize: DWORD; InheritDisposition: SECTION_INHERIT; AllocationType: ULONG;
 Protect: ULONG): NTSTATUS; stdcall; external DLL Name 'NtMapViewOfSection';
function NtUnmapViewOfSection(const ProcessHandle: THandle;
 const BaseAddress: Pointer): NTSTATUS; stdcall;
 external DLL Name 'NtUnmapViewOfSection';
function NtOpenSection(out SectionHandle: THandle;
 const DesiredAccess: ACCESS_MASK; ObjectAttributes: PNtObjectAttributes): NTSTATUS;
 stdcall; external DLL Name 'NtOpenSection';
implementation
const
 OBJ_KERNEL_HANDLE = $0000200;
var
 status: DWORD;
procedure InitializeObjectAttributes(InitializedAttributes: PNtObjectAttributes;
 pObjectName: PNtUnicodeString; const uAttributes: ULONG;
 const hRootDirectory: THandle; pSecurityDescriptor: PSECURITY_DESCRIPTOR);
begin
 with InitializedAttributes^ do
 begin
 Length := SizeOf(TNtObjectAttributes);
 ObjectName := pObjectName;
 Attributes := uAttributes;
 RootDirectory := hRootDirectory;
 SecurityDescriptor := pSecurityDescriptor;
 SecurityQualityOfService := nil;
 end;
end;
function OpenPhysicalMemory: DWORD;
var
 hPhysMem: DWORD;
 UniPhysicalMemory: TNtUnicodeString;
 AnsiPhysicalMemory: TNtAnsiString;
 oa: TNtObjectAttributes;
begin
 RtlInitAnsiString(@AnsiPhysicalMemory, '\Device\PhysicalMemory');
 status := RtlAnsiStringToUnicodeString(@UniPhysicalMemory, @AnsiPhysicalMemory, True);
 InitializeObjectAttributes(@oa, @UniPhysicalMemory, OBJ_KERNEL_HANDLE, 0, nil);
 status := NtOpenSection(hPhysMem, SECTION_MAP_READ, @oa);
 if status <> 0 then
 Result := 0
 else
 Result := hPhysMem;
end;
function MapPhysicalMemory(hPhysMem: tHANDLE; pdwAddress: DWORD;
 pdwLength: DWORD; pdwBaseAddress: pDWORD): DWORD;
var
 SectionOffset: pLARGE_INTEGER;
begin
 SectionOffset.HighPart := 0;
 SectionOffset.LowPart := pdwAddress;
 NtMapViewOfSection(hPhysMem, 0, pdwBaseAddress, 0, 0, nil, 0, ViewNone, 0, PAGE_READONLY);
 Result := 1;
end;
function UnmapPhysicalMemory(dwBaseAddress: DWORD): DWORD;
begin
 NtUnmapViewOfSection(0, @dwBaseAddress);
 Result := 1;
end;
end.
Используя данный модуль получаем доступ к функциям ядра которые, в свою очередь, позволяют получить проекцию нужного участка памяти.
На форме разместим компонент StringGrid - для представления информации в табличном виде, Button, Label и Edit и пишем такой код.
unit Read_Mem;
interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls, Grids, ExtCtrls, PhysMemWorks;
type
 TForm1 = class(TForm)
 gridData: TStringGrid;
 Label12: TLabel;
 editAddr: TEdit;
 btnRead: TButton;
 Label2: TLabel;
 procedure btnReadClick(Sender: TObject);
 procedure FormCreate(Sender: TObject);
 end;
var
 Form1: TForm1;
implementation
{$R *.DFM}
{$R WindowsXP.res}
type
 XData = array[1..16] of byte;
 YData = array[1..16] of XData;
 TPhysPointer = ^YData;
procedure TForm1.btnReadClick(Sender: TObject);
var
 i, j: LongInt;
 nAddr: Int64;
 s1, s2: string;
 b: Byte;
 ch: Char;
 arrayMemory: PByteArray;
 PointMemory: Pointer;
 hmemory: DWORD;
 xhex: Integer;
 yhex: Integer;
 ofsetHex: Integer;
begin
 with gridData do
 begin
 ColWidths[0] := Canvas.TextWidth(IntToHex(0, 9));
 ColWidths[1] := Canvas.TextWidth(Cells[1, 0]);
 end;
 nAddr := StrToInt('$' + editAddr.Text);
 Label2.Caption := IntToStr(nAddr div 1024) + ' kб';
 hmemory := OpenPhysicalMemory;
 PointMemory := MapViewOfFile(hmemory, FILE_MAP_READ, 0, nAddr, $2000);
 // размер секции 8 кб
 arrayMemory := PointMemory;
 xhex := nAddr and $0f;
 yhex := (nAddr and $00f0) div 16;
 ofsetHex := ((nAddr and $0f00) div 16);
 if yhex = 0 then
 yhex := 0;
 if PointMemory <> nil then
 begin
 for i := 1 to 16 do
 begin
 gridData.Cells[0, i] := IntToHex(nAddr, 8);
 s1 := ;
 s2 := ;
 for j := 1 to 16 do
 begin
 b := arrayMemory^[((i + ofsetHex + yhex - 1) * 16) + (j + xhex - 1)];
 s1 := s1 + IntToHex(b, 2) + ' ';
 if b >= $20 then
 ch := Char(b)
 else
 ch := '.';
 s2 := s2 + ch;
 end;
 gridData.Cells[1, i] := s1;
 gridData.Cells[2, i] := s2;
 nAddr := nAddr + 16;
 end;
 with gridData do
 begin
 ColWidths[2] := Canvas.TextWidth(Cells[2, 1] + ' ');
 end;
 end
 else
 MessageDlg('Этот участок памяти' + ^M + ' недоступен!!!',
 mtWarning, [mbOK], 0);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
 i: Integer;
begin
 with gridData do
 begin
 Cells[0, 0] := ' ADDR';
 Cells[1, 0] := ;
 for i := 0 to 15 do
 Cells[1, 0] := Cells[1, 0] + IntToHex(i, 2) + ' ';
 Cells[2, 0] := ' ASCII';
 end;
end;
end.
Готово! У нас есть приложение позволяющее просматривать физическую память. Наберите, например, в поле адреса 000FFF00 , нажмите "Read" и в ячейках начиная с FFFF5 прочитайте дату прошивки BIOS Вашей материнской платы.
Используя данные функции Вы легко получаете возможность просмотра всего объёма физической памяти, за исключением системных адресов операционной системы.
Copyright© 2006 Альберт Мамедов (MagDelphi) Специально для Delphi Plus
Author: Альберт Мамедов
ID: 02079