将程序加载到RAM并执行它们NASM 16b

时间:2012-04-04 06:38:24

标签: assembly x86 nasm

我迫切想要解决这个问题。我正在尝试开发汇编代码,允许我加载和执行(通过用户输入)2个其他Assembly .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位。

2 个答案:

答案 0 :(得分:7)

经过一番黑客攻击,我能够让这个工作起来。它并不像我希望的那样简单,所以坚持你的座位。

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

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

我将分批解释装配源,以便您(或许)可以更好地了解幕后发生的事情。

程序以

开头
org 100h

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

org指令,它指定实际加载到内存中时文件的来源 - 在我们的例子中,这是100h。接下来是三个标签的声明,exenameexename2是要执行的程序的以空值终止的路径,cmdline指定新创建的进程应该接收的命令行。请注意,它不仅仅是一个普通字符串:第一个字节是命令行中的字符数,然后是命令行本身和回车符。在这种情况下,我们没有命令行参数,所以整个事情归结为db 0,0dh。假设我们想要将-h -x 3作为参数传递:在这种情况下,我们需要将此标签声明为db 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位长。它们以小端方式写在内存中,偏移量是第一位的。因此,我们将命令行指定为偏移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

这实际上是应用程序中最棘手的问题之一。当一个COM文件被DOS加载到内存中时,默认情况下会为它分配所有可用的内存(CPU不知道这个,因为它处于实模式,但DOS内部仍会跟踪它)。因此,调用EXEC系统调用会导致它失败并显示No memory available。因此,我们需要通过执行" RESIZE MEMORY BLOCK"来告诉DOS我们并不真正需要所有内存。 AH=4Ah致电(Ralf Brown)bx寄存器应该以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系统调用,终止父程序的退出代码为零。

感谢来自Freenode的## asm的vulture-寻求帮助。我用DOSBox和MS-DOS 6.22测试了这个,所以希望它对你也有用。

答案 1 :(得分:4)

根据this reference,您没有设置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来电。