프로그래밍을 하다 보면, 특히나 리눅스 환경에서 프로그래밍을 하다 보면 쉘 스크립트(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에서 실행되면, 쉘이 나타나게 되는 것이죠. 저러한 코드를 쉘 코드라고 부릅니다.
요 녀석은 일반적인 개발에서는 사용할 일이 거의 없는데, 해킹을 할 때에는 필수적인 녀석 중 하나입니다.
해커들이 보통 하는짓들이 프로그램의 취약점을 찾아내서, 의도치 않은 쉘을 띄우게 하는 것이기 때문이죠. 이 때, 쉘을 띄우게 하기 위해서 저 쉘코드를 주입한 뒤, 프로그램 카운터를 쉘코드의 시작 주소로 이동시켜서 쉘을 실행시킵니다.
아무쪼록 쉘코드와 쉘스크립트의 차이점에 대해 잘 이해가 되었기를 기원하며 이만 글을 줄입니다.
'팁 & 기타' 카테고리의 다른 글
64비트 32비트 CPU와 운영체제 에 대하여 (3) | 2019.09.27 |
---|---|
Ubuntu 18.04에서 iptime A2000UA Wireless-LAN 드라이버 잡기(삽질기) (0) | 2019.06.27 |
프린터 스풀러 아키텍쳐(Print Spooler Architecture) (0) | 2019.01.07 |
윈도우즈 프린팅 관련 문서 번역(Introduction to Printing) (0) | 2019.01.07 |
간편한 한글 기반 일본어 입력기 (6) | 2018.06.05 |