기계어는 0과 1로 이루어졌기에, 간단한 프로그램도 코드가 매우길고, 읽기도 힘들다.
코드의 길이를 줄이고, 사람이 읽기 편하게 "어셈블리어"를 만들었다.
👉 어셈블리어
기계어를 사람이 읽기 쉬운 단어와 1대 1로 대응 시킨 것
[ 예시 ] - 설명하기 위한 예시이므로 실제와 다름
기존 기계어 : 0100000000000000000010010 명령어 작성 - 데이터를 전송해라
어셈블리어 : mov 명령어 작성 - 0100000000000000000010010 명령을 실행해라
필요성
우리가 자주사용하는 프로그래밍 언어를, 더 잘 활용할 수 있다 ( 쌉고수 )
주의사항
CPU마다, 다른 언어를 사용한다!! ( 어셈블리어가 다름 )
👉 어셈블러
어셈블리어로 쓰여진 명령들을, 기계어 명령들로 바꿔주는 프로그램
어셈블리 실행파일 생성과정 ( 컴파일과 다름! )
[ 어셈블러 ]
1. test.asm ( 어셈블리 텍스트 ) 로 목적코드 test.obj 파일 생성!
[ 링커 ]
2. test.obj 파일 + 라이브러리 파일을 사용해, 최종 실행 파일을 만든다. ( test.exe .. )
( 컴파일과정에, 어셈블러 작업이 포함되어 있다 )
컴파일과, 어셈블(?)의 차이점
고급언어 프로그램
1. 문법 검사
2. 목적 코드 생성 (obj)
3. 한문장을, 여러개의 기계코드로 변환
어셈블리어 프로그램
1. 기계코드와 바로 대응
👉 어셈블리어 종류
AT&T
UNIX 에서 사용하기 위해 만든 어셈블리어
Intel (x86)
Window 에서 사용하기 위해 만든 어셈블리어
Macro 어셈블리어 , Turbo 어셈블리어
MIPS
👉 레지스터란? ( 일반 == 범용 )
CPU에 들어있는 아주 작은 메모리
- 모든 데이터가 거쳐간다. ( 모든 계산시, 레지스터를 사용해서 데이터를 저장한다 )
( 어셈블리어로, 레지스터를 직접 관리 할 수 있다! )
EAX (AX, AH, AL) (Accumulate)
- 실행할 함수를 저장할 때 사용 ( 시스템 콜 - 아래 설명 있음 )
EBX (BX, BH, BL) (Base)
- 특정 주소를 저장할 때 사용 ( 포인터 )
ECX (CX, CH, CL) (Count)
- 숫자를 1씩 셀때 사용, ( 반복 명령 수행 )
EDX (DX, DHL, DL) (Data)
- 데이터를 저장할 때 사용, ( 입출력 연산에서, 간접 주소 지정 )
EBP (BP) (BasePointer)
- 스택 데이터에 접근하기 위한 주소를 저장? ( 포인터 )
ESP (SP) (StackPointer)
- 현재 스택의 주소를 저장? ( 포인터, 스택 최상위 오프셋 )
ESI (SI) (SouceIndex)
- 읽기 인덱스 ( 어디서 부터 읽을 것인지 - 파일 Read )
- 문자열 전송과 비교에 사용 ( 주로 소스 문자열의 오프셋 )
EDI (DI) (Destination)
- 쓰기 인덱스 ( 어디서 부터 쓸것인지 - 파일 Write )
EIP (Instruction Pointer)
- 명령어 포인터, 실행할 다음 명령어의 주소를 담고 있다!
EFLAGS
- CPU 동작 제어 or CPU 연산 결과 적용
추가 설명
e (a,b,c,d) x 이 4가지 레지스터는 구조가 비슷하다.
- EAX = Extended AX
EAX > AX (16bit) > AH, AL ( High/Low )
일반 명령어
데이터 저장 ( 변수 ) - Intel 기준
# 오른쪽에서 왼쪽으로 복사
# eax 레지스터에 1을 저장
mov eax, 1
# ebx 레지스터에 저장된 값을, eax 레지스터에 저장(복사)
mov eax, ebx
# eax 레지스터에 저장된 값을, 0040200 메모리 주소에 저장(복사)
mov 0040200, eax
CPU별 어셈블리어 비교
# eax -> ebx 데이터 복사
# AT&T
movl %eax, %ebx
# Intel - AT&T와 반대 순서
mov ebx, eax
# Apple Silicon - 레지스터 이름이 다르다!!
# x16 - eax, x0 - ebx
mov x0, x16
MacOS 어셈블리 프로그래밍
호출 규약 이란? ( Calling Convention )
함수 실행 규칙을 적어놓은 파일
( 어떻게 실행할래? - 어셈블리 명령어도 결국 함수 )
# MacOS 호출 규약 syscall.h 파일 위치
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h
호출 규약 확인하기
cat 경로/syscall.h
Hello World 프로그램 작성
// hello.s - 어셈블리 파일 생성
// [ 활용할 함수(syscall) & 레지스터 )
// write() - 파일 쓰기, syscall 번호 0x4
// exit() - 프로그램 종료, syscall 번호 0x1
// x16(eax) - 연산용 레지스터
// x0(ebx) - 주소 저장용 레지스터
// mov - 값 저장 (복사)
// adr - 변수 할당, 주소 저장
// svc - OS kernal에게 실행 요청
.global _start // _start 프로그램 진입점 지정, 추후 linker에게 주소 전달
.align 2 // Align the code to 2 bytes
// 문자열 출력하기 위한 변수 설정
_start: mov x0, #1 // 1 = 표준출력(stdout) 저장
adr x1, msg // 문자열 저장할 포인터
mov x2, #13 // 문자열 길이 저장
mov x16, #4 // 4 = 쓰기 시스템 호출 (#4)
svc #0x80 // 시스템 호출 (OS kernel) - 실행해줘.
mov x0, #0 // 0 = 반환 값 (exit code)
mov x16, #1 // 1 = 종료 시스템 호출 (#1)
svc #0x80 // 시스템 호출 (OS kernel)
// msg 변수에 ascii 문자열 주소 저장
msg: .asciz "Hello World!\n"
어셈블리 빌드
# 목적 파일 만들기
as -arch arm64 -o hello hello.s
# 실행 파일 만들기!
ld -arch arm64 -e _start -o hello hello.o -lSystem -syslibroot `xcrun --show-sdk-path --sdk macosx`
# -arch arm64 : arm64 architecture 지정
# -o hello : 출력 파일 이름
# -e _start : 프로그램 진입점! (작성한 _start)
# -lSystem : 시스템 라이브러리 링크하기 (필수아님)
# -syslibroot `xcrun --show-sdk-path --sdk macosx` : libSystem.dylib 경로 설정
실행 파일 실행
# arm64 환경에서 실행
file HelloWorld
# shell에서 실행
./HelloWorld
실행파일 어셈블리로 되돌리기 (디스어셈블)
# Mac에 기본적으로 설치되어 있는 objdump 이용
objdump -D HelloWorld
Shell Script로 디스어셈블
#!/bin/bash
# Mach-O 파일에서 shellcode 추출하기 (C 형식으로 출력)
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <shellcode.macho:shellcode.o>"
exit 1
fi
for c in $(objdump -d $1 | grep -oE '[0-9a-f]{2} ' | tr -d ' '); do
echo -n "\x$c"
done
echo
# shell script
# #!/bin/bash : bash shell로 실행
# if [[ $# -ne 1 ]]; then # 인자개수가($#) 1이 아니면 (not equal)
# echo "Usage: $0 <shellcode.macho:shellcode.o>" # 사용법 출력
# exit 1 # 종료
# fi # if문 끝
# for c in $(objdump -d $1 | grep -oE '[0-9a-f]{2} ' | tr -d ' '); do # objdump로 디스어셈블한 결과에서 16진수 2자리씩 추출
# echo -n "\x$c" # 16진수 2자리를 8진수로 출력
# done # for문 끝
# echo # 개행
4칸(주소) / 1칸(어셈블리어) / 2칸(인자) 단위로 끊어서 보면 된다
Reference
Apple Silicon macOS에서 ARM64 assembly programming
누가 macOS에서 어셈블리 프로그래밍을 할까? 하겠냐만.. 그냥 심심해서 "이게 되네?"를 해보고자 테스트. 우선 실험환경은 Apple Silicon 버전의 일명 M1 macmini 이고 macOS Monterey(12.5) 운영체제 환경이다
cpuu.postype.com
어셈블리어 CheetSheet
https://www.bencode.net/blob/nasmcheatsheet.pdf