• 全部
  • 语言基础/算法/系统设计
  • 数据库相关
  • 图形处理/多媒体
  • 网络通信/分布式开发
  • VCL组件开发及应用
  • Windows SDK/API
  • 问答

运行期间生成代码的动态执行

ehom 2003-04-26 05:13:37
程序要执行首先要分配内存,在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;
就完成了上面代码的动态调用执行。
...全文
94 点赞 收藏 137
写回复
137 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
Devlopered 2003-09-18
up
回复
fliman 2003-08-31
这么好的方法不用来写病毒有点可惜了:-)
回复
IORILI 2003-08-21
珍藏
回复
Eastunfail 2003-08-16
To Firetoucher,嘿嘿~~~~~~~
1.第一个问题,因为这个CALL不是FAR调用方式,而不像GetProcAddress得到的是绝对地质。
2.我在QQ里面给你回复
回复
SuperSeventh 2003-08-16
我要学习的东西太多了!
回复
afei78223 2003-08-13
gz
回复
firetoucher 2003-08-13
有几个疑问
1 @ShowMessage地址是在代码设计期得到,并不能想getprocaddress一样让用户输入函数名字符串然后查找地址
2 需要调用的参数的个数和类型也是在设计期间写死的,并不能在运行时动态得到
如果是这样,岂不是要将所有的函数在代码中列出来然后用case。。。。
回复
Eastunfail 2003-08-13
不用学Delphi就可以写出那样的代码,学好大学的课程就可以了
回复
bengan 2003-08-13
有能力写出这样的程序,你们在delphi上学习了多长的时间啊?高手们!
回复
kuan 2003-08-09
关注
回复
startjoy 2003-08-08
收藏
回复
failer 2003-08-03
收藏
回复
bw1996 2003-07-30
mark
回复
Gide 2003-07-30
请 Eastunfail(恶鱼杀手)等高手帮忙
http://expert.csdn.net/Expert/topic/2088/2088473.xml?temp=.5743372
回复
Gide 2003-07-30
高手给看看这个吧
http://expert.csdn.net/Expert/topic/2088/2088473.xml?temp=.5743372
回复
span_space 2003-07-27
up
回复
邹工 2003-07-22
收藏
回复
michaelpeng7799 2003-07-18
thebest123(凌然) 刚create就free了,可不没有么。
回复
michaelpeng7799 2003-07-18
Kao! assembler 没有学过 :(
回复
thebest123 2003-07-16
foxyy8888(为升星眼也花了) 你好,帮我看看这段代码有没有错:
procedure TfrmMain.N6Click(Sender: TObject);
begin
frmDictionary := TfrmDictionary.create(nil);
frmDictionary.showmodal;
frmDictionary.free;
end;
运行的时候不能出现想要的窗口,如何可以得到?!谢谢各位大哥了!
回复
加载更多回复
相关推荐
发帖
Delphi
创建于2007-08-02

4892

社区成员

Delphi 开发及应用
申请成为版主
帖子事件
创建了帖子
2003-04-26 05:13
社区公告
暂无公告