You may however be able to get different legitimate instructions than originally intended by jumping to an address in the middle of a multi-byte instruction that happens to decode into a useful series of operations. It follows that there are more usable "returns" in a body of code than just those written in the original source.
There are a multiple usable "returns" in functions because "return" is just "pop" then "jump", so a block that has the effect of manipulating the stack and then transferring control can often be used to the same effect as a return.
on 32-bit x86 the RET instructions are 0xC3 and 0xCB. Any other instruction containing these bytes can be subverted into a return if you can make the processor read the preceding instructions from the wrong starting point.
Sure; the authors in the Roemer paper found a couple of those. The best place to get a sense of how this works is the "Gadget Catalog" section of that paper.
Comments
You may however be able to get different legitimate instructions than originally intended by jumping to an address in the middle of a multi-byte instruction that happens to decode into a useful series of operations. It follows that there are more usable "returns" in a body of code than just those written in the original source.
There are a multiple usable "returns" in functions because "return" is just "pop" then "jump", so a block that has the effect of manipulating the stack and then transferring control can often be used to the same effect as a return.
on 32-bit x86 the RET instructions are 0xC3 and 0xCB. Any other instruction containing these bytes can be subverted into a return if you can make the processor read the preceding instructions from the wrong starting point.
Sure; the authors in the Roemer paper found a couple of those. The best place to get a sense of how this works is the "Gadget Catalog" section of that paper.
Don't forget the variants with a stack-adjusting immediate, 0xC2 and 0xCA. Those can also come in handy.