○APIを動的に呼び出す

API名から動的に呼び出す方法を考えてみたいと思います
APIといっても実際はDLLですよね。
そこで、まずは通常の関数を動的に呼び出す方法を考えます

呼び出される側の関数を用意します

int func(int a,int b){
	return 0;
}

関数の引数は右側からスタックに積まれて、呼び出し側が破棄するから
引数の数以上を積んでおけばいいんじゃないかと次のように書いてみました

typedef int (*FUNC)(int,int,int,int,int,int);
FUNC f=(FUNC)func;
f(1,2,3,4,5,6);

C言語の関数ならこれで動きます、そのままの勢いで改造してAPIを呼び出してみます

typedef int (*FUNC)(int,int,int,int,int,int);

int declare(char*libName,char*funcName,int arg0,int arg1,int arg2,int arg3,int arg4,int arg5){
	HINSTANCE hDllInstance = GetModuleHandle(libName); 
	if(!hDllInstance) hDllInstance= LoadLibrary(libName);
	FUNC pf=(int (*)(int,int,int,int,int,int))GetProcAddress(hDllInstance,funcName);
	int r=(*pf)(arg0,arg1,arg2,arg3,arg4,arg5);
	//FreeLibrary(hDllInstance);
	return r;
}

int main(){
	declare("USER32.DLL","MessageBoxA",0,(int)"test",(int)"MessageBox",MB_OKCANCEL,0,0);
	return 0;
}

APIのメッセージボックスが表示されるのですが、その後、プログラムが落ちます。
その原因は、関数が終わった時の引数をだれが処理するか?に問題があるからです。
関数の引数はスタックに積まれて渡されますが、最後に誰がスタックを戻すかにより
呼び出し規則が変わります。
C言語の関数の場合は呼び出し側がスタックをもどしましたが、
APIの場合は関数側がスタックを戻します。
そこで、インラインアセンブラを使ってAPIを呼び出してみることにします。

#include <stdio.h>
#include <windows.h>

char*str="aaa";

int main(){
	void*func=(void*)MessageBoxA;
	int r;
	_asm {
		push 1
		push str
		push str
		push 0
		call func
		mov r,eax
	 }
	printf("%d\n",r);
	return 0;
}

関数の引数を右側からスタックにPUSHして呼び出しています
戻り値はEAXレジスタに入ってきますのでMOVして表示します。

上のプログラムを実行するとメッセージボックスが表示されて、
戻り値も正常に返ってきました。
うまくいっているみたいです。




▲トップページ > プログラミングの実験