Self-Improvement

x64dbg 사용법 및 리버싱 기초 분석 정리 ( x64dbg, 전역변수, 지역변수, 함수호출, 비교문) 본문

리버싱 기초

x64dbg 사용법 및 리버싱 기초 분석 정리 ( x64dbg, 전역변수, 지역변수, 함수호출, 비교문)

JoGeun 2020. 4. 29. 14:33

소스코드1

1
2
3
4
5
6
7
#include <stdio.h>
 
int main()
{
    printf("hellow world!!");
    return 0;
}
cs

 

Visual Studio로 컴파일을 해준뒤 x96dbg로 열어주면 hellow world를 출력하는 main과 printf를 찾지 못하며 이상한? 곳에서 시작을한다. 이는 컴파일러가 기본적인 구성을 확인하기 위한 절차로 컴파일한 프로그램, 버전 등에 상이하게 되어있다.

 

위의 소스코드의 어셈블리어를 볼라면 Main을 찾아 BP을 걸어준다.

그 다음 F9를 눌러 BP를 설정한 곳까지 진행을 해준 뒤 F7로 진입해준다.

 

진입을 하면 드디어 우리가 찾는 소스코드의 어셈블리어를 볼 수 있다. 여기서는 F8로 한 라인씩 진행을 해줘도 된다.

 

해당 어셈블리 코드에서 스택공간 확보, Visual Studio 특성의 어셈블리어를 제외한 소스코드는 아래와 같다.

 

처음인 Push F97C20은 메모리에서 보면 hello world!! 문자가 존재하게 되며 스택으로 Push을 해준다.

 

Call F91046은 printf 함수를 호출하며 그 전에 Push한 hello world!!를 인자로 가져가게 되어 이루어지는 구조이다.

Call F91046 다음에 BP를 설정하면 hello world!!가 출력되는 것을 확인할 수 있다.

 


소스코드 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int sum(int a, int b)
{
    int c = 0;
    c = a + b;
    return c;
}
 
int main()
{
    int a = 3;
    int b = 2;
    printf("sum result : % d\n", sum(a, b));
    return 0;
}
cs

 

이번 소스코드는 지역변수 선언과 sum() 함수 호출까지 되어있다. 역시나 x96dbg로 열어준 후 main으로 가서 어셈블리어를 확인해본다.

 

역시나 불필요한 부분은 제거하고 중요한 부분만 보면 아래와 같다.

 

맨 처음 "mov dword ptr ss:[ebp-8],3"은 지역변수를 뜻하면 EBP-8 주소의 값에 4바이트 만큼 3을 할당하라는 뜻이다.

지역변수 a=3과 같은 것이다.

스택 : EBP-8 주소의 값에 3 할당

 

"mov dword ptr ss:[ebp-14],2"는 위와 비슷하게 EBP-14(14는 hex 값이며 10진수로는 20이다) 주소의 값에 4바이트 만큼 2를 할당하라는 뜻이다. 즉 지역변수 b=2와 같다.

스택 :&nbsp;EBP-14 주소의 값에 2 할당

 

"mov eax, dword ptr ss:[ebp-14]"는 ebp-14에 저장된 2를 eax에 할당하는 것이며 "push eax"를 통해 스택에 저장한다.

"mov ecx, dword ptr ss:[ebp-8]"는 ebp-8에 저장된 3을 ecx에 할당하며 "push ecx"를 통해 스택에 저장한다.

 

그 다음 "call BF1393"으로 어떠한 함수를 호출하고 있으며 그 함수는 sum(int, int)로 되어있다. 이 부분에서도 F7을 통해 함수 내부로 들어가 본다.

 

sum(int, int)의 어셈블리어는 아래와 같다.

 

여기서도 중요한 부분 부터 분석을 해본다.

"mov dword ptr ss:[ebp-8],0"으로 ebp-8 위치 값에 0을 할당하고 있다. 즉 sub(int, int) 함수의 지역변수 c=0을 해준것과 같다.

"mov eax, dword ptr ss:[ebp+8]"을 보면 이번에는 ebp+8을 가르키고 있다.

 

그 전에 리버싱의 call과 return에 대해 로직을 알고 있다면 ebp+8은 call 하기전의 스택에 넣었던 3이 위치해 있을 것이며 ebp+C에는 2가 위치해 있다. 

 

