본문 바로가기
CTF/dreamhack(pwnable)

어셈블리어

by kimboan 2024. 3. 24.

* 어셈블리어 목적파일 생성 후 링킹작업으로 실행 프로그램 만들기

helloworld를 출력하는 어셈블리어 코드

nasm -f elf64 -o helloworld.o helloworld.s

  • -f elf64 : ELF64로 파일 형식 지정
  • -o helloworld.o : 출력 파일의 이름 지정
  • helloworld.s : 어셈블리 코드 파일의 이름

-> helloworld.s 파일을 읽어와서 64비트 elf형식의 오브젝트 파일 'helloworld.o'로 어셈블링

ld -o helloworld helloworld.o

  • ld : GNU Linker를 실행하는 명령어
  • 링커는 여러 오브젝트 파일을 결합하여 실행파일을 생성하는 역할

-> 링커를 사용하여 elf형식의 오브젝트 파일을 실행 파일로 링크하는 명령어

* x64 syscall 표

https://rninche01.tistory.com/entry/Linux-system-call-table-정리x86-x64

rax : 특정한 함수가 끝날 때 그 반환값을 갖는 레지스터

-> rax는 함수 실행결과가 담김.

* 어셈블리어로 에코 프로그램 만들기

echo.s 코드 작성

section .text

global _start

_start:

xor rax, rax #rax 값 0으로 초기화 -> system call 0인 sys_read를 불러옴

mov rbx, rax #rbx 값 0으로 초기화

mov rcx, rax #rcx 값 0으로 초기화

mov rdx, rax #rdx 값 0으로 초기화

sub rsp, 64 #RSP에서 64를 빼주는 것은 64만큼 공간을 확보하겠다는 의미

mov rdi, 0 #read니깐 fd를 0으로 초기화

mov rsi, rsp #rsi는 출발지 주소를 넣는 레지스터, rsp부터

mov rdx, 63 #63만큼 문자를 입력 받겠다.

syscall #맨 처음 rax를 0으로 초기화했으니, syscall 표를 보면 0은 read

mov rax, 1 #syscall 1은 write

mov rdi, 1 #write니깐 fd를 1로 초기화

mov rsi, rsp #rsp부터

mov rdx, 63 #63까지 출력해라.

syscall #rax를 1로 초기화했으므로, write를 불러옴

mov rax, 60 #rax 60은 syscall표에서 exit / 즉 프로그램 종료를 의미

syscall # 프로그램 exit

nasm -f elf64 -o echo.o echo.s

-> echo.s 어셈블리어 코드로부터 echo.o 목적파일 생성

ld -o echo echo.o

-> echo.o 목적파일로부터 echo 실행파일 링킹

* 어셈블리어 기본 문법

MOV : A의 값을 B의 값으로 옮긴다.

MOV EAX, 100 : EAX에 100이라는 값을 넣는다. / 다만 구체적인 연산을 포함할 수 없다.

LEA : A의 값을 B의 값으로 연산을 포함하여 복사한다.

LEA, EAX, [EAX + 1000] : EAX에 1000을 넣은 값을 다시 EAX에 삽입한다. 이처럼 연산을 포함할 수 있다.

JMP : 특정한 윙치로 건너 뛰어 코드를 실행한다.

JMP A : A의 위치로 뛰어서 코드가 실행 된다. 비슷하게 조건 점프 명령도 존재한다. JA, JB, JE 등의 명령어는 두 인자를 받아서 비교한 뒤에 결과에 따라서 다른 방향으로 점프할 수 있다.

CALL : 함수를 호출했다가 다시 원래 위치로 돌아올 때 사용한다. JMP와 다른 점은 실행한 뒤 끝나게 되면 RET에 저장하고 다시 원래 상태로 돌아온다는 점이다.

NOP : 아무 것도 하지 않는 명령어, 1Byte의 빈 공간을 차지한다.

RET : 현재 함수가 끝난 뒤에 돌아갈 주소를 지정하기 위해 사용

PUSH : 스택에 해당 값을 넣는다.

POP : 스택에 있는 값을 빼낸다.

LEAVE : 현재까지의 메모리 스택을 비우고 RBP를 자신을 호출한 메모리 주소로 채운다. 실행 중인 함수를 종료하기 위해 정리하는 작업에 사용된다.

* 어셈블리어로 반복문 구현

loop.s 코드 작성

section .data

msg db "A" # 문자열 'A'를 담은 변수 msg 선언

section .text

global _start #start 함수 정의

_start:

mov rax, 1 # syscall 1은 write

mov rdi, 1 # fd를 1로 설정 / 어떠한 것을 출력하겠다는 의미

mov rsi, msg # 'A'가 담긴 변수 msg의 시작 주소를 rsi에 담음(rsi는 출발지 주소!)

mov rdx, 1 # rsi시작 주소부터 한 글자 가져옴 / 'A' 문자열 하나만 가져오겠다는 의미

mov r10, 1 # r10 레지스터의 값을 1로 설정(r10은 잘 사용하지 않는 레지스터라 가져와서 사용)

again:

cmp r10, 100 # r10이 100인지 비교해서

je done # 100이라면 done이라는 함수로 이동

syscall # 100이 아니라면 syscall을 불러서 A문자 출력

mov rax, 1 # 다시 rax에 1을 넣어서 출력할 수 있도록 만들어 줌

inc r10 # r10을 1증가 -> r10이 100이 될 수 있도록 만들어 줌

jmp again # 다시 again 함수 실행

done:

mov rax, 60 # syscall 60은 exit

mov rdi, 0 # rdi 0으로 초기화

syscall # exit

 

nasm -f elf64 -o loop.o loop.s

-> loop.s 어셈블리어 코드로부터 loop.o 목적파일 생성

ld -o loop loop.o

-> loop.o 목적파일로부터 loop 실행파일 링킹

* 디버깅 strace 명령어

strace -ifx ./echo # echo 파일 strace 명령으로 실행

read상태로 사용자 입력 대기 중

입력값으로 'A' 5개 넣어줌

버퍼값인 63바이트 중 'A' 5개와 \n 까지 6바이트를 입력 받은 것을 확인할 수 있고, 뒤에 남은 버퍼(57바이트)는 null값으로 초기화되어 있는 것을 확인할 수 있음.

'CTF > dreamhack(pwnable)' 카테고리의 다른 글

[Dreamhack] shell_basic  (1) 2024.03.24