티스토리 뷰

728x90
반응형

<실습 환경>

가상 환경 :    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 -> 초기화한 전역변수 선언 영역

데이터 지시어

출처  : https://opentutorials.org/module/1596/9762

 

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 선언한 변수는 그냥 입력해주면 된다.

 

<결과>

 

728x90
반응형

'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
댓글