C++ 리버싱, 덤프에서 vtable 영역을 찾기 위한 고찰
- 리버싱/리버싱
- 2020. 3. 29. 18:36
여기 적혀있는 내용은 심볼이 없는 덤프에서 vtable 영역을 어떻게 하면 찾을수 있을까에 대한 개인적인 의견이기 때문에 정확한 정보가 담긴 포스팅은 아니다. 참고용으로만 확인해주길 바라며 틀린 내용에 대한 지적은 언제든 환영이다.
심볼이 있는 경우
심볼이 있는 경우 vtable 영역을 찾는 건 쉽다. 아래 포스팅에서도 기술했다시피 < x XXX!*vftable* > 와 같은 방식으로 간단히 찾을 수 있기 때문이다.
가상 함수 호출 패턴 검색
하지만 심볼이 없는 경우는 얘기가 달라진다. 심볼이 없으면 `vftable' = <no type information> 과 같이 표시가 되지 않기 때문에 위와 같은 방식으로 간단하게 vtable 영역을 찾을 수 없게된다.
그래서 처음 생각한 방식이 가상 함수 호출 패턴과 비슷한 코드(레지스터에 넣은 후 참조해서 호출하는 패턴)를 찾은 후 위쪽에서 객체 생성을 위한 < mov reg offset > 형태의 코드를 찾아 해당 offset이 vtable 영역인지 검사하는 방식이다. vtable 영역이라면 아무래도 일단 데이터 영역일테고 같은 영역대의 주소값이 배열형태로 나열되어 있을테니 이런 특징을 사용해 vtable인지 여부를 확인하는 것이다.
그런데 가상함수 호출 패턴?이 정형화되기가 힘들었다. [esi]가 vtable 영역이고 가상함수 호출 전에 특정 레지스터에 mov로 값을 이동시킨 후 그 레지스터 +XX 에 접근하는 방식으로 호출이 이루어지는데 사실 함수 호출 후 mov 명령어가 나오는 것은 상당히 일반적이고 딱 특정 레지스터만 사용하는 것도 아니라... 패턴을 정하기는 좀 힘들 것 같았다. 그리고 클래스 객체 생성을 해당 함수 위쪽에서 할 것이라는 보장도 없다.
아래 다른 덤프를 예로 들어보면, (AssultCube 오픈소스 FPS, 심볼이 자꾸 안지워져서 못지웠다..) wepon 클래스의 vftable을 [esi]에 넣고 있는데 객체를 생성하는게 아니라 operator delete로 지우고있다. 당연히 근처에 [esi]를 활용한 가상함수 호출도 없다. 실제 객체를 생성하는 부분이라 하더라도 위와 같은 패턴을 보이지 않는 경우가 더 많았다.
rdata 영역 전체, 정규표현식으로 확인
진짜 단순하게, 데이터 영역(rdata가 아닐 수도 있음)을 전부 확인하면서 함수 포인터 배열이 있는지 확인하는 무식한? 방법도 있다. 다만 이런 경우 windbg로 보기는 좀 힘드므로 vtable 영역을 확인하고자 하는 이미지 부분을 덤프를 떠서 IDA로 확인한다.
그래도 워낙 데이터 영역이 크기 때문에 스크롤 내리면서 확인하는 건 좀 오바고 서브라임에라도 옮겨서 정규표현식으로 < off_.*dd offset sub_ > 와 같은 패턴을 검색한다. 객체가 생성되면 무조건 vtable영역을 먼저 가리키고 그 다음에 멤버변수가 오기 때문에 첫번째로는 무조건 sub_ 이와야 vtable 영역일 가능성이 있다. 다만, 왜그런지는 모르겠지만 데이터 영역 중 일부가 제대로 써지지 않는 경우가 꽤 있었다.
가상함수 시작 지점 패턴 검색
세번째로 생각한 방법은, 가상함수 시작이 < mov ecx ~~~ >로 시작한다는 점을 이용한 것이다. 물론 모든 가상함수가 mov ecx로 시작하는 것은 당연히 아닐 것이다.
바이너리 서치로 대략 함수 시작부분에 mov ecx (8B 0D)가 있는 함수를 찾아준다. 그리고 해당 함수 시작주소가 데이터 영역에 있는지 확인해준다.
그런데 꼭 8b 다음에 0D가 아닐수도 있기 때문에 이런 경우는 #을 사용해 문자열로 써서 검색하는 게 더 나은 것 같다.
이렇게 찾는 경우 아래와 같이 함수 시작지점에 < mov ecx, dword ptr > 코드가 있고 함수 시작 주소가 데이터영역에 존재하고 해당 영역이 함수포인터 배열이기 때문에 딱 맞는 조건이지만, 작은 프로그램이면 모를까.. 조금만 프로그램이 커지면 사실 하나하나 찾기는 정말 힘들다. ac_client만 하더라도 400000 부터 시작인데 아래 가상함수 주소가 4e로 시작한다. e0000 정도되는 크기에 mov ecx를 다 찾는건 정말 비효율적이고 말도안된다.
심볼이 없는 경우, vtable 영역을 찾을 수 있는 3가지 방법을 생각해보았는데 세번째 방법을 수동으로 하지 않고 코드를 짜서 자동화할 수 있게 하면 제일 괜찮은 방법인 것 같다. 할일만 좀 정리되면 바로 코드제작을 해볼 생각이다.
'리버싱 > 리버싱' 카테고리의 다른 글
PE Viewer 개발 (5) - IAT(Image Address Table), INT(Image Name Table) (0) | 2020.04.02 |
---|---|
PE Viewer 개발 (4) - IMPORT Directory Table (0) | 2020.04.01 |
C++ 리버싱을 위한 vtable 분석 (2) | 2020.03.27 |
[크래쉬 분석] Access violation - code c0000005 (오디오편집 프로그램) (0) | 2020.03.25 |
DLL 인젝션을 통한 지뢰찾기 IAT 후킹 (Hooking) 구현 (4) | 2020.03.24 |