기본 콘텐츠로 건너뛰기

리버스엔지니어링바이블 - 03. C++ 클래스와 리버스 엔지니어링

C++에서는 멤버 변수, 함수 캡슐화와 상속등의 개념 때문에 리버싱하기가 어려워진다. 풍부한 코딩 경험과 다양한 분석 경험으로서의 감이 필요하다. 풍부한 코딩 경험은 독자의 몫이지만 감을 전수해주고자 한다.

최초로 등장하는 함수부터 디스어셈블 된다. 예를 들어 코드 상에서 상위에 클래스 선언과 이후에 클래스 함수의 선언이있을 경우 디스어셈블코드에서는 클래스 함수의 내용이 온다.

02장에서 말한바와 같이 클래스의 멤버를 사용할때는 ecx에 클래스이 포인터를 넣는다. ecx를 기준으로 오프셋을 더해 사용한다. 이것이 this콜이다.

클래스를 지역 변수로 선언하는 경우 스택변수 esp,ebp를 기준으로 접근하지만 전역 변수를 선언하는 경우 dword xxxx 와 같은 주소에 접근하여 사용한다. 전역 변수로 선언한 클래스의 경우 .data 섹션에 위치하는 것을 알 수 있다.

객체를 동적으로 생성해서 사용하는 경우 객체의 크기 만큼 스택을 확보하는 것이 아니라 new로 할당받은 메모리(heap 영역)를 저장하고, 해당 값을 불러와 오프셋을 더해 사용한다.

생성자와 소멸자의 경우 02장에서 말한바와 같이 메모리 할당 후에 cmp, jz와 같은 흐름을 통해 메모리 할당이 성공하면 생성자를 호출하고 아니면 0을 대입하는 형태를 띈다. 그리고 생성자의 리턴값(코드 상에서는 존재하지 않지만 실제로는 존재함)을 불러와 객체의 멤버접근에 사용하는 형태를 띈다.

디스어셈블코드상에서 여러 변수를 0으로 초기화하고 memset(), malloc()이  흐름이 나타난다면 90%이상 생성자라고 봐도 무방하다.

생성자의 경우 뒤부분에 위치하며, 메모리를 해제하거나 close 관련 api를 호출하는 형태를 띈다.

캡슐화의 경우 개발상에서는 구분가능하지만 디스어셈블에서는 접근시에 차이점이 없기 때문에 구분할 수 없다.

다형성의 경우(virtual 함수가 존재하는 클래스) 별도의 테이블이 생성된다. 그 테이블의 포인터가 객체의 가장 앞에 오게 된다. 따라서 생성자의 디스어셈블코드를 보면, 객체 앞의 4바이트에 특정값(.rdata)이 할당되는 것을 확인할 수 있다.

댓글

이 블로그의 인기 게시물

맥스 어만(Max Ehrmann) - 소망(진정 바라는 것)

진정 바라는 것                                                                       -맥스 어만 소란스럽고 바쁜 일상속에서도  침묵 안에 평화가 있다는 사실을 기억하십시오 포기하지 말고 가능한한 모든 사람들과 잘 지내도록 하십시오 조용하면서도 분명하게 진실을 말하고  어리석고 무지한 사람들의 말에도 귀를 기울이십시오  그들 역시 할 이야기가 있을테니까요  목소리가 크고 공격적인 사람들은 피하십시오  그들은 영혼을 괴롭힙니다 자신을 다른 사람들과 비교하면 자신이 하찮아 보이고  비참한 마음이 들수도 있습니다  더 위대하거나 더 못한 사람들은 언제나...