当前位置: 首页 > news >正文

一个dos版的exe加载器

一个完整的 DOS 版 EXE 加载器,用 16 位汇编语言编写,可解析并运行标准的 MZ 格式可执行文件。它手动完成内存分配、PSP 创建、重定位表修正和最终跳转,不依赖 DOS 的 EXEC 调用。

;########################################################################### ; DOS 版 EXE 加载器 (LOADEXE.COM) ; 功能:加载并执行指定的 MZ 格式 .EXE 文件,支持命令行参数传递。 ; 编译:masm /om load_exe.asm; link /t load_exe.obj; ; 或直接使用 tasm /m /t load_exe.asm ; 用法:LOADEXE 程序名.exe [参数...] ;########################################################################### .model tiny .code org 100h start: ; 初始化段寄存器 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, offset stack_top ; 解析命令行,获取 EXE 文件名和参数字符串 call parse_cmdline jc error_exit ; 若无文件名则退出 ; 打开 EXE 文件 mov dx, offset filename mov ax, 3d00h ; 只读打开 int 21h jc open_error mov file_handle, ax ; 读取 MZ 头部 (前 0x40 字节) mov dx, offset mz_header mov cx, 40h mov bx, file_handle mov ah, 3fh int 21h jc read_error cmp ax, 40h jne read_error ; 校验签名 "MZ" cmp word ptr [mz_header.mz_sign], 'ZM' ; 'MZ' 在内存中是 'ZM' jne invalid_exe ; 获取关键字段 mov ax, [mz_header.mz_reloc_items] ; 重定位项数 mov reloc_count, ax mov ax, [mz_header.mz_reloc_offset] ; 重定位表偏移 (文件) mov reloc_table_off, ax mov ax, [mz_header.mz_header_para] ; 头部大小 (段落) mov header_para, ax mov ax, [mz_header.mz_init_cs] mov init_cs, ax mov ax, [mz_header.mz_init_ip] mov init_ip, ax mov ax, [mz_header.mz_init_ss] mov init_ss, ax mov ax, [mz_header.mz_init_sp] mov init_sp, ax mov ax, [mz_header.mz_min_extra] ; 最少额外分配段落数 mov min_extra, ax ; 计算文件映像大小 (字节) = 文件总长度 - 头部大小(字节) ; 先获取文件长度 mov bx, file_handle mov ax, 4202h ; 移动文件指针到末尾,得到文件大小 xor cx, cx xor dx, dx int 21h jc read_error mov file_size, ax ; 低16位文件长度(< 64KB,简化处理) ; 头部占用的字节数 = header_para * 16 xor dx, dx mov ax, header_para shl ax, 4 mov header_bytes, ax ; 映像大小 = 文件大小 - 头部字节 mov ax, file_size sub ax, header_bytes mov image_size, ax ; 计算所需内存大小 (段落) ; 所需段落 = ( PSP(16段落) + 映像所需段落 + min_extra ) 向上取整 ; 映像所需段落 = (image_size + 15) / 16 mov ax, image_size add ax, 15 shr ax, 4 ; 映像段落数 add ax, 16 ; 加上 PSP 占用的 256 字节 = 16 段落 add ax, min_extra ; 加上程序需要的额外内存 mov alloc_para, ax ; 分配内存块 mov ah, 48h mov bx, alloc_para int 21h jc mem_error mov load_seg, ax ; 分配到的起始段地址 (PSP 段) ; 在分配的内存块中建立 PSP mov es, ax call setup_psp ; 设置映像基址:PSP 段 + 头部段落数 (因为映像从偏移 header_para*16 开始) mov ax, load_seg add ax, header_para mov image_base_seg, ax ; 将映像 (代码+数据) 从文件读入内存 ; 先移动到文件头部之后的映像起始位置 mov bx, file_handle mov ax, 4200h xor cx, cx mov dx, header_bytes int 21h ; 读入映像 mov dx, word ptr [image_base_seg] ; 目标段地址 mov es, dx xor di, di ; 偏移 0 mov cx, image_size mov bx, file_handle mov ah, 3fh int 21h jc read_error cmp ax, image_size jne read_error ; 读取重定位表并修正 ; 重定位表在文件中的偏移 = reloc_table_off ; 每一项两个 WORD:偏移量、需要修正的段值(相对映像基址) mov bx, file_handle mov ax, 4200h xor cx, cx mov dx, reloc_table_off int 21h mov cx, reloc_count mov si, offset reloc_buf read_reloc_loop: cmp cx, 0 je done_reloc ; 每次读两个 WORD (4 字节) push cx mov cx, 4 mov dx, si mov bx, file_handle mov ah, 3fh int 21h pop cx cmp ax, 4 jne read_error ; 重定位修正: (实际段地址) = image_base_seg + [相对段值] ; 需要修正在内存中的特定位置:偏移 = reloc_offset,写入修正后的段值 mov ax, word ptr [reloc_buf] ; reloc_offset mov bx, word ptr [reloc_buf+2] ; reloc_seg (相对映像基址) add bx, image_base_seg ; 最终段地址 ; 计算目标地址 (ES:DI) push es mov es, image_base_seg mov di, ax mov word ptr es:[di], bx pop es add si, 4 dec cx jmp read_reloc_loop done_reloc: ; 关闭文件 mov bx, file_handle mov ah, 3eh int 21h ; 计算最终 CS:IP 和 SS:SP mov ax, image_base_seg add ax, init_cs mov new_cs, ax mov new_ip, init_ip mov ax, image_base_seg add ax, init_ss mov new_ss, ax mov new_sp, init_sp ; 设置 DS、ES 为 PSP 段 (标准 DOS 程序期望) mov ax, load_seg mov ds, ax mov es, ax ; 将命令行参数复制到 PSP+80h (已由 setup_psp 预留空间并初始化长度) ; 实际调用 parse_cmdline 时已提取参数字符串,直接复制到 PSP+81h push ds mov ds, load_seg mov si, offset cmdline_args mov di, 81h mov cx, cmdline_len rep movsb pop ds ; 跳转到程序入口 cli mov ss, new_ss mov sp, new_sp sti jmp dword ptr [new_jump] ;-------------------------------------------------------------------- ; 错误处理 error_exit: mov dx, offset msg_usage call print mov ax, 4c01h int 21h open_error: mov dx, offset msg_open_fail call print jmp error_exit read_error: mov dx, offset msg_read_fail call print jmp error_exit invalid_exe: mov dx, offset msg_bad_exe call print jmp error_exit mem_error: mov dx, offset msg_mem call print jmp error_exit print: mov ah, 9 int 21h ret ;-------------------------------------------------------------------- ; 解析命令行 (DS:SI 指向原始命令行) ; 输入: 无 (命令行位于 PSP+80h) ; 输出: filename 和 cmdline_args 填充,CF=0 成功,CF=1 失败 parse_cmdline proc mov si, 80h lodsb mov cl, al xor ch, ch jcxz .no_file ; 跳过开头的空格 mov di, offset filename mov bx, offset cmdline_args xor dx, dx ; cmdline_len .skip_space: lodsb cmp al, ' ' je .skip_space dec si inc cx ; 复制文件名直到空格或回车 mov dx, 0 .copy_file: lodsb cmp al, ' ' je .file_done cmp al, 0dh je .file_done cmp al, 0 je .file_done stosb inc dx loop .copy_file .file_done: mov byte ptr [di], 0 ; 文件名结尾 ; 剩余部分作为参数,跳过中间的空格 dec si inc cx .skip_param_space: lodsb cmp al, ' ' je .skip_param_space dec si inc cx ; 复制参数字符串 mov di, bx xor dx, dx .copy_param: lodsb cmp al, 0dh je .param_done cmp al, 0 je .param_done stosb inc dx loop .copy_param .param_done: mov byte ptr [di], 0dh ; 参数以回车结束 inc dx mov byte ptr [di+1], 0 mov cmdline_len, dx clc ret .no_file: stc ret parse_cmdline endp ;-------------------------------------------------------------------- ; 初始化 PSP (程序段前缀) ; 输入: ES = PSP 段地址 ; 输出: PSP 关键字段填充 setup_psp proc ; 清空 256 字节 xor di, di mov cx, 128 xor ax, ax rep stosw ; 设置 Int 20 指令 (CD 20) mov word ptr es:[0], 20cdh ; 设置程序结束地址 (保留) mov word ptr es:[0ah], 0 ; 设置环境块段 (继承加载器的环境) mov ax, es:[2ch] ; 从父 PSP 获取环境段 mov es:[2ch], ax ; 设置命令行长度及缓冲区 (先置 0) mov byte ptr es:[80h], 0 ret setup_psp endp ;-------------------------------------------------------------------- ; 数据区 mz_header struc mz_sign dw ? ; 'MZ' mz_last_size dw ? ; 最后一页字节数 mz_file_pages dw ? ; 文件页数 mz_reloc_items dw ? ; 重定位项数 mz_header_para dw ? ; 头部段落大小 mz_min_extra dw ? ; 最少额外分配段落 mz_max_extra dw ? ; 最多额外分配段落 mz_init_ss dw ? ; 初始 SS (相对映像基址) mz_init_sp dw ? ; 初始 SP mz_checksum dw ? ; 校验和 mz_init_ip dw ? ; 初始 IP mz_init_cs dw ? ; 初始 CS (相对映像基址) mz_reloc_offset dw ? ; 重定位表偏移 (文件) mz_overlay_num dw ? ; 覆盖号 mz_header ends file_handle dw ? file_size dw ? header_bytes dw ? header_para dw ? image_size dw ? reloc_count dw ? reloc_table_off dw ? min_extra dw ? alloc_para dw ? load_seg dw ? image_base_seg dw ? init_cs dw ? init_ip dw ? init_ss dw ? init_sp dw ? filename db 128 dup(0) cmdline_args db 128 dup(0) cmdline_len dw 0 new_cs dw ? new_ip dw ? new_ss dw ? new_sp dw ? new_jump dd ? reloc_buf dd ? msg_usage db 'Usage: LOADEXE program.exe [arguments]$',13,10 msg_open_fail db 'Error: Cannot open file.$' msg_read_fail db 'Error: File read error.$' msg_bad_exe db 'Error: Invalid MZ executable.$' msg_mem db 'Error: Insufficient memory.$' stack_top: end start

