어셈블리 Quine

6037 단어 x86-64quineassembly
이 글은 Yahoo! JAPAN 18 졸업생 2Advent Calendar 2018 11일째 되는 글이다.
지난번에는 @walk8243씨가 쓴 "CaaS 비싸잖아!IaS죠!"입니다.
다음은 @teakun씨의 Scratch로 시작하여 어른을 위한 어린이 프로그래밍 입문에 의거합니다.

개시하다


Linux에서 x86 복습64 그룹의 Quine을 소개합니다.Quine는 코드와 같은 문자열을 출력하는 프로그램이지만 테스트가 쉽고 언어와 환경을 이해하기 위해 유연하게 활용할 수 있다고 생각합니다.
※ 본 기사에 기재된 구성요소는 Gas입니다.

코드


quine.S
mov $m, %rsi
mov $280, %rdx
mov $1, %rdi
mov $1, %rax
syscall
push $34
mov %rsp, %rsi
mov $1, %rdx
mov $1, %rax
syscall
mov $m, %rsi
mov $280, %rdx
mov $1, %rax
syscall
push $0x00000a22
mov %rsp, %rsi
mov $2, %rdx
mov $1, %rax
syscall
xor %rdi, %rdi
mov $60, %rax
syscall
m:.ascii"mov $m, %rsi
mov $280, %rdx
mov $1, %rdi
mov $1, %rax
syscall
push $34
mov %rsp, %rsi
mov $1, %rdx
mov $1, %rax
syscall
mov $m, %rsi
mov $280, %rdx
mov $1, %rax
syscall
push $0x00000a22
mov %rsp, %rsi
mov $2, %rdx
mov $1, %rax
syscall
xor %rdi, %rdi
mov $60, %rax
syscall
m:.ascii"

해설


프로그램 개요


위조 코드로 쓰면 매우 간단한 구성이다.
定数mを出力する
"を出力する
定数mを出力する
"\nを出力する
終了する
mを宣言する(宣言時にmは""で囲われている)

구성 요소


절차의 개관이 결정되면 나머지는 구성 요소만 이해하면 자연히 완성된다.

레지스터


사용하는 레지스터는 rax,rdi,rsi,rdx이다.
레지스터
사용법rax시스템 호출 번호를 지정한 상태syscall에서 시스템 호출을 실행하고 해당하는 시스템 호출 번호의 명령을 실행한다.rdi시스템 호출 명령의 첫 번째 인자를 지정합니다.rsi시스템 호출 명령의 두 번째 인자를 지정합니다.rdx시스템 호출 명령의 세 번째 인자를 지정합니다.

syscall

syscall 시스템 호출의 형식을 확인합니다.
$ man 2 syscall # 以下抜粋
SYNOPSIS
       #define _GNU_SOURCE        /* or _BSD_SOURCE or _SVID_SOURCE */
       #include <unistd.h>
       #include <sys/syscall.h>   /* For SYS_xxx definitions */

       int syscall(int number, ...);
시스템 호출 번호를 매개 변수에 건네주다.시스템 호출은 다음 형식으로 정의됩니다.
sys/syscall.h
#define SYS__xxx __NR__xxx
즉 xxx의 시스템 호출 번호를 얻기 위해 상수선언__NR__xxx을 볼 수 있다는 것이다.그러나 선언을 하나하나 보는 것도 귀찮아 출력시스템 호출의 이름과 번호의 매핑ausyscall 명령을 사용해 조사하기로 했다.
$ man ausyscall # 以下抜粋
DESCRIPTION
ausyscall is a program that prints out the mapping from syscall name to number and reverse for the given arch.

write

syscall에서 실행된 시스템 호출 번호입니다.
$ ausyscall write --exact
1
1.write도 시스템 호출의 형식을 확인한다.
$ man 2 write # 以下抜粋
SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
첫 번째 인자, 파일 설명자, 두 번째 인자, 문자열 버퍼, 세 번째 인자에 크기를 전달합니다.파일 설명자가 표준 출력을 선택하고 삽입1하십시오.또한 스택을 제외하고 문자열을 지정하는 .ascii 지시를 통해 간단하게 설명할 수 있습니다.
여기까지 오면 텍스트 출력이 간단해요.
hello.S
mov $m, %rsi
mov $13, %rdx
mov $1, %rdi
mov $1, %rax
syscall
m:.ascii "Hello World!\n"

_exit

syscall에서 실행된 시스템 호출 번호입니다.
$ ausyscall exit --exact
60
60._exit도 시스템 호출의 형식을 확인한다.
$ man 2 exit # 以下抜粋
SYNOPSIS
       #include <unistd.h>

       void _exit(int status);
상태 코드를 첫 번째 매개 변수로 전달합니다.케이스에 명령이 정상적으로 끝났다는 정보를 전달하기 위해 미리 넣는다0.
_exit 시스템 호출 segmentation fault (core dumped) 을 호출하면 더 이상 발생하지 않습니다.
exit.S
mov $0, %rdi
mov $60, %rax
syscall

ASCII


.ascii 디렉터리는 쓸 수 없습니다. "\n 는 즉석 값으로 써야 합니다.
$ man ascii # 以下抜粋
 Oct   Dec   Hex   Char      
 ---------------------------------------
 012   10    0A    LF  '\n' (new line)

 042   34    22    "  

실행 결과


다음 실행 환경에서 동작 확인이 이루어졌습니다.
$ uname -a
Linux instance-1 2.6.32-754.6.3.el6.x86_64 #1 SMP Tue Oct 9 17:27:49 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23)
확인 절차와 실행 결과는 차이가 없다.
$ gcc -nostdlib quine.S -o quine
$ diff quine.S <(./quine)
$ file quine
quine: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

후기


초심으로 돌아가 가급적 인터넷에서 찾지 말고man 실제 조작을 시도해 보자.평소에 어셈블리를 쓰고 시스템 호출을 호출할 기회는 많지 않지만 고급 언어로 기술할 때 내부에서 어떻게 작동하는지 관심을 가지는 것도 중요하다.

참고 문헌

  • https://ja.wikibooks.org/wiki/X86%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9/GAS%E3%81%A7%E3%81%AE%E6%96%87%E6%B3%95
  • https://www.mztn.org/lxasm64/amd04.html

  • 이 글은 창의 공통 모델 4.0 국제 허가증 표시의 아래에서 제공한 것이다.

    좋은 웹페이지 즐겨찾기