Использование native kernel32 функций для получения проекции физической памяти
Материал из DRKB
Использование 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