编译与使用方法

  1. 编译(以 MASM 为例):

    masm load_exe; link /t load_exe;
  2. 运行

    LOADEXE MYPROG.EXE arg1 arg2

    加载器会解析MYPROG.EXE并执行,同时将arg1 arg2传递给被加载的程序(符合 DOS 命令参数惯例)。

工作原理

  1. 解析命令行:从 PSP 获取原始命令串,分离出 EXE 文件名和参数字符串。

  2. 读取 MZ 头:校验签名,获取重定位表位置、初始 CS/IP/SS/SP、头部段落数、额外内存需求等。

  3. 内存分配:根据映像大小 + PSP(256字节) + min_extra 计算所需段落,调用INT 21h AH=48h分配内存。

  4. 建立 PSP:在分配的内存块开头填入必要的 PSP 字段(中断向量、环境块、命令行空间)。

  5. 加载映像:跳过文件头部,将代码/数据段读入内存的指定位置(PSP 后偏移header_para*16处)。

  6. 重定位修正:读取重定位表,对每个条目计算实际段地址(映像基址 + 相对段值),并写回内存。

  7. 设置入口:计算最终 CS:IP 和 SS:SP(映像基址 + 初始值),并将命令行字符串复制到 PSP+81h。

  8. 转移控制:修改 SS:SP,然后远跳转到新 CS:IP,将控制权交给被加载的程序。

