3. 상황별 어셈블리 명령어
어셈블리 명령어에 대한 이해를 돕기 위해서 상황별로 어떻게 사용되는지 정리하도록 하자.
데이터 이동
어셈블리에서 데이터를 옮기는 방법은 MOV 명령어를 사용하면 된다.
MOV [복사될 곳], [읽어들일 곳]
MOV EAX, EBX의 경우 EBX 레지스터를 EAX에 대입하는 것이다.
MOV EAX, [EBX]로 표현되어 있는 경우에는 EBX가 가리키는 값을 EAX에 대입한다.
MOV EAX, [EBP + 10]의 경우 EBP의 주소에서 10만큼 증가한 주소지의 값을 EAX로 대입한다.
MOV EAX, [EBP – 10]의 경우 EBP의 주소에서 10만큼 감소한 주소지의 값을 EAX로 대입한다.
[EBP + 10]과 [EBP – 10]의 경우에 함수 내부에 존재하는 명령이었다면 EBP를 기준으로 10 증가된 경우에는 파라미터로 넘어오는 값일 수 있고, EBP를 기준으로 10 감소한 경우에는 함수 내부에서 쓰이는 지역변수 일 수 있다.
LEA EAX, [EBP + 10] 의 경우에는 EBP 주소에서 10만큼의 주소지를 더한 값을 의미하는 것이 아니라 EBP의 주소지 값에서 10을 더한 값을 EAX로 대입하게 된다.
전역변수
전역변수의 경우 data 섹션에 저장이 되고, 프로그램을 초기화하는 과정에서 세팅되거나 실행 중에 변경이 될 수도 있다. 예를 들어 문자열이 참조되는 방법을 보도록 하자.
.data:0088A1A1 ‘This Program is powerful’, 0
data 섹션의 문자열이 이렇게 존재하면, 어떻게 사용하는지 확인하자.
.text:006A2A22 mov eax, 88A1A1h
.text:006A2A27 retn
비교구문 cmp와 test
cmp 명령어는 주어지는 두 값을 뺄셈을 해서 처리하고, test 명령어는 주어지는 두 값을 더해서 처리하는 점이 다르며, 두 명령어가 가진 조건 분기문을 결정하기 위해서 사용된다는 점은 같다.
cmp eax, ebx는 두 값을 빼서 0이면 참(같은 값)이 되는 형태이다.
test 연산은 보통 호출된 함수들이 일반적으로 리턴 값을 EAX 레지스터에 저장하는 것을 이용하여, 다음과 같이 사용된다.
CALL my_function
TEST EAX, EAX
JZ 주소지
함수를 call하고, 함수의 리턴 값으로 EAX 값이 세팅되면, 비교를 하는 것이다.
CMP가 영향을 미치는 FLAG들은 ZF, OF, SF, CF이고, TEST가 영향을 미치는 FLAG는 SF, ZF, PF가 있다. 만약 ZF가 1로 세팅되었다면 주어진 두 값이 같았다는 것을 의미한다. 그 외에 같지 않았을 경우에는 OF, SF, CF를 이용해서 어느 쪽이 더 큰지를 알 수 있다.
분기문의 사용
분기문은 일반적으로 점프 구문을 이야기하며, if문을 어떻게 사용하는지에 따라서 달라지는 어셈블리 코드들을 살펴보겠다.
□ C언어 소스
if (변수 == 0) {
if문 내의 함수 호출
} 이후 동작
□ 어셈블리어 변환
mov eax, 변수값
test eax, eax
jnz 이후 동작
if문 내의 함수 호출
if문에서 사용되는 변수를 먼저 mov로 값을 대입하고, test로 비교를 한 다음에 JNZ를 이용하여 이후 동작을 할지 if문 내부의 함수를 호출할지를 결정한다.
JZ가 아닌, JNZ를 사용하여 반대의 조건을 뜻하는 N(not)이 추가되어서 처리되는 것을 알 수 있다. 이외에 if문은 if 다음에 else 구문이 여러 번 더 나타날 수 있다는 점을 고려해서 변형된 코드들도 비슷한 방식으로 분석하면 된다.
반복문의 사용
반복문의 경우에는 같은 내용이 반복되어 처리되다가 반복문을 종료하는 비교문을 만나서 점프하여 반복문을 벗어나도록 구현되어 있다.
loop:
mov al, [ecx]
mov [edx], al
inc ecx
inc edx
cmp al, ‘p’
jnz short loop
리버싱 분석 중에 이와 같은 코드를 만나면 inc와 cmp, jnz를 보고 대략 파악이 되고, jnz에서 다시 위쪽의 loop 레이블로 가게 되어있는 것이 정확히 반복문이라는 것을 이해하게 해준다.
ECX와 EDX는 메모리상의 문자열을 지정하는 포인터이고, ECX가 가리키는 문자열에서 EDX가 가리키는 문자열로 복사가 일어나고 있다는 것을 보면 알 수 있다. 반복을 할 때마다 포인터가 둘 다 1씩 증가되고, ‘p’ 문자열이 나타날 때까지 복사를 하는 것으로 생각할 수 있다.
함수의 사용
함수는 시작하는 부분과 끝나는 부분을 파악할 수 있는 구분자가 있어서 확인이 그리 어렵지 않다.
push ebp
mov ebp, esp
sub esp, 20
EBP 값은 스택에 넣어두고, 현재 ESP 값을 EBP에 대입하고, 지역변수를 만들 공간을 확보하기 위해서 ESP 값을 변경한다.
그리고 끝나는 부분은 호출이 끝나서 리턴되기 때문에 RET가 쓰이게 된다.
mov eax, -1
mov esp, ebp
pop ebp
ret
이와 같이 ESP는 원래 값으로 돌아가고, EBP는 저장되었던 값으로 복구된다. 그런 후에 RET를 이용하여 함수를 CALL했던 부분으로 다시 돌아간다. 그리고 eax에 -1을 넣어서 결과가 -1을 표현해주기도 한다.
'기본개념' 카테고리의 다른 글
[SystemV x86_64] repne scabs, calling convention (0) | 2020.01.21 |
---|---|
CTF 푸는방법 (0) | 2020.01.15 |
[ubuntu] 64비트에서 32비트 소스 컴파일시 에러 (0) | 2020.01.14 |
x86 Aseembly (0) | 2020.01.07 |
[ARM] ldr pre-index, post-index (0) | 2020.01.05 |