

The most practical reason is in the OSED Exam Guide. It is part of the exam.

Second, you control what APIs to call and the asm instructions you use. Sometimes you are “restricted” on what APIs you can use or you may want to achieve different functionalities other than spawning calc.exe or executing a reverse shell. You can also (most probably) avoid bad chars right from the get go, so you don’t have to worry about pesky things like ROP decoders.
Third, you could use automated tools such as msfvenom or epi05’s shellcoder.py (which is great by the way) but you end up not understanding how it works and when something dont work out (and they will), you will be left unable to troubleshoot why. Oh and if you are using WriteProcessMemory to bypass DEP (which is something i go through here >insert link!), you cant use a msfvenom payload because it comes with a decoder stub which fails because your “codecave” is set back to non-write after WriteProcessMemory executes.
Last, you feel leet i guess?
In summary, our shellcode should do the following:
Note: For any calling convention on a 32-bit system, the EAX, EDX, and ECX registers are considered volatile, which means they can be clobbered during a function call. Use EAX, EDX and ECX for temporal manipulation operations since these are unlikely to have any effects beyond the function.
There are a lot of WinAPIs out there that do specific things like creating a new process (CreateProcessA). These APIs can either be found inside the usual kernel32.dll, or can be found in other places such as user32.dll, userenv.dll, advapi32.dll for example.
For the purpose of this OSED prep series, we will focus on the essentials.