티스토리 뷰

최종 수정: 2014-06-22


안녕하세요. Hackability 입니다.


이번 포스팅은 리눅스에서 기초적인 쉘 코드를 만들어 봄으로써 쉘 코드가 어떻게 만들어 지고 동작될 수 있는지 보도록 하겠습니다.


제가 테스트할 환경과 사용할 프로그램은 다음과 같습니다.

- Linux Backtrack 5 R2 (Ubuntu)

- nasm

- objdump

- gcc


* 앞으로 쉘 코드는 어셈블리 언어로 작성을 할텐데 따로 공부하실 필요 없이 작성 하시면서 보셔도 이해하는데 무리가 없을 것 같습니다.


먼저 다음과 같은 파일을 하나 만들어 줍니다. 


02_001.asm

1
2
3
4
5
6
7
[SECTION .text]
BITS 32
 
mov eax, 1
mov ebx, 0
 
int 0x80


각 라인의 의미는 다음과 같습니다. 


[Line 1]: 현재 내용이 코드 세그먼트임을 명시합니다. 일반적인 프로그램의 경우, 프로그래머가 만든 프로그램의 소스에 대한 코드들은 기계어로 변환되어 코드 세그먼트에 저장되고 프로그램은 코드 세그먼트를 읽어 프로그램을 실행 시킵니다.


[Line 2]: 현재 제가 사용하는 아키텍쳐가 32-bit 아키텍쳐 임을 명시 합니다.


[Line 4]: mov 명령은 대입 명령으로써, eax 라는 레지스터에 1을 넣으라는 의미 입니다.


[Line 5]: 위와 마찬가지로 ebx라는 레지스터에 0을 넣으라는 의미입니다.


[Line 7]: int 는 인터럽트의 준말로 int 0x80 을 하게 되면 시스템 콜 호출을 의미합니다. 정확하진 않지만 이해를 돕기 위하면 함수 호출을 알리는 명령이라고 생각하시면 됩니다. 앞으로 호출을 위해서는 계속 int 0x80 을 사용할 것입니다.


각 라인에 대한 내용은 다음과 같은데 저것만 봐서는 "그래서 이게 뭔데?" 라고 생각이 들 수 있습니다. 전체 내용을 이해하기 위해서는 먼저 int 0x80 명령부터 시작합니다.


int 0x80은 위에서 설명했듯이 어떤 함수의 호출을 알리는 신호 입니다. 이 명령은 eax에 저장된 함수를 호출합니다. 위에서는 eax에 1을 넣었는데 그러면 어떤 함수가 호출 되는 것 일까요?


시스템 콜 번호는 다음 링크를 참고해주세요. [LINK]



위 내용을 보면 eax가 1일 때는 sys_exit 함수 임을 알 수 있으며 함수에 인자를 하나 받고 있는데 해당 인자는 ebx에서 가져옴을 알 수 있습니다. 따라서 우리가 만든 02_001.asm 파일을 c 언어로 작성하면 다음과 같은 역할을 함을 알 수 있습니다.


1
2
3
4
void main()
{
    exit(0);
}


위에서 만든 어셈블리 코드를 nasm 을 이용하여 컴파일하고 objdump를 통해 오브젝트 파일을 확인 합니다. 해당 명령어는 다음과 같습니다.


#nasm -f elf 02_001.asm  (이 명령을 통해 02_001.o 파일이 생성)

#objdump -d 02_001.o



마지막에 텍스트 섹션을 보면 우리가 만든 mov eax, 1; mov ebx, 0; int 0x80 이 16진수로 표현됨을 알 수 있습니다. 이렇게 만들어진 16진수를 모으면 쉘 코드가 됩니다.


우리가 만든 쉘 코드를 테스트하기 위해서는 몇 가지 작업이 필요 합니다. 먼저 쉘 코드를 동작시키기 위한 c 파일을 하나 작성 합니다.


shell_test.c

1
2
3
4
5
6
7
8
char shell_code[] = "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80";
 
int main(int argc, char **argv)
{
        int *ret;
        ret = (int *)&ret + 2;
        (*ret) = (int)shell_code;
}


main에 뭔가 내용이 이상한 행동을 하는데 저 내용의 의미는 프로그램의 흐름을 shell_code로 옮기겠다고만 이해하시면 됩니다. 중요한 것은 우리가 만든 코드들을 shell_code안에 위와 같이 넣어 주시면 됩니다. objdump에서 나온 hex 값을 순서대로 넣어 주시면 됩니다. (앞으로 위 파일은 계속 사용되고 shell_code의 내용만 달라지게 됩니다.)


다음에 작성된 c 코드를 gcc 를 이용하여 컴파일 합니다.

#gcc shell_test.c -o shell_test



결과는? 아무것도 안뜨고 다음 프롬프트가 껌뻑 껌뻑 거립니다. 사실 우리가 만든 내용은 단순히 exit(0); 이기 때문에 아무것도 안뜨는게 정상적인 결과 입니다.


여기까지 쉘 코드를 만들기 위해 exit(0); 행위를 하는 asm 파일을 만들어 nasm으로 컴파일 하였고, nasm에서 나온 오브젝트 파일 (.o)을 objdump 로 덤프 하여 어셈블리 코드를 기계어로 변환하여 테스트 프로그램에 넣어 동작을 시켜 봣습니다.


이제 점점더 멋진 쉘 코드를 만들기 위한 준비가 된 것 같습니다. :P

댓글
댓글쓰기 폼