提案されたアプローチの考え方は、間接関数呼び出しを使用して関数アドレスを最初に計算してから呼び出す必要があるという考え方です。 Cプリプロセッサは、実際の関数に対してプロキシ関数を定義する方法を提供するために使用され、このプロキシ関数は、プロキシ関数がアクセスを提供する実際の関数の実際のアドレスを決定するために必要な計算を提供します。
プロキシデザインパターンを使用すると、プロキシとしてのラッパークラスを作成することにより、他の オブジェクトへのインタフェースを提供することができます:
が、これは言っているプロキシデザインパターンの詳細についてはWikipedia article Proxy patternを参照してください。プロキシであるラッパークラス は、オブジェクトのコードを変更することなく、 のオブジェクトに追加の機能を追加できます。
私は、間接呼び出しの同じタイプのソースコードの読み取りが困難にするようしかし、それは、このような形で実装の詳細を隠蔽するためにCプリプロセッサを使用する必要はありませんを実装し、代替をお勧めします。
Cコンパイラでは、struct
に関数ポインタをメンバーとして含めることができます。これは、構造体が定義されているときに関数ポインタを持つ外部的に見える構造体変数を定義することができるということです。構造体変数の定義で指定された関数はstatic
であることを意味します(What does "static" mean in a C program参照)。 )
したがって、struct
タイプ、外部に見える構造変数の宣言、static
修飾子で使用される関数、および修飾子として使用される関数を定義するヘッダファイルfunc.hと実装ファイルfunc.cの2つのファイルがあります。関数のアドレスを使って外部から見える構造体変数の定義。
魅力的なのは、ソースコードを読みやすく、ほとんどのIDEが間接的にこのような間接的な処理を行うという点です。Cプリプロセッサはコンパイル時にソースを作成するために使用されないため、 IDEなどのソフトウェアツールを使用します。関数を使用してCソース・ファイルにインクルードされるだろう例のfunc.hファイルは、次のようになり
:
:
// define a type using a typedef so that we can declare the externally
// visible struct in this include file and then use the same type when
// defining the externally visible struct in the implementation file which
// will also have the definitions for the actual functions which will have
// file visibility only because we will use the static modifier to restrict
// the functions' visibility to file scope only.
typedef struct {
int (*p1)(int a);
int (*p2)(int a);
} FuncList;
// declare the externally visible struct so that anything using it will
// be able to access it and its members or the addresses of the functions
// available through this struct.
extern FuncList myFuncList;
そしてfunc.cファイルの例は次のようになります
#include <stdio.h>
#include "func.h"
// the functions that we will be providing through the externally visible struct
// are here. we mark these static since the only access to these is through
// the function pointer members of the struct so we do not want them to be
// visible outside of this file. also this prevents name clashes between these
// functions and other functions that may be linked into the application.
// this use of an externally visible struct with function pointer members
// provides something similar to the use of namespace in C++ in that we
// can use the externally visible struct as a way to create a kind of
// namespace by having everything go through the struct and hiding the
// functions using the static modifier to restrict visibility to the file.
static int p1Thing(int a)
{
return printf ("-- p1 %d\n", a);
}
static int p2Thing(int a)
{
return printf ("-- p2 %d\n", a);
}
// externally visible struct with function pointers to allow indirect access
// to the static functions in this file which are not visible outside of
// this file. we do this definition here so that we have the prototypes
// of the functions which are defined above to allow the compiler to check
// calling interface against struct member definition.
FuncList myFuncList = {
p1Thing,
p2Thing
};
この外部から見える構造体を使用して簡単なCソースファイルは次のようになり
:
#include "func.h"
int main(int argc, char * argv[])
{
// call function p1Thing() through the struct function pointer p1()
myFuncList.p1 (1);
// call function p2Thing() through the struct function pointer p2()
myFuncList.p2 (2);
return 0;
}
としてあなたはこの関数呼び出しは、間接的な機能は、内のオフセットで指定された構造体を通じて呼び出す今ある見ることができるように
; 10 : myFuncList.p1 (1);
00000 6a 01 push 1
00002 ff 15 00 00 00
00 call DWORD PTR _myFuncList
; 11 : myFuncList.p2 (2);
00008 6a 02 push 2
0000a ff 15 04 00 00
00 call DWORD PTR _myFuncList+4
00010 83 c4 08 add esp, 8
; 12 : return 0;
00013 33 c0 xor eax, eax
:上記main()
のためのVisual Studio 2005から放出されたsemblerは以下が指定されたアドレスを介して計算されたコールを示すようになります。構造体。
このアプローチの良い点は、データ領域から関数を呼び出す前に正しい関数アドレスが置かれていれば、関数ポインタを含むメモリ領域に何でもできます。したがって、実際には、正しいアドレスでエリアを初期化する機能と、エリアをクリアする2つの機能を持つことができます。したがって、関数を使う前に関数を呼び出して領域を初期化し、関数の終了後に関数を呼び出して領域をクリアします。これはmain()
を修正よう
// file scope visible struct containing the actual or real function addresses
// which can be used to initialize the externally visible copy.
static FuncList myFuncListReal = {
p1Thing,
p2Thing
};
// NULL addresses in externally visible struct to cause crash is default.
// Must use myFuncListInit() to initialize the pointers
// with the actual or real values.
FuncList myFuncList = {
0,
0
};
// externally visible function that will update the externally visible struct
// with the correct function addresses to access the static functions.
void myFuncListInit (void)
{
myFuncList = myFuncListReal;
}
// externally visible function to reset the externally visible struct back
// to NULLs in order to clear the addresses making the functions no longer
// available to external users of this file.
void myFuncListClear (void)
{
memset (&myFuncList, 0, sizeof(myFuncList));
}
だから、あなたが何かをすることができます:
myFuncListInit();
myFuncList.p1 (1);
myFuncList.p2 (2);
myFuncListClear();
しかし、あなたが本当にやりたいだろうかmyFuncListInit()
への呼び出しは、場所の近くではないでしょうソースのどこかになることです関数は実際に使用されます。
もう1つの興味深いオプションは、データ領域を暗号化し、プログラムを使用するために、正しいポインタアドレスを取得するためにデータを適切に復号化するために正しいキーを入力する必要があります。
あなたのコメントをお寄せいただきありがとうございます – robert
これは、人々が**より良い**コードを書いて、悪くないように助けることです。 – Olaf
@Olaf私は商用ソフトウェアを保護しようとしています。 – robert