http://www.jsqmd.com/news/923440/

相关文章:

  • 如何用kill-doc浏览器脚本免费下载30+平台文档?完整使用指南
  • VLD搭配CMake真香!一份搞定VS和CLion跨平台C++内存泄漏检测配置
  • 抖音视频批量下载神器:5分钟学会无水印视频高效获取
  • Cortex-M4 tarmac.log文件解析与调试技巧
  • 2026娄底市防水补漏公司权威推荐:卫生间、阳台、屋顶、地下室、飘窗、外墙漏水,专业防水公司TOP5口碑榜+全维度测评(2026年6月最新深度行业资讯) - 防水百科
  • 到底为什么PHP-FPM 难以维持长连接?
  • 【LeetCode刷题日记】538.把二叉搜索树转换为累加树
  • LinkSwift网盘直链下载助手:八大主流网盘高速下载终极指南
  • 大模型求职必看:收藏这份分层准备指南,从新手到大厂Offer收割机
  • FPGA加速Transformer与VLM视觉任务的优化实践
  • 国信中业—原位XPS(In-situ XPS)将“反应”和“测试”同步进行
  • AnimateDiff动画生成指南:5分钟从静态图像到动态视频的完整教程
  • 工业云脑:11 未来:6G、卫星、量子加密
  • Δ-Motif算法:GPU并行化子图同构匹配技术解析
  • Windows 11终极优化指南:如何用Win11Debloat一键清理系统垃圾和提升性能
  • Layerdivider快速入门指南:免费AI智能分层工具3分钟生成PSD文件
  • OpCore-Simplify:告别黑苹果配置噩梦,30分钟搞定专业级EFI配置
  • 不同场景下电动挡烟垂壁怎么选
  • LanzouAPI技术揭秘:如何通过PHP实现蓝奏云直链解析的高效方案
  • PHP遇到报错,不只搜解决方案,要看 堆栈跟踪,读 源码。
  • 2026梧州市防水补漏公司权威推荐:卫生间、阳台、屋顶、地下室、飘窗、外墙漏水,专业防水公司TOP5口碑榜+全维度测评(2026年6月最新深度行业资讯) - 防水百科
  • DQC1量子计算模型与迹估计技术详解
  • .NET Windows Desktop Runtime:彻底解决Windows桌面应用部署难题的终极指南
  • 大模型应用层开发学习路径:从传统后端到AI高薪岗位,收藏这份进阶指南!
  • 零基础从零到一PHP打断点的庖丁解牛
  • 原位红外(in situ FTIR)光谱:从技术突破到反应机理研究
  • 终极指南:如何用TradingAgents-CN搭建AI股票分析平台
  • WarcraftHelper:魔兽争霸3现代电脑完美运行终极指南
  • 杭州余杭永鸿再生资源:余杭区废旧金属回收公司 - LYL仔仔
  • 2026肇庆市防水补漏公司权威推荐:卫生间、阳台、屋顶、地下室、飘窗、外墙漏水,专业防水公司TOP5口碑榜+全维度测评(2026年6月最新深度行业资讯) - 防水百科