2-5、包含多个段的程序

news2025/2/4 19:33:43

语雀原文链接

文章目录

    • 1、概述
    • 2、代码段中使用数据
      • 示例1:不指定程序入口
      • 示例2:指定程序入口
      • 原理梳理
    • 3、在代码段中使用栈
      • 例子1
      • 例子2
    • 4、数据、代码、栈放入不同的段
      • 例子1:end start指定程序入口
        • 第一步:设置栈顶
        • 第二步:设置DS
        • 第三步:push入栈
        • 第四步:pop出站
        • 第五步:程序退出
        • 误解
      • 例子2:段占据空间是16的倍数
      • 例子3:db的使用

1、概述

  • 在操作系统的环境中,合法地通过操作系统取得的空间都是安全的,因为操作系统不会让一个程序所用的空间和其他程序以及系统自己的空间相冲突。在操作系统允许的情况下,程序可以取得任意容量的空间。
  • 程序取得所需空间的方法有两种,一是在加载程序的时候为程序分配,再就是程序在执行的过程中向系统申请。这里我们主要研究下第一种方式:加载程序的时候为程序分配空间。
  • 我们若要一个程序在被加载的时候取得所需的空间,则必须要在源程序中做出说明。我们通过在源程序中定义段来进行内存空间的获取。

2、代码段中使用数据

  • 目前有这样一个需求,计算8个数据之和,结果存储在AX寄存器中。可以先将这8个数放在一个连续的内存单元中。但是这里有个问题,这个内存单元具体在哪里?
  • 从规范的角度来讲,我们是不能自己随便决定哪段空间可以以使用的,应该让系统来为我们分配。我们可以在程序中,定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的一部分写到可执行文件中。当可执行文件中的的程序被加载入内存时,这些数据也同时被加载入内存中。与此同时,我们要处理的数据也就自然而然地获得了存储空间。

示例1:不指定程序入口

  • dw 的含义是定义字型数据,dw 即 define word,字型数据可以直接放入寄存器中去,因为数据寄存器的大小也是一个字的大小
  • db 的含义是定义字节型数据,db 即 define byte,字节型数据应该使用数据寄存器的高 8 位或是低 8 位进行存放。
assume cs:code
code segment
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    mov bx,0
    mov ax,0

    mov cx,8
  s:add ax,cs:[bx]
    add bx,2
    loop s

    mov ax,4c00h
    int 21h

code ends

end
  • 编译链接debug调试如下

image.png

  • 程序是从CS:IP=076A:0000开始存放,但是-u命令是看不懂的程序,和我们的程序不太一样。仔细一看是我们076A:0000 000F保存的是我们的8个数据,每个占用2个字节,总共16个字节;从076A:0010开始才是我们的程序

image.png

  • 上述程序我们无法直接执行,只有将IP=0010h后才能指向程序的第一条指令,才能执行。

示例2:指定程序入口

  • end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。
  • 下述代码表示程序的入口在标号start处,也就是mov bx,0
assume cs:code
code segment
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    start:      mov bx,0
                mov ax,0

                mov cx,8
        s:      add ax,cs:[bx]
                add bx,2
                loop s

                mov ax,4c00h
                int 21h

code ends

end start
  • 编译链接debug运行后,发现CS:IP=076A:0010,也能正常执行了

image.png

原理梳理

  • 接下来我们在梳理下大致原理,在单任务系统中,可执行文件中的程序执行过程如下
    • 由其他的程序(Debug、command或其他程序)将可执行文件牛中的程序加载入内存;
    • 设置CS:IP指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行;
    • 程序运行结束后,返回到加载者。
  • 现在的问题是,根据什么设置CPU的CS:IP指向程序的第一条要执行的指令?这一点,是由可执行文件中的描述信息指明的。我们知道可执行文件由描述信息和程序组成,程序来自于源程序中的汇编指令和定义的数据:描述信息则主要是编译、连接程序对源程序中相关伪指令进行处理所得到的信息。用伪指令end描述了程序的结束和程序的入口。在编译、连接后,由"end start"指明的程序入口,被转化为一个入口地址,存储在可执行文件的描述信息中。
  • 标号不局限于start,也可以使用其他符号
