Talk:Windows NT 3.1 April 1991 build

Build Number[edit source]

I ran a quick scan on the files, and I don't believe it has a version number. GetVersion returns 4 and that happens to be 4.0 (lower 16 bits = major and higher 16 bits = minor). CMD's ver command should also report this. On the other hand, the version string printed onto the desktop should be NT 32-bit Windows [???] (MIKEKE) (MikeKe is the username of the employee who compiled this build). I'm not entirely sure what the ??? should be yet but it appears to be some data pulled from a HWND passed to xxxDesktopWndProc (which would be the HWND for the desktop window?). I currently don't have anything about this early build's WND structure so I'm not sure what the exact string is, but an educated guess would be something like the name of the user the desktop window belongs to or the path to the currently running executable. Regardless of what it is, I don't think it's the build number.

Now I just want to mention that there is no solid proof that this build predates build 127. Recorder's status.txt simply mentioned build 127 and the last update date of 1st of May, but does it really say that build 127 was compiled shortly before 1st of May? We just know that status.txt was last modified on the 1st of May but it doesn't say the note about build 127 was added on that day. It could've been compiled and tested in say February and then johnsp edited that .txt file to correct a typo in May. While that is unlikely, it's possible.

Lastly, there were some discussions about this build being an early R4000 version, but they didn't bother to change/create new machine type. I don't think this is very likely as I have not seen any MIPS III instructions (yet, at least) and there's the string mips\init3000.c inside the kernel. --Lucas Brooks (talk) 12:43, 8 November 2023 (UTC)

COFF Structures[edit source]

I'm writing a COFF to modern PE converter (it mostly works now, header and export table conversion done). Dumping some structures here.

typedef struct _COFF_FILE_HEADER
{
	USHORT TargetMachine; // 0
	USHORT NumberOfSections; // 2
	ULONG TimeDateStamp; // 4
	ULONG PointerToSymbolTable; // 8
	ULONG NumberOfSymbols; // 12
	USHORT SizeOfOptionalHeader; // 16
	USHORT Characteristics; // 18
} COFF_FILE_HEADER;
typedef COFF_FILE_HEADER *PCOFF_FILE_HEADER;

typedef struct _COFF_DATA_DIRECTORY
{
	ULONG VirtualAddress; // 0
	ULONG Size; // 4
} COFF_DATA_DIRECTORY;
typedef COFF_DATA_DIRECTORY *PCOFF_DATA_DIRECTORY;

typedef struct _COFF_OPTIONAL_HEADER
{
	USHORT TargetVersionStamp; // 0
	USHORT LinkerVersionStamp; // 2
	ULONG SizeOfCode; // 4
	ULONG SizeOfInitializedData; // 8
	ULONG SizeOfUninitializedData; // 12
	ULONG AddressOfEntryPoint; // 16
	ULONG BaseOfCode; // 20
	ULONG BaseOfData; // 24
	ULONG ImageBase; // 28
	USHORT TargetOperatingSystem; // 32
	USHORT TargetSubsystem; // 34
	ULONG ImageVersionStamp; // 36
	ULONG SizeOfImage; // 40
	ULONG SizeOfHeaders; // 44
	ULONG SizeOfHeapReserve; // 48
	USHORT SizeOfHeapCommit; // 52
	ULONG SizeOfStackReserve; // 56
	ULONG SizeOfStackCommit; // 60
	ULONG ZeroBits; // 64
	ULONG CheckSum; // 68
	COFF_DATA_DIRECTORY DataDirectory[11]; // 72
	// ??? AdditionalMachineValues; // 133???
} COFF_OPTIONAL_HEADER;
typedef COFF_OPTIONAL_HEADER *PCOFF_OPTIONAL_HEADER;

typedef struct _COFF_STD_SECTION_HEADER
{
	CHAR Name[8]; // 0
	ULONG PhysicalAddress; // 8
	ULONG VirtualAddress; // 12
	ULONG SizeOfRawData; // 16
	ULONG PointerToRawData; // 20
	ULONG PointerToRelocations; // 24
	ULONG PointerToLinenumbers; // 28
	USHORT NumberOfRelocations; // 32
	USHORT NumberOfLinenumbers; // 34
	ULONG Characteristics; // 36
} COFF_STD_SECTION_HEADER;
typedef COFF_STD_SECTION_HEADER *PCOFF_STD_SECTION_HEADER;

