8086 Assembly / MS-DOS, passing file name from the command line - dos

Say I have PROGRAM.ASM - I have the following in the data segment:
.data
Filename db 'file.txt', 0
Fhndl dw ?
Buffer db ?
I want 'file.txt' to be dynamic I guess? Once compiled, PROGRAM.exe needs to be able to accept a file name via the command line:
c:\> PROGRAM anotherfile.txt
EXECUTION GOES HERE
How do I enable this? Thank you in advance.

DOS stores the command line in a legacy structure called the Program Segment Prefix ("PSP"). And I do mean legacy. This structure was designed to be backwards-compatible with programs ported from CP/M.
Where's the PSP?
You know how programs built as .COM files always start with ORG 100h? The reason for that is precisely that - for .COM programs - the PSP is always stored at the beginning of the code segment (at CS:0h). The PSP is 0FFh bytes long, and the actual program code starts right after that (that is, at CS:100h).
The address is also conveniently available at DS:00h and ES:00h, since the key characteristic of the .COM format is that all the segment registers start with the same value (and a COM program typically never changes them).
To read the command line from a .COM program, you can pick its length at CS:80h (or DS:80h, etc. as long as you haven't changed those registers). The Command Line starts at CS:81h and takes the rest of PSP, ending with a Carriage Return (0Dh) as a terminator, so the command line is never more than 126 bytes long.
(and that is why the command line has been 126 bytes in DOS forever, despite the fact we all wished for years it could be made longer. Since WinNT uses provides a different mechanism to access the command line, the WinNT/XP/etc. command line doesn't suffer from this size limitation).
For an .EXE program, you can't rely on CS:00h because the startup code segment can be just about anywhere in memory. However, when the program starts, DOS always stores the PSP at the base of the default data segment. So, at startup, DS:00h and ES:00h will always point to the PSP, for both .EXE and .COM programs.
If you didn't keep track of PSP address at the beginning of the program, and you change both DS and ES, you can always ask DOS to provide the segment value at any time, via INT 21h, function 62h. The segment portion of the PSP address will be returned in BX (the offset being of course 0h).

Related

How to binary analyze a Windows exe file?

I want to binary analyze a Windows EXE file without Windows API call (because I will do it from another OS). I want to disginguish 2 x 2 types:
Is it a windowed program or a command line program?
Is it a Win32 or a Win64 program?
I hope that there are general bit structures which I can query.
The link Microsoft PE and COFF Specification was useful, but a little tricky. Here is my result now:
Every Windows program has got a DOS program block showing a text like "This program cannot be run under DOS" or a similar text. The length of the DOS block can differ. The "real Windows program" section begins later. The beginning offset address of the Windows program is coded in the bytes offset 0x3c and 0x3d. 0x3d holds the hi and 0x3c the lo value. So you have to calulate 256*(0x3d) + (0x3c) to get the offset address of the real Windows program.
The real Windows program begins with four bytes: "PE", followed by two nullbytes. The fifth and sixth byte is 0x4c01 if it is a Win32 program and 0x6486 if it is a Win64 program.
To check if the program is textbased, you have to read offset byte (counted from "PE"=0x00) 0x5c. A value of 3 means text based, 2 means a Windowed GUI program.

DOS inserting segment addresses at runtime

I noticed a potential bug in some code i'm writing.
I though that if I used mov ax, seg segment_name, the program might be non-portable and only work on one machine in a specific configuration since the load location can vary from machine to machine.
So I decided to disassemble a program containing just that one instruction on two different machines running DOS and I found that the problem was magically solved.
Output of debug on machine one: 0C7A:014C B8BB0C MOV AX,0CBB
Output of debug on machine two: 06CA:014C B80B07 MOV AX,070B
After hex dumping the program I found that the unaltered bytes are actually B84200.
Manually inserting those bytes back into the program results in mov ax, 0042
So does the PE format store references to those instructions and update them at runtime?
As Peter Cordes noted, MS-DOS doesn't use the PECOFF executable format that Windows uses. It has it's own "MZ" executable format, named after the first two bytes of the executable that identify as being in this format.
The MZ format supports the use of multiple segments through a relocation table containing relocations. These relocations are just simple segment:offset values that indicate the location of 16-bit segment values that need to be adjusted based on where the executable was loaded in memory. MS-DOS performs these adjustments by simply adding the actual load segment of the program to the value contained in the executable. This means that without relocations applied the executable would only work if loaded at segment 0, which happens to be impossible.
Note this isn't just necessary for a program to work on multiple machines, it's also necessary for the same program to work reliably on the same machine. The load address can change based on what various configuration details, was well as other programs and drivers that have already been loaded in memory, so the load address of an MS-DOS executable is essentially unpredictable.
Working backwards from your example, we can tell where your example program was loaded into memory on both machines. Since 0042h was relocated into 0CBBh on the first machine and into 070Bh on the second machine, we know MS-DOS loaded your program on the two machines at segments 0C79h and 06C9h respectively:
0CBB - 0042 = 0C79
070B - 0042 = 06C9
From that we can determine that your example executable has the entry 0001:014D, or equivalent segment:offset value, in it's relocation table:
0C7A:014D - 0C79:0000 = 0001:014D
06CA:014D - 06C9:0000 = 0001:014D
This entry indicates the unrelocated location of the 16-bit immediate operand of the mov ax, seg segname instruction that needs adjusting.

Is there a safe way to identify MS-DOS executable?

I'm trying to identify and filter out all MS-DOS executables files out of executable files I have.
As far as I know, PE differ from MS-DOS by the headers he have which MS-DOS doesn't have, but for some reason some of the samples I have are recognized by TrID as MS-DOS even though they are PE.
I can't find any documentation on the subject, and I searched a lot.
Thanks!
The problem with identifying MS-DOS executables is that technically Windows PECOFF executables are also valid MS-DOS executables. PECOFF executables are prefixed with an "MS-DOS Stub", which is a complete MS-DOS program that in most executables prints a message like "This program cannot be run in DOS mode".
So the first thing to is do is to look at the MS-DOS executable header, and see if if it's valid. It looks like this (from Ralf Brown's Interrupt List):
00h 2 BYTEs .EXE signature, either "MZ" or "ZM" (5A4Dh or 4D5Ah)
(see also #01593)
02h WORD number of bytes in last 512-byte page of executable
04h WORD total number of 512-byte pages in executable (includes any
partial last page)
06h WORD number of relocation entries
08h WORD header size in paragraphs
0Ah WORD minimum paragraphs of memory required to allocate in addition
to executable's size
0Ch WORD maximum paragraphs to allocate in addition to executable's size
0Eh WORD initial SS relative to start of executable
10h WORD initial SP
12h WORD checksum (one's complement of sum of all words in executable)
14h DWORD initial CS:IP relative to start of executable
18h WORD offset within header of relocation table
40h or greater for new-format (NE,LE,LX,W3,PE,etc.) executable
1Ah WORD overlay number (normally 0000h = main program)
The key values to check are at offsets 00h and 18h. The two bytes at the start of the file, the signature, must be "MZ" or 54ADh. While "ZM" also works for MS-DOS program, Windows requires that PECOFF executables use the more common "MZ" signature. The next thing to check is the 16-bit value at offset 18h. It needs to be greater than or equal to 40h for this to be an PECOFF executable.
If the values at offsets 00h and 18h check out then the next thing to do is to read the 32-bit value at offset 3Ch. This contains the offset of the actual PECOFF header. You then need to check the header stars with the signature "PE\0\0", that is, the two characters "P" and "E", followed by two 0 bytes.
Note that its possible to find other letters at the location given at offset 3Ch, like "NE", "LE", "LX" which were used for 16-bit Windows executables, VxDs, and 32-bit OS/2 executables respectively. These other executable formats also have MS-DOS stubs and locate their real headers the same way.

8051 with Keil-C filesize issue using a Megawin processor

I've written a program using Keil C for a MegaWin 8051 MPC82G516A. When I check the file size of the Intel generated hex file it has a size of 8kb (I see the code in the binary code window), but when I go to program the device using Megawin's tool it increases the code size to around 29kb!? Can anyone provide the reason for why it might be doing this?
Also, something else that is strange is the fact that it seems to be writing the code at the top of the processor memory and not at the start. There are like 4 bytes at the start of the code, but the complete rest of it is in the end of the memory.
Please help
Cameron.
you write that the file size of the intel hex file with your code is about 8k. Part of your program is written to the bottom of the address space.
Another part is written to the bottom of the address space.
The intel hex file not only contains the program code but also the address where that code should be written to.
You can check yourself if your file contains code for the bottom and the top of the address space.
Some information about intel hex format: http://www.keil.com/support/man/docs/oh166/oh166_ih_record.htm
If this is the case you can check the .m51 file which is generated by the linker during the build process.
This file contains information about the modules included in your programm and the addresses they are linked to.
Perhaps there is some linker setting in your project which tells the linker behave as you tell.
I was miss-reading the file size on the editor. Also, Keil's free version starts at position 2000Kb. It's part of it's limitations of the evaluation version.

x86 segmentation, DOS, MZ file format, and disassembling

I'm disassembling "Test Drive III". It's a 1990 DOS game. The *.EXE has MZ format.
I've never dealt with segmentation or DOS, so I would be grateful if you answered some of my questions.
1) The game's system requirements mention 286 CPU, which has protected mode. As far as I know, DOS was 90% real mode software, yet some applications could enter protected mode. Can I be sure that the app uses the CPU in real mode only? IOW, is it guaranteed that the segment registers contain actual offset of the segment instead of an index to segment descriptor?
2) Said system requirements mention 1 MB of RAM. How is this amount of RAM even meant to be accessed if the uppermost 384 KB of the address space are reserved for stuff like MMIO and ROM? I've heard about UMBs (using holes in UMA to access RAM) and about HMA, but it still doesn't allow to access the whole 1 MB of physical RAM. So, was precious RAM just wasted because its physical address happened to be reserved for UMA? Or maybe the game uses some crutches like LIM EMS or XMS?
3) Is CS incremented automatically when the code crosses segment boundaries? Say, the IP reaches 0xFFFF, and what then? Does CS switch to the next segment before next instruction is executed? Same goes for SS. What happens when SP goes all the way down to 0x0000?
4) The MZ header of the executable looks like this:
signature 23117 "0x5a4d"
bytes_in_last_block 117
blocks_in_file 270
num_relocs 0
header_paragraphs 32
min_extra_paragraphs 3349
max_extra_paragraphs 65535
ss 11422
sp 128
checksum 0
ip 16
cs 8385
reloc_table_offset 30
overlay_number 0
Why does it have no relocation information? How is it even meant to run without address fixups? Or is it built as completely position-independent code consisting from program-counter-relative instructions? The game comes with a cheat utility which is also an MZ executable. Despite being much smaller (8448 bytes - so small that it fits into a single segment), it still has relocation information:
offset 1
segment 0
offset 222
segment 0
offset 272
segment 0
This allows IDA to properly disassemble the cheat's code. But the game EXE has nothing, even though it clearly has lots of far pointers.
5) Is there even such thing as 'sections' in DOS? I mean, data section, code (text) section etc? The MZ header points to the stack section, but it has no information about data section. Is data and code completely mixed in DOS programs?
6) Why even having a stack section in EXE file at all? It has nothing but zeroes. Why wasting disk space instead of just saying, "start stack from here"? Like it is done with BSS section?
7) MZ header contains information about initial values of SS and CS. What about DS? What's its initial value?
8) What does an MZ executable have after the exe data? The cheat utility has whole 3507 bytes in the end of the executable file which look like
__exitclean.__exit.__restorezero._abort.DGROUP#.__MMODEL._main._access.
_atexit._close._exit._fclose._fflush._flushall._fopen._freopen._fdopen
._fseek._ftell._printf.__fputc._fputc._fputchar.__FPUTN.__setupio._setvbuf
._tell.__MKNAME._tmpnam._write.__xfclose.__xfflush.___brk.___sbrk._brk._sbrk
.__chmod.__close._ioctl.__IOERROR._isatty._lseek.__LONGTOA._itoa._ultoa.
_ltoa._memcpy._open.__open._strcat._unlink.__VPRINTER.__write._free._malloc
._realloc.__REALCVT.DATASEG#.__Int0Vector.__Int4Vector.__Int5Vector.
__Int6Vector.__C0argc.__C0argv.__C0environ.__envLng.__envseg.__envSize
Is this some kind of debugging symbol information?
Thank you in advance for your help.
Re. 1. No, you can't be sure until you prove otherwise to yourself. One giveaway would be the presence of MOV CR0, ... in the code.
Re. 2. While marketing materials aren't to be confused with an engineering specification, there's a technical reason for this. A 286 CPU could address more than 1M of physical address space. The RAM was only "wasted" in real mode, and only if an EMM (or EMS) driver wasn't used. On 286 systems, the RAM past 640kb was usually "pushed up" to start at the 1088kb mark. The ISA and on-board peripherals' memory address space was mapped 1:1 into the 640-1024kb window. To use the RAM from the real mode needed an EMM or EMS driver. From protected mode, it was simply "there" as soon as you set up the segment descriptor correctly.
If the game actually needed the extra 384kb of RAM over the 640kb available in the real mode, it's a strong indication that it either switched to protected mode or required the services or an EMM or EMS driver.
Re. 3. I wish I remembered that. On reflection, I wish not :) Someone else please edit or answer separately. Hah, I did know it at some point in time :)
Re. 4. You say "[the code] has lots of instructions like call far ptr 18DCh:78Ch". This implies one of three things:
Protected mode is used and the segment part of the address is a selector into the segment descriptor table.
There is code there that relocates those instructions without DOS having to do it.
There is code there that forcibly relocates the game to a constant position in the address space. If the game doesn't use DOS to access on-disk files, it can remove DOS completely and take over, gaining lots of memory in the process. I don't recall whether you could exit from the game back to the command prompt. Some games where "play until you reboot".
Re. 5. The .EXE header does not "point" to any stack, there is no stack section you imply, the concept of sections doesn't exist as far as the .EXE file is concerned. The SS register value is obtained by adding the segment the executable was loaded at with the SS value from the header.
It's true that the linker can arrange sections contiguously in the .EXE file, but such sections' properties are not included in the .EXE header. They often can be reverse-engineered by inspecting the executable.
Re. 6. The SS and SP values in the .EXE header are not file pointers. The EXE file might have a part that maps to the stack, but that's entirely optional.
Re. 7. This is already asked and answered here.
Re. 8. This looks like a debug symbol list. The cheat utility was linked with the debugging information left in. You can have completely arbitrary data there - often it'd various resources (graphics, music, etc.).

Resources