assume cs:code
code segment
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    start123:      mov bx,0
                mov ax,0

                mov cx,8
        s:      add ax,cs:[bx]
                add bx,2
                loop s

                mov ax,4c00h
                int 21h

code ends

end start123
  • 因此,程序的框架大致如下

image.png

3、在代码段中使用栈

  • 将程序中定义的数据逆序存放,利用栈来实现
assume cs:code
code segment
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    start:      mov ax,cs
                mov ss,ax
                mov sp,30h

                mov bx,0
                mov cx,8

        s:      push cs:[bx]
                add bx,2
                loop s

                mov bx,0
                mov cx,8

        s0:     pop cs:[bx]
                add bx,2
                loop s0

                mov ax,4c00h
                int 21h

code ends

end start
  • cs:0~cs:f 存储8个字,16个字节,保存数据
  • cs:10~cs:2f 存储16个字,32个字节,作为栈来用

image.png

  • 程序从CS:IP=076A:0030

image.png

  • 执行

image.png

  • 将数据push入栈(这里有个疑问:076A:0010 这行最后的数据为什么变了)

image.png

  • 出栈刚好实现逆序存放(这里有个疑问:076A:0020这行出栈后数据也发生了改变,076A:0010这行也发生了改变)

image.png
image.png

例子1

  • 下面的代码实现把内存0:0~0:F单元中的内容改写程序中的数据(也就是改写CS:0中的程序)
assume cs:codesg

codesg segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

start: 	mov ax,0
				mov ds,ax
				mov bx,0

				mov cx,8
    s:	mov ax,[bx]
				mov cs:[bx],ax
				add bx,2
    		loop s
    
    		mov ax,4c00h
    		int 21h
	
codesg ends

end start
  • 运行结果
    • DS=075A:0000 倒076A:0000 存储的事PSP区域
    • 076A:0 000F存储的是dw的8个字 16个字节长度
    • 076A:0010 开始存储程序 mov ax,0

image.png
image.png

例子2

  • 下面的代码实现把内存0:0~0:F单元中的内容改写程序中的数据(也就是改写CS:0中的程序),用栈来实现
assume cs:codesg

codesg segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
	dw 0,0,0,0,0,0,0,0,0,0

start: 	mov ax,cs
    		mov ss,ax
    		mov sp,24h
    
    		mov ax,0
    		mov ds,ax
    		mov bx,0
    		mov cx,8
    s:	push [bx]
    		pop cs:[bx]
    		add bx,2
    		loop s

		mov ax,4c00h
		int 21h
codesg ends

end start
  • 初始状态

截屏2023-12-10 16.00.25.png
截屏2023-12-10 16.02.32.png

  • 运行结果

截屏2023-12-10 16.02.32.png

4、数据、代码、栈放入不同的段

  • 前面的例子中,我们将数据、栈和代码都放到一个段里面,编程的时候我们要注意何处是数据,何处是栈,何处是代码,这样有两个问题
    • 1 全部防在一个段中程序显得混乱
    • 2 如果数据、栈、代码需要的空间超过64KB,就不能防在一个段中

例子1:end start指定程序入口

  • 接下来我们用一个例子,将数据、栈、代码放在不同的段中。下例实现将8个字数据倒叙排列
    • ds:data:将段data的地址赋值给ds寄存器
    • ss:stack:将段stack的地址赋值给ss寄存器
    • cs:code:将段code的地址赋值给cs寄存器
assume cs:code,ds:data,ss:stack

data segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends


stack segment
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends


code segment