typedef struct _COFF_EXPORT_DIRECTORY
{
	ULONG Characteristics;
	ULONG Name;
	ULONG TimeDateStamp;
	USHORT MajorVersion;
	USHORT MinorVersion;
	ULONG Base;
	ULONG NumberOfFunctions;
	ULONG NumberOfNames;
	ULONG AddressOfFunctions;
	ULONG AddressOfNameOrdinals;
	ULONG Unknown; // Not sure yet, perhaps an address
} COFF_EXPORT_DIRECTORY;
typedef COFF_EXPORT_DIRECTORY *PCOFF_EXPORT_DIRECTORY;

Still got a fair bit to do to get proper modern PEs (import table, resources and COFF symbols must be converted as well), will add more structures as I reverse more stuff. --Lucas Brooks (talk) 13:03, 10 November 2023 (UTC)

Import directory conversion done, now PEs loaded into IDA will show correct imports and exports. Next thing on the list would be symbols, then we'll be able to reverse these binaries exactly like all other PEs (resources aren't that important). Here's the import descriptor structure.
typedef struct _COFF_IMPORT_DESCRIPTOR
{
	ULONG Characteristics;
	ULONG Name;
	ULONG TimeDateStamp;
	ULONG FirstThunk;
} COFF_IMPORT_DESCRIPTOR;
typedef COFF_IMPORT_DESCRIPTOR *PCOFF_IMPORT_DESCRIPTOR;
--Lucas Brooks (talk) 14:25, 10 November 2023 (UTC)

PE Converter[edit source]

