I am trying to understand classical buffer overflow exploits where an input buffer overwrites the stack, the function return address that is saved on the stack and upper memory regions (where you usually place the shell code). There are many examples of this on the internet and I think I have understood this pretty well:
You put more data in some input buffer that the developer has made fixed size
Your input overwrites the function arguments and the return address to the calling function on the stack
- That address is loaded into EIP when the OS tries to return from the function the overflow happened in and that is what allows you to get data you control into the EIP register (when not reading carefully some articles you get the impression you can overwrite CPU registers. Of course, this is not the case, you can only overwrite the stack but the CPU will load addresses from the stack into its registers)
- If the exploit is well designed the value loaded into EIP will make the program jump to the beginning of the shell code (see point 5)
- The next thing I think I have understood well is the "JMP ESP" mechanism. When replicating the crash in your lab environment you look for a place in memory that contains the "JMP ESP" instruction and you overwrite the EIP (now my wording is not precise, I know...) with that address. That address needs to be identical no matter how often you run this, at what time, which thread is executing your stuff etc., at least for the same OS version and the address must not contain any forbidden bytes. The code will jump to that address (at the same time the stack is reduced by 4 bytes) so "jmp esp" will jump to the next address in my overflow buffer after the place where I had put the value to overwrite EIP with and that is usually the place where the shellcode goes, maybe prepended with NOPs.
Now comes the question.
All articles I've read so far are looking for the address of the "JMP ESP" instructions in a DLL (which must not be relocatable, not compiled with ASLR, etc.). Why not looking in exe itself for a "jmp esp"? Why does it need to be in a DLL?
I have run the "!mona modules" command in Immunity Debugger and the only module displayed that satisfies all these conditions is the exe itself. When I look in popular exploit databases the address is always in loaded DLLs.
I cannot see any obvious reason for this. The exe can also be located at the same address in memory the same way a DLL can. What is the difference?