start: 	mov ax,stack
      	mov ss,ax
      	mov sp,20h
      
      	mov ax,data
      	mov ds,ax
      
      	mov bx,0
      	mov cx,8
    s:	push [bx]
      	add bx,2
      	loop s
      	
      	mov bx,0
      	mov cx,8
    s0:	pop [bx]
      	add bx,2
      	loop s0
      
      	mov ax,4c00h
      	int 21h
	
code ends

end start

  • debug加载程序后,初始状态如下

image.png

  • 程序是从DS=075A开始,默认有256个字节的PSP区域,也就是从075A:0000开始的256字节是不可用的,程序和数据真真是从076A:0000开始
    • 076A:0000 ~ 076A:000F 存储的是要逆序的8个字(16个字节)
    • 076A:0010 ~ 076A:002F 存储的是定义的栈内存,占据16个字(32个字节)
    • 程序是从076A:0030开始的,也就是CS:IP中保存076D:0000

截屏2023-12-07 22.16.49.png

第一步:设置栈顶
  • 代码如下,其中的stack是段名,编译期会将他转换成段地址。也就是前面定义的16个字(32个字节)的栈内存,并且让栈顶指向
start: 	mov ax,stack
      	mov ss,ax
      	mov sp,20h
  • 此时栈顶SS:SP=076B:0020,此时栈空,栈顶刚好在076D:0000
  • 运行结果如下

截屏2023-12-07 22.31.53.png

第二步:设置DS
  • data段之前定义了8个字,mov ax,data中data会被转换成段地址,就是这8个字保存的段地址,偏移地址就是0000~0007
      	mov ax,data
      	mov ds,ax
  • debug运行,最终DS=076A

截屏2023-12-07 22.33.28.png

第三步:push入栈
      	mov bx,0
      	mov cx,8
    s:	push [bx]
      	add bx,2
      	loop s
  • 实现了将076A:0000 ~ 076A:000F的8个字逆序存储倒076A:0020 ~ 076A:002F中

截屏2023-12-07 22.41.50.png

第四步:pop出站
      	mov bx,0
      	mov cx,8
    s0:	pop [bx]
      	add bx,2
      	loop s0
  • 最终实现了076A:0000 ~ 076A:000F的8个字的逆序存储

截屏2023-12-07 22.45.47.png

第五步:程序退出

image.png

误解
  • 并不是程序中写assume cs:code,ds:data,ss:stack,程序就会自动将cs指向code,ds指向data,ss指向stack,这个完全靠程序中后续具体指令来决定的
  • 段的标号也可以随便定义
assume cs:b,ds:a,ss:c

a segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
a ends


c segment
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
c ends


b segment

d: 	mov ax,stack
  	mov ss,ax
  	mov sp,20h
  
  	mov ax,data
  	mov ds,ax
  
  	mov bx,0
  	mov cx,8
 s: push [bx]
  	add bx,2
  	loop s
  	
  	mov bx,0
  	mov cx,8
s0:	pop [bx]
  	add bx,2
  	loop s0
  
  	mov ax,4c00h
  	int 21h
	
b ends

end d

例子2:段占据空间是16的倍数

  • 代码
assume cs:code,ds:data,ss:stack

data segment
	dw 0123h,0456h
data ends

stack segment
	dw 1h,2h
stack ends

code segment

start: 	mov ax,stack
		mov ss,ax
		mov sp,10h

		mov ax,data
		mov ds,ax
		
		push ds:[0]
		push ds:[2]
		pop ds:[2]
		pop ds:[0]

		mov ax,4c00h
		int 21h
code ends

end start

  • 结论:数据段和栈段在程序加载后实际占据的空间以16个字节为单位,其余补零

image.png

例子3:db的使用

  • 将a段和b端中的数据依次相加,结果存储倒c中去
assume cs:code

a segment
    db 1, 2, 3, 4, 5, 6, 7, 8
a ends

b segment
    db 1, 2, 3, 4, 5, 6, 7, 8
b ends

c segment
    db 0, 0, 0, 0, 0, 0, 0, 0
