프로그래밍을 하다 보면, 특히나 리눅스 환경에서 프로그래밍을 하다 보면 쉘 스크립트(Shell script)라는 단어를 들을 일이 있는데, 비슷한 단어인 쉘 코드(shell code)라는 단어와 햇갈릴 수 있습니다.

 

이 두 용어는 확실히 다른 단어로 혼용하시면 안됩니다.

 

간단하게 말하자면

 

쉘 스크립트쉘에서 사용되는 명령어를 나열한 실행가능한 텍스트파일이며,

쉘 코드쉘을 실행시키는 코드입니다.

 

실제 제가 사용하는 쉘 스크립트를 예를 들어 설명해보도록 하겠습니다.

 

리눅스 시스템에는 가장 범용적인 커멘드라인 쉘인 bash가 있습니다.

사용자는 이 bash 쉘에 명령어를 입력해서 무언가 하고싶은 동작들을 할 수 있습니다.

 

이 리눅스의 사용자인 저는, 리눅스 환경에서 알고리즘 문제를 풀려고, 문제별로 디렉토리와 소스코드 파일, 및 입력 테스트 케이스 파일들을 생성해서 잘 정리해 놓습니다.

 

그런데 매 문제를 만들 때 마다 디렉토리 구조를 만들고, 하는 것들이 항상 번거로워서 이를 자동화 하려고 합니다.

 

따라서 쉘 스크립트 파일 mk.sh 를 만들고, 이를 이용해서 디렉토리를 쉽게 만들려고 합니다.

 

아래에 첨부된 쉘 스크립트에 실행 권한을 준 뒤 다음 명령어를 입력하면 디렉토리가 생성됩니다.

 

./mk.sh dir main.cpp 3

위와 같이 인자를 주게 되면 다음과 같은 디렉토리 구조가 생성되게 됩니다.

> dir
-----> bin
-----> src
----------> main.cpp
-----> input1.txt
-----> input2.txt
-----> input3.txt

쉘 스크립트의 첫번째 인자는 문제 이름을 나타내는 디렉토리 명이며, 두번째 인자는 소스코드의 명입니다. 세번째 인자는 input.txt 파일의 개수가 됩니다.

 

mk.sh 쉘 스크립트의 내용은 다음과 같습니다.

#!/bin/bash

dir_path=$1
src_fname=$2
num_args=$#


if [ "$num_args" -ge 2 ]; then
    echo "mkdir -p `pwd`/$dir_path/src"
    mkdir -p `pwd`/$dir_path/src
    echo "touch `pwd`/$dir_path/src/$src_fname"
    if [ `echo $src_fname | cut -d"." -f2` == "cpp" ]; then
        echo -e "#include <bits/stdc++.h>\nusing namespace std;\n\nint main() {\n\treturn 0;\n}\n" > `pwd`/$dir_path/src/$src_fname
    else
        touch `pwd`/$dir_path/src/$src_fname
    fi
    if [ "$num_args" -eq 3 ]; then
        num_input=$3
        
        i=1
        while [ $i -le $num_input ]
        do
            echo "touch `pwd`/$dir_path/input.$i.txt"
            touch `pwd`/$dir_path/input.$i.txt
            i=`expr $i + 1`
        done

    else
        echo "touch `pwd`/$dir_path/input.1.txt"
        touch `pwd`/$dir_path/input.1.txt    
    fi

else
    echo "Usage: $0 [directory path] [source file name with extension] (number of input file. optional)"
    echo "Supported file extension: cpp, py, asm"
    echo "사용법: $0 [디렉토리 경로] [확장자 포함 소스파일 이름] (입력 파일 개수. 옵션)"
    echo "지원하는 확장자: cpp, py, asm"
    

fi

 

위와 같이 쉘 스크립트를 텍스트 파일로 작성한 뒤, 실행을 하면 스크립트 안에 있는 내용들이 bash 쉘에서 순차적으로 실행되어 결과를 내게 됩니다.

 

이와 같이 쉘 스크립트쉘에서 동작하는 명령어들을 한꺼번에 텍스트 파일로 모아서, 여러번 사용가능하게 만들어 놓은 것이 쉘 스크립트입니다.

 

 

이와는 다르게 쉘 코드쉘을 실행시키는 코드입니다.

 

가령 Python으로 작성을 한다면 다음과 같은 코드가 쉘 코드가 되겠죠

import os

os.system("/bin/bash")

아니면 다음과 같은 방식으로도 생성할 수 있습니다.

쉘 코드 생성을 처음 공부할 때에는 많이 사용하는 방법 중 하나입니다.

 

다음과 같은 C언어 코드를 작성합니다.

#include <stdlib.h>
int main()
{
	char *str[2];
	str[0] = "/bin/bash";
	str[1] = 0;	
	execve(str[0], str, 0);
}

 

위 녀석을 컴파일하여 기계어로 만든 뒤, 독립적으로 실행되어도 쉘이 떨어지도록 정제를 합니다.

이 부분에 디테일한 부분은 생략하도록 하고, 좀더 내용을 보고싶으면 참고한 해커스쿨 쉘코드 강좌 링크를 눌러보세요.

 

오래된 자료라서 지금과는 많이 환경이 맞지 않는 부분이 있을 수는 있으나, 개념적인 부분은 도움이 많이 될 것입니다.

 

과정은 생략하고, 결과적으로는 

\xeb\x15\x31\xc0\x5b\x89\x43\x07\x89\x1e\x89\x46\x04\xb0\x0b\x31\xe4\x8d\x0e \x31\xd2\xcd\x80\xe8\xe6\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68

와 같은 기계어 코드를 얻을 수 있습니다.

 

이 기계어 코드가 CPU에서 실행되면, 쉘이 나타나게 되는 것이죠. 저러한 코드를 쉘 코드라고 부릅니다.

 

요 녀석은 일반적인 개발에서는 사용할 일이 거의 없는데, 해킹을 할 때에는 필수적인 녀석 중 하나입니다.

 

해커들이 보통 하는짓들이 프로그램의 취약점을 찾아내서, 의도치 않은 쉘을 띄우게 하는 것이기 때문이죠. 이 때, 쉘을 띄우게 하기 위해서 저 쉘코드를 주입한 뒤, 프로그램 카운터를 쉘코드의 시작 주소로 이동시켜서 쉘을 실행시킵니다.

 

아무쪼록 쉘코드와 쉘스크립트의 차이점에 대해 잘 이해가 되었기를 기원하며 이만 글을 줄입니다.

+ Recent posts