c++ virtual function 被很多人拿來研究, 由於沒有 cfront, 大部分的人都是從反組譯來觀察, 太苦了。
目前我已經把
cfront 建構出來, 可以使用 cfront 來看看 virtual function 轉出的 c code 長什麼樣, 破解 c++ virtual function。
hello_a.C
1 #include <stream.h>
2
3 class A
4 {
5 public:
6 virtual void foo(int a = 0)
7 {
8 printf("A %d\n", a);
9 }
10 virtual void va(int a)
11 {
12 printf("va: A %d\n", a);
13 }
14 };
15
16 class B : public A
17 {
18 public:
19 virtual void foo(int a = 1)
20 {
21 printf("B a: %d\n", a);
22 }
23 };
24
25
26 main()
27 {
28 A *p = new A();
29 p->foo();
30 p->va(25);
31 }
hello.a.c 是 cfont 轉出來的 c code, 31 行的 c++ 程式碼透過 cfront 轉出 773 行 c code, 先來看看 class A, class B 被轉成什麼? hello.a.c L633, L643 就是對應的 class A, class B, 被轉成 strcut:
633 struct A { /* sizeof A == 8 */
636 struct __mptr *__vptr__1A ;
637 };
639
643 struct B { /* sizeof B == 8 */
646 struct __mptr *__vptr__1A ;
647 };裡頭有一個 __vptr__1A:
646 struct __mptr *__vptr__1A ;
struct __mptr 定義在 L17。
17 struct __mptr {short d; short i; __vptp f; };注意其中的 __vptp f 即可, 這個就是用來儲存 virtual function, 其實 virtual function 就是一般的 c function, 所以也只是把 virtual function 的位址存起來。
再來是 L766 的 __ptbl_vec__hello_C_, 這邊建構了__vtbl__1A__hello_C
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768
769 };__vtbl__1A__hello_C 在 L699 定義:
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};__vtbl__1A__hello_C[0]: 0,0,0
__vtbl__1A__hello_C[1]: 0,0, foo__1AFi 就是 class A 的 virtual void foo(int a = 0)
__vtbl__1A__hello_C[2]: 0,0, va__1AFi 就是 class A virtual void va(int a)
A *p = new A(); 會用 new (__nw__FUl 就是 new) 建構 struct A, 把 p->__vptr__1A = __ptbl_vec__hello_C_[0]
對應到 hello.a.c L668
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;p->foo(); 就是執行 p->__vptr__1A[1].f
對應到 hello.a.c L671
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
p->va(25); 就是執行 p->__vptr__1A[2].f
對應到 hello.a.c L672
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;這些很恐怖的轉型/設定程式碼就是在做這些事情 (花點時間應該可以看懂), 也就是為什麼 class B 可以呼叫 class A 的 virtaul function va 的祕密。
hello.a.c
1 #line 1 "hello.C"
2
3 /* <<AT&T C++ Language System <3.0.3> 05/05/94>> */
4 char __cfront_version_303_xxxxxxxx;
5 /* < hello.C > */
6
7 #pragma lib "ape/libap.a"
8
9 #pragma lib "c++/libC.a"
10
11 #line 1 "hello.C"
12 void *__vec_new (void *, int , int , void *);
13
14 #line 1 "hello.C"
15 void __vec_delete (void *, int , int , void *, int , int );
16 typedef int (*__vptp)(void);
17 struct __mptr {short d; short i; __vptp f; };
18
19 #line 1 "hello.C"
20 extern struct __mptr* __ptbl_vec__hello_C_[];
21
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639
640 #line 14 "hello.C"
641
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648
649 #line 14 "hello.C"
650
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656
657 #line 26 "hello.C"
658 int main (void ){ _main();
659 #line 27 "hello.C"
660 {
661 #line 28 "hello.C"
662 struct A *__1p ;
663
664 #line 29 "hello.C"
665 struct A *__0__X52 ;
666
667 #line 28 "hello.C"
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
673 }
674 }
675 #line 31 "hello.C"
676 void __sti__hello_C_main_ (void )
677 #line 664 "incl-master/incl-linux32/iostream.h"
678 { __ct__13Iostream_initFv ( & iostream_init ) ;
679
680 #line 664 "incl-master/incl-linux32/iostream.h"
681 }
682
683 #line 31 "hello.C"
684 void __std__hello_C_main_ (void )
685 #line 664 "incl-master/incl-linux32/iostream.h"
686 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
687
688 #line 664 "incl-master/incl-linux32/iostream.h"
689 }
690 static void foo__1BFi (
691 #line 19 "hello.C"
692 struct B *__0this ,
693 #line 19 "hello.C"
694 int __2a );
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};
703 static void foo__1BFi (struct B *__0this ,
704 #line 19 "hello.C"
705 int __2a )
706 #line 20 "hello.C"
707 {
708 #line 21 "hello.C"
709 printf ( (const char *)"B a: %d\n",
710 #line 21 "hello.C"
711 __2a ) ;
712 }
713
714 #line 10 "hello.C"
715 static void va__1AFi (struct A *__0this ,
716 #line 10 "hello.C"
717 int __2a )
718 #line 11 "hello.C"
719 {
720 #line 12 "hello.C"
721 printf ( (const char *)"va: A %d\n",
722 #line 12 "hello.C"
723 __2a ) ;
724 }
725
726 #line 6 "hello.C"
727 static void foo__1AFi (struct A *__0this ,
728 #line 6 "hello.C"
729 int __2a )
730 #line 7 "hello.C"
731 {
732 #line 8 "hello.C"
733 printf ( (const char *)"A %d\n",
734 #line 8 "hello.C"
735 __2a ) ;
736 }
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768
769 };
770
771 #line 31 "hello.C"
772
773 /* the end */
hello_b.C 和 hello_a.C 不同之處在於 new B(), 對於整個轉出來的程式當中, 只有一點點的不同:
hello_b.C
1 #include <stream.h>
2
3 class A
4 {
5 public:
6 virtual void foo(int a = 0)
7 {
8 printf("A %d\n", a);
9 }
10 virtual void va(int a)
11 {
12 printf("va: A %d\n", a);
13 }
14 };
15
16 class B : public A
17 {
18 public:
19 virtual void foo(int a = 1)
20 {
21 printf("B a: %d\n", a);
22 }
23 };
24
25
26 main()
27 {
28 A *p = new B();
29 p->foo();
30 p->va(25);
31 }
hello.b.c 733
773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776
777 };
多建構了一個 __vtbl__1B__hello_C
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};
__vtbl__1B__hello_C[0]: 0,0,0
__vtbl__1B__hello_C[1]: 0,0, foo__1AFi 就是 class B 的 virtual void foo(int a = 1)
__vtbl__1B__hello_C[2]: 0,0, va__1AFi 就是 class A 的 virtual void va(int a)
A *p = new B(); 會用 new 建構 struct B, 把 p->__vptr__1A = __ptbl_vec__hello_C_[1]
對應到 hello.b.c L671
671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B))) 672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ; p->foo();
對應到 hello.b.c L678
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ; p->va(25);
對應到 hello.b.c L679
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ; 這些很恐怖的轉型/設定程式碼就是在做這些事情, 而呼叫 virtual function 的程式碼是相同的, 只有在設定 __vptr__1A 有所不同而已。
看懂這些資料結構之後, virtaul function 就沒有那麼神秘了。
至於
17 struct __mptr {short d; short i; __vptp f; }; 神秘的 d, i, 應該是用在繼承上的, 可能是其他的繼承方式, 有興趣的朋友可以繼續追蹤下去。
hello.b.c
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639
640 #line 14 "hello.C"
641
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648
649 #line 23 "hello.C"
650
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656
657 #line 26 "hello.C"
658 int main (void ){ _main();
659 #line 27 "hello.C"
660 {
661 #line 28 "hello.C"
662 struct A *__1p ;
663
664 #line 29 "hello.C"
665 struct B *__0__X52 ;
666
667 #line 29 "hello.C"
668 struct A *__0__X51 ;
669
670 #line 28 "hello.C"
671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B)))
672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ;
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
680 }
681 }
682 #line 31 "hello.C"
683 void __sti__hello_C_main_ (void )
684 #line 664 "incl-master/incl-linux32/iostream.h"
685 { __ct__13Iostream_initFv ( & iostream_init ) ;
686
687 #line 664 "incl-master/incl-linux32/iostream.h"
688 }
689
690 #line 31 "hello.C"
691 void __std__hello_C_main_ (void )
692 #line 664 "incl-master/incl-linux32/iostream.h"
693 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
694
695 #line 664 "incl-master/incl-linux32/iostream.h"
696 }
697 static void foo__1BFi (
698 #line 19 "hello.C"
699 struct B *__0this ,
700 #line 19 "hello.C"
701 int __2a );
702 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
703 0,0,(__vptp)foo__1BFi ,
704 0,0,(__vptp)va__1AFi ,
705 0,0,0};
706 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
707 0,0,(__vptp)foo__1AFi ,
708 0,0,(__vptp)va__1AFi ,
709 0,0,0};
710 static void foo__1BFi (struct B *__0this ,
711 #line 19 "hello.C"
712 int __2a )
713 #line 20 "hello.C"
714 {
715 #line 21 "hello.C"
716 printf ( (const char *)"B a: %d\n",
717 #line 21 "hello.C"
718 __2a ) ;
719 }
720
721 #line 10 "hello.C"
722 static void va__1AFi (struct A *__0this ,
723 #line 10 "hello.C"
724 int __2a )
725 #line 11 "hello.C"
726 {
727 #line 12 "hello.C"
728 printf ( (const char *)"va: A %d\n",
729 #line 12 "hello.C"
730 __2a ) ;
731 }
732
733 #line 6 "hello.C"
734 static void foo__1AFi (struct A *__0this ,
735 #line 6 "hello.C"
736 int __2a )
737 #line 7 "hello.C"
738 {
739 #line 8 "hello.C"
740 printf ( (const char *)"A %d\n",
741 #line 8 "hello.C"
742 __2a ) ;
743 }
744
773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776
777 };
778
779 #line 31 "hello.C"
780
781 /* the end */
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。