c ends

code segment

start:
    mov ax, a
    mov ds, ax

    mov ax, b
    mov es, ax

    mov bx, 0
    mov cx, 8

s:
    mov al, ds:[bx]
    add es:[bx], al
    inc bx
    loop s

    mov ax, c
    mov ds, ax

    mov bx, 0
    mov cx, 8

s0:
    mov al, es:[bx]
    add ds:[bx], al
    inc bx
    loop s0
    
    mov ax,4c00h
    int 21h

code ends

end start
  • 初始化状态
    • 076A:0 f 存储a段的数据
    • 076A:10 1F 存储b段数据
    • 076A:20 2F 存储c段数据

image.png

  • 运行结果

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang channle(管道)基本介绍、快速入门

channel(管道)-基本介绍 为什么需要channel?前面使用全局变量加锁同步来解决goroutine的通讯,但不完美 1)主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。 2)如果主线程休眠时间长了&#xff0c…

tgf - 一个开箱即用的golang游戏服务器框架

tgf框架 tgf框架是使用golang开发的一套游戏分布式框架.属于开箱即用的项目框架,目前适用于中小型团队,独立开发者,快速开发使用.框架提供了一整套开发工具,并且定义了模块开发规范.开发者只需要关注业务逻辑即可,无需关心用户并发和节点状态等复杂情况. 使用介绍 创建业务逻辑…

m1通过源码编译xgboost4j的jar

1、下载源码 git clone --recursive https://github.com/dmlc/xgboost cd xgboost 编译xgboost的动态链接库dylib,m1源码编译xgboost的动态链接库dylib文件 2、编译XGBoost的jar文件: A、如果没有安装maven可以通过以下命令进行安装,如果安…

邮件营销软件:10个创新邮件营销策略,提升投资回报率(一)

电子商务和电子邮件营销密不可分。尽管电子商务在蓬勃发展,而很多人对邮件营销颇有微词。但是在电子商务中,邮件营销的确是一种有效营销方式。在本文中,我们将讨论一下邮件营销在电子商务中的有效运用,帮助您的企业在今年尽可能地…

045:Vue读取本地上传JSON文件,导出JSON文件方法

第045个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…

HibernateJPA快速搭建

1. 先创建一个普通Maven工程&#xff0c;导入依赖 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><depe…

windows 10多用户同时远程登陆配置【笔记】

系统环境&多用户访问情况&#xff1a; 1、【win】【R】键入【gpedit.msc】 2、依次选择【计算机配置】→ 【管理模板】 → 【Windows组件】 → 【远程桌面服务】 → 【远程桌面会话主机】 →【连接】 2.1、右键 【允许用户通过使用远程桌面服务进行远程连接】 编辑 …

Go语言基础知识学习(一)

Go基本数据类型 bool bool型值可以为true或者false,例子&#xff1a; var b bool true数值型 类型表示范围int8有符号8位整型-128 ~ 127int16有符号16位整型-32768 ~ 32767int32有符号32位整型-2147783648 ~ 2147483647int64有符号64位整型uint8无符号8位整型0 ~ 255uint16…

linux通过命令切换用户

在Linux中&#xff0c;你可以使用su&#xff08;substitute user或switch user&#xff09;命令来切换用户。这个命令允许你临时或永久地以另一个用户的身份运行命令。以下是基本的用法&#xff1a; 基本切换到另一个用户&#xff08;需要密码&#xff09;&#xff1a;su [用户…

C# 静态构造函数与类的初始化

静态构造函数&#xff1a; 基本概念&#xff1a; 静态构造函数用于初始化任何静态数据。 静态构造函数的常见特性&#xff1a; 静态构造函数不使用访问修饰符或不具有参数。因为静态构造函数由系统调用&#xff0c;无法人为调用&#xff0c;所以就不存在public、private等。…

Gazebo 跟踪8字形和U形轨迹(1) — 错误处理