그러함으로 "add eax, dword ptr ss:[ebp+c]"까지 진행한다면 eax에는 3과 2가 합쳐진 5가 있을 것이다. 

 

그 다음엔 "mov dword ptr ss:[ebp-8], eax"와 "mov eax, dword ptr ss:[ebp-8]"으로 eax를 ebp-8 위치에 저장하며 다시 ebp-8의 값을 eax에 저장하고 sub(int, int)함수가 끝난다. eax는 리턴하는 값을 저장하는 용도로도 사용된다.

 

sum(int, int)에서 나오게 되면 "add esp,8"로 esp 값에 8을 더해준다.

"push eax"로 eax 값 5를 스택에 넣어준다.

 

 

그리고 "push BF7C20"은 메모리를 보면 "sum result : %d"가 들어있으며 이것을 스택에 넣어준다.

 

"call BF1046"으로 printf()함수를 호출하며 스택에 넣은 5, sum result : %d를 인자로 가져가게 되며 출력이 된다.

 

 


 

소스코드 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
int global = 10;
 
void globalTest() {
    global += 5;
    printf("함수에서 전역 변수 : %d\n", global);
 
}
 
int main() {
    int result = 12;
    printf("전역변수 : %d\n", global);
    printf("지역변수 : %d\n", result);
 
    globalTest();
 
    return 0;
}
cs

 

이번에는 전역변수 global를 추가해서 봐본다. 

역시나 x96dbg로 열어준 뒤 main으로 가서 어셈블리어를 봐본다.

 

하나씩 차례대로 분석해 나간다.

"mov dword ptr ss:[ebp-8],c"는 앞서 봐왔듯이 지역변수로 ebp-8 주소 값에 hex값인 C(10진수로는 12)을 넣어준다.

즉 지역변수 a=12이다

스택

"mov eax, dword ptr ds:[<int global>]"에서 ss가 아닌 ds 같은 경우는 데이터 부분을 가르키는 뜻으로 전역변수를 말하기도 한다. 오른쪽 마우스에서 "덤프에서 따라가기"를 눌르면 해당 데이터 주소로 가게되며 4바이트 값인 A(10진수로는 10)이 eax에 저장된다.

즉 전역변수 b=10, global=10 이다

 

"push eax"로 eax의 값 A를 스택에 넣어준다

"push B37B50"으로 메모리 데이터 부분을 보게되면 COFCBFAABAAFBCF6203A2025640A0000으로 ASCII로 보면 알 수 없는 형태이다.

 

이 hex값을 한글로 변환한다면 "전역변수 : %d"로 나오게 된다.

 

"call B31046"은 prinft()함수를 호출하는 것임으로 즉 push한 2개의 인자 A,전역변수 : %d를 출력하게 되는 것이다.

 

그 다음 형태도 비슷하게 되어있다 ebp-8은 C로 eax에 넣어준 뒤 printf()함수 호출에 C, 지역변수 : %d 인자를 주고있다.

 

결과적으론 "지역변수 : 12"가 출력이 된다.

 

call 을통해 globalTest(void) 함수를 호출하고 있습니다. F7을 통해 어떠한 작업을 하는지 알아 보도록한다.

 

globalTest(void)함수의 어셈블리어이며 중요한 코드로 분석을 이어나간다.

 

"mov eax,dword ptr ds:[<int global>]"은 메모리에서 데이터 값 A를 가져와 eax에 넣어주는 것으로 전역변수를 말한다.

 

"add eax,5"을 통해 eax에 들어있는 A에 5를 더해주는 것이다. 그러면 eax는 F가 될 것이다.

 

"mov dword ptr ds:[<int global>], eax"와 "mov eax, dword ptr ds:[<int global>]"을 통해 eax 값인 F를 원래 A 값이였던 메모리 데이터에 넣어주고 다시 eax에 할당해준다.

 

그 다음 eax 값인 F를 스택에 넣어주고 "push B37B30"의 값 데이터 또한 스택에 넣어준 뒤 printf() 함수를 call로 호출하게 된다.

 

 

* 잘못된 점이나 이상한게 있으면 지적해주세요.