잘못된 내용이 있을 수 있습니다! 오류가 있다면 말씀해주세요!
그리고 스택 주소값은 FFFFFFFC + 4를 하면 당연히 FFFFFFF0이 아닙니다. (자리올림수가 발생하니까) 그러나 일단 넘어갑시다.
코드설명은 필요 없을것이다.
1~3행 : 잘 모르겠다... 아시는분은 알려주시길
4~6행 : $SG7386은 "Hello, World"라는 문자열의 임시 이름?이다.
8행 : 함수의 이름을 알려준다.
9~10행 : 함수의 프롤로그
11행 : $SG7386이라는 "Hello, World!"라는 문자열의 주소값을 스택에 넣는다.
12행 : printf함수 호출
13행 : 이제 $SG7386값은 필요가 없으니 스택에서 빼준다. 이때 난 왜 add인지 궁금했는데, 스택의 구조를 보니 알겠다.
현재 스택은 이런 상태인데, (좀 잘못되긴 했고, 주소값은 임의로 했지만 일단 넘어가자) 스택의 최상단을 가리키는 레지스터의 값에 4를 더해주어(x64는 8을 더해준다) esp레지스터가 가리키는 값을 FFFFFFF8로 바꾼다. 이를 통해 필요 없어진 $SG7386의 값을 스택에서 빼내준다.
14행 : 리턴값을 저장하는 eax레지스터끼리 xor연산을 하면, 0이 된다. 이 값을 eax에 저장한다. 즉 mov eax, 0과 같은데, 이렇게 하는 이유는 XOR은 2바이트짜리 명령어이고, MOV는 5바이트이기 때문이라고 한다.
결론 : 리턴값 0을 eax레지스터에 저장한다.
15~16행 : 함수의 에필로그. 리턴값 0을 함수 호출자에게 리턴한다.
공부하는 김에 출력을 많이 해봤다.
이번에는 \n과 \t를 넣으면 무언가 다를게 있을까 싶어서 넣어보았다.
5~6행 : 문자열 끝에 개행문자를 출력했더니, 무언가 바뀌었다. 원래는 문자열 뒤에 00H라는 값만 있었는데, 0aH라는 값이 앞에 등장했다. \n을 적어준 I like C Language에서도 0aH가 동일하게 나타나는것을 보니 개행문자를 나타내나보다.
근데 ORG $+2는 뭔지 잘 모르겠다. $SG7386은 "Hello, World"문자열의 임시 이름?이다.
7~8행 : "Hello World\n"과 마찬가지로 뒤에 추가로 0aH가 붙었다. $SG7387은 "I like C Language"의 임시 이름이다.
9~10행 : "I like C#"은 $SG7388이라는 임시 이름을 가지고 있다. 일부러 \n과 구별하려고 한번 탭 하는 \t를 여기서는 썼는데, 역시 값은 0aH가 아닌 09H라는 값이다. 아마 이 위치에 있는 값들이 \n, \t와 같은 값들을 나타내는것 같다.
12행 : 실행될 함수의 이름은 main이다.
13~14행 : 함수의 프롤로그이다.
15행 : "Hello, World"라는 문자열을 가리키는 주소값을 스택에 넣는다.
현재 스택 상태 : (물론 주소값은 난 알지 못한다.
16행 : printf함수 호출하여 "Hello World\n"출력
17행 : esp(FFFFFFF4)에 4를 터해서 $SG7386의 값을 스택에서 없앤다. 근데... 여기서 좀 어려운게 그럼 스택의 esp는 현재 FFFFFFF8이겠지? 그럼 다음 18행에서 $SG7387의 값을 스택에 넣는데 그러면 FFFFFFF8에다가 저장되나? 아니면 다시 sub esp, 4해서 FFFFFFF4라는 값에 저장되나? 근데 아마 sub esp, 4라는 명령어가 없었으니 FFFFFFF8에 저장될것 같다.
18행 : "I like C Language\n"라는 문자열의 주소를 스택에 저장한다.
현재 스택 상태 :
19행 : "I like C Language\n"출력
20행 : esp레지스터에 4를 더해서 $SG7387이라는 값을 스택에서 빼낸다.
현재 스택 상태 : 맨 위의 값은 esp레지스터가 가리키는 값이다.
21행 : "I like C#\t"라는 문자열이 저장된 주소값을 스택에 저장한다.
현재 스택 상황 :
22행 : "I lkie C#\t"가 출력된다.
23행 : esp레지스터에 4를 더해서 $SG7388의 값을 스택에서 뺀다.
현재 스택 상황 :
24행 : eax레지스터에 리턴값인 0을 대입한다.
25~26 : 함수의 에필로그
Hello, World를 출력하는 프로그램을 리눅스에서 gcc 컴파일러로 컴파일한 파일을 IDA로 열어보았다
C로 짠 소스코드는 C소스코드1 과 동일하다.
여기에는 행 숫자가 안 적혀있는데, push rbp를 1행으로 하겠다.
1~2행 : 함수의 프롤로그
3행 : rdi에 format의 주소값을 넣는다. 여기서 format은 "Hello, World!"라는 문자열일것이다. (lea는 format의 주소값을 rdi에 저장하므로, format은 주소값이 아닌 문자열이다.)
4행 : eax에 0을 넣는다. 이건 왜하는지 모르겠다. msvc에서는 없었는데, 아마 gcc로 컴파일할떄 printf는 리턴값으로 0을 리턴하나?
5행 : "Hello, World!를 출력한다.
6행 : eax에 리턴값 0을 저장한다.
7~8행 : 함수의 에필로그, 8행은 리턴값 0을 함수 호출자로 리턴한다.
동일한 코드를 gef로도 돌려봤다.
<main+1> ~ <main+4> : 함수의 프롤로그이다.
<main+11> : rdi에 [rip+0x9f]라는 값을 저장한다. 아마 이 주소값은 문자열이 저장되어 있는 주소를 가리킬 것 같다. 즉 rdi에는 문자열이 저장되어있는 주소값이 들어있다.
나머지는 다 동일해서 일단 설명을 생략한다.
<main+11>의 예상이 맞는지 확인하기 위해서 <main+11>까지 실행한 후에, info reg rdi명령어로 rdi에 저장된 값을 알아보았다. 0x5555554006f4라는 값이 저장되어있다. 다시 그 주소에 저장되어있을 문자열이 값을 알아보기위해 x/s 0x5555554006f4 (0x5555554006f4에 저장되어있는 값을 문자열 형태로 보여준다) 명령어로 알아보았다.
역시 그 주소에는 "Hello, World!"라는 문자열이 들어있었다.
위에서 3개의 문자열을 출력하는 것도 파일을 gcc로 컴파일한 후 나온 파일을 IDA로 분석해보았다.
여기서도 첫번째 행은 push rbp를 기준으로 하겠다.
1~2행 : 함수 프롤로그
3행 : rdi레지스터에 s라는 값의 주소를 저장한다. 아마 s는 "Hello, World"일것이다. (물론 친절한 IDA가 옆에서 다 알려주긴 한다만..)
4행 : "Hello, World"를 출력한다. 그런데 궁금한 점이 생겼다. 분명 나는 puts함수를 사용하지 않고 printf를 사용했는데 왜 IDA는 puts함수를 호출한다고 한 걸까? 또한 밑의 "I like C#\t"를 출력할때는 printf라는데, 위의 \n이 있는 문자열은 다 puts에다가 mov eax, 0도 없다.. 뭐지..
그리고 puts함수를 호출한 후에는 분명히 이제 필요없는 문자열 상수의 주소값을 스택에서 빼주어야 할것 같은데 여기에는 add esp, 4가 없다. 나중에 알게 되면 적어둬야지
5행 : rdi에 aILikeCLanguage라는 값("I like C Language")가 저장되어있는 주소값을 넣는다.
6행 : "I like C Language"를 출력한다.
7행 : 음.. 모르겠다.
8행 : rdi에 format("I like C#\t")라는 값의 주소값을 넣는다.
9행 : printf 함수를 호출해서 "I like C#\t"라는 문자열을 출력한다. 대체 왜 얘만 printf함수라고 제대로 인식했을까? 추측해 보자면 이 문자열만 \n개행문자를 사용하지 않았다는 것이다. 그래서 그런 것 같다. (답을 아시는 분은 알려주시길..)
10행 : eax레지스터에 64h라는 값을 넣는다. 아마 h가 16진수를 나타낼테니, 16 * 6 + 4 = 100일것이다. C코드를 보아도 맞다. 즉 eax레지스터에 리턴값 100을 넣는다.
11~12행 : 함수 에필로그
같은 파일을 gdb(gef)로도 분석해보았다.
<main+0> ~ <main +1> : 함수의 프롤로그이다.
<main +4> rdi레지스터에 0x555555400744라는 데이터의 주소값을 저장한다. 이 후에 바로 puts(분명 난 printf를 호출하는 코드를 짰는데 왜 puts인지 모르겠다.)가 호출되는것으로 보아하니, 0x555555400744는 아마 puts의 인자인 문자열 상수일것이다. 이를 확인하기 위해 두가지 방법을 데이터를 확인했다.
1. rdi레지스터에 저장된 주소가 가리키는 데이터 값 조회하기
<main+4>까지 실행 후 info reg rdi 명령어로 rdi레지스터에 저장된 주소값을 알아낸다.
rdi레지스터에는 0x555555400744라는 주소가 들어있었고, x/s 0x555555400744명령어로 이 주소에 있는 데이터 값을 문자열 형태로 조회했다. 그러자 역시 예상했던 puts 함수의 인자인 "Hello, World"가 들어있었다.
물론 gef에서 스택에 있는 데이터중 0x555555400744값의 데이터를 찾아서 봐도 무방하다.
나머지도 같은 방식으로 작동되었다.