程序要執行首先要分配內存,在Win32下每個進程的內存地址空間都是虛擬的,其內存地址不是實際的物理地址,所以使用VirtualAlloc來完成虛擬內存的分配!
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWord flAllocationType,
DWord flProtect
);
lpAddress 為申請的內存的起始地址,在Win32中你可以自己指定申請內存的地址范圍!Win32中每個進程的地址空間為4GB,從0X00000000到0XFFFFFFFF。那是否可以在這個范圍內任意分配內存?答案是否定的!在這其中只有一部分是可以供Win32應用使用的。使用GetSystemInfo獲取系統信息,其中的lpMinimumApplicationAddress、lpMaximumApplicationAddress分別是可以使用的最小地址和最大地址。當然這裡可以簡單的設其值為Null,套用MSDN中的一句話:“If this parameter is NULL, the system determines where to allocate the region.”,直接將其交給系統去處理!
dwSize 為申請內存大小,以字節為單位,但如果其大小不為頁的整數倍,系統將會加大內存達到頁的整數倍。所以盡量按頁來申請。同樣可以用GetSystemInfo來獲取dwPageSize。如Win9X下一頁為4K,即4096字節。
flAllocationType 指定申請方式,flProtect 指定內存的保護方式,具體信息可查看MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/memory/base/virtualalloc.ASP
這裡根據需要將兩參數設為MEM_COMMIT(提交已經申請的內存)、PAGE_EXECUTE_READWRITE(執行和讀寫)
完成了內存分配的工作後,下面就是將待執行的代碼保存到內存中。機器語言指令是多字節指令,所以接下來要做的就是將一串具體數字按字節寫入內存中。
下面的代碼完成了一段子程序的生成:
var
Code:PByte;
Str:String;
Data:Longint;
num:integer;
procedure AddCode(const CodeByte:Byte);//將數據賦給指針指向的位置
begin
Code^:=CodeByte;
Inc(Code);
Inc(num);
end;
begin
num:=0;
Code:=VirtualAlloc(nil, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
AddCode($50);//PUSH EAX
AddCode($B8);//MOV EAX
Str:='Hello World!';
Data:=Longint(Pointer(PChar(Str)));//取字符串地址
AddCode(Data and $FF);
AddCode(Data and $FF00 shr 8);
AddCode(Data and $FF0000 shr 16);
AddCode(Data and $FF000000 shr 24);
AddCode($E8);//CALL
Data:=Longint(@ShowMessage) - Longint(Code) - 4;//計算Showmessage的相對地址
AddCode(Data and $FF);
AddCode(Data and $FF00 shr 8);
AddCode(Data and $FF0000 shr 16);
AddCode(Data and $FF000000 shr 24);
AddCode($58);//POP EAX
AddCode($C3);//RET
Dec(Code,num);//指針移動,返回首地址
end;
也許可以通過相應技術手冊來查詢相應指令的含義,但在Win32下其實有種簡單的理解方法。其實只要對任意一個Win32程序進行反編譯,即可獲得相應指令和匯編助記符的對應關系。
用匯編代碼表述其意義為:
PUSH EAX
MOV EAX,[字符串地址]
CALL ShowMessage
POP EAX
RET
接下來,通過嵌入ASM代碼:
asm
Call Code
end;
就完成了上面代碼的動態調用執行。