virtual function / vtable
참고 : https://cosyp.tistory.com/228
v-table: 함수 포인터가 들어있는 테이블
함수 포인터: 함수의 주소를 저장하는 포인터
-> 콜백 함수를 위해 많이 쓰인다.
v-table은 virtual function이라는 기능을 위해 쓰이고,
v-table안에는 함수 포인터들이 배열의 형태로 저장되어있다.
Override
결과를 보면 상속받은 부모 객체의 함수가 실행되는 것이 아닌 자식 클래스의 객체가 실행되고 있다. 이렇게 상속받은 함수의 자식 클래스에서 재정의하는 것을 Override라고 한다.
virtual function
또한, Parent 타입의 p 포인터 변수에 Child 객체의 주소를 넣고 실행을 시켜도 Parent 타입의 함수가 호출이 된다. 이는 단순히 함수를 오버라이딩하더라도 컴파일러는 p가 Parent 타입이므로 Parent의 show 함수가 바인딩 되어있는 상태여서 Parent의 show를 호출하게 된다. 이는 객체지향의 특성 중 '다향성'을 무시하는 결과가 된다. 다형성이란 하나의 객체가 여러개의 자료형을 가질 수 있는 것을 말한다. 즉, 특정 클래스의 객체이나, 다른 클래스의 자료형으로도 사용할 수 있어야 한다.
하지만 이처럼 virtual이라는 키워드를 붙이면 또 출력이 달라지게 된다.
이를 통해 다형성의 문제를 해결할 수 있다.
virtual function table
가상함수를 사용하면 같은 타입으로 선언된 객체 포인터 변수더라도 가리키는 객체에 따라 오버라이딩 된 함수를 호출하는 것을 위의 소스코드를 통해 확인했다.
컴파일 시에 가상함수가 정의된 클래스가 있다면 가상함수테이블 (vtable)이 만들어지고, rdata 영역에 기록되어 해당 클래스로 만들어진 객체가 함수를 호출할 때 이 vtable을 참조하게 된다.
이처럼 자식클래스의 vtable은 부모 클래스의 vtable을 복사한 후 오버라이딩 된 함수 주소만 재정의한다.
만약 아래처럼 부모에게는 없는 함수를 자식에게 선언한다면 부모에서의 주소는 없고, 자식에게는 vtable 마지막에 추가가 된다.
여기서 vtable은 rdata 영역에 위치하므로 대부분 vtable 내의 함수 포인터의 주소를 덮을 수 없는 경우가 많다.
그러므로 vptr이라는 vtable을 가리키는 변수에 공격자가 원하는 주소를 덮고, 원형을 그대로 살려 공격하는 방법이 있다.
v-table의 유무는 C++로 짜여진 대부분의 프로그램에 존재하고, 임베디드 장비 펌웨어 분석시 매우 유용하다.
object -> vtable -> method()의 형태로 구성된 함수의 호출을 확인한다.
정적분석할 때 call [rax + offset]의 형태로 offset 단위로 함수를 호출하는 코드가 있다면 vtable에 있는 함수포인터 호출일 수 있다.