리버스 엔지니어링

리버싱 연습 1

Injel me 2020. 6. 30. 19:40
c++ 클래스를 간단하게 만들었다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
class Data
{
public:
    Data();
    ~Data();
    int & operator[] (int n)
    {
        return data;
    }
    void operator= (int data)
    {
        this->data = data;
    }
    const int initialized_num()
    {
        return my_init_num;
    }
public:
    static int init_num;//initalize-num;
private:
    int my_init_num;
    int data;
};
int Data::init_num = 0;//static member data set
int main()
{
    Data data[10];
    
    for (int i = 0; i < 10; i++)
    {
        data[i] = i + 1;
    }
    for (int i = 0; i < 10; i++)
    {
        printf("DATA class [%d:%p] : %d\n", data[i].initialized_num(), &data[i], data[i]);
    }
}
Data::Data()
{
    init_num++;
    my_init_num = init_num;
}
Data::~Data()
{
}
cs

Data라는 클래스는 static 변수로 init num 을 가지고 있고
멤버변수로 my init num, data를 가지고 있다.

생성될 때 static 변수의 값이 1증가, my init num은 static 변수의 현재 값을 갖게 된다.

가령
1
2
Data data;
data = 10;
cs
이런 식으로 한다면 data 멤버 변수의 my init num 은 1이고 data는 10이게 된다.

올리 디버거로 열었을 때 메인 코드는 어떤 형태를 하고 있을까.

엔트리 포인트까지 찾아가는 구간은 스킵했다.


메인이 시작되는 부분에 함수 프롤로그가 보인다. 메인도 함수기 때문에 프롤로그의 형태를 띄고 있는 것이 보인다.

중간 부분이 Data 멤버 변수 배열을 선언한 부분으로 보인다.

PUSH Data::Data는 확실히 잘 모르겠지만 Data 클래스의 생성자 함수 이름을 따라간 것을 보니 생성자를 호출하기 위한 주소인 것 같다.
생성자의 주소이거나

0A는 10진수로 10, 8은 8인데 10은 배열의 크기, 8은 클래스가 담고 있는 실제 클래스 크기인 것 같다.(int형 두 개)

클래스 내에서 static int 변수는 전역변수처럼 따로 메모리가 잡히기 때문에 클래스의 크기로 치지 않은 것 같다.

for문으로 data[i]번째의 data 멤버 변수에 i의 값을 대입하는 코드가 있는데,
희한하게도 반복문의 형태를 띄지 않고 주소에 10번 대입하게 한 것이 보인다.

컴파일러 최적화 옵션 때문인 것으로 추정하고 있다. 
반복문의 형태를 띄게 되면 주소를 레지스터에 넣고 레지스터를 증가, 레지스터가 가지고 있는 주소에 값을 대입해 가면서 반복문의 형태를 띄게 되는데, 직접 주소에 10번 대입하는게 코드는 길어져도 절차상 속도가 더 빠르기 때문에 컴파일러 최적화 옵션을 거치면 이렇게 변화하는 것 같기도 하다.

대입하는 부분에 한 줄 한 줄 내려갈 수록 [ESP + 숫자]에서 숫자가 8씩 일정하게 증가하는데,
이는 클래스 크기로 보인다.

그런데 출력하는 부분은 반대로 반복문의 형태를 띄게 어셈블리어가 구성되어있다.
출력하는 부분에서 my init num, data[i]의 주소, data[i]가 가지고 있는 데이터를 출력한다.

JNZ가 있는 곳에서 그 전에 비교하는 문인 CMP가 없는데,
SUB EDI, 1을 하고 JNZ로 점프하는 것을 보면 EDI가 0이 되기 전에는 반복하는 것으로 보인다.(EDI의 값을 비교한다)

EDI가 0이 될 때까지 출력과 EDI 값 감소를 통하여 반복한다.

그리고 위랑 반대되는 내용이 나오는데, 0A, 8을 대입하고, destructor이라고 써진 것을 보니 소멸자를 호출하는 부분인 것 같다.


클래스 스태틱 변수로 my init num이 설정되는 과정은
처음에 생성자가 호출되기 직전 local.22 주소에서(static 변수의 주소) 설정된 값인 1을 가져와서 EAX 레지스터에 넣고 EAX 레지스터를 push 해 주어 생성자를 호출하는 과정에서 사용하는 것 같다.


참고로 클래스의 static 변수는 public로, data와 my init num은 private로 되어 있는데 어셈블리어 상태로 보여있을 때(?) 주소로 나타나 져 있는 걸로는 private와 public 멤버 변수를 구별할 수 없는 것 같다.