티스토리 뷰
<실습 환경>
가상 환경 : VMware Workstation 10
OX : Linux-Ubuntu 16.04.02-64bit
Setting : sudo apt-get install vim
sudo apt-get install nasm
<형식>
입력 : 사용자로부터 N 입력 받기 ex)>N
출력 : 입력 받은 값의 N단 출력
multi2.asm 코드 전체
<코드 분석>
buffer : 입력 받은 값 저장할 변수
res : 곱 결과를 저장할 변수
increase : 입력 값에 곱할 증가 값을 저장할 변수
출력할 문자들을 초기화해서 저장
section .bss -> 초기화하지 않은 전역변수 선언 영역
section .data -> 초기화한 전역변수 선언 영역
데이터 지시어
section .text -> 실행할 코드가 저장되는 영역
extern -> C의 표준 라이브러리 함수를 사용하기 위해서
실행의 흐름대로 코드를 분석해 보겠다.
main 부분
C언어의 main 함수 부분이다. 실제 코드 실행의 시작부분이며 이 함수가 끝나면 프로그램이 종료된다.
mov rsi, buffer 레지스터 rsi에 buffer 주소 값을 넣어주는 부분이다. 이러면 rsi -> buffer가 됨
mov rdi, fmt 마찬가지로 rdi에 fmt("%d"가 저장되어 있음) 주소 값을 넣어주는 부분이다.
위의 명령어들은 scanf 함수를 콜하고 거기에 인자로 전달하기 위함이다.
call scanf 위에서 extrun한 scanf를 호출한다. C에서의 기능과 똑같이 사용자의 입력을 받는 함수로, 호출되면 사용자의 입력을 받기 위해 프롬프트가 깜빡인다.
C언어로 바꾸면 scanf("%d", &buffer)가 호출된 것이다. 입력을 하면 buffer에 입력한 값이 저장된다.
call multi & call exit 작성한 multi 함수를 호출하고 라이브러리 함수 exit을 호출해 정삭적으로 종료한다.
multi 부분
레지스터 rax와 rbx를 사용하기 위해 먼저 xor 연산을 통해 0으로 초기화한 다음 inc 명령어로 +1씩 해줘 1로 만들어준다.
rax는 곱 연산의 결과를 저장할 거고, rbx는 증가 값을 저장하는데 사용할 것이다.
mov rcx, 9 카운터 레지스터인 rcx에 9를 넣어준다. (9번 반복하겠다는 의미)
cmp rbx, rcx 그런 다음 비교 명령어 cmp로 rbx와 rcx를 비교한다. (내부적으로 마이너스 연산을 하게 된다. Jg와 함께 쓰면 > 효과)
만약 rbx > rcx 이면 다음 분기 명령어인 jg를 실행해 _end로 분기하고, 같지 않다면 jg를 무시하고 그 다음 명령어를 실행한다.
mov rax, rbx rax에 rbx 값을 넣어준다. 후에 mul 명령어를 처리하기 위함.
mov rdi, [buffer] rdi에 buffer 값을 넣어주는 명령어로, 이 역시 mul 명령어로 곱 연산을 하기 위해 레지스터에 옮겨 담는 작업이다. 여기서 buffer에는 main에서 scanf로 입력한 값이 저장되어 있다. 주의해야 할 점은 []로 묶지 않으면 buffer의 주소 값이 들어가게 된다. 실제 buffer가 가리키고 있는 값을 넣어주려면 []로 묶어줘야 한다.(어셈블러 nasm의 특성) C의 포인터 연산을 생각하면 됨.
mul rdi rax -> rax * rdi를 실행하라는 명령어이다. mul은 mul 다음에 오는 레지스터와 rax를 곱 연산한 값을 rax가 다시 가리키게 된다. 위의 코드에서는 64bit로 작성했기 때문에 rax와 연산하는 것이다.
위에서 rax에 rbx를 넣고, rdi에 buffer 값을 넣은 이유가 rax -> rax * rdi를 rax -> rbx * buffer 처럼 하고 싶었기 때문이다.
mov [increase], rbx & mov [res], rax 로 각각 레지스터 값을 변수에 옮겨 담는 이유는 레지스터 값을 기억(저장)해 놓기 위해서다. 레지스터는 후에 계속해서 사용이 되고, 원치않게 여기 저기서 불려 값이 변할 수 있기 때문에 기억하고 싶은 값은 변수에 담아 놓는 것이 좋다. 위의 값들은 구구단 한 줄을 출력할 때 사용할 것이다.
여기서도 주의해야 할 점은 변수를 []로 감싸야 변수가 담고 있는 실제 값이 변경된다. 그렇지 않으면 메모리 주소를 변경하는 코드가 되어버림
연산을 할 떄 변수로 직접 하지 않고 레지스터로 하는 이유
CPU의 구성 요소 중 ALU에서 연산을 하게 된다. 하지만 ALU는 메모리(메인 메모리)에 직접 접근을 할 수 없고 오로지 레지스터를 이용해서 접근을 해야 한다. 때문에 레지스터로 변수가 위치한 메모리에 접근을 해 레지스터에 값을 저장하고 이 레지스터로 연산을 수행하는 것이다. 매우 비효울적으로 보이지만, 실제로는 레지스터로 연산하는 것이 매우 빠르다. 이러한 이유 때문에 mov 명렁어로 변수의 값을 레지스터에 담고 연산을 레지스터로 하는 것이다.
call print_line 은 구구단의 한 줄을 출력하기 위해 만든 함수다. 이 함수를 호출하면 [buffer(input)]*[increase]=[res]\n
가 출력된다.
inc rbx & jmp _loop rbx를 1 증가시키고 다시 반복의 첫 부분으로 돌아간다.
여기까지가 multi의 반복문이다.
print_line 부분
이 함수는
[buffer(input)]*[increase]=[res]\n
를 문자 하나하나 출력하는 함수다. ( ;로 끊어서 읽으면 된다)
위에서 언급 했듯이 rdi는 argument 1 rsi는 argument 2다. 여기 역시 bss에 선언했던 변수들은 []로 묶어주고 data에 선언한 변수는 그냥 입력해주면 된다.
<결과>
'Security' 카테고리의 다른 글
ELF 파일 구조 (2) | 2019.05.15 |
---|---|
Easy CrackMe 리버싱 (1) | 2019.05.08 |
RTL (Return to Libc) (0) | 2019.02.05 |
plt got (1) | 2019.02.03 |
리버싱 실습(Easy_ELF) (0) | 2019.01.24 |
- Total
- Today
- Yesterday
- 쉽게 배우는 운영체제
- 파이썬 for Beginner 솔루션
- Spring Boot
- 패킷 스위칭
- JPA
- Do it! 정직하게 코딩하며 배우는 딥러닝 입문
- Spring
- Gradle
- jsp
- spring mvc
- 스프링 mvc
- 운영체제 반효경
- Computer_Networking_A_Top-Down_Approach
- 선형 회귀
- 프로그래머스
- 지옥에서 온 git
- Thymeleaf
- 김영환
- Python Cookbook
- 생활코딩 javascript
- 스프링 컨테이너
- Spring Data JPA
- git branch
- 스프링 테스트
- 방명록 프로젝트
- 파이썬 for Beginner 연습문제
- git merge
- 쉘 코드
- 스프링
- git
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |