Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

とある関数の接合部 SEXYHOOK2

Copy of とある関数の接合部 SEXYHOOK
by

rti 7743

on 4 March 2011

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of とある関数の接合部 SEXYHOOK2

第1部 イントロダクション 第2部
SEXYHOOK使い方 SEXYHOOK_BEGIN(戻り値,呼出規約,&関数名,(引数....) )
{
フックする処理
return 返却したい値;
}
SEXYHOOK_END(); とある関数の接合部(スィーム) sexyhook2の使い方と実装について by rti http://rtilabs.net/ 自己紹介 ハンドル:rti love:c++,アセンブラ,php,javascript シーランド公国伯爵 プログラム設計、インフラ設計 さまざまなフック 関数、
クラスメソッド、
API 第2部
SEXYHOOK使い方 http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Users_Manual 関数をフックしたい場合 //ターゲット
//上の例でもあった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(); 仮想関数は少し特殊 Child child;

SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS, &Child::g , () )
{
return 103; //Child::g
}
SEXYHOOK_END(&child); //thisを渡す WindowsAPIを
フックしたい場合 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); more sample? SEXYHOOKの main.cpp に
テスト兼サンプルソースが
載っています ま

め 第2部 完 SEXYHOOKの魔術はこれからだ。
俺たちの冒険はまだまだ続く。 @super_rti 終章 元のソースコードを一切変更せずに
テスト用の接合部を
作ることができます。 SEXYHOOKは 関数、クラスメソッド、
APIの3つをフックできます。 実装はアセンブリとマシン語が
入り乱れる魔術サイドです SEXYHOOKを利用して
みんなもっとセクシーになりましょう。 えんいー\e まとめ イントロダクション テストを書いていると、
一時的に関数の挙動を書き換えたいときがあります。 //2000年以上か?
bool isOver2000year()
{
//現在動かしたら、絶対 TRUE にしかならない
return time(NULL) >= 946652400; //2000-01-01 00:00:00
} APIや他のクラスに依存している
関数のテストは非常に難しい こんな関数のテストするとして、
time()関数をライブラリの中に書いてあるので、
失敗ルーチンのテストを書くのが非常に難しい。。。 PCの時計を変更 うーん、めんどくさい。 テストルーチンの時だけ、
一時的に time() を
昔に戻せないだろうか。 そんなことで困ってませんか? {
//現在は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()関数を
一時的に書き換える 一時的にAPIや関数、クラスメソッドを
自由に書き換えることが出来ます。 sexyhookを使えば、 レガシーコード改善ガイド(Working Effectively With Legacy Code) 関数の依存性を下げ、
ソースコード上に「接合部」を
作り出すことでテストを行いやすく たいていのソースコードは
例のようなAPIのハードコーディング、
深い依存性の上に創れています。 接合部ありますか? クラスを作成するとき
インターフェース継承で作成したり、
APIをすべてクラスで抽象化して
作ることの方がマレ リファクタリング工数 既存のソースコードに一切手を加えずに
新たな接合部を自由に作成できる。 SEXYHOOKを利用すると、 最近、JSOXやら監査やらうぜぇぇぇ //2000年以上か?
bool isOver2000year(time_t t)
{
//現在なら間違いなく true. これを falseにするには?
return t >= 946652400; //2000-01-01 00:00:00
} リファクタリング //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概要 コンパイラの仕様との戦い http://code.google.com/p/sexyhook/ プロジェクト ご自由にお使いください。 動作環境 ・Windows
VC++6 / VC++2003
VC++2005 / VC++2008
VC++2010

・Linux
GCC 32bit/バッグビルド VC2003以降は /ZI→ /Zi http://support.microsoft.com/kb/199057/ja                        ヘ(^o^)ヘ いいぜ
                         |∧  
                     /  /
                 (^o^)/ てめえが
                /(  )    テストできないほど他者に
       (^o^) 三  / / >      依存しているというのなら
 \     (\\ 三
 (/o^)  < \ 三 
 ( /
 / く  まずはそのふざけた
       関数をフックする とうまー ねーとうまー
私はおなかがすいたんだよ FAQ Q:
リリースビルド(最適化)で
動作しません 仕様です。
SEXYHOOKはデバッグビルドのみで
利用できます。 Q
どこが SEXY? コード
実装部分を知ると
よりセクシーになれます。 第一部 完 SEXYHOOKの使い方を書こうとしたが、
それを書くにはこの余白は小さすぎる 第一部 イギリス シーランド公国とは? 人口4人の自称国家。
第2次大戦後の要塞を勝手に占拠して独立宣言。

爵位販売中 画像:wikipediaから 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 になる。 //オペレータオーバーロードのテスト
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); //固定値
} //テンプレート
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);
} 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);
} //関数
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(); 呼び出し規約 //非クラス系
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
} gcc の __fastcall について。 ディフォルトでは、
__attribute__ ((regparm(3))) として定義しています。
自分でレジスタ数を定義できるバージョンも用意しました。


