티스토리 뷰

안녕하세요!

 

지난 시간에 이어서 오늘은 어셈블리어 명령어 중 lea, and, or, xor 명령어에 대해서 알아보겠습니다.

 

LEA (Load Effective Address) : 유효 주소 로드

# 출발지는 r/m32

# 목적지는 레지스터

# 레지스터 연산 결과를 레지스터에 저장(not 포인터)

 

MOV와 무엇이 다른가??

# MOV는 값을 로드한다.            mov eax, [ebx+ecx]

# LEA는 유효 주소를 로드한다.     lea  eax, [ebx+ecx]

 

lea 명령어를 사용해서 프로그램을 짜보겠습니다.

 

 

gdb를 사용하여 분석을 하기전에 이 프로그램이 어떻게 동작할지 미리 고민을 해보시기 바랍니다.

 

mov연산을 실행하고 나서 eax에는 1, ebx에는 4, ecx에는 7이 저장될 것입니다.

 

위에서 설명했듯이 lea 명령어에서 대괄호는 유효 주소를 로드합니다. 따라서 대괄호 안의 주소를 가져와 복사를 합니다.

 

lea    eax, [eax+ecx] -> eax+ecx의 연산결과가 eax에 저장된다.

 

eax는 1이 저장되어있고, ecx에는 7이 저장되어 있으므로 8이 eax에 저장이 될 것입니다.

 

lea    ebx, [ebx*4] -> ebx*4의 연산결과가 ebx에 저장된다.

ebx에는 4가 저장되어 있으므로 16이 ebx에 저장될 것입니다.

 

mov    eax, [eax+ecx] -> eax+ecx의 연산결과인 주소가 가리키는 값을 가져와 eax에 저장한다.

 

eax+ecx의 연산 결과인 주소 15가 가리키는 값을 가져와 eax에 저장을 할 것입니다.

 

주소 15가 가리키는 곳에 어떤 값이 저장되어 있는지는 아직 모르기 때문에 gdb로 확인을 해보겠습니다.

 

 

자 gdb로 프로그램을 열어보면 처음에 직접 짠 코드와 달라진 점이 있다는 것을 확인할 수 있습니다.

 

이것은 어셈블러가 자동으로 변환해준다는 것이라고 알고 계시면 됩니다.

 

우리가 궁금한 부분은 lea 명령어이므로 lea 명령어 부분에 break point를 걸고 확인해보겠습니다.

 

 

main에서 15만큼 offset이 떨어진 곳에 break point를 걸고 프로그램을 실행시킨 후에 레지스터를 확인해보니 mov 명령어를 통해 각각의 레지스터에 값이 정상적으로 들어가 있는 것을 확인할 수 있었습니다.

 

이제 명령어를 하나씩 실행시켜보면서 레지스터 값의 변화를 분석해보겠습니다.

 

 

0x0804806f의 lea 명령어를 실행한 후에 예상했듯이 eax에는 8이 저장되어있음을 확인할 수 있습니다.

 

 

0x08048072의 lea 명령어를 실행한 후에는 예상했듯이 ebx에는 16이 저장되어있음을 확인할 수 있습니다.

 

마지막 mov 명령어를 실행하면 Segmentation fault가 발생합니다.

 

그 이유는 mov 명령어에서 대괄호는 연산의 결과가 가리키는 주소의 값을 가져와 복사를 하는데, [eax+ecx]가 가리키는 주소에 접근할 수가 없다는 것을 확인할 수 있습니다.

 

따라서 접근할 수 없는 주소에 접근하려고 해서 Segmentation fault가 발생하는 것입니다.

 

다음에는 논리 연산을 보겠습니다.

 

AND : 두 비트가 모두 1일 때만 1

# 목적지는 r/m32 또는 레지스터

# 출발지는 r/m32 또는 레지스터 또는 상수값

# OF, CF, SF, ZF, PF

 

OR : 두 비트 중 하나라도 1이면 1

# 목적지는 r/m32 또는 레지스터

# 출발지는 r/m32 또는 레지스터 또는 상수값

# OF, CF, SF, ZF, PF

 

XOR : 두 비트가 다를 때만 1

# 목적지는 r/m32 또는 레지스터

# 출발지는 r/m32 또는 레지스터 또는 상수값

# OF, CF, SF, ZF, PF

# XOR은 리버싱에서 유용하게 쓰임.

 

프로그램을 직접 구현하여 분석을 해보겠습니다.

 

 

자 상수값들을 모두 2진수로 변환을 해보겠습니다.

 

0x33 -> 0011 0011

0x55 -> 0101 0101

0x42 -> 0100 0010

0xac -> 1010 1100

 

gdb로 프로그램을 분석해보기 전에 이 프로그램이 어떻게 동작할지 고민해보도록 하겠습니다.

 

mov 연산들을 통해 각각의 레지스터에 상수값들이 들어갈 것입니다.

 

and 연산을 보면 eax와 ebx를 and 연산을 한 결과 값을 eax에 저장합니다.

 

eax에는 0x33, 즉 00110011 이 저장되어 있습니다.

ebx에는 9x55, 즉 01010101 이 저장되어 있습니다.

 

 00110011

and  01010101

--------------------

      00010001

 

결과 값이 00010001가 되고, 즉  17이 eax에 저장될 것입니다.

 

or 연산을 보면 eax와 ecx를 or 연산을 한 결과 값을 eax에 저장합니다.

 

eax에는 00010001 이 저장되어 있고, ecx에는 01000010 이 저장되어 있습니다.

 

 00010001

or    01000010

---------------------

01010011

 

결과 값이 01010011 가 되고, 즉 83이 eax에 저장될 것입니다.

 

xor 연산을 보면 eax와 0xac를 xor 연산을 한 결과 값을 eax에 저장합니다.

 

eax에는 01010011 이 저장되어 있고, 0xac는 10101100 이므로 xor 연산을 해보면

 

 01010011

xor  10101100

---------------------

11111111

 

결과 값이 11111111 가 되고, 즉 255가 eax에 저장될 것입니다.

 

예측한 값들이 정확한지 gdb를 사용하여 프로그램을 분석해보겠습니다.

 

 

우리가 궁금한 부분은 논리연산이므로 main에서 offset이 15만큼 떨어진 곳에 break point를 걸고 확인해보겠습니다.

 

 

mov 연산을 마치고 and 연산을 수행하기 직전의 레지스터들의 정보입니다.

 

eax, ecx, ebx에 상수값들이 정상적으로 들어갔음을 확인할 수 있습니다.

 

 

and 연산의 결과로 17이 eax에 저장됨을 확인할 수 있습니다.

 

 

or 연산의 결과로 83이 eax에 저장됨을 확인할 수 있습니다.

 

 

xor 연산의 결과로 255가 eax에 저장됨을 확인할 수 있습니다.

 

지금까지 lea, and, or, xor 명령어들을 알아보았습니다.

 

다음시간에는 어셈블리어 inc, dec, jcc, call 명령어들을 알아보겠습니다!

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함