执行存放在的数据区的机器指令的方法

本文详细介绍了在32位扁平模式下如何执行数据段的代码,并通过实例解释了相关原理。包括代码段与数据段的区别、如何在代码段预留空间复制数据段指令、以及遇到的编译错误和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请标明是引用于 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了。

<think>好的,我现在需要详细解释计算机中指令执行的过程,特别是涉及CPU的各个阶段,比如取指、译码、执行、写回等。首先,我应该回顾用户提供的引用内容,看看里面有哪些相关的信息可以利用。 用户给的引用中有关于CPU的基本功能、控制器的组成部分(如程序计数器PC、指令寄存器IR、控制单元CU)、CPU周期、流水线技术等内容。这些信息可以帮助构建指令执行过程的步骤。 首先,指令周期通常分为几个阶段:取指、译码、执行、写回。引用[3]提到CPU周期(时钟周期)是划分指令周期的基础,每个机器周期完成一个基本操作。而引用[4]提到程序计数器PC负责记录当前指令地址,生成下一条指令地址。这应该是在取指阶段的关键步骤。 在取指阶段,PC中的地址被送到内存,取指令后存入指令寄存器IR,同时PC递增。引用[2]指控制器包括PC、IR、CU,所以取指阶段需要PC和IR的协作。同时引用[3]提到时序信号的作用,控制各个阶段的时序。 译码阶段需要将指令的操作码解析,确定操作类型和操作数。引用[2]中的控制单元CU可能负责译码,并生成相应的控制信号。引用[5]中的流水线技术可能提到译码阶段与其他阶段的并行处理,但用户的问题可能更关注基本流程,暂时不涉及流水线优化。 执行阶段根据译码结果执行操作,比如算术运算、内存访问等。引用[5]提到执行阶段可能涉及ALU(算术逻辑单元)操作,或者访问内存的LSU(加载存储单元)。例如,在RISC架构中,LSU管理内存操作,而CISC可能直接操作内存。需要说明执行阶段的具体动作,比如计算地址、执行加法等。 写回阶段将结果保存到寄存器或内存。引用[5]提到写回阶段将结果写回通用寄存器堆。如果是存储指令,可能需要访问内存,这里要注意不同架构的区别,比如RISC需要LSU,而CISC直接操作。 另外,指令周期可能包括中断处理,但用户的问题可能不需要深入这个部分。需要确保每个阶段的描述准确,结合引用的内容,例如PC的作用、IR的用途、CU的控制信号等。 可能还需要提到指令周期中的各个步骤如何由时序信号协调,引用[3]中的时序产生器和控制方式,确保各阶段按顺序执行。此外,引用[5]中的流水线技术如果涉及,可以简要提到,但用户的问题主要关注基本过程,所以可能作为扩展内容放在相关问题里。 在组织内容时,应该按照阶段分步骤,每个阶段详细说明涉及的部件和操作,并引用对应的引用资料。例如,取指阶段引用[4]的PC和[2]的IR,译码阶段引用[2]的CU,执行阶段引用[5]的LSU或ALU,写回阶段引用[5]的写回部分。 最后,生成相关问题时要围绕指令周期、各阶段细节、相关技术(如流水线、控制单元)等方面提问,确保问题有助于深入理解主题,并参考用户提供的引用内容中的其他知识点,如CPU的基本功能、控制器组成、流水线技术等。</think>### 指令执行过程详解 指令执行是CPU的核心工作流程,分为**取指、译码、执行、写回**四个阶段,每个阶段由控制器协调完成[^2]。以下是具体步骤: --- #### 1. **取指阶段(Fetch)** - **程序计数器(PC)** 提供当前指令的地址,将地址发送到内存[^4]。 - 内存根据地址读取指令,将指令内容存入**指令寄存器(IR)**。 - PC自动递增(或根据指令长度调整),指向下一条指令地址[^3]。 - 关键部件:PC、内存、IR。 --- #### 2. **译码阶段(Decode)** - **控制单元(CU)** 解析IR中的指令,识别操作类型(如加法、跳转)和操作数(寄存器或内存地址)。 - 根据操作码生成控制信号,指导后续阶段的动作。 - 关键部件:CU、IR。 --- #### 3. **执行阶段(Execute)** - **算术逻辑单元(ALU)** 或 **加载存储单元(LSU)** 执行具体操作: - **算术/逻辑运算**(如加减法):ALU根据操作数计算结果。 - **内存访问**(如`LOAD/STORE`):LSU从内存读取数据或将数据写入内存[^5]。 - **跳转指令**(如`JMP`):修改PC的值以实现分支或循环。 - 关键部件:ALU、LSU、CU。 --- #### 4. **写回阶段(Write Back)** - 将执行结果写入目标位置: - **寄存器**:如通用寄存器堆(RISC架构常用)。 - **内存**:通过LSU将数据存储到指定地址(CISC架构可能直接操作)[^5]。 - 关键部件:寄存器堆、LSU。 --- #### 指令周期与流水线技术 - **指令周期**:完成一条指令所需的时间,包含上述四个阶段[^3]。 - **流水线优化**:通过并行处理多条指令的不同阶段提升效率。例如,在译码阶段同时执行下一条指令的取指。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值