#define SEXYHOOK_CLASS_FASTCALL \
__attribute__ ((regparm(3)))
#define SEXYHOOK_CLASS_FASTCALLN(N) \
__attribute__ ((regparm(N))) フック中に this が使いたい SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
return
SEXYHOOK_THIS(classMethodCallTest*)->Sub(5);
} SEXYHOOK_THIS(クラス名*) SEXYHOOK_THIS(myClass*)->Member = 10; 例 public メソッド、
public メンバ変数にしかアクセス出来ません。 フック中に、元の関数を呼び出したい。 その1 SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
//一時的にフックを解除する
SEXYHOOK_UNHOOK();

return
SEXYHOOK_THIS
(classMethodCallTest*)->Add(a);
} SEXYHOOK_UNHOOK(); ブロックの終了まで、フックを解除する SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
{
//フックしている
}
{
//一時的に元に戻す.
SEXYHOOK_UNHOOK();

//元に戻っている
}
{
//再度フックされる.
}
} フック中に、元の関数を呼び出したい。 その2 SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&classMethodCallTest::Add,(int a))
{
//一時的にフックを解除する
SEXYHOOK_UNHOOK();

return CallOrignalFunction(a);
} CallOrignalFunction(); 元の関数を呼び出す 自動的に UNHOOK
されればいいんだけど、、
今は無理。 all your function are belong to us. 第3部
SEXYHOOKの実装部 第3部
SEXYHOOKの実装部 http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual 魔術サイド //一時的にtime 関数をフックする。
SEXYHOOK_BEGIN
(time_t,SEXYHOOK_CDECL,&time,(time_t * a) )
{
//time が呼ばれたらここがコールされる。
//昔の時刻を返すようにする。
return 915116400;//1999-01-01 00:00:00
}
SEXYHOOK_END(); マクロ展開 関数フック //呼び出される関数
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 let's ハンドアセンブル SEXYHOOKFuncBase::FunctionHookFunction? 関数の下の方 //書き換えるマシン語
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); we love 0xE9 //呼び出される関数
6: int add(int a,int b)
7: {
00401220 jmp フックルーチン //書き換えた!!
...
00401226 add eax,dword ptr [b]
9: }
00401229 pop ebp
0040122A ret jmp フックルーチン

0xE9 [フックルーチンへの相対アドレス] 5バイトの命令 フックルーチンアドレス =
命令実行時のアドレス(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


//呼び出される関数
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関数が始まっているアドレスが違う 関数呼び出し その1
ILTを経由する場合 //呼び出し側
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 一度
ILTを
経由で



される (void*)&addは
ここのアドレス 関数の本体の
アドレスを追跡する 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;
} (void*)&add 等で取得した関数ポインタの先頭が
0xe9 (JMP命令)で始まっていれば、
ILTを経由していると判断してアドレスを再計算しています。 ここからアセンブラな
低レイヤーの魔術サイド 関数呼び出し その2
直接関数が呼ばれる場合 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] と



楽 関数呼び出し その3
vcall 仮想関数 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 void* p = SEXYHOOK_DARKCAST( &Child::f ); != call @ILT+205(Child::f) (004010d2) アドレスは動的に決まる 求めたアドレスは実体ではない。
void* p のアドレスは vcall vcall 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 バイト等の数字 ecxに格納されている thisから
virtual なアドレス解決をする 実体へ
飛ぶ 追跡するコード 魔



