There is no guarantee that all functions will use a standard stack frame with ebp being pushed and then set to esp at the start of the function. It's not that uncommon for functions to use ebp as a general purpose register, and then refer to function parameters and local variables via the esp register.
That's obviously more complicated for the code generator, because the value of esp will change over time (for example as variables are being pushed in a function call), but it's certainly possible to generate code that way.
At best, you could try and guess the return address by scanning up the stack, looking for a potential return address (e.g. by checking if the VMA for that address has the VM_EXEC
flag set). Then having found a potential address, you would need to scan backwards from that address, looking for code that appears to be a function call (one example being an E8 five bytes back).
You could go further, by checking where the function call (assuming it's not an indirect call) is pointing to an address somewhere near your current IP, although figuring out what is a safe definition of "near" is not an easy decision either.
The bottom line is that it's going to very complicated, and there's still no guarantee that you're going to find the correct address.