9

我迫切需要解决这个问题。我正在尝试开发汇编代码,允许我加载和执行(通过用户输入)2 个其他汇编 .EXE 程序。我有两个问题:

  • 我似乎无法将路径名分配给有效的寄存器(或者语法可能不正确)

  • 我需要能够在第一个程序(可能是其中一个)开始执行后执行另一个程序。

这是我到目前为止所拥有的:

mov ax,cs ; moving code segment to data segment
mov ds,ax

mov ah,1h ; here I read from keyboard
int 21h
mov dl,al

cmp al,'1' ; if 1 jump to LOADRUN1 
JE LOADRUN1 

cmp al,'2' ; if 2 jump to LOADRUN2 
JE LOADRUN2

LOADRUN1:
    MOV AH,4BH
    MOV AL,00
    LEA DX,[PROGNAME1] ; Not sure if it works
    INT 21H


LOADRUN2:
    MOV AH,4BH
    MOV AL,00
    LEA DX,[PROGNAME2] ; Not sure if it works
    INT 21H

; Here I define the bytes containing the pathnames
PROGNAME1 db 'C:\Users\Usuario\NASM\Adding.exe',0 
PROGNAME2 db 'C:\Users\Usuario\NASM\Substracting.exe',0

在一个已经执行之后,我只是不知道如何通过在“父”程序中输入来启动另一个程序。

在此先感谢您的帮助!我很乐意提供任何其他信息。

  • 不是叠加层。
  • 我正在使用 NASM 16 位,Windows 7 32 位。
4

2 回答 2

7

经过一些黑客和玩弄之后,我能够得到这个工作。这并不像我希望的那样简单,所以请抓住你的座位。

首先,您需要意识到(听起来很抽象)DOS 是一个单用户、非多任务系统。在这种特殊情况下,这意味着您不能同时运行两个进程。您需要等待一个进程完成执行,然后才能移动到另一个进程。进程并发可以用 TSR(终止并保持驻留)进程在某种程度上模拟,尽管被终止,它们仍保留在内存中,并且可以通过从其代码中挂钩一些中断并稍后从其他一些代码调用它来恢复它们的执行。尽管如此,它与现代操作系统(如 Windows 和 Linux)使用的并发性不同。但这不是重点。

您说您使用 NASM 作为您选择的汇编程序,因此我假设您将代码输出到 COM 文件,这些文件又由 DOS 命令提示符执行。COM 文件由命令提示符在偏移处100h加载(在加载到该位置的跳转执行后)并且不包含除“精简”代码和数据之外的任何其他内容 - 没有标题,因此它们是最容易生成的。

我将分段解释汇编源代码,以便您(也许)可以更好地了解引擎盖下发生的事情。

该计划以

org 100h

section .data
exename db "C:\hello.com",0
exename2 db "C:\nasm\nasm.exe",0
cmdline db 0,0dh

org指令在实际加载到内存时指定文件的来源 - 在我们的例子中,它是100h. 随后声明了三个标签,它们是要执行的程序的空终止路径,以及exename指定新创建的进程应该接收的命令行。请注意,它不仅仅是一个普通的字符串:第一个字节是命令行中的字符数,然后是命令行本身,以及一个回车符。在这种情况下,我们没有命令行参数,所以整个事情归结为. 假设我们想作为 params 传递:在这种情况下,我们需要将此标签声明为(注意开头的额外空格!)。继续...exename2cmdlinedb 0,0dh-h -x 3db 8," -h -x 3",0dh

dummy times 20 db 0

paramblock dw 0
dw cmdline
dw 0 ; cmdline_seg
dw dummy ; fcb1
dw 0 ; fcb1_seg
dw dummy ; fcb2
dw 0 ; fcb2_seg

标签dummy只有 20 个字节,其中包含零。接下来是paramblock标签,它是 Daniel Roethlisberger 提到的 EXEC 结构的表示。第一项是零,这意味着新进程应该具有与其父进程相同的环境。三个地址如下:命令行、第一个 FCB 和第二个 FCB。您应该记住,实模式下的地址由两部分组成:段的地址和段的偏移量。这两个地址都是 16 位长。它们以小端方式写入内存,偏移量在前。因此,我们将命令行指定为 offset cmdline,并将 FCB 的地址指定为标签的偏移量dummy,因为 FCB 本身不会被使用,但地址需要指向一个有效的内存位置。这些段需要在运行时填充,因为加载程序会选择加载 COM 文件的段。

