Introducing
Your new presentation assistant.
Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.
Trending searches
テストを書いていると、
一時的に関数の挙動を書き換えたいときがあります。
//2000年以上か?
bool isOver2000year()
{
//現在動かしたら、絶対 TRUE にしかならない
return time(NULL) >= 946652400; //2000-01-01 00:00:00
}
PCの時計を変更
うーん、めんどくさい。
そんなことで困ってませんか?
//time関数を乗っ取る all your function are belong to us
SEXYHOOK_BEGIN(time_t,SEXYHOOK_CDECL,&time,(time_t * a))
{
//昔の時刻を返すようにする
return 915116400;//1999-01-01 00:00:00
}
SEXYHOOK_END();
//現在は2010年なので、結果は TRUE になりそうだが、
//上で関数をフックしているので、結果は FALSE
bool r = isOver2000year();
printf("%d" , (int)r); //0
SEXYHOOK_BEGIN(戻り値,呼出規約,&関数名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
//ターゲット
//上の例でもあったtime関数をフックしてみる
time(NULL);
//関数をフックするときは、 SEXYHOOK_FUNC_HOOK_1_BEGIN を使います。
SEXYHOOK_BEGIN(time_t,SEXYHOOK_CDECL,&time,(time_t * a) )
{
return 915116400;//1999-01-01 00:00:00
}
SEXYHOOK_END();
//例 その2
//strstr関数をフックしてみる。
const char * p = strstr("hello","hel");
//strstr をフックする場合
SEXYHOOK_(const char*,SEXYHOOK_CDECL,
&strstr,(const char * a , const char * b) )
{
//絶対失敗する strstr 関数にする
return NULL;
}
SEXYHOOK_END();
//例 その3
//自作関数をフックしたい場合
int add(int a,int b)
{
return a + b;
}
//add関数を書き換えて「引き算」にする。
SEXYHOOK_BEGIN(int,SEXYHOOK_CDECL,&add,(int a,int b) )
{
return a - b; //引き算に書き換える
}
SEXYHOOK_END();
SEXYHOOK_BEGIN(戻り値,呼び出し規約,&メソッド名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
//テンプレート
template<typename _T> class TempTest
{
public:
int add(_T a,_T b)
{
return a + b;
}
};
{
int r;
TempTest<int> t;
//ここからフック
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,&TempTest<int>::add,int a,int b)
{
return a - b; //足し算を引き算に書き換える
}
SEXYHOOK_END();
r = t.add(1,2);
printf("\r\nテンプレートクラスのテスト\r\n");
printf("TempTest<int> : %d //足し算を引き算に\r\n",r);
SEXYHOOK_ASSERT(r == -1);
}
Child child;
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS, &Child::g , () )
{
return 103; //Child::g
}
SEXYHOOK_END(&child); //thisを渡す
SEXYHOOK_BEGIN
(戻り値,SEXYHOOK_STDCALL,&API名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
//HeapCreate API を失敗させてみる。
SEXYHOOK_BEGIN(HANDLE,SEXYHOOK_STDCALL,
&HeapCreate,(HANDLE a1,DWORD a2,SIZE_T a3) )
{
return NULL;
}
SEXYHOOK_END();
//必ず失敗する.
HANDLE h = HeapCreate(0,0,100);
SEXYHOOK_ASSERT(h == NULL);
//非クラス系
void func1(){}; //SEXYHOOK_CDECL
void __stdcall func2(){}; //SEXYHOOK_STDCALL
void __fastcall func3(){}; //SEXYHOOK_FASTCALL
//クラス系
class MyClass
{
public:
void func1(){}; //SEXYHOOK_CLASS
void __stdcall func2(){}; //SEXYHOOK_CLASS_STDCALL
void __cdecl func3(){}; //SEXYHOOK_CLASS_CDECL //別らしい
void __fastcall func4(){}; //SEXYHOOK_CLASS_FASTCALL
//staticは関数として呼び出してください
static void func5(){}; //SEXYHOOK_CDECL
static void __stdcall func6(){}; //SEXYHOOK_STDCALL
static void __fastcall func6(){}; //SEXYHOOK_FASTCALL
}
//一時的にtime 関数をフックする。
SEXYHOOK_BEGIN
(time_t,SEXYHOOK_CDECL,&time,(time_t * a) )
{
//time が呼ばれたらここがコールされる。
//昔の時刻を返すようにする。
return 915116400;//1999-01-01 00:00:00
}
SEXYHOOK_END();
//プリプロセッサによりこのように展開されます。
class SEXYHOOKFunc72 : public SEXYHOOKFuncBase
{
public:
SEXYHOOKFunc72() { }
void Hook(void* inVCallThisPointer = 0,void* inFuncAddress2 = 0)
{
*(getSexyhookThisPointer()) = (uintptr_t)this;
FunctionHookFunction(
inFuncAddress2 ? inFuncAddress2 : SEXYHOOK_DARKCAST(0,(HookDef)&time)
,SEXYHOOK_DARKCAST(0,&SEXYHOOKFunc72::HookFunction)
,SEXYHOOK_DARKCAST(0,&SEXYHOOKFunc72::CallOrignalFunction)
,inVCallThisPointer
);
}
virtual ~SEXYHOOKFunc72()
{
FunctionUnHookFunction();
}
typedef time_t ( SEXYHOOKFuncBase::* HookDef) (time_t * a) ;
typedef time_t ( SEXYHOOKFuncBase::* HookDefConst) (time_t * a) ;
static uintptr_t* getSexyhookThisPointer()
{
static uintptr_t thisSaver = 0;
return &thisSaver ;
}
time_t CallOrignalFunction (time_t * a)
{
throw 0;
}
int HookFunction (time_t * a)
{
SEXYHOOKFunc72* sexyhookThis = ((SEXYHOOKFunc72*)(*getSexyhookThisPointer()) );
void * sexyhookOrignalThis = (void*) this;
{
//time が呼ばれたらここがコールされる。
//昔の時刻を返すようにする。
return 915116400;//1999-01-01 00:00:00
}
}
} objectFUNCHook74;
objectFUNCHook74.Hook();
//呼び出される関数
6: int add(int a,int b)
7: {
00401220 push ebp //←ここを書き換える
00401221 mov ebp,esp
8: return a + b;
00401223 mov eax,dword ptr [a]
00401226 add eax,dword ptr [b]
9: }
00401229 pop ebp
0040122A ret
//呼び出される関数
6: int add(int a,int b)
7: {
00401220 jmp フックルーチン //書き換えた!!
...
00401226 add eax,dword ptr [b]
9: }
00401229 pop ebp
0040122A ret
フックルーチンアドレス =
命令実行時のアドレス(eip) + 飛ぶアドレス
関数はどうやって呼び出されるか?
//呼び出し側
95: int a = add(1,2);
0040145D push 2
0040145F push 1
00401461 call @ILT+70(add) (0040104b)
00401466 add esp,8
00401469 mov dword ptr [a],eax
↓
@ILT+70(?add@@YAHHH@Z):
0040104B jmp add (00401220)
↓
//呼び出される関数
6: int add(int a,int b)
7: {
00401220 push ebp
00401221 mov ebp,esp
8: return a + b;
00401223 mov eax,dword ptr [a]
00401226 add eax,dword ptr [b]
9: }
00401229 pop ebp
0040122A ret
(void*)&add 等で取得した関数ポインタの先頭が
0xe9 (JMP命令)で始まっていれば、
ILTを経由していると判断してアドレスを再計算しています。
uintptr_t overraideFunctionAddr = 0;
if (*((unsigned char*)inFunctionAddress+0) == 0xe9)
{
//フック関数も ILT経由で飛んでくる場合
//0xe9 call [4バイト相対アドレス]
uintptr_t jmpaddress = *((uintptr_t*)
((unsigned char*)inFunctionAddress+1));
overraideFunctionAddr =
(((uintptr_t)inFunctionAddress) + jmpaddress) + 5;
//+5は e9 00 00 00 00 (ILTのサイズ)
}
else
{
//即、プログラム領域に飛んでくる場合
overraideFunctionAddr = (uintptr_t)inFunctionAddress;
}
95: time_t t = time(NULL);
0040145D push 0
0040145F call time (00402880)
00401464 add esp,4
00401467 mov dword ptr [t],eax
--- time.c ------------------------------
time:
00402880 push ebp
00402881 mov ebp,esp
00402883 sub esp,0D8h
00402889 lea eax,[loct]
0040288C push eax
0040288D call dword ptr [__imp__GetLocalTime@4 (0042a290)]
00402893 lea ecx,[gmt]
class Parent
{
public:
virtual int f()
{
return 1;
}
virtual int g() =0;
};
class Child : public Parent
{
public:
virtual int f()
{
return 2;
}
virtual int g()
{
return 3;
}
};
Child child;
int cf = child.f();
204: cf = child.f();
00401714 lea ecx,[child]
00401717 call @ILT+205(Child::f) (004010d2)
0040171C mov dword ptr [cf],eax
vcall:
00402BA0 mov eax,dword ptr [ecx]
00402BA2 jmp dword ptr [eax]
マシン語
8B 01 FF 20
or (2番目virtualメソッドの場合)
004025D0 mov eax,dword ptr [ecx]
004025D2 jmp dword ptr [eax+4]
マシン語
8B 01 FF 60 04
↑4バイト目が 60 の場合、
次の1バイトが +4 バイト等の数字
overraideFunctionAddr = (uintptr_t)
*(
(void**)*(
(void***)inVCallThisPointer
) + plusAddress
);
//アセンブリでは、
//さくっとかけるポインタ処理を
//C言語で汎用的に書くのは大変です。
lea ecx,[child]
mov eax,dword ptr [ecx]
jmp dword ptr [eax+4]
//アドレスを求めてみる。
printf("&Child::aaa: %p\r\n", &Child::aaa);
printf("&Child::f: %p\r\n", &Child::f);
printf("&Child::g: %p\r\n", &Child::g);
VC++
&Child::aaa: 00401127
&Child::f: 0040116D
&Child::g: 00401186
gcc
&Child::aaa: 0x804871c
&Child::f: 0x1 ← !?
&Child::g: 0x5 ← !?
//定義
int add(int a,int b)
{
return a + b;
}
//関数のフックのテスト
{
//add関数を書き換えて引き算にする。
SEXYHOOK_BEGIN
(int,SEXYHOOK_CDECL,&add,(int a,int b) )
{
return a - b;
}
SEXYHOOK_END();
int cc = add(10,20);
printf("\r\n関数のフックのテスト\r\n");
printf("cc: %d\r\n",cc);
SEXYHOOK_ASSERT(cc == -10);
}
class myClass{
public:
int setA(int a){
this->Member = a;
}
};
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&myClass::setA,(int a))
{
this->Member = a; //エラー
}
SEXYHOOK_END();
class myClass{
public:
int setA(int a){
this->Member = a;
}
};
class SEXYHOOKFunc72 :
public SEXYHOOKFuncBase
{
public:
//フックした中身
int HookFunction (int a)
{
this->Member = a; //エラー
}
};
private_testclass c;
//ここではまだフックしていない
int r = c.public_add(10,20);
SEXYHOOK_ASSERT(r == 30);
//private メソッドのアドレスを算出してフック.
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
SEXYHOOKAddrUtil::strstr_addr("private_testclass::private_add"),
(int a,int b) )
{
return a - b;
}
SEXYHOOK_END();
//フックできたことを確認!
r = c.public_add(10,20);
SEXYHOOK_ASSERT(r == -10);
元のソースコードを一切変更せずに
テスト用の接合部を
作ることができます。
関数、クラスメソッド、
APIの3つをフックできます。
実装はアセンブリとマシン語が
入り乱れる魔術サイドです
SEXYHOOKを利用して
みんなもっとセクシーになりましょう。
SEXYHOOKを利用すると、
既存のソースコードに一切手を加えずに
新たな接合部を自由に作成できる。
第1部
ヘ(^o^)ヘ いいぜ
|∧
/ /
(^o^)/ てめえが
/( ) テストできないほど他者に
(^o^) 三 / / > 依存しているというのなら
\ (\\ 三
(/o^) < \ 三
( /
/ く まずはそのふざけた
関数をフックする
とうまー ねーとうまー
私はおなかがすいたんだよ
えんいー\e
リファクタリング
//2000年以上か?
bool isOver2000year(time_t t)
{
//現在なら間違いなく true. これを falseにするには?
return t >= 946652400; //2000-01-01 00:00:00
}
最近、JSOXやら監査やらうぜぇぇぇ
リファクタリング工数
sexyhook概要
イントロダクション
接合部ありますか?
たいていのソースコードは
例のようなAPIのハードコーディング、
深い依存性の上に創れています。
SEXYHOOKは
クラスを作成するとき
インターフェース継承で作成したり、
APIをすべてクラスで抽象化して
作ることの方がマレ
第一部
完
SEXYHOOKの使い方を書こうとしたが、
それを書くにはこの余白は小さすぎる
レガシーコード改善ガイド(Working Effectively With Legacy Code)
Q:
リリースビルド(最適化)で
動作しません
仕様です。
SEXYHOOKはデバッグビルドのみで
利用できます。
関数の依存性を下げ、
ソースコード上に「接合部」を
作り出すことでテストを行いやすく
FAQ
Q
どこが SEXY?
コード
実装部分を知ると
よりセクシーになれます。
まとめ
プロジェクト
第一部
終章
http://code.google.com/p/sexyhook/
コンパイラの仕様との戦い
イントロダクション
ご自由にお使いください。
VC2003以降は /ZI→ /Zi
http://support.microsoft.com/kb/199057/ja
テストルーチンの時だけ、
一時的に time() を
昔に戻せないだろうか。
sexyhookを使えば、
一時的にAPIや関数、クラスメソッドを
自由に書き換えることが出来ます。
動作環境
・Windows
VC++6 / VC++2003
VC++2005 / VC++2008
VC++2010
・Linux
GCC
APIや他のクラスに依存している
関数のテストは非常に難しい
32bit/バッグビルド
{
//現在は2010年なので結果はTRUE
bool r = isOver2000year();
printf("%d" , (int)r); //1
}
{
//time関数を乗っ取る all your function are belong to us
SEXYHOOK_BEGIN(time_t,SEXYHOOK_CDECL,&time,(time_t * a))
{
//昔の時刻を返すようにする
return 915116400;//1999-01-01 00:00:00
}
SEXYHOOK_END();
//現在は2010年なので、結果は TRUE になりそうだが、
//上で関数をフックしているので、結果は FALSE
bool r = isOver2000year();
printf("%d" , (int)r); //0
}
{
//ブロックを抜けたのでフックが解除される
//現在は2010年なので true になる.
bool r = isOver2000year();
printf("%d" , (int)r); //1
}
time()関数を
一時的に書き換える
こんな関数のテストするとして、
time()関数をライブラリの中に書いてあるので、
失敗ルーチンのテストを書くのが非常に難しい。。。
第2部
SEXYHOOK使い方
http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Users_Manual
関数、
クラスメソッド、
API
さまざまなフック
第2部
SEXYHOOK使い方
関数をフックしたい場合
クラスメソッドを
フックしたい場合
//オペレータオーバーロードのテスト
class OpClass
{
int A;
public:
OpClass(int inA) : A(inA)
{
}
int getA() const
{
return this->A;
}
OpClass operator+( const OpClass& x )
{
OpClass z( this->A + x.A );
return z;
}
};
//演算子オーバーロードのテスト
{
OpClass a(10);
OpClass b(20);
//フック関数を定義する前だからフックされない
OpClass c = a + b;
//ここからフック
SEXYHOOK_BEGIN(OpClass,SEXYHOOK_CLASS,
&OpClass::operator +, (const OpClass& x) )
{
OpClass z( 1000 );
return z;
}
SEXYHOOK_END();
//これはフックされる.
OpClass d = a + b;
SEXYHOOK_ASSERT(c.getA() == 30);
SEXYHOOK_ASSERT(d.getA() == 1000); //固定値
}
class testclass2
{
public:
bool check()
{
if (this->checkCore(1))
{
return true;
}
else
{
//ここのテストがしたい
return false;
}
}
bool checkCore(int a ) // ←これをフックする
{
return a ? true : false;
}
};
SEXYHOOK_BEGIN(bool,SEXYHOOK_CLASS,&testclass2::checkCore,(int a) )
{
return false;
}
SEXYHOOK_END();
testclass2 myclass2;
bool r = myclass2.check(); //FALSE になる。
第2部
more sample?
完
仮想関数は少し特殊
SEXYHOOKの main.cpp に
テスト兼サンプルソースが
載っています
SEXYHOOKの魔術はこれからだ。
俺たちの冒険はまだまだ続く。
WindowsAPIを
フックしたい場合
ま
と
め
//関数
SEXYHOOK_BEGIN
(戻り値,呼出規約,&関数名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
//クラスメソッド
//呼び出し規約はたいてい SEXYHOOK_CLASS
SEXYHOOK_BEGIN
(戻り値,呼出規約,&メソッド名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
//クラスメソッド(仮想関数)
SEXYHOOK_BEGIN
(戻り値,呼出規約,&メソッド名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END(クラスインスタンス);
//WindowsAPI
SEXYHOOK_BEGIN
(戻り値,SEXYHOOK_STDCALL,&メソッド名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END();
class Parent
{
public:
virtual int f()
{
return 1;
}
virtual int g() =0;
};
class Child : public Parent
{
public:
int aaa()
{
return 0;
}
virtual int f()
{
return 2;
}
virtual int g()
{
return 3;
}
};
{
Child child;
//ここからフック
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,&Child::g ,() )
{
return 103; //Child::g
}
SEXYHOOK_END(&child); //this
int cg = child.g();
SEXYHOOK_ASSERT(cg == 103);
}
フック中に、元の関数を呼び出したい。 その2
呼び出し規約
フック中に、元の関数を呼び出したい。 その1
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
//一時的にフックを解除する
SEXYHOOK_UNHOOK();
return CallOrignalFunction(a);
}
フック中に this が使いたい
CallOrignalFunction();
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
return
SEXYHOOK_THIS(classMethodCallTest*)->Sub(5);
}
元の関数を呼び出す
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
//一時的にフックを解除する
SEXYHOOK_UNHOOK();
return
SEXYHOOK_THIS
(classMethodCallTest*)->Add(a);
}
自動的に UNHOOK
されればいいんだけど、、
今は無理。
SEXYHOOK_THIS(クラス名*)
SEXYHOOK_UNHOOK();
例
ブロックの終了まで、フックを解除する
SEXYHOOK_THIS(myClass*)->Member = 10;
public メソッド、
public メンバ変数にしかアクセス出来ません。
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
{
//フックしている
}
{
//一時的に元に戻す.
SEXYHOOK_UNHOOK();
//元に戻っている
}
{
//再度フックされる.
}
}
gcc の __fastcall について。
ディフォルトでは、
__attribute__ ((regparm(3))) として定義しています。
自分でレジスタ数を定義できるバージョンも用意しました。
#define SEXYHOOK_CLASS_FASTCALL \
__attribute__ ((regparm(3)))
#define SEXYHOOK_CLASS_FASTCALLN(N) \
__attribute__ ((regparm(N)))
sexyhook2の使い方と実装について
by rti
人口4人の自称国家。
第2次大戦後の要塞を勝手に占拠して独立宣言。
爵位販売中
プログラム設計、インフラ設計
シーランド公国とは?
love:c++,アセンブラ,php,javascript
イギリス
シーランド公国伯爵
ハンドル:rti
自己紹介
画像:wikipediaから
と
っ
て
も
楽
関数呼び出し その2
直接関数が呼ばれる場合
第3部
SEXYHOOKの実装部
http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
魔術サイド
vcall
ecxに格納されている thisから
virtual なアドレス解決をする
http://rtilabs.net/
実体へ
飛ぶ
関数呼び出し その3
vcall 仮想関数
ここからアセンブラな
低レイヤーの魔術サイド
//仮想関数の vcallだった場合...
if (
*((unsigned char*)overraideFunctionAddr+0) == 0x8B
&& *((unsigned char*)overraideFunctionAddr+1) == 0x01
&& *((unsigned char*)overraideFunctionAddr+2) == 0xFF
)
{
int plusAddress = 0;
if (*((unsigned char*)overraideFunctionAddr+3) == 0x20)
{
//[[this] + 0] にジャンプ
plusAddress = 0;
}
else if (*((unsigned char*)overraideFunctionAddr+3) == 0x60)
{
//[[this] + ?] にジャンプ
plusAddress = (int) *((unsigned char*)overraideFunctionAddr+4); //4バイト目の1バイト分が加算する値
}
else
{
//[[this] + ?] にジャンプを計算出来ませんでした...
SEXYHOOK_BREAKPOINT;
}
//C言語のおせっかいで、ポインタは型分プラスしてしまうので、ポインタのサイズで割っとく.
plusAddress = plusAddress / sizeof(void*);
//このような関数に一時的に飛ばされている場合...
// vcall:
// 00402BA0 mov eax,dword ptr [ecx]
// 00402BA2 jmp dword ptr [eax]
//8B 01 FF 20
//
// or
//
//004025D0 mov eax,dword ptr [ecx]
//004025D2 jmp dword ptr [eax+4]
//8B 01 FF 60 04
if ( inVCallThisPointer == NULL )
{
//vcallのフックには、 thisポインタが必要です。
//SEXYHOOK_CLASS_END_VCALL(thisClass) を利用してください。
SEXYHOOK_BREAKPOINT;
}
/*
//こういう演算をしたい inVCallThisPointer = &this;
_asm
{
mov ecx,inVCallThisPointer;
mov ecx,[ecx];
mov ecx,[ecx];
mov overraideFunctionAddr,ecx;
}
or
_asm
{
mov ecx,inVCallThisPointer;
mov ecx,[ecx];
mov ecx,[ecx+4]; //+? は定義された関数分 virtualの数だけ増えるよ
mov overraideFunctionAddr,ecx;
}
*/
//多分こんな感じ,,,泣けてくるキャストだ.
overraideFunctionAddr = (uintptr_t) *((void**)*((void***)inVCallThisPointer) + plusAddress);
//そこにあるのは 関数の本体 jmp への命令のはず.
if (*((unsigned char*)overraideFunctionAddr+0) == 0xe9)
{
//ついでなので関数の中を書き換えるため、関数の実体へのアドレスを求める.
uintptr_t jmpaddress = *((uintptr_t*)((unsigned char*)overraideFunctionAddr+1));
overraideFunctionAddr = (((uintptr_t)overraideFunctionAddr) + jmpaddress) + 5; //+5は e9 00 00 00 00 (ILTのサイズ)
}
else if (*((unsigned char*)overraideFunctionAddr+0) == 0x55)
{
//push ebxなどの関数の実体はここだ!
overraideFunctionAddr = overraideFunctionAddr;
}
else
{
//vcallの解析に失敗しました...
SEXYHOOK_BREAKPOINT;
}
}
アドレスは動的に決まる
追跡するコード
void* p = SEXYHOOK_DARKCAST( &Child::f );
魔
術
サ
イ
ド
!=
関数の本体の
アドレスを追跡する
call @ILT+205(Child::f) (004010d2)
求めたアドレスは実体ではない。
void* p のアドレスは vcall
第3部
SEXYHOOKの実装部
@super_rti
ポインタ参照はメンドイ
一度
ILTを
経由で
呼
び
出
される
(void*)&addは
ここのアドレス
関数呼び出し その1
ILTを経由する場合
all your function are belong to us.
関数の実体を求めて
gcc と仮想関数フック
gccは、
vtableのindexが帰ってくる
解決策
thisの保存と挿げ替え.
第3部
//呼び出し側
95: int a = add(1,2);
0040145D push 2
0040145F push 1
00401461 call @ILT+70(add) (0040104b) ←
00401466 add esp,8
00401469 mov dword ptr [a],eax
//呼び出される関数
6: int add(int a,int b)
7: {
00401220 push ebp
00401221 mov ebp,esp
8: return a + b;
00401223 mov eax,dword ptr [a]
00401226 add eax,dword ptr [b]
9: }
00401229 pop ebp
0040122A ret
完
ご愛読
ありがとうございました。
call されたアドレスと
add関数が始まっているアドレスが違う
class myClass{
public:
int setA(int a){
asm{
jmp SEXYHOOKFunc72::HookFunction;
};
}
};
class SEXYHOOKFunc72 :
public SEXYHOOKFuncBase
{
public:
//フックした中身
int HookFunction (int a)
{
//SEXYHOOKFunc72 自体の
//自分の thisはどこに?
this->Member = a;
}
};
別の問題:
逆にSEXYHOOK側の
thisがなくなっちゃう。
マクロ展開
つけてもキャストミスると
不思議なアドレス
HookDef:int ( SEXYHOOKFuncBase::* )() )
本来 :int ( Child::* )() )
(void*)&Child.f //OK
(int (Child::*)())&Child.f //OK
(HookDef)&Child.f //NG
class myClass{
public:
int setA(int a){
asm{
//呼び出し規約が全く同じなので、
//thisもスタックも引き継ぐ形になる。
jmp SEXYHOOKFunc72::HookFunction;
};
}
};
class SEXYHOOKFunc72 :
public SEXYHOOKFuncBase
{
public:
//フックした中身
int HookFunction (int a)
{
//thisはコンパイル時には、SEXYHOOKを
//実行時には、本来のクラスを指している。
this->Member = a; //実行時は有効。
}
};
LAT経由での手法もあとで提供したい
しかし、実行時には
このthisは有効である。
コンパイル時のみ無効になる。
thisポインタの
引継問題について
お客様の中で
に詳しい方は
いらっしゃいませんか?
NTカーネル
we love 0xE9
SEXYHOOKの中では thisを使えない。
関数フック
SEXYHOOK_BEGINは
クラス内クラスを作るため。
jmp フックルーチン
↓
0xE9 [フックルーチンへの相対アドレス]
5バイトの命令
DLLのメモリ共有と
NTカーネル
//書き換えるマシン語
FUNCTIONHOOK_ASM interraoutASMCode ;
//フックされる関数の先頭を書き換えて、フックルーチンへ制御を移すようにする。
//参考 http://www.artonx.org/diary/200809.html
// http://hrb.osask.jp/wiki/?faq/asm
*((unsigned char*)interraoutASMCode+0) = 0xe9; //近隣ジャンプ JMP
*((uintptr_t*)((unsigned char*)interraoutASMCode+1)) =
hookFunctionAddr - (uintptr_t)overraideFunctionAddr ;
this->OrignalFunctionAddr = (void*)overraideFunctionAddr;
ReplaceFunction(this->OrignalFunctionAddr , interraoutASMCode , &this->OrignalAsm);
番外編
未来のせくしー
DLLってNTカーネル系でも、
メモリ共有しているのでしょうか。
XPで試してみると、
DLLのコード領域を破壊しても、
他のプログラムには影響がない。。。
SEXYHOOKFuncBase::FunctionHookFunction? 関数の下の方
let's ハンドアセンブル
APIフック
C++でリフレクションが
実装できるかも。。。。
とりあえず動く。
dbghelp.lib
関数アドレスをシンボルから検索
privateメソッドだって、
symbolはあるはずだから、
symbol経由で呼べばいいんだよ!
privateメソッドのフック
dbghelp.lib
疑問点:
DLLのプログラム部分を書き換えていいの?
理由:ソースと書き方の統一
*.pdb(mapファイルみたいなもの)から
シンボルを検索したり、
シンボルからソースコードを求めたり、
ソースコードからシンボルを求めたり etc...
ver2からデグレードしました。
LAT から ふつーのフックに
変更しました。
sexyhook_addrutil.h
なぜ privateメソッドが
フックできないのか?
こんなのセクシーぢゃない。
privateだから、アクセス出来ない。
SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&myclass::private_add,(int a,int b)) //エラー
{
return a - b;
}
SEXYHOOK_END();
なんとかならないの?