Hook関数

フック(Hook)関数とやらについて。
バイトで使う機会があったので、メモがてら。

フック(Hook)
プログラム中の特定の箇所に、利用者が独自の処理を追加できるようにする仕組みである

参照:フック (プログラミング) - Wikipedia

サンプルコード(C言語)

以下Intel x86アーキテクチャについてのコード、コンパイラgccとする。

#include 

#define HOOK __asm__ volatile ("movl %%ebp, %0":"=r"(ebp_buf):);	\
  ret_addr = (unsigned int *)ebp_buf[1];				\
  ebp_buf[1] = (unsigned int)hook;		


unsigned int *ebp_buf;
unsigned int *ret_addr;

void hook(){
  
  int z;
  __asm__ volatile ("movl %%eax, %0":"=r"(z):);
  
  z=z*2;
  printf("hook!\n");

  __asm__ volatile ("movl %0 ,%%eax"::"r"(z));
  __asm__ volatile ("movl %0 ,%%ebx"::"m"(ret_addr));
  __asm__ volatile ("movl %%ebp, %%esp"::);  
  __asm__ volatile ("popl %%ebp"::);
  __asm__ volatile ("jmp *%ebx");

}


int func(){
  
  int x,y,z;

  HOOK;

  x=2;
  y=3;
  z=x+y;

  return z;
}

int main(){
 
  int z = func(); 
  
  printf("z = %d\n",z);
  
  return 0;
}


ややこしやーですね。

実行結果

[hat-tun@mimo]% ./a.out                                           [~/algorithm]
hook!
z = 10

簡単に説明

main関数

まずmain関数はfunc関数を呼んで、その返り値を表示してるだけですね。

func関数

次にfunc関数ですが、これも一見ただ単に2+3の足し算をしてその答えを
returnしてるだけです。
が、しかし、func内でHOOKなるマクロが使われております。

HOOKマクロ

ここがミソですね。
インラインアセンブリ言語を使って、こねこねします。
大域変数ebp_bufにebpレジスタ(ベースポインタ)の情報をコピーし、
ret_addrに関数のリターンアドレス*1をコピーしたのち、
リターンアドレスをhook関数の存在するアドレス*2
に上書きしてます。


具体的に言うと、func関数実行中のベースポインタをとっておき、
func関数がreturnした後に戻るリターンアドレス、つまりここでは
main関数内でzに値を代入するところらへんのアドレスを退避させておき、
func内でreturnしたらhook関数へ飛ぶようにしてるわけです。

hook関数

フック関数本体です、ここに追加で行いたい処理なんかを書いちゃうわけです。
ここでは、return値を2倍するという操作をしています。


return値はeaxレジスタに入ってるので、それを変数zに取り出して、
計算してまたeaxに戻す。
そして、さっき退避しておいたベースポインタとリターンアドレスを戻し、
funcの帰るべきだったリターンアドレスへジャンプします。






HOOKの一言で、制御を奪えるので、これを引っ掛ける意味でフックというそうです。
だいぶテクニカルですね。
デバッグなんかに使えるらしいですが、
ただ実際こいつがどんだけすごい能力持ってるのかは、
わかりきってませんね。 精進します。  
でも、何かの参考になれば。



インラインアセンブリについてはここを参考しました。
gccのx86インラインアセンブリに関して

*1:ebp_buf[1]はebp_bufが指しているアドレスの4バイト先のリターンアドレスを指す

*2:関数名だけだと格納されているアドレスを指す