Gazebo 跟踪8字形和U形轨迹(1) — 错误处理 整个过程还是比较曲折的&#xff0c;主要都是一些细小的问题&#xff0c;跑了很多遍模型才发现 参考轨迹生成问题不大&#xff0c;主要是参考横摆角和参考曲率部分有问题 atan和atan2 首先看下两者的区别 atan 函数&#xff1a;…

智能监控型电源老化房方案

电源适配器专用老化房主要适用于充电器等电源成品&#xff08;半成品&#xff09;作一般性老化测试。其负载主体采用程控式电子负载&#xff0c;保证其稳定度和可调节性。该老化车配备电脑操作监控系统。 模拟量采集/老化房采集软件 一、老化房功能&#xff1a; 1 负载主体&am…

32.768KHz时钟RTC晶振精度PPM值及频差计算

一个数字电路就像一所城市的交通&#xff0c;晶振的作用就是十字路口的信号灯&#xff0c;因此晶振的品质及其电路应用尤其关键。数字电路又像生命体&#xff0c;它的运行就像人身体里的血液流通&#xff0c;它不是由单一的某个器件或器件单元构成&#xff0c;而是由多个器件及…

xtts和ogg不选择?

不选择ogg的理由&#xff1a; 1.需要在源端创建用户赋权&#xff0c;启用数据库最小日志&#xff0c;附加日志等操作--对生产影响较大 2.外键约束过多&#xff0c;割接启用可能很慢https://www.modb.pro/db/201126--割接停机时间影响 3.初始化配置expdp导出可能快照过旧&#x…

11.光线追踪

1.为什么要引入光线追踪 光栅化阶段有些全局效果做的并不好&#xff0c;首先不太好做软阴影&#xff0c;然后是类镜面反射&#xff0c;表面很光滑但是又达不到镜面反射那么光滑&#xff0c;光线反射到这类物体上之后会经过好几次反射&#xff0c;其次是间接光照&#xff0c;光…

File has been changed outside the editor, reload?

编译keil工程&#xff0c;一直提示&#xff1a;该文件在编译器之外被修改&#xff0c;是否重新加载。 解决办法&#xff1a; 关闭.map后缀的文件即可&#xff0c;然后重新build/rebulid可以发现不会重新弹出该错误。

数据集成和人工智能驱动的见解

数字时代使数据成为人们关注的焦点&#xff0c;将其从单纯的二进制序列转变为有价值的组织资产。随着企业越来越多地转向数据驱动战略&#xff0c;数据管理的复杂性也随之增加。当前的任务不仅仅是存储甚至收集数据&#xff0c;而是将其转化为可操作的情报。本博客旨在剖析寻求…

【漏洞复现】华脉智联指挥调度平台命令执行漏洞

Nx01 产品简介 深圳市华脉智联科技有限公司&#xff0c;融合通信系统将公网集群系统、专网宽带集群系统、不同制式、不同频段的短波/超短波对讲、模拟/数字集群系统、办公电话系统、广播系统、集群单兵视频、视频监控系统、视频会议系统等融为一体&#xff0c;集成了专业的有线…

Weblogic-wls-wsat-unserialize_CVE-2017-10271

文章目录 Weblogic < 10.3.6 wls-wsat XMLDecoder 反序列化漏洞1. 漏洞描述2. 漏洞复现2.1 环境启动2.2 漏洞扫描2.3 漏洞验证 3. 修复建议 Weblogic < 10.3.6 ‘wls-wsat’ XMLDecoder 反序列化漏洞 1. 漏洞描述 说明内容漏洞编号CVE-2017-10271漏洞名称Weblogic <…

【牛牛送书 | 第三期】《一本书讲透Java线程:原理与实践》带你深入JAVA多线程

目录 摘要&#xff1a; 多线程对于Java的意义 为什么Java工程师必须掌握多线程 Java多线程使用方式 如何学好Java多线程 参与方式&#x1f947; 摘要&#xff1a; 互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#x…