Перехват записи в памяти процесса по определённому адресу
Както задавал вопрос, но с тех пор немного продвинулся.
Код:
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
function AddVectoredExceptionHandler(FirstHandler: CARDINAL; VectoredHandler: POINTER): POINTER; stdcall; external 'Kernel32.dll' name 'AddVectoredExceptionHandler';
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
PEXCEPTION_RECORD = ^EXCEPTION_RECORD;
EXCEPTION_RECORD = packed record
ExceptionCode: DWORD;
ExceptionFlags: DWORD;
ExceptionRecord: PEXCEPTION_RECORD;
ExceptionAddress: POINTER;
NumberParameters: DWORD;
ExceptionInformation: ULONG_PTR ;
end;
type
PEXCEPTION_POINTERS = ^EXCEPTION_POINTERS;
EXCEPTION_POINTERS = packed record
ExceptionRecord: PEXCEPTION_RECORD;
ContextRecord: PCONTEXT;
end;
const
CALL_LAST = 0;
CALL_FIRST = 1;
EXCEPTION_CONTINUE_SEARCH = $0;
EXCEPTION_EXECUTE_HANDLER = $1;
EXCEPTION_CONTINUE_EXECUTION = $FFFFFFFF;
var
Form1: TForm1;
SI: SYSTEM_INFO;
MemAddr, MemAddr2: POINTER;
lpflOldProtect, WriteEvent: CARDINAL;
implementation
{$R *.dfm}
// ФУНКЦИЯ ПОТОКА ОЖИДАЮЩЕГО КОГДА ПРОИЗОЙДЁТ ЗАПИСЬ И ЗАПРЕЩАЮЩЕГО ЕЁ
function MyWaitThread(Data: POINTER): INTEGER; stdcall;
var
A: PAnsiString;
begin
A := Data;
repeat
WaitForSingleObject(WriteEvent, INFINITE);
Sleep(1);
VirtualProtect(MemAddr, SI.dwPageSize, PAGE_READONLY, @lpflOldProtect);
Form1.Memo1.Lines.Add(A^);
Form1.Memo1.Lines.Add('Запись запрещена!');
until 2 < 1;
end;
// ОБРАБОТЧИК ИСКЛЮЧЕНИЙ ПОВЕРХ СИСТЕМНОГО
function MyVectoredHandler(ExceptionInfo: PEXCEPTION_POINTERS): INTEGER; stdcall;
begin
if (ExceptionInfo.ExceptionRecord.ExceptionCode = EXCEPTION_ACCESS_VIOLATION) and (ExceptionInfo.ContextRecord.Eax = CARDINAL(MemAddr))then
begin
VirtualProtect(MemAddr, SI.dwPageSize, PAGE_READWRITE, @lpflOldProtect);
SetEvent(WriteEvent);
result := EXCEPTION_CONTINUE_EXECUTION;
end
else
result := EXCEPTION_CONTINUE_SEARCH;
end;
// КНОПКА ВЫЗЫВАЕТ ИСКЛЮЧЕНИЕ И ОНО ОБРАБАТЫВАЕТСЯ МОИМ ОБРАБОТЧИКОМ
procedure TForm1.Button1Click(Sender: TObject);
var
A: PAnsiString;
begin
A := MemAddr;
A^ := 'TeSt STRing';
end;
// КНОПКА ВЫЗЫВАЕТ ИСКЛЮЧЕНИЕ, НО МОЙ ОБРАБОТЧИК ЕГО НЕ ОБРАБАТЫВАЕТ
procedure TForm1.Button2Click(Sender: TObject);
var
A: PAnsiChar;
begin
A := MemAddr2; // Тут программа ведёт себя стандартно,
A^ := 'S'; // т.е. появляется сообщение Access Violation
end;
procedure TForm1.FormCreate(Sender: TObject);
var
PrevHandler: POINTER;
HThread, TId: CARDINAL;
begin
Memo1.Clear;
GetSystemInfo(SI);
MemAddr := NIL;
MemAddr2 := NIL;
MemAddr := VirtualAlloc(NIL, SI.dwPageSize * 10, MEM_COMMIT, PAGE_READONLY); // Выделение памяти только для чтения
MemAddr2 := VirtualAlloc(NIL, SI.dwPageSize * 10, MEM_COMMIT, PAGE_READONLY); // Ещё
if not Assigned(MemAddr) or not Assigned(MemAddr2) then ShowMessage('Ошибка при выделении памяти: ' + IntToStr(GetLastError)) else
begin
WriteEvent := CreateEvent(NIL, FALSE, FALSE, 'Write');
if WriteEvent = 0 then ShowMessage('Ошибка в создании объекта события: ' + IntToStr(GetLastError));
HThread := CreateThread(NIL, 0, Addr(MyWaitThread), MemAddr, 0, TId);
if HThread = 0 then ShowMessage('Ошибка при создании потока: ' + IntToStr(GetLastError));
// Установка обработчика исключений
PrevHandler := AddVectoredExceptionHandler(CALL_FIRST, Addr(MyVectoredHandler));
if not Assigned(PrevHandler) then ShowMessage('Ошибка при установке обработчика исключений: ' + IntToStr(GetLastError));
Edit1.Text := IntToHex(CARDINAL(MemAddr), 8) + ' ' + IntToHex(CARDINAL(MemAddr2), 8);
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
VirtualFree(MemAddr, SI.dwPageSize * 10, 0);
VirtualFree(MemAddr2, SI.dwPageSize * 10, 0);
end;
end.
Насколько я понял(из прочитаного об исключениях) при возникновении исключения управление передаётся обработчику и если он сообщит что исключение обработано то управление опять передаётся на инструкцию вызвавшую исключение.
То есть по идее запись всё равно должна произойти не смотря на то что я сигнализировал потоку запретить запись.
На практике этот код работает, но можно ли пологаться на такие вещи в Windows ХР
2. Полагаться на поведение векторизованных обработчиков исключений, в данном случае страничных - можно как раз начиная с XP, ибо векторизованные исключения начиная с него и введены.
PS: Бросьте дельфей на помойку, где их заслуженное место
Векторизованные обработчики как показывает практика работают как часы. Вопрос как раз в том можно ли расчитывать на то что запись после срабатывания обработчика будет выполнена, до того как другие потоки получат управление.
Одноразовый перехват вообще чёткий, но как его сделать многоразовым пока не соображу.
см.
Suspend - приостанавливает выполнение потока;
Resume - продолжает выполнение потока;
Terminate - пытается завершить поток путем установки свойства Terminated в True. Для прекращения выполнения потока во время работы, необходимо периодически проверять свойство Terminated в пределах метода Execute (см. ниже) и во всех методах, которые Execute вызывает, и выходить, если Terminated=True.
Execute - содержит основной код потока, т.е. те действия, которые Вы решили поместить в поток. Если Вы обратили внимание, этот метод абстрактный, т.е. его надо переопределять в пределах Вашего класса-потомка TThread.
Synchronize - используется для предотвращения одновременного доступа разных потоков к одному элементу VCL. Дело в том, что код VCL выполняется в главном потоке программы, так что поток должен синхронизироваться, если он использует код VCL. Параметр метода - это метод без параметров, который обычно принадлежит Вашему потоковому классу.
или
см.
Вот здесь можно создать пользовательское событие после завершения потока, проверить условия If Then и вызывать снова пользовательское событие.
Одно могу сказать как на Delfi это делается не знаю. VC++,VC#, VB знаю.
Так-что остановимся на этом:
begin
while True do
if Teminated then break;
end;
Спасибо. Вы очень стремились помоч, и в некоторых вопросах без сомнений помогли. Наша переписка в личке не прошла даром, после этого я почитал некоторые материалы.
Не зная замысла полностью и не видя большую часть кода Вы действительно не плохо помогли.
Код уже написан, вчера тестировал. Программа проработала около двух часов без вылетов, при этом процент перехвата пока установить сложно(это ещё предстоит придумать какой нибудь тест) но на глаз выше 90%
Теперь нужно приступать к полному тестированию работы которая длилась несколько месяцев.
В общем будем провацировать баги, надеюсь их будет минимум.