unit Unit1;
(*
An example of self-modifying code.
Tested with Delphi 4 on Windows 98 and NT 4.
You may use this code freely, but make sure you
know what you aredo
ing.
Global disclaimer: It's not my fault! ;-)
Feb '99 - Marcus Moennig - minibbjd@gmx.de
*)
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Green: TButton;
Red: TButton;
procedure CallModifiedCode(Sender: TObject);
procedure ModifyCode(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{$O-} //make sure nop'sdo
n't get optimized out of the compiled exe
procedure TForm1.CallModifiedCode(Sender: TObject);
var b:boolean;
c:TColor;
label 1;
begin
c:=clgreen;
b:=true;
// The next line will get modified in memory.
// The comments show what the asm code and the hexadecimal
// representation of the asm code in the compiled exe will look like.
// You can set a breakpoint on the next line and open Delphi's CPU view
// to see the change.
if b then
goto 1;
// "jnz SOME_ADDRESS" "$75 SOME_ADDRESS"
// We will modify the line above to:
// "jz SOME_ADDRESS" "$74 SOME_ADDRESS" what means:
// if NOT(b) then
goto 1;
// The following asm codedo
es nothing (nop=no operation).
// It's simply used as a marker to find the position
// of the jump above in memory.
asm
nop // $90
nop // $90
nop // $90
nop // $90
nop // $90
nop // $90
end;
c:=clred;
1:
form1.Color:=c;
end;
procedure TForm1.ModifyCode(Sender: TObject);
const BUFFMAX=65536;
type TBytes6=Array[0..5] of byte;
TMemblock=array[0..BUFFMAX-1] of byte;
Function ReadBufferFromMemory(ad, size:integer;
var MB:TMemBlock):cardinal;
var cnt:cardinal;
begin
ReadProcessMemory(Getcurrentprocess, pointer(ad), @MB[0], size, cnt);
// return number of bytes read
ReadBufferFromMemory := cnt;
end;
procedure WriteByteToMemory(ad:cardinal;rt:byte);
var cnt:cardinal;
oldprotect:dword;
begin
//make sure we have write access to this address
VirtualProtect(pointer(ad),sizeof(rt),PAGE_EXECUTE_READWRITE,@oldprotect);
WriteProcessMemory(Getcurrentprocess, pointer(ad), @rt, sizeof(rt), cnt);
//restore the previous access protection
VirtualProtect(pointer(ad),sizeof(rt),oldprotect,@oldprotect);
end;
var st:TBytes6;
rt:Byte;
stcount:word;
BytesRead:cardinal;
sad,ead,ad:cardinal;
x,y,z:cardinal;
found:boolean;
MemBlock:TMemBlock;
begin
//define the "search term"
st[0]:=$90;
//in asm this means: nop
st[1]:=$90;
st[2]:=$90;
st[3]:=$90;
st[4]:=$90;
st[5]:=$90;
stCount:=6;
//replace term depending on which button was clicked
if (sender=red) then
rt:=$74 // in asm: jz
else
rt:=$75;
// in asm: jnz
//address range to be searched
sad:=($00400000);
ead:=($7fffffff);
//current address
ad:=sad;
found:=false;
repeat
//read one block of length BUFFMAX from the current address ad
BytesRead:=ReadBufferFromMemory(ad, BUFFMAX, MemBlock);
//no bytes read;
so get out
if BytesRead=0 then
break;
// make sure wedo
n't miss the search term when it spans two blocks
If BytesRead=BUFFMAX then
BytesRead:=BytesRead-stCount;
// search this MemoryBlock
// cycle through the MemoryBlock
For x:=0 To BytesRead-1do
begin
found:=true;
// check for search term
For y:=0 To stCount-1do
If MemBlock[x+y]<>st[y] then
begin
found:=false;
break;
end;
If found then
begin
// the search term starts at ad+x+y-stcount
z:=ad+x+y-stcount;
// the code we want to replace starts 2 bytes before
WriteByteToMemory(z-2,rt);
break;
// stop searching
end;
end;
ad:=ad+BytesRead;
until (ad>=ead) or found;
end;
end.