有一个解决方案,但它更像是一种 hack 而不是
独立于平台。它仅适用于 32 位 x86 架构
当使用 cdecl 调用约定时。
#include <iostream>
const int PARAMS = 0xCAFEBABE; // set this value to be something unique (read below)
typedef void(*CALLBACK)(void);
void functionICantModify(CALLBACK cb) {
cb();
}
/*
* It depends on how on x86 with cdecl calling convention the stack frame
* is generated by most compilers.
*
*
* Stack frame when called as myCallback();
* <--- esp is now same as ebp and points to old ebp on stack (mov ebp, esp)
* [ebp] ; this is old ebp (push ebp)
* [returnAddress] ; return address to functionICantModify
* [undefined] ; possibly local variable of functionICantModify or old ebp
*
*
* Stack frame when called as myCallback(PARAMS, 10, 50)
* <--- esp is now same as ebp and points to old ebp on stack (mov ebp, esp)
* [ebp] ; this is old ebp (push ebp)
* [returnAddress] ; return address to function from where i called myCallback(PARAMS, ...)
* [PARAMS] ; this is our flag
* [param1]
* [param2]
*
*
* ebp register acts like a frame pointer.
* When access to local variables, ebp is substracted (first local 32bit variable is [ebp-4], second [ebp-8], ...)
* When access to function arguments, ebp is added (first 32bit argument is [ebp+8], because [ebp] points to old ebp and [ebp+4] is return address (on 32bit architecture)
*
* You can see from those stack frames abowe that when you will access the paramsFlag in myCallback when
* myCallback called from functionICantModify, you will get that undefined value. So it is important
* to set the PARAMS value to be something unique that will never appears on stack in that place when
* myCallback is called. By checking paramsFlag, you can detect if the function is called by
* functionICantModify or by your logic.
*/
void myCallback(int paramsFlag, int param1, int param2) {
/*
* This is how prolog is generated by most compilers on x86 (intel syntax)
* push ebp
* mov ebp, esp
*/
static int _param1 = 0;
static int _param2 = 0;
if (paramsFlag == PARAMS) { // called from my logic, setup state
std::cout << "Called to set state";
_param1 = param1;
_param2 = param2;
}
else { // called from functionICantModify, don't access the function parameters !!!!!!!!!!!!!
std::cout << "Called from functionICantModify, _param1=" << _param1 << ", _param2=" << _param2 << std::endl;
}
/*
* This is how epilog is generated by most compilers on x86 with cdecl (intel syntax)
* mov esp, ebp
* pop ebp
* ret
*/
}
int main(int argc, char* argv[]) {
myCallback(PARAMS, 10, 50);
functionICantModify((CALLBACK)myCallback);
}