Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Модификация исполняемого кода в Delphi ?


Автор: Robus 30.1.2009, 14:48
Привет всем !!!

Нужно сделать доступ к памяти, где лежит код программы. Конечный результат это модификация кода в момент его исполнения. Я всегда работал с ТМТ под виндовс и не знал с таким проблем !!! То есть на ходу модифицировал код. В делфи столкнулся с проблемой ... Как только записывю что-то в часть памяти с кодом, - получаю EXCEPTION ... Ну и со всеми вытекающими ... Вот пример кода:

Код

...
        ASM
          MOV EAX,11
          MOV DWORD PTR DS:[@M1],EAX
          JMP @EXIT
@M1:      DD 12345678H
@EXIT:    MOV EAX,DWORD PTR DS:[@M1]
          MOV N,EAX
        END;
        Button1.Caption :=IntToHex(N,8);
...


Собственно тут "MOV DWORD PTR DS:[@M1],EAX" мы получаем EXCEPTION ... Настройки компилятора перебрал все, линкера так же перебрал ... Можно легко подлинковать DLL из ТМТ но это не красиво, хочется всё аккуратно !!!

Хочу обратить внимание на то, что запись в DS не есть ошибка !!! Хоть сегменты DS и СS имеют разное значение, указывают они на один кусок физической памяти ... Я пробовал ис СS и ЕS - результат тот же !!!

Как побороть проблему ???

Автор: Romikgy 30.1.2009, 15:47
если уж Вы занимается самомодификацией тогда дизассемблируйте 
Цитата(Robus @  30.1.2009,  13:48 Найти цитируемый пост)
ТМТ

да посмотрите что делает она
(правда даже не знаю что это за ТМТ)

Автор: Rrader 30.1.2009, 16:28
Вот тебе примеры:

1) Модификация данных через селектор:смещение:
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyClass = class(TObject)
  public
    FInt: Integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  TestClass: TMyClass;
  TypInfo: Pointer;
begin
  TypInfo := TMyClass;
  asm
          MOV    EAX, TypInfo
          MOV    DL, 1
          CALL   TMyClass.Create
          MOV    EDX, EAX
          MOV    TestClass, EDX
          MOV    EAX, 12345
          { Можно и SS, права позволяют :) }
          MOV    DWORD PTR DS:[EDX + 4], EAX
  end;
  Button1.Caption := IntToStr(TestClass.FInt);
  TestClass.Free;
end;

end.

2) Модификация кода (будем считать, что работаем в пределах одной страницы):
Код

procedure TForm1.Button2Click(Sender: TObject);
var
  N: Integer;
  Addr, Offs: DWORD;
  Info: TMemoryBasicInformation;
  OldProtect: DWORD;
  M2Addr: Pointer;
begin
  OldProtect := 0;
  N := 0;
  asm
          MOV    N, 100
          MOV    EAX, DWORD PTR DS:[@@M2]
          MOV    Addr, EAX
          MOV    Offs, offset @@M2
          JMP    @@M2
  @@M2:
          MOV    EAX, 150
          MOV    N, EAX
  @@M3:
          NOP
  end;
  { Protect = PAGE_EXECUTE_READ }
  if VirtualQuery(Pointer(Offs), Info,
    SizeOf(TMemoryBasicInformation)) <> 0 then
  begin
    if not VirtualProtect(Info.BaseAddress, Info.RegionSize,
      PAGE_EXECUTE_READWRITE, @OldProtect) then
    begin
      ShowMessage(SysErrorMessage(GetLastError));
      Exit;
    end;
    { Protect = PAGE_EXECUTE_READWRITE }
    if VirtualQuery(Pointer(Offs), Info,
      SizeOf(TMemoryBasicInformation)) <> 0 then
    asm
            MOV    EDX, DWORD PTR DS:[@@M3]
            { Patch }
            MOV    DWORD PTR SS:[@@M2], EDX
            MOV    M2Addr, offset @@M2
            MOV    EAX, 500
            MOV    ECX, 200
            JMP    M2Addr
    @@M2:
            { Затёрто }
            MOV    ECX, EAX
            NOP
            NOP
    @@M3:
            MOV    ECX, ECX
            NOP
            NOP
            MOV    N, ECX
    end;
  end;
  Button2.Caption := IntToStr(N);
end;

Переписал второй пример, просьба всех потестировать! Должно выводить 200 на Caption. Если закомментировать строчку под { Path }, то 500

Автор: Robus 30.1.2009, 17:12
Rrader, поробовал твой вариант #2 !!! Получаю EXCEPTION вот в этом месте:

Код

if not VirtualProtect(Info.BaseAddress, Info.RegionSize,
      PAGE_EXECUTE_READWRITE, @OldProtect)


Вообще-то ... Я не совсем понял что оно вообще делает ??? По "JMP M2Addr" мы должны попасть в первый АСМ кусочек ... Но не доходим до этого !!!

Я понимаю, что нужно включить разрешение записи в СЕГМЕНТ, просто не знаю как это делать из под винды !!! Но в любом случае "VirtualProtect" не исполняется корректно и вызывает дикие огорчения у DELPHI !!!

И ещё ... В OldProtect ничего не записано !!! Это так надо ??? Или "VirtualProtect" туда должен записать ?

Автор: Rrader 30.1.2009, 17:20
Robus, сможешь тестовый проект дать, и какая версия Delphi? 

Выкладываю свой проект, просьба всех протестировать! Указания есть в моем предыдущем посте.

Автор: Qu1nt 31.1.2009, 00:28
Delphi 7, Turbo Delphi. Все работает корректно = )

Автор: Robus 25.6.2009, 08:21
Спасибо, я разобался ... Теперь могу строить само-изменяющийся код в момент исполнения ...

Автор: CodeMonkey 25.6.2009, 10:45
Код
function WriteProtectedMemory(BaseAddress, Buffer: Pointer; Size: Cardinal; out WrittenBytes: Cardinal): Boolean;
var
  OldProtect, Dummy: Cardinal;
begin
  WrittenBytes := 0;
  if Size > 0 then
  begin
    Result := VirtualProtect(BaseAddress, Size, PAGE_EXECUTE_READWRITE, OldProtect);
    if Result then
    try
      Move(Buffer^, BaseAddress^, Size);
      WrittenBytes := Size;
      if OldProtect in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY] then
        FlushInstructionCache(GetCurrentProcess, BaseAddress, Size);
    finally
      VirtualProtect(BaseAddress, Size, OldProtect, Dummy);
    end;
  end;
  Result := (WrittenBytes = Size);
end;

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)