17

The context is that I have additional data in the .exe file and wish to access it.

I know this is possible, as plenty of programs would store extra data after the loadable part of an MZ .exe — most 32-bit programs would do this; the 16-bit part would set up the DPMI environment and then load the bulk of the data from the rest of the file. But to do this, I need to know where the file is. I'd expect there to be some reference (either a file handle or an old-fashioned FCB) in the PSP, but there's nothing I can find. I have found a few suggestions that it's passed in in the environment somewhere, but can't find a definitive reference...

1
  • 6
    After the environment's double-NUL byte (which marks the end of the regular variables) there may be a word with the value 1. Following that word there will then be an ASCIZ string with the executed pathname. You will have to open this with 21.3D or 21.6C yourself. Also answered in bttr-software.de/forum/forum_entry.php?id=17398
    – ecm
    Aug 8, 2022 at 22:47

2 Answers 2

15

[Edit: Ooops, that question already been answered a few years ago on Stack Overflow]

I'd expect there to be some reference (either a file handle or an old-fashioned FCB) in the PSP, but there's nothing I can find.

Almost. While there is no file handle or FCB to the program file, as it is already closed before execution starts, DOS (starting with 3.0) stores the program name as ASCIZ within the environment memory block, right after all environment variables.

To retrieve

  • Test for min DOS version 3.0
  • Get the environment segment from offset 002Ch of the PSP
  • Advance after the last environment variable
    • marked by two consecutive null bytes
  • There's a word with the number of strings following
  • If at least 1, the program name follows
  • Fetch the program name (a full ASCIZ path name)
  • Open that file
  • Do whatever you want to do :))

This works for .COM and .EXE programs - but only with DOS 3.0 and above.


In Assembly this may look a bit like:

    ORG   100h

    MOV   AH,3000h       ; Get DOS version
    INT   21h
    JC    LOWDOS         ; Call failed -> DOS 1.x ?
    CMP   AL,3           ; At least DOS 3
    JAE   DOSOK          ; Yes -> continue
LOWDOS:  ; DOS version too low: terminate with exit code = 126
    MOV   AX,4C7Eh
    INT   21h
DOSOK:
    MOV   AX,CS:[2Ch]    ; Environment Segment
    MOV   ES,AX          ; to DS
    XOR   DI,DI          ; Point ES:DI at start of environment vars
    MOV   AX,DI          ; Search for 0-bytes
    CLD                  ; Upward
    MOV   CX,32767       ; Maximum size of an environment segment
SKIPVAR:
;Loop skipping all environment variables
    REPNE SCASB          ; Skip a string
    JCXZ  NFND           ; Searched max.length without finding a zero
    CMP   AL,BYTE ES:[DI] ; next byte as well Zero ?
    JNE   SKIPVAR        ; No -> Skip next var
    INC   DI             ; Skip over zero byte
    CMP   WORD ES:[DI],AX; String count not zero ?
    JE    NFND           ; No -> Not Found
    INC   DI             ; Skip string count
    INC   DI
FOUND:
; Full Program Name at ES:DI
; Do whatever is to be done
; like opening it via 3Dh:

    PUSH  DS
    MOV   AX,ES
    MOV   DS,AX
    MOV   DX,DI           ; Filename in DS:DX
    MOV   AL,<openmode>   ; Read/Write/etc...
    MOV   CL,0
    MOV   AH,3Dh          ; Open File
    INT   21h
    POP   DS
    JNC   FILE_OPENED
; Open Error: terminate with exit code = openerror code 
    MOV   AH,4Ch
    INT   21h
NFND:
; Name Not Found: terminate with exit code = 127
    MOV   AX,4C7Fh
    INT   21h

FILE_OPENED:
; Read or write from/to the program file
; File Handle in AX

    ...

    END

Rather easy, isn't it?

5
  • 5
    Actually, this "feature" was never documented anywhere, so it's not in the list of officially supported MSDOS-APIs - I can still confirm it works well with any DOSes starting from 3.3 to 6.0. While MSDOS is unlikely to change much during the next few years ( ;) ), your mileage using many of the non-MS DOSes (like DRDOS or OS/2) might vary.
    – tofro
    Aug 9, 2022 at 8:33
  • @tofro very true. Still it's the most clean way I know. Of course one can go ahead, retrieve the path variable and search along that, in hope to find its own program among - works mostly as long as it hasn't been renamed. Past DOS 4 looking at the MCB will provide a hint at the name DOS used/found. Still, all rather clumsy.
    – Raffzahn
    Aug 9, 2022 at 8:46
  • 8
    DR DOS and OS/2 store the executable path in the same location after the environment variables. I think this was even documented in the DR DOS system programming manual. Aug 9, 2022 at 10:57
  • 2
    Wow, that's awful. But then, this is DOS, so I'm not sure what I was expecting... I have, BTW, tracked down the DJGPP exe stub loader, which has a full production quality example: delorie.com/bin/cvsweb.cgi/djgpp/src/stub/stub.asm?rev=1.8 One fun gotcha is that the program may be run with no free file handles, making it impossible to open the exe unless you free some up first! Aug 9, 2022 at 11:02
  • @DavidGiven oh, that' serious bad code - including 'fake' instructions to save a single byte. What are they thinking - if at all...?
    – Raffzahn
    Aug 9, 2022 at 17:39
11

If definitive references is what you’re looking for, the usual one to consult is the RBIL:

Format of Program Segment Prefix (PSP):
Offset  Size    Description     (Table 01378)
[…]
 2Ch    WORD    DOS 2+ segment of environment for process (see #01379)
[…]


Format of environment block:
Offset  Size    Description     (Table 01379)
 00h  N BYTEs   first environment variable, ASCIZ string of form "var=value"
      N BYTEs   second environment variable, ASCIZ string
        ...
      N BYTEs   last environment variable, ASCIZ string of form "var=value"
        BYTE    00h
---DOS 3.0+ ---
        WORD    number of strings following environment (normally 1)
      N BYTEs   ASCIZ full pathname of program owning this environment
                other strings may follow

As this is a pathname and not an already-open handle, you will have to open the file yourself, which is of course subject to the usual race conditions. (Not that anyone cares about such things when writing for DOS.)

2
  • What usual race conditions do you mean? What race conditions there could even be?
    – Justme
    Aug 9, 2022 at 21:14
  • 3
    Someone could've deleted or renamed or replaced the program file between the time when your program was launched and the time when you (re)open the file. Not a major concern on a single-user OS like MS-DOS, but there have been many security issues caused by such race conditions on multi-user systems, like the famous Unix setuid script vulnerability. Aug 9, 2022 at 21:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .