转载请标明是引用于 http://blog.youkuaiyun.com/chenyujing1234
编译工具:VS2005
最近在论坛上看到这个主题,想对此做些总结。
基本知识:
1. 在32bit flat模式,Windows对代码section是有保护的,一般不允许修改,对于数据section,可读可写可执行。所以执行数据section的代码是没有问题的,代码section和数据section是统一编址的。用一条call指令即可实现。
2. 在16bit dos 模式,dos对代码段没有保护,故代码段依然是可写的,你可以在代码段预留一块空间,将数据区的指令复制到代码段保留区,然后执行。如果你非要运行数据段的代码,则比较麻烦,需要修改cs寄存器。
我们的电脑是32 bit flat的,故数据段是可读可写可执行的,所以执行数据段的代码没有问题。
这里可以用下面的例子说明这一点:
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
typedef int (*lpfn_add_c)(int a, int b);
char bin_pattern[]=
{
0x8b, 0x44, 0x24, 0x08,
0x8b, 0x4c, 0x24, 0x04,
0x03, 0xc1,
0xc3,
};
int main(int argc, char* argv[])
{
char bytecode[16];
int a,b,c;
lpfn_add_c g_fn_add;
memset(bytecode,0,sizeof(bytecode));
memcpy(bytecode,bin_pattern,sizeof(bin_pattern));
a=3;
b=5;
// 用下面的方法做过渡
int *temp = (int*)bytecode;
g_fn_add= (lpfn_add_c)(temp);
// 编译出错
// error C2440: “类型转换”: 无法从“char [16]”转换为“lpfn_add_c”
//g_fn_add= (lpfn_add_c)(bytecode);
//接下来去执行在数据区的代码
c=g_fn_add(a,b);
printf("a=%d,b=%d,a+b=%d\n",a,b,c);
return 0;
}
.386
.model flat ; 32 bit memory model
option casemap :none ; case sensitive
WriteFile PROTO STDCALL:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
CloseHandle PROTO STDCALL:DWORD
ExitProcess PROTO STDCALL:DWORD
wsprintfA PROTO C:DWORD,:VARARG
includelib user32.lib
includelib kernel32.lib
.const
_formatString db '%d+%d=%d',0dh,0ah,0
.data? ;非初值化的数据,不占可执行文件空间
_buff db 128 dup (?)
align 4
_tmp1 dd ?
_c dd ?
.data ;初值化的数据
fun_add db 8bh, 44h, 24h, 08h, 8bh, 4ch, 24h, 04h,03h, 0c1h, 0c3h
align 4
_a dd 3
_b dd 5
.code
_start:
mov eax,_a
mov edx,_b
push eax
push edx
lea ebx, fun_add ;eax 执行函数的地址
call ebx
mov _c, eax ;返回值 --> 全局变量 _c
invoke wsprintfA, addr _buff, addr _formatString, _a, _b, _c
add esp, 20 ;5 * dword?
invoke WriteFile,7,OFFSET _buff,32,OFFSET _tmp1,0 ; 输出到控制台,输出32个字符
; 7是标准输出文件的句柄
invoke ExitProcess, 0 ; 退出进程
end _start
把上面的汇编保存为test.asm
编译test.asm成exe 的方法是:
编译ASM时得添加规则:
右击项目-> 自定义生成规则->
此时编译会提示:
1>LINK : fatal error LNK1221: 无法推导出子系统,必须定义它
这时要在工程属性->链接->命令行下添
/subsystem:console
再编译,产生exe了。