section .text
entry:
    mov     ax,             cs
    mov     [paramblock+4], ax
    mov     [paramblock+8], ax
    mov     [paramblock+12],ax

paramblock我们通过在结构中设置段字段来开始程序。由于对于 COM 文件,CS = DS = ES = SS即所有段都是相同的,我们只需将这些值设置为cs寄存器中的值。

mov     ax, 4a00h
mov     bx, 50
int     21h

这实际上是应用程序中最棘手的问题之一。当 DOS 将 COM 文件加载到内存中时,默认情况下会为其分配所有可用内存(CPU 不知道这一点,因为它处于实模式,但 DOS 内部无论如何都会跟踪它)。因此,调用 EXEC 系统调用会导致它以No memory available. AH=4Ah因此,我们需要通过执行“RESIZE MEMORY BLOCK”调用(Ralf Brown)来告诉 DOS,我们并不真正需要所有内存。这bxregister 应该具有以 16 字节为单位(“段落”)的内存块的新大小,因此我们将其设置为 50,我们的程序有 800 个字节。我不得不承认这个值是随机选择的,我尝试将其设置为有意义的值(例如,基于实际文件大小的值),但我一直无处可去。ES是我们想要“调整大小”的段,在我们的例子中是CS(或任何其他的,因为在加载 COM 文件时它们都是相同的)。完成此调用后,我们就可以将新程序加载到内存并执行它了。

    mov     ax, 0100h
    int     21h
    cmp     al, '1'
    je      .prog1
    cmp     al, '2'
    je      .prog2
    jmp     .end

.prog1:
    mov     dx, exename
    jmp     .exec

.prog2:
    mov     dx, exename2

这段代码应该是不言自明的,它根据标准输入选择插入程序的路径DX

.exec:
    mov     bx, paramblock
    mov     ax, 4b00h
    int     21h

这是调用实际EXEC系统调用 ( AH=4Bh) 的地方。AL包含0,表示程序应该被加载并执行。DS:DX包含可执行文件的路径地址(由前面的代码段选择),并ES:BX包含paramblock标签的地址,其中包含EXEC结构。

.end:
    mov     ax,     4c00h
    int     21h

在完成调用的程序的执行后exec,父程序通过执行AH=4Ch系统调用以退出代码零终止。

感谢vulture-来自 Freenode 上的##asm 的帮助。我用 DOSBox 和 MS-DOS 6.22 对此进行了测试,希望它也适用于你。

于 2012-04-09T00:15:33.853 回答
4

根据此参考资料,您没有设置 EXEC 参数块:

Format of EXEC parameter block for AL=00h,01h,04h:

Offset  Size    Description     (Table 01590)
00h    WORD    segment of environment to copy for child process (copy caller's
environment if 0000h)
02h    DWORD   pointer to command tail to be copied into child's PSP
06h    DWORD   pointer to first FCB to be copied into child's PSP
0Ah    DWORD   pointer to second FCB to be copied into child's PSP
0Eh    DWORD   (AL=01h) will hold subprogram's initial SS:SP on return
12h    DWORD   (AL=01h) will hold entry point (CS:IP) on return

引用的页面缺少该表的<pre>/</pre>标记,这就是页面中难以阅读的原因。

您必须设置这样一个参数块并将 ES:BX 指向它的地址。


您针对 16 位(DOS API)而不是 Win32 API 有什么特别的原因吗?假设您可以避开以 Win32 API 为目标,您可以使用WinExec调用以类似以下框架的方式启动外部可执行文件:

global _WinMain@16

; WinExec(char *lpCmdLine, int uCmdShow)
extern _WinExec@8

[section .code]
_WinMain@16:
    ; ... read input and jump to loadrun1 or loadrun2 here

loadrun1:
    push dword 1
    push dword progname1
    call _WinExec@8
    ret

loadrun2:
    push dword 1
    push dword progname2
    call _WinExec@8
    ret

[section .data]
    progname1 db 'C:\Users\Usuario\NASM\Adding.exe',0 
    progname2 db 'C:\Users\Usuario\NASM\Substracting.exe',0

或者,您可以使用更现代的ShellExecute调用。

于 2012-04-07T23:35:48.780 回答