> Consequently, modern exploits tend to look like: (1) figure out how memory is laid out, or close enough, (2) calculate where a useful function is, that you can call with arbitrary parameters (aka a gadget), (3) abuse that function to do your bidding by controlling its parameters and the instruction pointer.
This is all correct, but generally a gadget is what a snippet of assembly / machine code that you call to get the CPU into a state to call a function. So its less a function and more a set of instructions (anywhere) that are useful.
So for example, you might have a gadget to set specific registers with values off the stack so you can call malloc with the correct size, etc.
Now get the hell off my lawn you whippersnappers, before I throw my teef at you.
This is important as modern no-execute et al. prevent you from simply dumping arbitrary code into memory and running it.
Consequently, modern exploits tend to look like: (1) figure out how memory is laid out, or close enough, (2) calculate where a useful function is, that you can call with arbitrary parameters (aka a gadget), (3) abuse that function to do your bidding by controlling its parameters and the instruction pointer.
(2) gets around your inability to write + execute code directly, because the system is by definition already set up to allow built-in code to execute. And that built-in code generally does lots of useful things (like reading and writing memory).
ASLR makes (1) harder, but fundamental efficiency reasons at the operating system code level mean than usually "everything" isn't fully randomized.