CPLUSPLUSCalling function pointers without definitions

Posts 1–5 of 5 · Page 1 of 1
Calling function pointers without definitions
I've had this huge bug on me for the longest time in C++/C in that you cannot call abstract function pointers without defining the arguments like so:
Code:
int (*func)(int arg1,char* fedup,int keeptypingmyarguments);
Now you should know that good practice is expecting your arguments to always be 4 bytes, since standard x86 architecture pushes 4 bytes at a time on to the stack. The return value should always be 4 bytes too since it is passed in a register that can only hold up to 4 bytes; So why can't C call a function without needing to define all the arguments? We know every argument is going to be 4 bytes!

Well we're going to break this good practice of 4 byte arguments so we can call abstract function pointers without needing to make a definition.

I thought about it: You can pass entire structures on the stack in C!
Entire arrays cannot be passed on the stack (they are converted to a 4 byte pointer), but we can pass structs

Code:
struct Passer {
    int args[13]; //52 bytes of argument stack space
    int pos; //Stack position (0 is 1st arg, 1 is 2nd arg, etc.) *Can be a char to save space
};
You don't need pos, but I use it when the user is deciding which arguments to pass without having to input the number of arguments or making a definition.

You can also replace int args[x] with char args[x*4]. I do this so I have access to each byte in the member. If you need to pass an entire integer, just cast it or use memcpy.

We can now define an abstract function type.
Code:
typedef int (*FUNC)(Passer pass);
FUNC func = ptr_to_func;
I think any experienced C programmer would know what to do from there.

Code:
#include <windows.h>
struct Passer {
    int args[13];
    int pos;
};
typedef int (*FUNC)(Passer pass);
FUNC func;
int main() {
    Passer pass;
    func = (FUNC)GetProcAddress(LoadLibrary("kernel32.dll"),"Beep");
    pass.args[0] = 1500;
    pass.args[1] = 500;
    func(pass);
    Sleep(100);
    func({{1500,500}}); //You don't even need to declare a Passer variable, just note the two pairs of {}s
}
The reason this works is because C puts the entire structure on the stack in order.

I hope this helps!

This trick goes great with my Importing library procedures without any IATs tutorial
@TrollerCoaster
that was a nice try, but unfortunately, it will fail.
let me explain why.

you tried it on a __stdcall function, where stack is fixed by the callee
now, the ordinary function expects 2 parameters, but you passed 14 of them!

so the stack would look like

Code:
1500
500
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized 
uninitialized
when it returns, it pops only first two, leaving those uninitialized ints on stack, which would cause errors.

also, if you call a __cdecl function, it will leave everything except one parameter!
and if you try to call __thiscall or __fastcall, you wont be able to modify ecx and edx registers!

anyway i wrote a function that will do the job for you, hope you are good at asm!
Code:
enum CALLINGCONV
{
	CALLINGONV_STDCALL,
	CALLINGCONV_CDECL,
	CALLINGCONV_THISCALL, 
	CALLINGCONV_FASTCALL,
};

int callsomething(void* func, CALLINGCONV cc, int* args, unsigned int count)
{
	int retval;

	if (cc == CALLINGCONV_THISCALL && count < 1) return 0; //__thiscall function must have at least one parameter (this pointer)

	__asm
	{
		push ecx;                                 //save original values
		push esi;
		push edi;

		push eax;                                 //calculate stack size
		mov eax, 4;
		mov ecx, count;
		mul ecx;
		mov ecx, eax;
		pop eax;

		sub esp, ecx;                             //put parameters on stack
		mov esi, args;
		mov edi, esp;
		rep movs [edi], byte ptr [esi];

		cmp cc, CALLINGCONV_THISCALL;             //check if calling convention was __thiscall
		jne not_thiscall;

		pop ecx;                                  //remove first parameter from stack and put it on ecx

not_thiscall:
		cmp cc, CALLINGCONV_FASTCALL;             //check if calling convention was __fastcall
		jne neither;

		mov esi, count;                           //check parameter count
		cmp esi, 1;                               //make sure we have 1 parameters or more
		jl neither;

		pop ecx;                                  //remove first parameter from stack and put it on ecx

		mov esi, count;	                          //check parameter count
		cmp esi, 2;                               //make sure we have 1 parameters or more
		jl neither;

		pop edx;                                  //remove second parameter from stack and put it on edx

neither:
		call func;                                //lets call the function
		mov retval, eax;                          //save return value

		cmp cc, CALLINGCONV_CDECL;                //if calling convention was __cdecl, perform a stack clean up
		jne not_cdecl;
		push eax;
		mov eax, 4;
		mov ecx, count;
		mul ecx;
		mov ecx, eax;
		pop eax;
		add esp, ecx;

not_cdecl:
		pop edi;                                  //restore original values
		pop esi;
		pop ecx;
	}
	return retval;
}
compile it in visual studio

example:
Code:
void hey(int arg1, int arg2, int arg3, int arg4) //default calling convention is __cdecl
{
	cout << arg1 << ",  " << arg2 << ",  " << arg3 << ",  " << arg4 << endl;
}

void __fastcall hey2(int arg1, int arg2, int arg3, int arg4)
{
	cout << arg1 << ",  " << arg2 << ",  " << arg3 << ",  " << arg4 << endl;
}

class myclass
{
public:
	int arg1;
	void hey3(int arg2, int arg3, int arg4) //this one should be __thiscall
	{
		cout << arg1 << ",  " << arg2 << ",  " << arg3 << ",  " << arg4 << endl;
	}
};

//you cant convert &myclass::hey3 to void*, so i came up with this trick..
struct hack 
{
	typedef void (myclass::*myfunc)(int,int,int);
	myfunc pp;
};

int main() 
{
	FARPROC func = GetProcAddress(LoadLibraryA("kernel32.dll"), "Beep");

	myclass my;
	my.arg1 = 5;
	hack hk;
	hk.pp = &myclass::hey3;

	int beeparg[2] = {1500, 500};
	int myargs[4] = {5, 10, 51, 11};
	int thisargs[4] = {(int)&my, 10, 51, 11};
	
	callsomething((void*)func, CALLINGONV_STDCALL, beeparg, 2);
	callsomething((void*)hey, CALLINGCONV_CDECL, myargs, 4);
	callsomething((void*)hey2, CALLINGCONV_FASTCALL, myargs, 4);
	callsomething((void*)(*(int*)(&hk)), CALLINGCONV_THISCALL, thisargs, 4);
}
hope comments explained everything
@giniyat101
I actually found a much better way in pure C. I might be posting it here
And it's okay, ASM used to be the only real programming language I knew.
Quote Originally Posted by TrollerCoaster View Post
@giniyat101
I actually found a much better way in pure C. I might be posting it here
And it's okay, ASM used to be the only real programming language I knew.
it would be cool if you can do it in c
hope you succeed
good info It helps me
Posts 1–5 of 5 · Page 1 of 1

Post a Reply

Similar Threads

Tags for this Thread

None

Talk with us