본문 바로가기

High Level Technique/Reversing

C++ 분석

C++ 분석


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
#include <iostream>
#include <Windows.h>
#include <tchar.h>
 
class Employee
{
public:
    int number;
    char name[128];
    long pay;
    void ShowData();
    void Test();
};
 
void Employee::ShowData()
{
    printf("Number: %d\n", number);
    printf("Name: %s\n", name);
    printf("pay: %d\n", pay);
    Test();
 
    return;
}
 
void Employee::Test()
{
    printf("Test Function\n");
    return;
}
 
int main(int argc, char* argv[])
{
    Employee k3y6reak;
    printf("Size: %x\n"sizeof(Employee));
 
    k3y6reak.number = 0x4855;
    strcpy(k3y6reak.name, "키브레이크");
 
    k3y6reak.ShowData();
 
    return 0;
}
cs



일단 위와 같은 소스코드를 작성합니다. 이때 릴리즈 모드, 최적화 옵션은 제거합니다.




이 부분이 main 함수의 시작입니다. 


E416C3 주소를 보면 PUSH 88을 하고 있습니다. printf()에서 인자로 "size: %x\n"과 sizeof(Employee)가 있으므로 2번째 인자인 sizeof(Employee)가 0x88이 됩니다.


E416D6 주소에서 MOV DWORD PTR SS:[EBP-8C], 4855 로 되어있습니다. 소스코드에서는 k3y6reak.number에 0x4855가 들어갑니다. 즉 k3y6reak.number의 주소가 EBP-8C라는 것이죠.


그 다음 구간은 strcpy 구간이고 CALL ShowData 부분이 나타납니다.





ShowData 부분입니다. 


C++에서 객체는 ECX의 값에 주소를 넣는다고 했습니다.  




E41634에서 MOV DWORD PTR SS:[EBP-=4], ECX 명령어가 나타나는데, 해당 부분의 ECX 값이 19F80C 입니다. 해당 값을 EBP-4인 19F800에 넣습니다.

그리고 그 값을 EAX에 넣고 이제 그 값을 ECX에 넣고 printf로 출력합니다.


이 부분에는 main에서 저장한 0x4855 값을 가져와 출력합니다.



두번째 printf 부분은 E41648 부분부터 시작합니다.



E4164B 에서 EBP-4의 값을 EDX에 저장합니다. 해당 값은 19F80C로 객체 주소를 나타냅니다. 그리고 ADD EDX, 4를 통해서 주소를 +4 합니다.

그리고 printf를 합니다. +4를 한다는 것을 통해서 다음 변수에 참조한다는 것을 알 수 있습니다. ECX에 객체의 주소를 담고있으니 EDX의 값에 다음 멤버변수가 들어간다고 생각할 수 있습니다.


마찬가지로 다음 printf도 같은 원리로 작동합니다.




그리고 다음으로는 Test Function이 나타납니다.



이 부분은 별다른 기능이 없으니 넘어가도록 하겠습니다.







객체를 동적할당 해체하는 경우



이번에는 객체를 new로 동적할당하고 delete로 해체하는 부분에 대해서 알아보도록 하겠습니다.


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
#include <iostream>
#include <Windows.h>
#include <tchar.h>
 
class Employee
{
public:
    int number;
    char name[128];
    long pay;
    void ShowData();
    void Test();
};
 
void Employee::ShowData()
{
    printf("Number: %d\n", number);
    printf("Name: %s\n", name);
    printf("pay: %d\n", pay);
    Test();
 
    return;
}
 
void Employee::Test()
{
    printf("Test Function\n");
    return;
}
 
int main(int argc, char* argv[])
{
    Employee *pk3y6reak;
    pk3y6reak = new Employee;
 
    pk3y6reak->number = 0x4855;
    strcpy(pk3y6reak->name, "키브레이크");
    pk3y6reak->pay = 0x100;
 
    pk3y6reak->ShowData();
 
    delete pk3y6reak;
 
    return 0;
}
cs



위 어셈코드가 main 함수 부분입니다.


10E16BB에서 CALL하는 부분이 나타나는데 이 부분이 new로 동적할당을 하는 부분입니다. 그런데 그전에 PUSH 88을 통해서 스택에 값을 쌓아두는데 이 값은 앞서 살펴본 것처럼 클래스의 크기입니다.



CALL을 통해서 반환된 값인 EAX의 값이 곧 Employee *pk3y6reak에 해당하는 pk3y6reak 포인터입니다.


객체를 동적할당으로 사용하는 경우에는 스택에 값을 쌓아서 사용을 하는 것이 아니라 new로 메모리를 할당해서 받은 heap 공간에 넣어서 사용됩니다.


스택을 사용하게 되는 경우에는 mov [ebp+4], 100과 같은 형식으로 스택에 값을 넣게되지만 동적할당으로 사용되면 heap 영역을 사용하기 때문에 mov dword [edx+84], 100과 같이 사용됩니다.








생성자와 소멸자


이번에는 생성자와 소멸자 부분에 대해서 분석해 보겠습니다.


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
53
54
55
56
57
#include <iostream>
#include <Windows.h>
#include <tchar.h>
 
class Employee
{
public:
    int number;
    char name[128];
    long pay;
    Employee();
    ~Employee();
    void ShowData();
    void Test();
};
 
Employee::Employee()
{
    printf("constructor\n");
}
 
Employee::~Employee()
{
    printf("Desstructor\n");
}
 
void Employee::ShowData()
{
    printf("Number: %d\n", number);
    printf("Name: %s\n", name);
    printf("pay: %d\n", pay);
    Test();
 
    return;
}
 
void Employee::Test()
{
    printf("Test Function\n");
    return;
}
 
int main(int argc, char* argv[])
{
    Employee *pk3y6reak;
    pk3y6reak = new Employee;
 
    pk3y6reak->number = 0x4855;
    strcpy(pk3y6reak->name, "키브레이크");
    pk3y6reak->pay = 0x100;
 
    pk3y6reak->ShowData();
 
    delete pk3y6reak;
 
    return 0;
}
cs




위 어셈코드가 main 함수 입니다.




위 어셈코드는 생성자 부분입니다.


해당 부분을 넘어가게되면 어셈코드가 해석되지 않은 채 진행됩니다.







위 코드는 소멸자 부분입니다.



'High Level Technique > Reversing' 카테고리의 다른 글

Obfuscation (난독화)  (0) 2016.09.07
Native API  (0) 2016.08.24
Debug Blocker  (0) 2016.08.10
PE Image Switching  (2) 2016.08.10
Self Creation Debugging  (0) 2016.08.09