ド ポインタ参照はメンドイ gcc と仮想関数フック gccは、
vtableのindexが帰ってくる //アドレスを求めてみる。
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 ← !? つけてもキャストミスると
不思議なアドレス HookDef:int ( SEXYHOOKFuncBase::* )() )
本来 :int ( Child::* )() ) (void*)&Child.f //OK
(int (Child::*)())&Child.f //OK

(HookDef)&Child.f //NG APIフック ver2からデグレードしました。
LAT から ふつーのフックに
変更しました。 第3部 完 ご愛読
ありがとうございました。 DLLのメモリ共有と
NTカーネル overraideFunctionAddr = (uintptr_t)
*(
(void**)*(
(void***)inVCallThisPointer
) + plusAddress
);



//アセンブリでは、
//さくっとかけるポインタ処理を
//C言語で汎用的に書くのは大変です。
lea ecx,[child]
mov eax,dword ptr [ecx]
jmp dword ptr [eax+4] -Wno-pmf-conversions
オプションで直るよ! 結局vtableのindexを
自前で求めた #ifdef __GNUC__
//gccでは仮想関数のポインタを取得しようとすると、 vtable からの index を返してしまう。
if ( (uintptr_t)inFunctionAddress < 100 )
{
//クラスのインスタンス(thisポインタ)が渡されていれば、indexから実体の場所を計算可能。
if (inVCallThisPointer == NULL)
{
//thisがないなら計算不可能なので、とりあえずとめる.
SEXYHOOK_BREAKPOINT;
}
//thisがあれば計算してvtableのアドレスを求める.
//参考: http://d.hatena.ne.jp/Seasons/20090208/1234115944
uintptr_t* vtable = (uintptr_t*) (*((uintptr_t*)inVCallThisPointer));

//index値の補正
//とりあえず、 (index - 1) / sizeof(void*) でアドレスが求まるみたい.
uintptr_t index = ((uintptr_t)inFunctionAddress - 1) / sizeof(void*);

//vtable から index を計算する.
inFunctionAddress = (void*) (vtable[index] );
}
#endif int* vtable =
(int*) *((int*)*this) 仮想関数  キャスト値 値計算結果
&Child::f0x01(0x01 - 1) / 4 = 0
&Child::g0x05(0x05 - 1) / 4 = 1 &Child::aaa: 0x80484e0
&Child::f: 0x80484ea
&Child::g: 0x80484f4 printf("&Child::aaa: %p\r\n", (void*)&Child::aaa);
printf("&Child::f: %p\r\n", (void*)&Child::f);
printf("&Child::g: %p\r\n", (void*)&Child::g); 本来の型または、void* 以外に
キャストするとダメ //最初の呼び出し
394: int cc = add(10,20);
00402137 push 14h
00402139 push 0Ah
0040213B call @ILT+165(add) (004010aa)----ILT経由でadd関数呼び出し
|
00402140 add esp,8 <<<<<<<<< <<< | <<<<<<<<<<<<<<---------------------戻り値
00402143 mov dword ptr [cc],eax | |
| |
| |
//ILT | |
@ILT+165(?add@@YAHHH@Z): | |
004010AA jmp add (00401470) <------------ |
|add関数の実体呼び出し |
| |
| |
| |
9: //関数のテスト | |
10: int add(int a,int b) | |
11: { / |
//強引に書き換えてフックルーチンへ飛ばす |
00401470 jmp `main'::`83'::SEXYHOOKFunc388::HookFunction (00402a5e) |
... | |
00401477 inc ebp //ここは実行されない | |
00401478 or al,5Dh //壊れたアセンブラ | |
0040147A ret //ここは実行されない | |
12 } | |
| |
| |
| |
//フックルーチンのインナークラス | |
class SEXYHOOKFunc388 : public SEXYHOOKFuncBase | |
{ | |
//中略 | |
//足し算を引き算に書き換え | |
389: { | |
00402A5E push ebp //←ここに飛ぶ <-- |
00402A5F mov ebp,esp |
390: return a - b; |
00402A61 mov eax,dword ptr [a] |
00402A64 sub eax,dword ptr [b] |
391: } |
00402A67 pop ebp |
00402A68 ret //←ここの ret 命令で、 |
//最初に add命令を呼び出したところ(00402140)に戻る. >--------------|
//フック関数と 呼び出し規約と引数をそろえているため、
//スタックは破壊されない。
//中略
} 関数の呼び出し
フロー図 協力してくれた
方々に感謝!! //仮想関数の 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;
}
} 理由:ソースと書き方の統一 DLLってNTカーネル系でも、
メモリ共有しているのでしょうか。

