An instruction set is CPU-specific, not OS specific. An assembly language for a instruction set is just an abstraction for that instruction set, and is not inherently OS-specific either. However, a particular assembler (the software translating from the assembly language to machine code) may only be available on one OS.
Also, what instructions you perform may differ between operating systems. While the language may not be platform-specific, your code certainly is. The most important difference between platforms is the calling convention, which defines how registers may be used, how a call stack may be used, and where you put which argument. In this sense, Windows Assembly is not a real thing, just a dialect or style of writing assembly.
Machine code does not give you more control over the hardware than a high-level language: in the end, the high-level language such as Go or C++ is just compiled to machine code as well.
The operating system protects and restricts every process, no matter how the code for that process was written. The CPU itself provides security levels that cannot be circumvented from a less-privileged level, see Protection rings on Wikipedia. The privileged operating system code runs in an inner ring. The CPU can switch rings via syscall instructions, but that causes a privileged handler to run rather than letting the old process continue in a more privileged mode. This is closely related to memory protection.
However, the instruction set may offer instructions that cannot be used by the high-level language. For example, a high-level language might not (directly) allow you to make syscalls, trigger interrupts, use SIMD instructions, access coprocessors, …. That can make it necessary to write parts of a software in assembly. On the other hand, some languages offer intrinsic functions for these instructions that the compiler knows to compile as that instruction rather than as an ordinary function call.