5

I'm using IDA to disassemble Test Drive III. It's a 1990 DOS game. The *.EXE has MZ format.

The game uses a number of anti-reversing features such as copying its code to segment (PSPseg+2be7) where PSPseg is the initial value of ES (i.e. the segment where PSP resides). As far as I know, COM executables are always loaded right after the end of Program Segment Prefix (PSP), so that both PSP and exe fit into one segment. What about MZ executables? Is there any fixed place where application's PSP is located relatively to the application itself?

In other words, is the base-PSPseg offset always the same? On my DOSBox at the start of the program execution CS is always 0x22CF, ES=DS=0x01FE, CS0 in the MZ header is 0x20C1, yielding base-PSPseg offset 0x0010 (16 segments, 256 bytes - exactly the size of PSP).

If this offset is not fixed and both PSP and the application are just loaded randomly in whatever memory location is big enough, then is there at least any guarantees about their addresses? Like that PSP address is always lower than the app's base address?

4 Answers 4

6

DOS didn't have a concept of more than one application being able to run at the same time, with each of those applications able to allocate memory. Programs that stayed resident after termination weren't able to allocate more memory while another program was running. So, there was no fragmentation in memory, and no "memory location that was big enough".

The memory from 0x00000 was occupied by the interrupt vector table and various bios variables, DOS loaded behind them. The end of the memory block that was used by DOS depended on lots of factors, but whenever a program got loaded, the PSP was created at the lowest possible location, and the program loaded directly behind that. So, yes, you can rely on your program using the memory behind the PSP, and you can rely on the difference between your segment registers and the PSP being constant as well.

The PSP segment itself was unpredictable to programmers - stuff like the ansi.sys console driver, foreign keyboard drivers, the Cdrom drivers, and various stay-resident programs (anyone remember sidekick?) could increase the size of the "dos block" causing the PSP address of loaded programs to increase (and available memory to decrease). But as long as you don't change the configuration of your DOSBOX, you should be able to rely on the PSP - and your segment registers - being the same for each run of the program.

4

MZ-format executables also have the PSP at CS-0x10 just before the data loaded from the file.

Quoting Tech Help!, probaly the best DOS programming reference:

EXE-format programs define multiple program segments, including a code, data, and stack segment. The EXE file is loaded starting at PSP:0100.

2
  • that should say "DS-0x10". MZ CS can be somewhere far from PSP. Commented Aug 10, 2014 at 22:02
  • Thanks Peter, but DS is supposed to point to the PSP. I hope my edit is better now.
    – Igor Skochinsky
    Commented Aug 11, 2014 at 7:50
2

It is typically true that DOS loads a .EXE program immediately after the PSP, meaning in your terms that base - PSP == 0x0010. But it's not necessarily true, and if you were writing this (or any) DOS executable, you might do better to know this. However, this is not you. Instead, here, you are a reverse engineer - or at least trying your hand at being one. What counts is therefore not what is true of DOS in all generality but what was in the programmer's mind, whether right or wrong, for the particular executable that you are studying.

If this program is relocating some of its code to a segment that it computes by adding what I take you to mean as a hard-coded 0x2BE7 to PSPseg, then the programmer was very likely relying on his program's base address to be PSPseg + 0x0010. Of course, more precise understanding of where 0x2BE7 comes from might show that the programmer has been more careful. Not that I've reverse-engineered any DOS code in a long, long time, but I recall that mapping the program's segmentation (including its allowances for uninitialised data, a heap, and a stack) was often a useful preparation.

Now to the generality... Whatever may be said in "probably the best DOS programming reference", a DOS programmer should not rely on program material to be loaded immediately after the PSP. Although it will be true overwhelmingly often in practice, an exceptional case exists and is: first, documented, if obscurely; and second, not entirely within the programmer's control.

The case arises when the e_maxalloc member of the IMAGE_DOS_HEADER is zero. This can be set at link-time, but also later via tools such as EXEHDR. DOS interprets zero for this member as a direction to arrange the program in an unusual way within whatever memory block DOS has found for loading the program into. The PSP goes at the base, as usual, but the program material is placed as high as possible within the block. Space between is left uninitialised.

0

The PSP is a process table, it holds information about a running process (application), it is created and filled by the loader. The loader uses the EXEHDR information to fill some of the fields of the PSP and else. After creating the PSP the loader grabs the application code (whatever comes after the EXEHDR up to the end of the file and loads just after the PSP.

The PSP occupies the beginning of an application (COM and EXE) allocated memory; the PSP offset in the segment is always 0000H - 100H. When your application starts, DS and ES segments points to PSP, you have to adjust them to your application data segment before proceeding.

Segments are not fixed, they are defined based on the free memory space, but offsets within a segment are always the same, otherwise your applicaton would break, all your data and code is defined (calculated) base on offsets from the beginning of a segment.

CS is filled by the loader after allocating memory and loading your program, IP is also filled by the loader, this information is extracted from the EXEHDR (the offset of the application entry point (it is calculated by the assembler/compiler when it builds the object code), but its defined by the programmer in assembly, it is the label set after the directive END in masm, or by the compiler, in c is the main function offset ).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.