My converter is now functional enough to convert some binaries to the modern PE format and have IDA correctly load the symbols. Some PEs cannot be converted yet and I believe the imports of all converted PEs are wrong (doesn't really matter for reverse engineering, since we have symbols). So here it is: https://mega.nz/file/u1x0AKKK#8mbG8CRqkziBKaIzuSArSjBt9WehqAWBomTYLVTuLfg. --Lucas Brooks (talk) 08:24, 11 November 2023 (UTC)

The import issue is worse than expected. I think the array of pointers of the imported functions start right after the import descriptor array in the idata segment. So, they start at 16 * (number of DLLs imported + 1), and code would just call function pointers at hard coded locations. This is a problem since the size of import descriptors of the modern PE format is 20 instead of 16. I don't think there is a solution to this problem, unless you patch the code which I don't think can be done automatically. Again, since we have symbols, it's not a big deal. --Lucas Brooks (talk) 10:36, 11 November 2023 (UTC)
Updated the converter to not overwrite code segment if header is shorter than the size of new PE header and fixed an issue with missing imports. New link: https://mega.nz/file/6lA1zLwI#3G3kPKCE1N6uIRQ8sTigNeGUax7oMSpI6qxXz4E0lXE. -Lucas Brooks (talk) 10:12, 15 November 2023 (UTC)
Updated the converter a bit more. Now I have two versions - one that does not care about the import problem mentioned earlier, and one that does attempt to fix it (in a very hackish way).
  • If the binary you're converting has symbols, use this one.
  • If the binary does not contain symbols (never the case for files from this build), use this one so that it attempts to correct the references to imported functions (beware that it carries a risk of patching the code wrong and causing all sorts of issues).
-Lucas Brooks (talk) 09:35, 16 November 2023 (UTC)

Unknown part of desktop watermark string[edit source]

Thanks for the PE converter (personally I would have patched IDA's PE loader, like I did to get a working IDA loader for pure-COFF bootloader executables, but that's just my viewpoint).

Using this I did some static analysis of usersrv.dll.

The unknown string is wnd->ptr_20->ptr_28; so I looked at xxxCreateWindowEx and saw that wnd->ptr_20 is set to wnd->ptr_4->ptr_34->ptr_0; ptr_4 is set by CreateObject to the current queue pointer. Looking at various queue init functions, CreateThreadInfo sets queue->ptr_34 to the new thread info structure; and xxxInitThread sets threadInfo->ptr_0 to the return value of xxxOpenDesktop.

Final piece of the puzzle is xxxCreateDesktop which sets desktop->ptr_28 to copy of the passed in desktop name.

First desktop created by xxxInitWindows is named "default", so printed string would be NT 32-bit Windows [default] (MIKEKE).

92.18.242.224 12:57, 11 November 2023 (UTC)

WOW Windows 3.1 Build[edit source]

I don't believe the WOW actually features a pre-release Windows 3.1 build. Maybe the drivers and config files came from some early build but Kernel, GDI and User are WOW files. GDI and User are just stubs while Kernel is the 3.0 kernel used by OS/2. --Lucas Brooks (talk) 11:15, 12 November 2023 (UTC)

  • The kernel calls itself "Microsoft Windows Kernel Environment Version 3.10" in the NE description, and also iirc is incompatible with regular gdi/user, and has 3.1 only functions. Starfrost (talk) 11:23, 12 November 2023 (UTC)

1024 x 768 Resolution[edit source]

Replace 06 00 0B 96 07 61 01 3C 44 00 2B AC 08 00 0C 96 07 61 01 3C 48 00 2C in JAZZ.DLL with 00 04 18 24 07 61 01 3C 44 00 38 AC 00 03 0F 34 07 61 01 3C 48 00 2F to force this build to run at 1024x768. At this resolution the black bar on the right-hand side does not show up. I know a lot of others disagree, but I think 1024x768 is the intended resolution, at least the resolution Microsoft used the most. It is true that there are lots of hard coded instances of 1280x1024, but I think those are just for 'fill the whole screen no matter what resolution we have' and 'check if the resolution is valid'. I also don't think the black bar was because of people mistaking vert res for horiz res and doing something like height x height, because as you can see 1024x768 does not become 768x768. -Lucas Brooks (talk) 13:38, 14 November 2023 (UTC)

I managed to figure out the cause of the black bar issue, see the actual page for info. Currently this build does not display correctly when you run it under 1024x768 in MAME, so if you want to avoid the black bar, either apply the patch described here to force the resolution to 1024x768, or apply the patch described on the actual page. I'm not a big fan of 1280x1024 though, for me 1024x768 looks much better. -Lucas Brooks (talk) 09:35, 16 November 2023 (UTC)

citation 2[edit source]

link is dead — Preceding unsigned comment added by 2600:1700:6c10:47e0:44fb:4a1d:518c:648d (talkcontribs)

Yes, I am aware. The podcast is available on countless streaming services so it doesn't really matter. --Ryuzaki (talk | contribs) 20:18, 23 November 2023 (UTC)

Software Development[edit source]

Attempt at rendering stuff on the screen with GRE.

Compiling apps for this ancient build of NT is now possible (don't get too excited yet, you won't be able to port Chromium/Firefox to it and use it as your daily driver). You'll want to compile your apps with build 239's toolchain (239's toolchain conveniently outputs R3000 binaries instead of R4000 binaries) and convert the executables to the COFF format used by this build. The conversion can be done with the latest version of my converter (see above, link will be updated soon).

Currently I've only succeeded in compiling and running native executables. For native apps, use the following template and define NtProcessStartup as the entry point:

#include <windows.h>

int main(int argc, char **argv, char **envp)
{
	/* Your code here. */
}

void NtProcessStartup(PEB *Peb)
{
    int argc; /* Argument count. */
    char **argv; /* Argument vector. */
    char **envp; /* Environment variables. */
    /* Code to setup argc, argv and envp for main() here. */
	NtTerminateProcess(-1, main(argc, argv, envp));
}

You'll also have to pay attention to the API differences between this build and build 239. You must call gre.dll!Initialize before you can use any of the GDI functions. Similarly, you must call user.dll!InitWindows before you can use any of the USER functions. --Lucas Brooks (talk) 09:58, 24 November 2023 (UTC)

Managed to successfully compile and run a Win32 app. Use the following template for Win32 apps and define BaseProcessStartup as the entry point:
#include <windows.h>

HANDLE ghInstance;

int main(int argc, char **argv, char **envp)
{
    /* ghInstance = 0x7FFFF0C4->ptr0h->ptr30h->ptr8h; */
	/* Your code here. */
}

void BaseProcessStartup(void)
{
    int argc; /* Argument count. */
    char **argv; /* Argument vector. */
    char **envp; /* Environment variables. */
    /* Code to setup argc, argv and envp for main() here. */
	ExitProcess(main(argc, argv, envp));
}
--Lucas Brooks (talk) 09:00, 25 November 2023 (UTC)

Working Toolchain[edit source]

Managed to put together a working toolchain. Download here: https://mega.nz/file/DhhFWASI#X0Ipuu94EIhvE9FTXIsE4QkgJw_4mT1yBP35BiHNY4w. You install this toolchain on build 239 and it'll produce executables for this April 1991 build. I've modified all the headers to not have ANSI and Wide suffixes for APIs that involve strings, generated import libraries based on this build's DLLs and wrote a tool to automatically convert 239's PEs to the COFF format used by this build. I've also included 4 samples, you can compile and test them. @Starfrost now you can test the APIs. Have fun! --Lucas Brooks (talk) 04:46, 30 November 2023 (UTC)