Использование 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