XPで試してみると、
DLLのコード領域を破壊しても、
他のプログラムには影響がない。。。 疑問点:
DLLのプログラム部分を書き換えていいの? お客様の中で


に詳しい方は
いらっしゃいませんか? NTカーネル LAT経由での手法もあとで提供したい 番外編
未来のせくしー privateメソッドのフック なぜ privateメソッドが
フックできないのか? SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&myclass::private_add,(int a,int b)) //エラー
{
return a - b;
}
SEXYHOOK_END(); privateだから、アクセス出来ない。 こんなのセクシーぢゃない。 なんとかならないの? dbghelp.lib *.pdb(mapファイルみたいなもの)から
シンボルを検索したり、
シンボルからソースコードを求めたり、
ソースコードからシンボルを求めたり etc... dbghelp.lib sexyhook_addrutil.h privateメソッドだって、
symbolはあるはずだから、
symbol経由で呼べばいいんだよ! 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); とりあえず動く。 関数アドレスをシンボルから検索 C++でリフレクションが
実装できるかも。。。。 //プリプロセッサによりこのように展開されます。
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(); //定義
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);
} thisポインタの
引継問題について SEXYHOOKの中では thisを使えない。 class myClass{
public:
int setA(int a){
this->Member = a;
}
};

SEXYHOOK_BEGIN(int,SEXYHOOK_CLASS,
&myClass::setA,(int a))
{
this->Member = a; //エラー
}
SEXYHOOK_END(); SEXYHOOK_BEGINは
クラス内クラスを作るため。 class myClass{
public:
int setA(int a){
this->Member = a;
}
};

class SEXYHOOKFunc72 :
public SEXYHOOKFuncBase
{
public:
//フックした中身
int HookFunction (int a)
{
this->Member = a; //エラー
}
}; しかし、実行時には
このthisは有効である。
コンパイル時のみ無効になる。 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; //実行時は有効。
}
}; 別の問題:
逆にSEXYHOOK側の
thisがなくなっちゃう。 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;
}
}; 解決策 thisの保存と挿げ替え. class SEXYHOOKFunc72 : public SEXYHOOKFuncBase
{
public:
//thisを保存するだけの領域として利用する.
static uintptr_t* getSexyhookThisPointer()
{
static uintptr_t thisSaver = 0;
return &thisSaver ;
}

//フックを開始する.
void Hook(void* inVCallThisPointer = 0,void* inFuncAddress2 = 0)
{
//フックする直前に自分の this を保存する.
*(getSexyhookThisPointer()) = (uintptr_t)this;
//フック開始
FunctionHookFunction(
inFuncAddress2 ? inFuncAddress2 : SEXYHOOK_DARKCAST(0,(HookDef)&time)
,SEXYHOOK_DARKCAST(0,&SEXYHOOKFunc72::HookFunction)
,SEXYHOOK_DARKCAST(0,&SEXYHOOKFunc72::CallOrignalFunction)
,inVCallThisPointer
);
}

//フックした中身
int HookFunction (int a)
{
//SEXYHOOKのthisを復元する.
SEXYHOOKFunc72* sexyhookThis = ((SEXYHOOKFunc72*)(*getSexyhookThisPointer()) );
//実行時、コンパイル時ともにエラーにならないように拾っとく.
void * sexyhookOrignalThis = (void*) this;
{
//キャストしてアクセスする.
((myClass*)sexyhookOrignalThis)->Member = a;
}
}
}; トリッキーな
コード大杉w 別クラス
Full transcript