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

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.

Related

DOS stub in a PE file [duplicate]

This question already has an answer here:
What's this extra bytes?
(1 answer)
Closed 2 years ago.
Lately, I analyzed some Windows executable files using a hex editor. The PE header starts at address 0x100, so there are 256 Bytes of data before the PE image actually starts. The first 256 Bytes:
I know the following about the file structure
0x00 - 0x3F: This is the MZ header (64 bytes long).
0x40 - 0x4D: These 14 bytes encode seven x86 (16 bit mode) instructions, which are used to print "This program cannot run in DOS mode.\r\r\n" to the screen, using a DOS system call (interrupt 0x21).
0x4E - 0x78: This is the string "This program cannot run in DOS mode.\r\r\n" with a dollar-sign at the end, which tells DOS that this is the end of the string.
0x79 - 0x7F: These are NULL bytes; I guess that they are inserted for alignment.
So I know what the first 128 bytes are for. My question is: What are the next 128 bytes (0x80 - 0xFF) used for? (The PE image starts after them at 0x100.)
It's the so-called undocumented "Rich header". It's a weakly encrypted block of data inserted by the Microsoft linker that indicates what Microsoft tools were used to make the executable. It includes version information from the object files linked, so includes information on what compilers, assemblers and other tools were used.
To decode the Rich header search for the Rich marker and then obtain the 32-bit encryption key that follows. Then working backwards from the Rich marker, XOR the key with the 32-bit values stored there until you find a decoded DanS marker. In between these two markers will be a list of pairs of 32-bit values. The first value of the pair identifies the Microsoft tool used, and the second value indicates how many linked object files were created using this tool. The upper 16-bit part of the tool id value indicates what kind of tool it was, and the lower 16-bit part identifies the build version of the tool.

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.

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

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).

Which of the MS-DOS header fields are mandatory/optional?

The above is the complete list of MS-DOS header fields, but I don't know which of them are mandatory and which are optional, does anyone know?
If you're trying to create PE Image, e_magic(Magic number) and elfanew(File address of new exe header) are the only mandatory fields that you have to fill in. elfanew should point to the PE IMAGE_NT_HEADER structure.
Well back in 2006 someone wanted to create the world most tiny PE. For this he wrote a small PE Fuzzer. With the smallest codebase posible.
return 42;
He managed to get the following sizes of PE's
you are too busy to read the entire page, here is a summary of the results:
Smallest possible PE file: 97 bytes
Smallest possible PE file on Windows 2000: 133 bytes
Smallest PE file that downloads a file over WebDAV and executes it: 133 bytes
You can check his work here:
http://www.phreedom.org/research/tinype/
He also states the required header values. These are:
e_magic
e_lfanew
Machine
NumberOfSections
SizeOfOptionalHeader
Characteristics
OptionalHeader:
Magic
AddressOfEntryPoint
ImageBase
SectionAlignment
FileAlignment
MajorSubsystemVersion
SizeOfImage
SizeOfHeaders
Subsystem
SizeOfStackCommit
SizeOfHeapReserve
For MS-DOS, all of the headers are mandatory.
For Win9x and above, e_lfanew must be the offset from the start of the image to the start of the IMAGE_NT_HEADERS, and e_magic must be IMAGE_DOS_SIGNATURE ('MZ').

Resources