_IO_jump_t 멤버함수 호출 방식

dandb3·2023년 6월 1일
0

pwnable

목록 보기
9/14

앞서 지나갔던 내용인
"실제로 수많은 매크로를 통해 vtable에서 함수가 호출되는 과정은 어떻게 될까?"
에 대해서 알아보도록 하겠다.

#define _IO_XSGETN(FP, DATA, N) JUMP2 (__xsgetn, FP, DATA, N)
#define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
#define _IO_JUMPS_FILE_plus(THIS) \
  _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable)
#define _IO_CAST_FIELD_ACCESS(THIS, TYPE, MEMBER) \
  (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \
				       + offsetof(TYPE, MEMBER)))
#define _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)

앞서 fread함수 분석 글에서 확인하려다가 실패(?) 한 매크로들이다.
하나하나 천천히 따라가 보자.

  • 그림으로 나타내면 다음과 같다.

    확실히 그림으로 나타내니까 보기가 편한 듯.
    아래에서부터 한 단계씩 위로 올라가 보자.

  • 먼저 _IO_MEMBER_TYPE 매크로인데, 첫 번째 인자로 들어온 구조체의 두 번째 인자로 들어온 멤버변수의 자료형을 의미한다.

  • 그 다음으로 offsetof매크로인데, 이름 그대로 첫 번째 인자로 들어온 구조체와 두 번째 인자로 들어온 멤버변수간의 offset을 의미한다.

    • 정의를 보면, 0의 메모리 주소로 포인터를 해석하여 멤버변수 vtable에 접근해서 그 주소를 반환하는데, 애초에 base address가 0이었기 때문에 순수 offset값과 동일하게 된다.
  • 정리하면 다음과 같다.

  • 이번에는 IO_validate_vtable에 대해서 알아보자. glibc 2.27 버전으로 가져왔다.
    static inline const struct _IO_jump_t *
    IO_validate_vtable (const struct _IO_jump_t *vtable)
    {
      /* Fast path: The vtable pointer is within the __libc_IO_vtables
         section.  */
      uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
      uintptr_t ptr = (uintptr_t) vtable;
      uintptr_t offset = ptr - (uintptr_t) __start___libc_IO_vtables;
      if (__glibc_unlikely (offset >= section_length))
        /* The vtable pointer is not in the expected section.  Use the
           slow path, which will terminate the process if necessary.  */
        _IO_vtable_check ();
      return vtable;
    }
    자세한 분석은 나중에 하기로 하고, 일단 이 함수가 정상적으로 종료된다면 들어온 인자 그대로 리턴해주는 것을 알 수 있다.
  • 그러고 나면 다음과 같이 된다.

    결국, 아래의 함수를 호출하는 것과 동일하다.
    (((_IO_FILE_plus *) FP)->vtable->FUNC) (FP, DATA, N)
    즉, 각 _IO_FILE구조체에 해당하는 vtable에 저장된 FUNC라는 함수를 호출하게 되는 것이다.
    물론, 그 이전에 IO_validate_vtable 함수의 검사를 통과해야만 제대로 함수가 호출된다.
  • 참고 자료
profile
공부 내용 저장소

0개의 댓글