flip
int (*flip(int (*fun)(int,int)))(int,int) { unsigned char *func = (char*)malloc(1000); func[ 0]=0x8B;func[ 1]=0x44;func[ 2]=0x24;func[ 3]=0x04; func[ 4]=0x87;func[ 5]=0x44;func[ 6]=0x24;func[ 7]=0x08; func[ 8]=0x89;func[ 9]=0x44;func[10]=0x24;func[11]=0x04; func[12]=0xB8;*(int**)(func+13)=(int*)fun; func[17]=0xFF;func[18]=0xE0; return (int(*)(int,int))func; }
flipのしていることは、アセンブラに直せば非常に単純でして
mov eax,[esp+4] xchg eax,[esp+8] mov eax,[esp+4] mov eax,[flipの引数に与えられたアドレス] jmp eax
という命令を func の先に書き込んでこれを関数ポインタにキャストして返す
、という感じになります。[esp+4],[esp+8]は引数なので、それを無理やり xchg で入れ替えた後に、 jmp で入れ替わる元の関数に飛んでいるのです。絶対ジャンプにするために一旦レジスタに入れております。
K のしていることは
たとえば、
K(1000)ならば
mov eax,1000 ret
というコードを作って返す、です。
ついでなので S K K を実行したときにどのようなコードが吐かれるかを書いておきます。
S K
55 PUSH EBP 8BEC MOV EBP,ESP 53 PUSH EBX 56 PUSH ESI 57 PUSH EDI 6A 2D PUSH 2D B8 A0174000 MOV EAX,stack.malloc FFD0 CALL EAX 83C4 04 ADD ESP,4 BE F8914C00 MOV ESI,DATA 8BF8 MOV EDI,EAX B9 2D000000 MOV ECX,2D F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[> ;文字列転送命令DATAのところを2D文字移す。 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 8950 0B MOV DWORD PTR DS:[EAX+B],EDX ;MOV EAX,0 の 0 を正しい命令に書き換える。 90 NOP 90 NOP 5F POP EDI 5E POP ESI 5B POP EBX 5D POP EBP C3 RETN CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD CD INT 0CD CD :DATA 55 PUSH EBP 8BEC MOV EBP,ESP 53 PUSH EBX 56 PUSH ESI 57 PUSH EDI 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 52 PUSH EDX B8 00000000 MOV EAX,0 FFD0 CALL EAX 83C4 04 ADD ESP,4 50 PUSH EAX 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 52 PUSH EDX B8 stack.k MOV EAX,stack.k FFD0 CALL EAX 83C4 04 ADD ESP,4 FFD0 CALL EAX 83C4 04 ADD ESP,4 5F POP EDI 5E POP ESI 5B POP EBX 5D POP EBP C3 RETN
S K K
55 PUSH EBP 8BEC MOV EBP,ESP 53 PUSH EBX 56 PUSH ESI 57 PUSH EDI 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 52 PUSH EDX B8 stack.k MOV EAX,stack.k FFD0 CALL EAX 83C4 04 ADD ESP,4 50 PUSH EAX 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 52 PUSH EDX B8 stack.k MOV EAX,stack.k FFD0 CALL EAX 83C4 04 ADD ESP,4 FFD0 CALL EAX 83C4 04 ADD ESP,4 5F POP EDI 5E POP ESI 5B POP EBX 5D POP EBP C3 RETN
で、この関数が恒等写像であるのは当然でしょう。
Sコンビネータで、mallocを直書きした理由にもつながる話を。バッファーオーバーフロー攻撃が成功すると、その後は色々なAPIを漁りに行きます。それは実は結構大変です。侵入したコードは何も知らない状態からどこに何の関数があるのかを探さないといけないのですから。ところが、ここでライブラリのリンク順序が決まっていると手元で調べて、そのアドレスを埋め込んでおくという手法が使えます。そこで linux ではセキュリティ上の理由からランダムな順序でライブラリをリンクしているらしいです。
前半は下記。
http://d.hatena.ne.jp/nuc/20051101/p1 関数型C言語
http://d.hatena.ne.jp/nuc/20051101/p2 K
http://d.hatena.ne.jp/nuc/20051102/p5 Sコンビネータ
http://d.hatena.ne.jp/nuc/20051102/p6 スタック